wmp: Add IWMPControls stub implementation.
[wine.git] / dlls / msi / action.c
blob71e45e8967a383dc9cdf5e0957ea1f9d1598ace1
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, SCRIPT_NONE);
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, UINT script)
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, script );
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 if (script == SCRIPT_ROLLBACK)
1551 for (i = package->script_actions_count[script]; i > 0; i--)
1553 rc = ACTION_PerformAction(package, package->script_actions[script][i-1], script);
1554 if (rc != ERROR_SUCCESS)
1556 ERR("Execution of script %i halted; action %s returned %u\n",
1557 script, debugstr_w(package->script_actions[script][i-1]), rc);
1558 break;
1562 else
1564 for (i = 0; i < package->script_actions_count[script]; i++)
1566 rc = ACTION_PerformAction(package, package->script_actions[script][i], script);
1567 if (rc != ERROR_SUCCESS)
1569 ERR("Execution of script %i halted; action %s returned %u\n",
1570 script, debugstr_w(package->script_actions[script][i]), rc);
1571 break;
1575 msi_free_action_script(package, script);
1576 return rc;
1579 static UINT ACTION_FileCost(MSIPACKAGE *package)
1581 return ERROR_SUCCESS;
1584 static void get_client_counts( MSIPACKAGE *package )
1586 MSICOMPONENT *comp;
1587 HKEY hkey;
1589 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1591 if (!comp->ComponentId) continue;
1593 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1594 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1596 comp->num_clients = 0;
1597 continue;
1599 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1600 NULL, NULL, NULL, NULL );
1601 RegCloseKey( hkey );
1605 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1607 MSICOMPONENT *comp;
1608 UINT r;
1610 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1612 if (!comp->ComponentId) continue;
1614 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1615 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1616 &comp->Installed );
1617 if (r == ERROR_SUCCESS) continue;
1619 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1620 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1621 &comp->Installed );
1622 if (r == ERROR_SUCCESS) continue;
1624 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1625 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1626 &comp->Installed );
1627 if (r == ERROR_SUCCESS) continue;
1629 comp->Installed = INSTALLSTATE_ABSENT;
1633 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1635 MSIFEATURE *feature;
1637 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1639 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1641 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1642 feature->Installed = INSTALLSTATE_ABSENT;
1643 else
1644 feature->Installed = state;
1648 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1650 return (feature->Level > 0 && feature->Level <= level);
1653 static BOOL process_state_property(MSIPACKAGE* package, int level,
1654 LPCWSTR property, INSTALLSTATE state)
1656 LPWSTR override;
1657 MSIFEATURE *feature;
1658 BOOL remove = !strcmpW(property, szRemove);
1659 BOOL reinstall = !strcmpW(property, szReinstall);
1661 override = msi_dup_property( package->db, property );
1662 if (!override)
1663 return FALSE;
1665 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1667 if (feature->Level <= 0)
1668 continue;
1670 if (reinstall)
1671 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1672 else if (remove)
1673 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1675 if (!strcmpiW( override, szAll ))
1677 feature->Action = state;
1678 feature->ActionRequest = state;
1680 else
1682 LPWSTR ptr = override;
1683 LPWSTR ptr2 = strchrW(override,',');
1685 while (ptr)
1687 int len = ptr2 - ptr;
1689 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1690 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1692 feature->Action = state;
1693 feature->ActionRequest = state;
1694 break;
1696 if (ptr2)
1698 ptr=ptr2+1;
1699 ptr2 = strchrW(ptr,',');
1701 else
1702 break;
1706 msi_free(override);
1707 return TRUE;
1710 static BOOL process_overrides( MSIPACKAGE *package, int level )
1712 static const WCHAR szAddLocal[] =
1713 {'A','D','D','L','O','C','A','L',0};
1714 static const WCHAR szAddSource[] =
1715 {'A','D','D','S','O','U','R','C','E',0};
1716 static const WCHAR szAdvertise[] =
1717 {'A','D','V','E','R','T','I','S','E',0};
1718 BOOL ret = FALSE;
1720 /* all these activation/deactivation things happen in order and things
1721 * later on the list override things earlier on the list.
1723 * 0 INSTALLLEVEL processing
1724 * 1 ADDLOCAL
1725 * 2 REMOVE
1726 * 3 ADDSOURCE
1727 * 4 ADDDEFAULT
1728 * 5 REINSTALL
1729 * 6 ADVERTISE
1730 * 7 COMPADDLOCAL
1731 * 8 COMPADDSOURCE
1732 * 9 FILEADDLOCAL
1733 * 10 FILEADDSOURCE
1734 * 11 FILEADDDEFAULT
1736 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1737 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1738 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1739 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1740 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1742 if (ret)
1743 msi_set_property( package->db, szPreselected, szOne, -1 );
1745 return ret;
1748 static void disable_children( MSIFEATURE *feature, int level )
1750 FeatureList *fl;
1752 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1754 if (!is_feature_selected( feature, level ))
1756 TRACE("child %s (level %d request %d) follows disabled parent %s (level %d request %d)\n",
1757 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1758 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1760 fl->feature->Level = feature->Level;
1761 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1762 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1764 disable_children( fl->feature, level );
1768 static void follow_parent( MSIFEATURE *feature )
1770 FeatureList *fl;
1772 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1774 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1776 TRACE("child %s (level %d request %d) follows parent %s (level %d request %d)\n",
1777 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1778 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1780 fl->feature->Action = feature->Action;
1781 fl->feature->ActionRequest = feature->ActionRequest;
1783 follow_parent( fl->feature );
1787 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1789 int level;
1790 MSICOMPONENT* component;
1791 MSIFEATURE *feature;
1793 TRACE("Checking Install Level\n");
1795 level = msi_get_property_int(package->db, szInstallLevel, 1);
1797 if (msi_get_property_int( package->db, szPreselected, 0 ))
1799 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1801 if (!is_feature_selected( feature, level )) continue;
1803 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1805 if (feature->Installed == INSTALLSTATE_ABSENT)
1807 feature->Action = INSTALLSTATE_UNKNOWN;
1808 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1810 else
1812 feature->Action = feature->Installed;
1813 feature->ActionRequest = feature->Installed;
1817 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1819 if (feature->Feature_Parent) continue;
1820 disable_children( feature, level );
1821 follow_parent( feature );
1824 else if (!msi_get_property_int( package->db, szInstalled, 0 ))
1826 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1828 if (!is_feature_selected( feature, level )) continue;
1830 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1832 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1834 feature->Action = INSTALLSTATE_SOURCE;
1835 feature->ActionRequest = INSTALLSTATE_SOURCE;
1837 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1839 feature->Action = INSTALLSTATE_ADVERTISED;
1840 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1842 else
1844 feature->Action = INSTALLSTATE_LOCAL;
1845 feature->ActionRequest = INSTALLSTATE_LOCAL;
1849 /* disable child features of unselected parent or follow parent */
1850 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1852 if (feature->Feature_Parent) continue;
1853 disable_children( feature, level );
1854 follow_parent( feature );
1858 /* now we want to set component state based based on feature state */
1859 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1861 ComponentList *cl;
1863 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1864 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1865 feature->ActionRequest, feature->Action);
1867 /* features with components that have compressed files are made local */
1868 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1870 if (cl->component->ForceLocalState &&
1871 feature->ActionRequest == INSTALLSTATE_SOURCE)
1873 feature->Action = INSTALLSTATE_LOCAL;
1874 feature->ActionRequest = INSTALLSTATE_LOCAL;
1875 break;
1879 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1881 component = cl->component;
1883 switch (feature->ActionRequest)
1885 case INSTALLSTATE_ABSENT:
1886 component->anyAbsent = 1;
1887 break;
1888 case INSTALLSTATE_ADVERTISED:
1889 component->hasAdvertisedFeature = 1;
1890 break;
1891 case INSTALLSTATE_SOURCE:
1892 component->hasSourceFeature = 1;
1893 break;
1894 case INSTALLSTATE_LOCAL:
1895 component->hasLocalFeature = 1;
1896 break;
1897 case INSTALLSTATE_DEFAULT:
1898 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1899 component->hasAdvertisedFeature = 1;
1900 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1901 component->hasSourceFeature = 1;
1902 else
1903 component->hasLocalFeature = 1;
1904 break;
1905 case INSTALLSTATE_UNKNOWN:
1906 if (feature->Installed == INSTALLSTATE_ADVERTISED)
1907 component->hasAdvertisedFeature = 1;
1908 if (feature->Installed == INSTALLSTATE_SOURCE)
1909 component->hasSourceFeature = 1;
1910 if (feature->Installed == INSTALLSTATE_LOCAL)
1911 component->hasLocalFeature = 1;
1912 break;
1913 default:
1914 break;
1919 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1921 /* check if it's local or source */
1922 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1923 (component->hasLocalFeature || component->hasSourceFeature))
1925 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1926 !component->ForceLocalState)
1928 component->Action = INSTALLSTATE_SOURCE;
1929 component->ActionRequest = INSTALLSTATE_SOURCE;
1931 else
1933 component->Action = INSTALLSTATE_LOCAL;
1934 component->ActionRequest = INSTALLSTATE_LOCAL;
1936 continue;
1939 /* if any feature is local, the component must be local too */
1940 if (component->hasLocalFeature)
1942 component->Action = INSTALLSTATE_LOCAL;
1943 component->ActionRequest = INSTALLSTATE_LOCAL;
1944 continue;
1946 if (component->hasSourceFeature)
1948 component->Action = INSTALLSTATE_SOURCE;
1949 component->ActionRequest = INSTALLSTATE_SOURCE;
1950 continue;
1952 if (component->hasAdvertisedFeature)
1954 component->Action = INSTALLSTATE_ADVERTISED;
1955 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1956 continue;
1958 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1959 if (component->anyAbsent && component->ComponentId)
1961 component->Action = INSTALLSTATE_ABSENT;
1962 component->ActionRequest = INSTALLSTATE_ABSENT;
1966 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1968 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1970 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1971 component->Action = INSTALLSTATE_LOCAL;
1972 component->ActionRequest = INSTALLSTATE_LOCAL;
1975 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1976 component->Installed == INSTALLSTATE_SOURCE &&
1977 component->hasSourceFeature)
1979 component->Action = INSTALLSTATE_UNKNOWN;
1980 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1983 TRACE("component %s (installed %d request %d action %d)\n",
1984 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1986 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1987 component->num_clients++;
1988 else if (component->Action == INSTALLSTATE_ABSENT)
1989 component->num_clients--;
1992 return ERROR_SUCCESS;
1995 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1997 MSIPACKAGE *package = param;
1998 LPCWSTR name;
1999 MSIFEATURE *feature;
2001 name = MSI_RecordGetString( row, 1 );
2003 feature = msi_get_loaded_feature( package, name );
2004 if (!feature)
2005 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2006 else
2008 LPCWSTR Condition;
2009 Condition = MSI_RecordGetString(row,3);
2011 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2013 int level = MSI_RecordGetInteger(row,2);
2014 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2015 feature->Level = level;
2018 return ERROR_SUCCESS;
2021 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2023 static const WCHAR name[] = {'\\',0};
2024 VS_FIXEDFILEINFO *ptr, *ret;
2025 LPVOID version;
2026 DWORD versize, handle;
2027 UINT sz;
2029 versize = GetFileVersionInfoSizeW( filename, &handle );
2030 if (!versize)
2031 return NULL;
2033 version = msi_alloc( versize );
2034 if (!version)
2035 return NULL;
2037 GetFileVersionInfoW( filename, 0, versize, version );
2039 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2041 msi_free( version );
2042 return NULL;
2045 ret = msi_alloc( sz );
2046 memcpy( ret, ptr, sz );
2048 msi_free( version );
2049 return ret;
2052 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2054 DWORD ms, ls;
2056 msi_parse_version_string( version, &ms, &ls );
2058 if (fi->dwFileVersionMS > ms) return 1;
2059 else if (fi->dwFileVersionMS < ms) return -1;
2060 else if (fi->dwFileVersionLS > ls) return 1;
2061 else if (fi->dwFileVersionLS < ls) return -1;
2062 return 0;
2065 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2067 DWORD ms1, ms2;
2069 msi_parse_version_string( ver1, &ms1, NULL );
2070 msi_parse_version_string( ver2, &ms2, NULL );
2072 if (ms1 > ms2) return 1;
2073 else if (ms1 < ms2) return -1;
2074 return 0;
2077 DWORD msi_get_disk_file_size( LPCWSTR filename )
2079 HANDLE file;
2080 DWORD size;
2082 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2083 if (file == INVALID_HANDLE_VALUE)
2084 return INVALID_FILE_SIZE;
2086 size = GetFileSize( file, NULL );
2087 CloseHandle( file );
2088 return size;
2091 BOOL msi_file_hash_matches( MSIFILE *file )
2093 UINT r;
2094 MSIFILEHASHINFO hash;
2096 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2097 r = msi_get_filehash( file->TargetPath, &hash );
2098 if (r != ERROR_SUCCESS)
2099 return FALSE;
2101 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2104 static WCHAR *create_temp_dir( MSIDATABASE *db )
2106 static UINT id;
2107 WCHAR *ret;
2109 if (!db->tempfolder)
2111 WCHAR tmp[MAX_PATH];
2112 UINT len = sizeof(tmp)/sizeof(tmp[0]);
2114 if (msi_get_property( db, szTempFolder, tmp, &len ) ||
2115 GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY)
2117 GetTempPathW( MAX_PATH, tmp );
2119 if (!(db->tempfolder = strdupW( tmp ))) return NULL;
2122 if ((ret = msi_alloc( (strlenW( db->tempfolder ) + 20) * sizeof(WCHAR) )))
2124 for (;;)
2126 if (!GetTempFileNameW( db->tempfolder, szMsi, ++id, ret ))
2128 msi_free( ret );
2129 return NULL;
2131 if (CreateDirectoryW( ret, NULL )) break;
2135 return ret;
2139 * msi_build_directory_name()
2141 * This function is to save messing round with directory names
2142 * It handles adding backslashes between path segments,
2143 * and can add \ at the end of the directory name if told to.
2145 * It takes a variable number of arguments.
2146 * It always allocates a new string for the result, so make sure
2147 * to free the return value when finished with it.
2149 * The first arg is the number of path segments that follow.
2150 * The arguments following count are a list of path segments.
2151 * A path segment may be NULL.
2153 * Path segments will be added with a \ separating them.
2154 * A \ will not be added after the last segment, however if the
2155 * last segment is NULL, then the last character will be a \
2157 WCHAR *msi_build_directory_name( DWORD count, ... )
2159 DWORD sz = 1, i;
2160 WCHAR *dir;
2161 va_list va;
2163 va_start( va, count );
2164 for (i = 0; i < count; i++)
2166 const WCHAR *str = va_arg( va, const WCHAR * );
2167 if (str) sz += strlenW( str ) + 1;
2169 va_end( va );
2171 dir = msi_alloc( sz * sizeof(WCHAR) );
2172 dir[0] = 0;
2174 va_start( va, count );
2175 for (i = 0; i < count; i++)
2177 const WCHAR *str = va_arg( va, const WCHAR * );
2178 if (!str) continue;
2179 strcatW( dir, str );
2180 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2182 va_end( va );
2183 return dir;
2186 BOOL msi_is_global_assembly( MSICOMPONENT *comp )
2188 return comp->assembly && !comp->assembly->application;
2191 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2193 msi_free( file->TargetPath );
2194 if (msi_is_global_assembly( file->Component ))
2196 MSIASSEMBLY *assembly = file->Component->assembly;
2198 if (!assembly->tempdir) assembly->tempdir = create_temp_dir( package->db );
2199 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2201 else
2203 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2204 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2207 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2210 static UINT calculate_file_cost( MSIPACKAGE *package )
2212 VS_FIXEDFILEINFO *file_version;
2213 WCHAR *font_version;
2214 MSIFILE *file;
2216 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2218 MSICOMPONENT *comp = file->Component;
2219 DWORD file_size;
2221 if (!comp->Enabled) continue;
2223 if (file->IsCompressed)
2224 comp->ForceLocalState = TRUE;
2226 set_target_path( package, file );
2228 if ((comp->assembly && !comp->assembly->installed) ||
2229 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2231 comp->Cost += file->FileSize;
2232 continue;
2234 file_size = msi_get_disk_file_size( file->TargetPath );
2235 TRACE("%s (size %u)\n", debugstr_w(file->TargetPath), file_size);
2237 if (file->Version)
2239 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2241 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2243 comp->Cost += file->FileSize - file_size;
2245 msi_free( file_version );
2246 continue;
2248 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2250 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2252 comp->Cost += file->FileSize - file_size;
2254 msi_free( font_version );
2255 continue;
2258 if (file_size != file->FileSize)
2260 comp->Cost += file->FileSize - file_size;
2263 return ERROR_SUCCESS;
2266 WCHAR *msi_normalize_path( const WCHAR *in )
2268 const WCHAR *p = in;
2269 WCHAR *q, *ret;
2270 int n, len = strlenW( in ) + 2;
2272 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2274 len = 0;
2275 while (1)
2277 /* copy until the end of the string or a space */
2278 while (*p != ' ' && (*q = *p))
2280 p++, len++;
2281 /* reduce many backslashes to one */
2282 if (*p != '\\' || *q != '\\')
2283 q++;
2286 /* quit at the end of the string */
2287 if (!*p)
2288 break;
2290 /* count the number of spaces */
2291 n = 0;
2292 while (p[n] == ' ')
2293 n++;
2295 /* if it's leading or trailing space, skip it */
2296 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2297 p += n;
2298 else /* copy n spaces */
2299 while (n && (*q++ = *p++)) n--;
2301 while (q - ret > 0 && q[-1] == ' ') q--;
2302 if (q - ret > 0 && q[-1] != '\\')
2304 q[0] = '\\';
2305 q[1] = 0;
2307 return ret;
2310 static WCHAR *get_install_location( MSIPACKAGE *package )
2312 HKEY hkey;
2313 WCHAR *path;
2315 if (!package->ProductCode) return NULL;
2316 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2317 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2319 msi_free( path );
2320 path = NULL;
2322 RegCloseKey( hkey );
2323 return path;
2326 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2328 FolderList *fl;
2329 MSIFOLDER *folder, *parent, *child;
2330 WCHAR *path, *normalized_path;
2332 TRACE("resolving %s\n", debugstr_w(name));
2334 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2336 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2338 if (!(path = get_install_location( package )) &&
2339 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2341 path = msi_dup_property( package->db, szRootDrive );
2344 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2346 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2348 parent = msi_get_loaded_folder( package, folder->Parent );
2349 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2351 else
2352 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2354 normalized_path = msi_normalize_path( path );
2355 msi_free( path );
2356 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2358 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2359 msi_free( normalized_path );
2360 return;
2362 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2363 msi_free( folder->ResolvedTarget );
2364 folder->ResolvedTarget = normalized_path;
2366 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2368 child = fl->folder;
2369 msi_resolve_target_folder( package, child->Directory, load_prop );
2371 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2374 static ULONGLONG get_volume_space_required( MSIPACKAGE *package )
2376 MSICOMPONENT *comp;
2377 ULONGLONG ret = 0;
2379 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2381 if (comp->Action == INSTALLSTATE_LOCAL) ret += comp->Cost;
2383 return ret;
2386 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2388 static const WCHAR query[] =
2389 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2390 '`','C','o','n','d','i','t','i','o','n','`',0};
2391 static const WCHAR szOutOfDiskSpace[] =
2392 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2393 static const WCHAR szPrimaryFolder[] =
2394 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2395 static const WCHAR szPrimaryVolumePath[] =
2396 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2397 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2398 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2399 'A','v','a','i','l','a','b','l','e',0};
2400 static const WCHAR szPrimaryVolumeSpaceRequired[] =
2401 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2402 'R','e','q','u','i','r','e','d',0};
2403 static const WCHAR szPrimaryVolumeSpaceRemaining[] =
2404 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2405 'R','e','m','a','i','n','i','n','g',0};
2406 static const WCHAR szOutOfNoRbDiskSpace[] =
2407 {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0};
2408 MSICOMPONENT *comp;
2409 MSIQUERY *view;
2410 WCHAR *level, *primary_key, *primary_folder;
2411 UINT rc;
2413 TRACE("Building directory properties\n");
2414 msi_resolve_target_folder( package, szTargetDir, TRUE );
2416 TRACE("Evaluating component conditions\n");
2417 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2419 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2421 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2422 comp->Enabled = FALSE;
2424 else
2425 comp->Enabled = TRUE;
2427 get_client_counts( package );
2429 /* read components states from the registry */
2430 ACTION_GetComponentInstallStates(package);
2431 ACTION_GetFeatureInstallStates(package);
2433 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2435 TRACE("Evaluating feature conditions\n");
2437 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2438 if (rc == ERROR_SUCCESS)
2440 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2441 msiobj_release( &view->hdr );
2442 if (rc != ERROR_SUCCESS)
2443 return rc;
2447 TRACE("Calculating file cost\n");
2448 calculate_file_cost( package );
2450 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2451 /* set default run level if not set */
2452 level = msi_dup_property( package->db, szInstallLevel );
2453 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2454 msi_free(level);
2456 if ((rc = MSI_SetFeatureStates( package ))) return rc;
2458 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2460 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2462 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2463 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2465 static const WCHAR fmtW[] = {'%','l','u',0};
2466 ULARGE_INTEGER free;
2467 ULONGLONG required;
2468 WCHAR buf[21];
2470 primary_folder[2] = 0;
2471 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2473 sprintfW( buf, fmtW, free.QuadPart / 512 );
2474 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2476 required = get_volume_space_required( package );
2477 sprintfW( buf, fmtW, required / 512 );
2478 msi_set_property( package->db, szPrimaryVolumeSpaceRequired, buf, -1 );
2480 sprintfW( buf, fmtW, (free.QuadPart - required) / 512 );
2481 msi_set_property( package->db, szPrimaryVolumeSpaceRemaining, buf, -1 );
2482 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2484 msi_free( primary_folder );
2486 msi_free( primary_key );
2489 /* FIXME: check volume disk space */
2490 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2491 msi_set_property( package->db, szOutOfNoRbDiskSpace, szZero, -1 );
2493 return ERROR_SUCCESS;
2496 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size )
2498 BYTE *data;
2500 if (!value)
2502 *size = sizeof(WCHAR);
2503 *type = REG_SZ;
2504 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2505 return data;
2507 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2509 if (value[1]=='x')
2511 LPWSTR ptr;
2512 CHAR byte[5];
2513 LPWSTR deformated = NULL;
2514 int count;
2516 deformat_string(package, &value[2], &deformated);
2518 /* binary value type */
2519 ptr = deformated;
2520 *type = REG_BINARY;
2521 if (strlenW(ptr)%2)
2522 *size = (strlenW(ptr)/2)+1;
2523 else
2524 *size = strlenW(ptr)/2;
2526 data = msi_alloc(*size);
2528 byte[0] = '0';
2529 byte[1] = 'x';
2530 byte[4] = 0;
2531 count = 0;
2532 /* if uneven pad with a zero in front */
2533 if (strlenW(ptr)%2)
2535 byte[2]= '0';
2536 byte[3]= *ptr;
2537 ptr++;
2538 data[count] = (BYTE)strtol(byte,NULL,0);
2539 count ++;
2540 TRACE("Uneven byte count\n");
2542 while (*ptr)
2544 byte[2]= *ptr;
2545 ptr++;
2546 byte[3]= *ptr;
2547 ptr++;
2548 data[count] = (BYTE)strtol(byte,NULL,0);
2549 count ++;
2551 msi_free(deformated);
2553 TRACE("Data %i bytes(%i)\n",*size,count);
2555 else
2557 LPWSTR deformated;
2558 LPWSTR p;
2559 DWORD d = 0;
2560 deformat_string(package, &value[1], &deformated);
2562 *type=REG_DWORD;
2563 *size = sizeof(DWORD);
2564 data = msi_alloc(*size);
2565 p = deformated;
2566 if (*p == '-')
2567 p++;
2568 while (*p)
2570 if ( (*p < '0') || (*p > '9') )
2571 break;
2572 d *= 10;
2573 d += (*p - '0');
2574 p++;
2576 if (deformated[0] == '-')
2577 d = -d;
2578 *(LPDWORD)data = d;
2579 TRACE("DWORD %i\n",*(LPDWORD)data);
2581 msi_free(deformated);
2584 else
2586 const WCHAR *ptr = value;
2588 *type = REG_SZ;
2589 if (value[0] == '#')
2591 ptr++; len--;
2592 if (value[1] == '%')
2594 ptr++; len--;
2595 *type = REG_EXPAND_SZ;
2598 data = (BYTE *)msi_strdupW( ptr, len );
2599 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2600 *size = (len + 1) * sizeof(WCHAR);
2602 return data;
2605 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2607 const WCHAR *ret;
2609 switch (root)
2611 case -1:
2612 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2614 *root_key = HKEY_LOCAL_MACHINE;
2615 ret = szHLM;
2617 else
2619 *root_key = HKEY_CURRENT_USER;
2620 ret = szHCU;
2622 break;
2623 case 0:
2624 *root_key = HKEY_CLASSES_ROOT;
2625 ret = szHCR;
2626 break;
2627 case 1:
2628 *root_key = HKEY_CURRENT_USER;
2629 ret = szHCU;
2630 break;
2631 case 2:
2632 *root_key = HKEY_LOCAL_MACHINE;
2633 ret = szHLM;
2634 break;
2635 case 3:
2636 *root_key = HKEY_USERS;
2637 ret = szHU;
2638 break;
2639 default:
2640 ERR("Unknown root %i\n", root);
2641 return NULL;
2644 return ret;
2647 static inline REGSAM get_registry_view( const MSICOMPONENT *comp )
2649 REGSAM view = 0;
2650 if (is_wow64 || is_64bit)
2651 view |= (comp->Attributes & msidbComponentAttributes64bit) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
2652 return view;
2655 static HKEY open_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, BOOL create, REGSAM access )
2657 WCHAR *subkey, *p, *q;
2658 HKEY hkey, ret = NULL;
2659 LONG res;
2661 access |= get_registry_view( comp );
2663 if (!(subkey = strdupW( path ))) return NULL;
2664 p = subkey;
2665 if ((q = strchrW( p, '\\' ))) *q = 0;
2666 if (create)
2667 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2668 else
2669 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2670 if (res)
2672 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2673 msi_free( subkey );
2674 return NULL;
2676 if (q && q[1])
2678 ret = open_key( comp, hkey, q + 1, create, access );
2679 RegCloseKey( hkey );
2681 else ret = hkey;
2682 msi_free( subkey );
2683 return ret;
2686 static BOOL is_special_entry( const WCHAR *name )
2688 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2691 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2693 const WCHAR *p = str;
2694 WCHAR **ret;
2695 int i = 0;
2697 *count = 0;
2698 if (!str) return NULL;
2699 while ((p - str) < len)
2701 p += strlenW( p ) + 1;
2702 (*count)++;
2704 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2705 p = str;
2706 while ((p - str) < len)
2708 if (!(ret[i] = strdupW( p )))
2710 for (; i >= 0; i--) msi_free( ret[i] );
2711 msi_free( ret );
2712 return NULL;
2714 p += strlenW( p ) + 1;
2715 i++;
2717 return ret;
2720 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2721 WCHAR **right, DWORD right_count, DWORD *size )
2723 WCHAR *ret, *p;
2724 unsigned int i;
2726 *size = sizeof(WCHAR);
2727 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2728 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2730 if (!(ret = p = msi_alloc( *size ))) return NULL;
2732 for (i = 0; i < left_count; i++)
2734 strcpyW( p, left[i] );
2735 p += strlenW( p ) + 1;
2737 for (i = 0; i < right_count; i++)
2739 strcpyW( p, right[i] );
2740 p += strlenW( p ) + 1;
2742 *p = 0;
2743 return ret;
2746 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2747 WCHAR **new, DWORD new_count )
2749 DWORD ret = old_count;
2750 unsigned int i, j, k;
2752 for (i = 0; i < new_count; i++)
2754 for (j = 0; j < old_count; j++)
2756 if (old[j] && !strcmpW( new[i], old[j] ))
2758 msi_free( old[j] );
2759 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2760 old[k] = NULL;
2761 ret--;
2765 return ret;
2768 enum join_op
2770 JOIN_OP_APPEND,
2771 JOIN_OP_PREPEND,
2772 JOIN_OP_REPLACE
2775 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2776 WCHAR **new, DWORD new_count, DWORD *size )
2778 switch (op)
2780 case JOIN_OP_APPEND:
2781 old_count = remove_duplicate_values( old, old_count, new, new_count );
2782 return flatten_multi_string_values( old, old_count, new, new_count, size );
2784 case JOIN_OP_PREPEND:
2785 old_count = remove_duplicate_values( old, old_count, new, new_count );
2786 return flatten_multi_string_values( new, new_count, old, old_count, size );
2788 case JOIN_OP_REPLACE:
2789 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2791 default:
2792 ERR("unhandled join op %u\n", op);
2793 return NULL;
2797 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2798 BYTE *new_value, DWORD new_size, DWORD *size )
2800 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2801 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2802 enum join_op op = JOIN_OP_REPLACE;
2803 WCHAR **old = NULL, **new = NULL;
2804 BYTE *ret;
2806 if (new_size / sizeof(WCHAR) - 1 > 1)
2808 new_ptr = (const WCHAR *)new_value;
2809 new_len = new_size / sizeof(WCHAR) - 1;
2811 if (!new_ptr[0] && new_ptr[new_len - 1])
2813 op = JOIN_OP_APPEND;
2814 new_len--;
2815 new_ptr++;
2817 else if (new_ptr[0] && !new_ptr[new_len - 1])
2819 op = JOIN_OP_PREPEND;
2820 new_len--;
2822 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2824 op = JOIN_OP_REPLACE;
2825 new_len -= 2;
2826 new_ptr++;
2828 new = split_multi_string_values( new_ptr, new_len, &new_count );
2830 if (old_size / sizeof(WCHAR) - 1 > 1)
2832 old_ptr = (const WCHAR *)old_value;
2833 old_len = old_size / sizeof(WCHAR) - 1;
2834 old = split_multi_string_values( old_ptr, old_len, &old_count );
2836 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2837 for (i = 0; i < old_count; i++) msi_free( old[i] );
2838 for (i = 0; i < new_count; i++) msi_free( new[i] );
2839 msi_free( old );
2840 msi_free( new );
2841 return ret;
2844 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2846 BYTE *ret;
2847 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2848 if (!(ret = msi_alloc( *size ))) return NULL;
2849 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2850 return ret;
2853 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2855 MSIPACKAGE *package = param;
2856 BYTE *new_value, *old_value = NULL;
2857 HKEY root_key, hkey;
2858 DWORD type, old_type, new_size, old_size = 0;
2859 LPWSTR deformated, uikey;
2860 const WCHAR *szRoot, *component, *name, *key, *str;
2861 MSICOMPONENT *comp;
2862 MSIRECORD * uirow;
2863 INT root;
2864 BOOL check_first = FALSE;
2865 int len;
2867 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2869 component = MSI_RecordGetString(row, 6);
2870 comp = msi_get_loaded_component(package,component);
2871 if (!comp)
2872 return ERROR_SUCCESS;
2874 comp->Action = msi_get_component_action( package, comp );
2875 if (comp->Action != INSTALLSTATE_LOCAL && comp->Action != INSTALLSTATE_SOURCE)
2877 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2878 return ERROR_SUCCESS;
2881 name = MSI_RecordGetString(row, 4);
2882 if( MSI_RecordIsNull(row,5) && name )
2884 /* null values can have special meanings */
2885 if (name[0]=='-' && name[1] == 0)
2886 return ERROR_SUCCESS;
2887 if ((name[0] == '+' || name[0] == '*') && !name[1])
2888 check_first = TRUE;
2891 root = MSI_RecordGetInteger(row,2);
2892 key = MSI_RecordGetString(row, 3);
2894 szRoot = get_root_key( package, root, &root_key );
2895 if (!szRoot)
2896 return ERROR_SUCCESS;
2898 deformat_string(package, key , &deformated);
2899 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2900 strcpyW(uikey,szRoot);
2901 strcatW(uikey,deformated);
2903 if (!(hkey = open_key( comp, root_key, deformated, TRUE, KEY_QUERY_VALUE | KEY_SET_VALUE )))
2905 ERR("Could not create key %s\n", debugstr_w(deformated));
2906 msi_free(uikey);
2907 msi_free(deformated);
2908 return ERROR_FUNCTION_FAILED;
2910 msi_free( deformated );
2911 str = msi_record_get_string( row, 5, NULL );
2912 len = deformat_string( package, str, &deformated );
2913 new_value = parse_value( package, deformated, len, &type, &new_size );
2915 msi_free( deformated );
2916 deformat_string(package, name, &deformated);
2918 if (!is_special_entry( name ))
2920 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2921 if (type == REG_MULTI_SZ)
2923 BYTE *new;
2924 if (old_value && old_type != REG_MULTI_SZ)
2926 msi_free( old_value );
2927 old_value = NULL;
2928 old_size = 0;
2930 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2931 msi_free( new_value );
2932 new_value = new;
2934 if (!check_first)
2936 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2937 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2939 else if (!old_value)
2941 if (deformated || new_size)
2943 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2944 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2947 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2949 RegCloseKey(hkey);
2951 uirow = MSI_CreateRecord(3);
2952 MSI_RecordSetStringW(uirow,2,deformated);
2953 MSI_RecordSetStringW(uirow,1,uikey);
2954 if (type == REG_SZ || type == REG_EXPAND_SZ)
2955 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2956 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
2957 msiobj_release( &uirow->hdr );
2959 msi_free(new_value);
2960 msi_free(old_value);
2961 msi_free(deformated);
2962 msi_free(uikey);
2964 return ERROR_SUCCESS;
2967 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2969 static const WCHAR query[] = {
2970 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2971 '`','R','e','g','i','s','t','r','y','`',0};
2972 MSIQUERY *view;
2973 UINT rc;
2975 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2976 if (rc != ERROR_SUCCESS)
2977 return ERROR_SUCCESS;
2979 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2980 msiobj_release(&view->hdr);
2981 return rc;
2984 static void delete_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2986 REGSAM access = 0;
2987 WCHAR *subkey, *p;
2988 HKEY hkey;
2989 LONG res;
2991 access |= get_registry_view( comp );
2993 if (!(subkey = strdupW( path ))) return;
2996 if ((p = strrchrW( subkey, '\\' )))
2998 *p = 0;
2999 if (!p[1]) continue; /* trailing backslash */
3000 hkey = open_key( comp, root, subkey, FALSE, access | READ_CONTROL );
3001 if (!hkey) break;
3002 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
3003 RegCloseKey( hkey );
3005 else
3006 res = RegDeleteKeyExW( root, subkey, access, 0 );
3007 if (res)
3009 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
3010 break;
3012 } while (p);
3013 msi_free( subkey );
3016 static void delete_value( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, const WCHAR *value )
3018 LONG res;
3019 HKEY hkey;
3020 DWORD num_subkeys, num_values;
3022 if ((hkey = open_key( comp, root, path, FALSE, KEY_SET_VALUE | KEY_QUERY_VALUE )))
3024 if ((res = RegDeleteValueW( hkey, value )))
3025 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
3027 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
3028 NULL, NULL, NULL, NULL );
3029 RegCloseKey( hkey );
3030 if (!res && !num_subkeys && !num_values)
3032 TRACE("removing empty key %s\n", debugstr_w(path));
3033 delete_key( comp, root, path );
3038 static void delete_tree( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
3040 LONG res;
3041 HKEY hkey;
3043 if (!(hkey = open_key( comp, root, path, FALSE, KEY_ALL_ACCESS ))) return;
3044 res = RegDeleteTreeW( hkey, NULL );
3045 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3046 delete_key( comp, root, path );
3047 RegCloseKey( hkey );
3050 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3052 MSIPACKAGE *package = param;
3053 LPCWSTR component, name, key_str, root_key_str;
3054 LPWSTR deformated_key, deformated_name, ui_key_str;
3055 MSICOMPONENT *comp;
3056 MSIRECORD *uirow;
3057 BOOL delete_key = FALSE;
3058 HKEY hkey_root;
3059 UINT size;
3060 INT root;
3062 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3064 component = MSI_RecordGetString( row, 6 );
3065 comp = msi_get_loaded_component( package, component );
3066 if (!comp)
3067 return ERROR_SUCCESS;
3069 comp->Action = msi_get_component_action( package, comp );
3070 if (comp->Action != INSTALLSTATE_ABSENT)
3072 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3073 return ERROR_SUCCESS;
3076 name = MSI_RecordGetString( row, 4 );
3077 if (MSI_RecordIsNull( row, 5 ) && name )
3079 if (name[0] == '+' && !name[1])
3080 return ERROR_SUCCESS;
3081 if ((name[0] == '-' || name[0] == '*') && !name[1])
3083 delete_key = TRUE;
3084 name = NULL;
3088 root = MSI_RecordGetInteger( row, 2 );
3089 key_str = MSI_RecordGetString( row, 3 );
3091 root_key_str = get_root_key( package, root, &hkey_root );
3092 if (!root_key_str)
3093 return ERROR_SUCCESS;
3095 deformat_string( package, key_str, &deformated_key );
3096 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3097 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3098 strcpyW( ui_key_str, root_key_str );
3099 strcatW( ui_key_str, deformated_key );
3101 deformat_string( package, name, &deformated_name );
3103 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3104 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3105 msi_free( deformated_key );
3107 uirow = MSI_CreateRecord( 2 );
3108 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3109 MSI_RecordSetStringW( uirow, 2, deformated_name );
3110 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3111 msiobj_release( &uirow->hdr );
3113 msi_free( ui_key_str );
3114 msi_free( deformated_name );
3115 return ERROR_SUCCESS;
3118 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3120 MSIPACKAGE *package = param;
3121 LPCWSTR component, name, key_str, root_key_str;
3122 LPWSTR deformated_key, deformated_name, ui_key_str;
3123 MSICOMPONENT *comp;
3124 MSIRECORD *uirow;
3125 BOOL delete_key = FALSE;
3126 HKEY hkey_root;
3127 UINT size;
3128 INT root;
3130 component = MSI_RecordGetString( row, 5 );
3131 comp = msi_get_loaded_component( package, component );
3132 if (!comp)
3133 return ERROR_SUCCESS;
3135 comp->Action = msi_get_component_action( package, comp );
3136 if (comp->Action != INSTALLSTATE_LOCAL)
3138 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3139 return ERROR_SUCCESS;
3142 if ((name = MSI_RecordGetString( row, 4 )))
3144 if (name[0] == '-' && !name[1])
3146 delete_key = TRUE;
3147 name = NULL;
3151 root = MSI_RecordGetInteger( row, 2 );
3152 key_str = MSI_RecordGetString( row, 3 );
3154 root_key_str = get_root_key( package, root, &hkey_root );
3155 if (!root_key_str)
3156 return ERROR_SUCCESS;
3158 deformat_string( package, key_str, &deformated_key );
3159 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3160 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3161 strcpyW( ui_key_str, root_key_str );
3162 strcatW( ui_key_str, deformated_key );
3164 deformat_string( package, name, &deformated_name );
3166 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3167 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3168 msi_free( deformated_key );
3170 uirow = MSI_CreateRecord( 2 );
3171 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3172 MSI_RecordSetStringW( uirow, 2, deformated_name );
3173 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3174 msiobj_release( &uirow->hdr );
3176 msi_free( ui_key_str );
3177 msi_free( deformated_name );
3178 return ERROR_SUCCESS;
3181 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3183 static const WCHAR registry_query[] = {
3184 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3185 '`','R','e','g','i','s','t','r','y','`',0};
3186 static const WCHAR remove_registry_query[] = {
3187 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3188 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3189 MSIQUERY *view;
3190 UINT rc;
3192 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3193 if (rc == ERROR_SUCCESS)
3195 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3196 msiobj_release( &view->hdr );
3197 if (rc != ERROR_SUCCESS)
3198 return rc;
3200 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3201 if (rc == ERROR_SUCCESS)
3203 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3204 msiobj_release( &view->hdr );
3205 if (rc != ERROR_SUCCESS)
3206 return rc;
3208 return ERROR_SUCCESS;
3211 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3213 return ERROR_SUCCESS;
3217 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3219 static const WCHAR query[]= {
3220 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3221 '`','R','e','g','i','s','t','r','y','`',0};
3222 MSICOMPONENT *comp;
3223 DWORD total = 0, count = 0;
3224 MSIQUERY *view;
3225 MSIFEATURE *feature;
3226 MSIFILE *file;
3227 UINT rc;
3229 TRACE("InstallValidate\n");
3231 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3232 if (rc == ERROR_SUCCESS)
3234 rc = MSI_IterateRecords( view, &count, NULL, package );
3235 msiobj_release( &view->hdr );
3236 if (rc != ERROR_SUCCESS)
3237 return rc;
3238 total += count * REG_PROGRESS_VALUE;
3240 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3241 total += COMPONENT_PROGRESS_VALUE;
3243 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3244 total += file->FileSize;
3246 msi_ui_progress( package, 0, total, 0, 0 );
3248 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3250 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3251 debugstr_w(feature->Feature), feature->Installed,
3252 feature->ActionRequest, feature->Action);
3254 return ERROR_SUCCESS;
3257 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3259 MSIPACKAGE* package = param;
3260 LPCWSTR cond = NULL;
3261 LPCWSTR message = NULL;
3262 UINT r;
3264 static const WCHAR title[]=
3265 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3267 cond = MSI_RecordGetString(row,1);
3269 r = MSI_EvaluateConditionW(package,cond);
3270 if (r == MSICONDITION_FALSE)
3272 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3274 LPWSTR deformated;
3275 message = MSI_RecordGetString(row,2);
3276 deformat_string(package,message,&deformated);
3277 MessageBoxW(NULL,deformated,title,MB_OK);
3278 msi_free(deformated);
3281 return ERROR_INSTALL_FAILURE;
3284 return ERROR_SUCCESS;
3287 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3289 static const WCHAR query[] = {
3290 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3291 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3292 MSIQUERY *view;
3293 UINT rc;
3295 TRACE("Checking launch conditions\n");
3297 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3298 if (rc != ERROR_SUCCESS)
3299 return ERROR_SUCCESS;
3301 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3302 msiobj_release(&view->hdr);
3303 return rc;
3306 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3309 if (!cmp->KeyPath)
3310 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3312 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3314 static const WCHAR query[] = {
3315 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3316 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3317 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3318 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3319 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3320 MSIRECORD *row;
3321 UINT root, len;
3322 LPWSTR deformated, buffer, deformated_name;
3323 LPCWSTR key, name;
3325 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3326 if (!row)
3327 return NULL;
3329 root = MSI_RecordGetInteger(row,2);
3330 key = MSI_RecordGetString(row, 3);
3331 name = MSI_RecordGetString(row, 4);
3332 deformat_string(package, key , &deformated);
3333 deformat_string(package, name, &deformated_name);
3335 len = strlenW(deformated) + 6;
3336 if (deformated_name)
3337 len+=strlenW(deformated_name);
3339 buffer = msi_alloc( len *sizeof(WCHAR));
3341 if (deformated_name)
3342 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3343 else
3344 sprintfW(buffer,fmt,root,deformated);
3346 msi_free(deformated);
3347 msi_free(deformated_name);
3348 msiobj_release(&row->hdr);
3350 return buffer;
3352 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3354 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3355 return NULL;
3357 else
3359 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3361 if (file)
3362 return strdupW( file->TargetPath );
3364 return NULL;
3367 static HKEY openSharedDLLsKey(void)
3369 HKEY hkey=0;
3370 static const WCHAR path[] =
3371 {'S','o','f','t','w','a','r','e','\\',
3372 'M','i','c','r','o','s','o','f','t','\\',
3373 'W','i','n','d','o','w','s','\\',
3374 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3375 'S','h','a','r','e','d','D','L','L','s',0};
3377 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3378 return hkey;
3381 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3383 HKEY hkey;
3384 DWORD count=0;
3385 DWORD type;
3386 DWORD sz = sizeof(count);
3387 DWORD rc;
3389 hkey = openSharedDLLsKey();
3390 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3391 if (rc != ERROR_SUCCESS)
3392 count = 0;
3393 RegCloseKey(hkey);
3394 return count;
3397 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3399 HKEY hkey;
3401 hkey = openSharedDLLsKey();
3402 if (count > 0)
3403 msi_reg_set_val_dword( hkey, path, count );
3404 else
3405 RegDeleteValueW(hkey,path);
3406 RegCloseKey(hkey);
3407 return count;
3410 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3412 MSIFEATURE *feature;
3413 INT count = 0;
3414 BOOL write = FALSE;
3416 /* only refcount DLLs */
3417 if (comp->KeyPath == NULL ||
3418 comp->assembly ||
3419 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3420 comp->Attributes & msidbComponentAttributesODBCDataSource)
3421 write = FALSE;
3422 else
3424 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3425 write = (count > 0);
3427 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3428 write = TRUE;
3431 /* increment counts */
3432 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3434 ComponentList *cl;
3436 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3437 continue;
3439 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3441 if ( cl->component == comp )
3442 count++;
3446 /* decrement counts */
3447 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3449 ComponentList *cl;
3451 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3452 continue;
3454 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3456 if ( cl->component == comp )
3457 count--;
3461 /* ref count all the files in the component */
3462 if (write)
3464 MSIFILE *file;
3466 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3468 if (file->Component == comp)
3469 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3473 /* add a count for permanent */
3474 if (comp->Attributes & msidbComponentAttributesPermanent)
3475 count ++;
3477 comp->RefCount = count;
3479 if (write)
3480 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3483 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3485 if (comp->assembly)
3487 const WCHAR prefixW[] = {'<','\\',0};
3488 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3489 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3491 if (keypath)
3493 strcpyW( keypath, prefixW );
3494 strcatW( keypath, comp->assembly->display_name );
3496 return keypath;
3498 return resolve_keypath( package, comp );
3501 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3503 WCHAR squashed_pc[SQUASHED_GUID_SIZE], squashed_cc[SQUASHED_GUID_SIZE];
3504 UINT rc;
3505 MSICOMPONENT *comp;
3506 HKEY hkey;
3508 TRACE("\n");
3510 squash_guid( package->ProductCode, squashed_pc );
3511 msi_set_sourcedir_props(package, FALSE);
3513 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3515 MSIRECORD *uirow;
3516 INSTALLSTATE action;
3518 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3519 if (!comp->ComponentId)
3520 continue;
3522 squash_guid( comp->ComponentId, squashed_cc );
3523 msi_free( comp->FullKeypath );
3524 comp->FullKeypath = build_full_keypath( package, comp );
3526 ACTION_RefCountComponent( package, comp );
3528 if (package->need_rollback) action = comp->Installed;
3529 else action = comp->ActionRequest;
3531 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3532 debugstr_w(comp->Component), debugstr_w(squashed_cc),
3533 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3535 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3537 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3538 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3539 else
3540 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3542 if (rc != ERROR_SUCCESS)
3543 continue;
3545 if (comp->Attributes & msidbComponentAttributesPermanent)
3547 static const WCHAR szPermKey[] =
3548 { '0','0','0','0','0','0','0','0','0','0','0','0',
3549 '0','0','0','0','0','0','0','0','0','0','0','0',
3550 '0','0','0','0','0','0','0','0',0 };
3552 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3554 if (action == INSTALLSTATE_LOCAL)
3555 msi_reg_set_val_str( hkey, squashed_pc, comp->FullKeypath );
3556 else
3558 MSIFILE *file;
3559 MSIRECORD *row;
3560 LPWSTR ptr, ptr2;
3561 WCHAR source[MAX_PATH];
3562 WCHAR base[MAX_PATH];
3563 LPWSTR sourcepath;
3565 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3566 static const WCHAR query[] = {
3567 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3568 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3569 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3570 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3571 '`','D','i','s','k','I','d','`',0};
3573 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3574 continue;
3576 if (!(row = MSI_QueryGetRecord(package->db, query, file->Sequence)))
3577 return ERROR_FUNCTION_FAILED;
3579 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3580 ptr2 = strrchrW(source, '\\') + 1;
3581 msiobj_release(&row->hdr);
3583 lstrcpyW(base, package->PackagePath);
3584 ptr = strrchrW(base, '\\');
3585 *(ptr + 1) = '\0';
3587 sourcepath = msi_resolve_file_source(package, file);
3588 ptr = sourcepath + lstrlenW(base);
3589 lstrcpyW(ptr2, ptr);
3590 msi_free(sourcepath);
3592 msi_reg_set_val_str( hkey, squashed_pc, source );
3594 RegCloseKey(hkey);
3596 else if (action == INSTALLSTATE_ABSENT)
3598 if (comp->num_clients <= 0)
3600 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3601 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3602 else
3603 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3605 if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
3607 else
3609 LONG res;
3611 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3612 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE );
3613 else
3614 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );
3616 if (rc != ERROR_SUCCESS)
3618 WARN( "failed to open component key %u\n", rc );
3619 continue;
3621 res = RegDeleteValueW( hkey, squashed_pc );
3622 RegCloseKey(hkey);
3623 if (res) WARN( "failed to delete component value %d\n", res );
3627 /* UI stuff */
3628 uirow = MSI_CreateRecord(3);
3629 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3630 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3631 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3632 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3633 msiobj_release( &uirow->hdr );
3635 return ERROR_SUCCESS;
3638 typedef struct {
3639 CLSID clsid;
3640 LPWSTR source;
3642 LPWSTR path;
3643 ITypeLib *ptLib;
3644 } typelib_struct;
3646 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3647 LPWSTR lpszName, LONG_PTR lParam)
3649 TLIBATTR *attr;
3650 typelib_struct *tl_struct = (typelib_struct*) lParam;
3651 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3652 int sz;
3653 HRESULT res;
3655 if (!IS_INTRESOURCE(lpszName))
3657 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3658 return TRUE;
3661 sz = strlenW(tl_struct->source)+4;
3662 sz *= sizeof(WCHAR);
3664 if ((INT_PTR)lpszName == 1)
3665 tl_struct->path = strdupW(tl_struct->source);
3666 else
3668 tl_struct->path = msi_alloc(sz);
3669 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3672 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3673 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3674 if (FAILED(res))
3676 msi_free(tl_struct->path);
3677 tl_struct->path = NULL;
3679 return TRUE;
3682 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3683 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3685 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3686 return FALSE;
3689 msi_free(tl_struct->path);
3690 tl_struct->path = NULL;
3692 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3693 ITypeLib_Release(tl_struct->ptLib);
3695 return TRUE;
3698 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3700 MSIPACKAGE* package = param;
3701 LPCWSTR component;
3702 MSICOMPONENT *comp;
3703 MSIFILE *file;
3704 typelib_struct tl_struct;
3705 ITypeLib *tlib;
3706 HMODULE module;
3707 HRESULT hr;
3709 component = MSI_RecordGetString(row,3);
3710 comp = msi_get_loaded_component(package,component);
3711 if (!comp)
3712 return ERROR_SUCCESS;
3714 comp->Action = msi_get_component_action( package, comp );
3715 if (comp->Action != INSTALLSTATE_LOCAL)
3717 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3718 return ERROR_SUCCESS;
3721 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3723 TRACE("component has no key path\n");
3724 return ERROR_SUCCESS;
3726 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3728 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3729 if (module)
3731 LPCWSTR guid;
3732 guid = MSI_RecordGetString(row,1);
3733 CLSIDFromString( guid, &tl_struct.clsid);
3734 tl_struct.source = strdupW( file->TargetPath );
3735 tl_struct.path = NULL;
3737 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3738 (LONG_PTR)&tl_struct);
3740 if (tl_struct.path)
3742 LPCWSTR helpid, help_path = NULL;
3743 HRESULT res;
3745 helpid = MSI_RecordGetString(row,6);
3747 if (helpid) help_path = msi_get_target_folder( package, helpid );
3748 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3750 if (FAILED(res))
3751 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3752 else
3753 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3755 ITypeLib_Release(tl_struct.ptLib);
3756 msi_free(tl_struct.path);
3758 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3760 FreeLibrary(module);
3761 msi_free(tl_struct.source);
3763 else
3765 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3766 if (FAILED(hr))
3768 ERR("Failed to load type library: %08x\n", hr);
3769 return ERROR_INSTALL_FAILURE;
3772 ITypeLib_Release(tlib);
3775 return ERROR_SUCCESS;
3778 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3780 static const WCHAR query[] = {
3781 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3782 '`','T','y','p','e','L','i','b','`',0};
3783 MSIQUERY *view;
3784 UINT rc;
3786 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3787 if (rc != ERROR_SUCCESS)
3788 return ERROR_SUCCESS;
3790 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3791 msiobj_release(&view->hdr);
3792 return rc;
3795 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3797 MSIPACKAGE *package = param;
3798 LPCWSTR component, guid;
3799 MSICOMPONENT *comp;
3800 GUID libid;
3801 UINT version;
3802 LCID language;
3803 SYSKIND syskind;
3804 HRESULT hr;
3806 component = MSI_RecordGetString( row, 3 );
3807 comp = msi_get_loaded_component( package, component );
3808 if (!comp)
3809 return ERROR_SUCCESS;
3811 comp->Action = msi_get_component_action( package, comp );
3812 if (comp->Action != INSTALLSTATE_ABSENT)
3814 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3815 return ERROR_SUCCESS;
3817 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3819 guid = MSI_RecordGetString( row, 1 );
3820 CLSIDFromString( guid, &libid );
3821 version = MSI_RecordGetInteger( row, 4 );
3822 language = MSI_RecordGetInteger( row, 2 );
3824 #ifdef _WIN64
3825 syskind = SYS_WIN64;
3826 #else
3827 syskind = SYS_WIN32;
3828 #endif
3830 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3831 if (FAILED(hr))
3833 WARN("Failed to unregister typelib: %08x\n", hr);
3836 return ERROR_SUCCESS;
3839 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3841 static const WCHAR query[] = {
3842 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3843 '`','T','y','p','e','L','i','b','`',0};
3844 MSIQUERY *view;
3845 UINT rc;
3847 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3848 if (rc != ERROR_SUCCESS)
3849 return ERROR_SUCCESS;
3851 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3852 msiobj_release( &view->hdr );
3853 return rc;
3856 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3858 static const WCHAR szlnk[] = {'.','l','n','k',0};
3859 LPCWSTR directory, extension, link_folder;
3860 LPWSTR link_file, filename;
3862 directory = MSI_RecordGetString( row, 2 );
3863 link_folder = msi_get_target_folder( package, directory );
3864 if (!link_folder)
3866 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3867 return NULL;
3869 /* may be needed because of a bug somewhere else */
3870 msi_create_full_path( link_folder );
3872 filename = msi_dup_record_field( row, 3 );
3873 msi_reduce_to_long_filename( filename );
3875 extension = strrchrW( filename, '.' );
3876 if (!extension || strcmpiW( extension, szlnk ))
3878 int len = strlenW( filename );
3879 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3880 memcpy( filename + len, szlnk, sizeof(szlnk) );
3882 link_file = msi_build_directory_name( 2, link_folder, filename );
3883 msi_free( filename );
3885 return link_file;
3888 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3890 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3891 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3892 WCHAR *folder, *dest, *path;
3894 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3895 folder = msi_dup_property( package->db, szWindowsFolder );
3896 else
3898 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3899 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3900 msi_free( appdata );
3902 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3903 msi_create_full_path( dest );
3904 path = msi_build_directory_name( 2, dest, icon_name );
3905 msi_free( folder );
3906 msi_free( dest );
3907 return path;
3910 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3912 MSIPACKAGE *package = param;
3913 LPWSTR link_file, deformated, path;
3914 LPCWSTR component, target;
3915 MSICOMPONENT *comp;
3916 IShellLinkW *sl = NULL;
3917 IPersistFile *pf = NULL;
3918 HRESULT res;
3920 component = MSI_RecordGetString(row, 4);
3921 comp = msi_get_loaded_component(package, component);
3922 if (!comp)
3923 return ERROR_SUCCESS;
3925 comp->Action = msi_get_component_action( package, comp );
3926 if (comp->Action != INSTALLSTATE_LOCAL)
3928 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3929 return ERROR_SUCCESS;
3931 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3933 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3934 &IID_IShellLinkW, (LPVOID *) &sl );
3936 if (FAILED( res ))
3938 ERR("CLSID_ShellLink not available\n");
3939 goto err;
3942 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3943 if (FAILED( res ))
3945 ERR("QueryInterface(IID_IPersistFile) failed\n");
3946 goto err;
3949 target = MSI_RecordGetString(row, 5);
3950 if (strchrW(target, '['))
3952 deformat_string( package, target, &path );
3953 TRACE("target path is %s\n", debugstr_w(path));
3954 IShellLinkW_SetPath( sl, path );
3955 msi_free( path );
3957 else
3959 FIXME("poorly handled shortcut format, advertised shortcut\n");
3960 path = resolve_keypath( package, comp );
3961 IShellLinkW_SetPath( sl, path );
3962 msi_free( path );
3965 if (!MSI_RecordIsNull(row,6))
3967 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3968 deformat_string(package, arguments, &deformated);
3969 IShellLinkW_SetArguments(sl,deformated);
3970 msi_free(deformated);
3973 if (!MSI_RecordIsNull(row,7))
3975 LPCWSTR description = MSI_RecordGetString(row, 7);
3976 IShellLinkW_SetDescription(sl, description);
3979 if (!MSI_RecordIsNull(row,8))
3980 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3982 if (!MSI_RecordIsNull(row,9))
3984 INT index;
3985 LPCWSTR icon = MSI_RecordGetString(row, 9);
3987 path = msi_build_icon_path(package, icon);
3988 index = MSI_RecordGetInteger(row,10);
3990 /* no value means 0 */
3991 if (index == MSI_NULL_INTEGER)
3992 index = 0;
3994 IShellLinkW_SetIconLocation(sl, path, index);
3995 msi_free(path);
3998 if (!MSI_RecordIsNull(row,11))
3999 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
4001 if (!MSI_RecordIsNull(row,12))
4003 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
4004 full_path = msi_get_target_folder( package, wkdir );
4005 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
4007 link_file = get_link_file(package, row);
4009 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
4010 IPersistFile_Save(pf, link_file, FALSE);
4011 msi_free(link_file);
4013 err:
4014 if (pf)
4015 IPersistFile_Release( pf );
4016 if (sl)
4017 IShellLinkW_Release( sl );
4019 return ERROR_SUCCESS;
4022 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
4024 static const WCHAR query[] = {
4025 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4026 '`','S','h','o','r','t','c','u','t','`',0};
4027 MSIQUERY *view;
4028 HRESULT res;
4029 UINT rc;
4031 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4032 if (rc != ERROR_SUCCESS)
4033 return ERROR_SUCCESS;
4035 res = CoInitialize( NULL );
4037 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4038 msiobj_release(&view->hdr);
4040 if (SUCCEEDED(res)) CoUninitialize();
4041 return rc;
4044 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4046 MSIPACKAGE *package = param;
4047 LPWSTR link_file;
4048 LPCWSTR component;
4049 MSICOMPONENT *comp;
4051 component = MSI_RecordGetString( row, 4 );
4052 comp = msi_get_loaded_component( package, component );
4053 if (!comp)
4054 return ERROR_SUCCESS;
4056 comp->Action = msi_get_component_action( package, comp );
4057 if (comp->Action != INSTALLSTATE_ABSENT)
4059 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4060 return ERROR_SUCCESS;
4062 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
4064 link_file = get_link_file( package, row );
4066 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4067 if (!DeleteFileW( link_file ))
4069 WARN("Failed to remove shortcut file %u\n", GetLastError());
4071 msi_free( link_file );
4073 return ERROR_SUCCESS;
4076 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4078 static const WCHAR query[] = {
4079 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4080 '`','S','h','o','r','t','c','u','t','`',0};
4081 MSIQUERY *view;
4082 UINT rc;
4084 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4085 if (rc != ERROR_SUCCESS)
4086 return ERROR_SUCCESS;
4088 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4089 msiobj_release( &view->hdr );
4090 return rc;
4093 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4095 MSIPACKAGE* package = param;
4096 HANDLE the_file;
4097 LPWSTR FilePath;
4098 LPCWSTR FileName;
4099 CHAR buffer[1024];
4100 DWORD sz;
4101 UINT rc;
4103 FileName = MSI_RecordGetString(row,1);
4104 if (!FileName)
4106 ERR("Unable to get FileName\n");
4107 return ERROR_SUCCESS;
4110 FilePath = msi_build_icon_path(package, FileName);
4112 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4114 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4115 FILE_ATTRIBUTE_NORMAL, NULL);
4117 if (the_file == INVALID_HANDLE_VALUE)
4119 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4120 msi_free(FilePath);
4121 return ERROR_SUCCESS;
4126 DWORD write;
4127 sz = 1024;
4128 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4129 if (rc != ERROR_SUCCESS)
4131 ERR("Failed to get stream\n");
4132 DeleteFileW(FilePath);
4133 break;
4135 WriteFile(the_file,buffer,sz,&write,NULL);
4136 } while (sz == 1024);
4138 msi_free(FilePath);
4139 CloseHandle(the_file);
4141 return ERROR_SUCCESS;
4144 static UINT msi_publish_icons(MSIPACKAGE *package)
4146 static const WCHAR query[]= {
4147 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4148 '`','I','c','o','n','`',0};
4149 MSIQUERY *view;
4150 UINT r;
4152 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4153 if (r == ERROR_SUCCESS)
4155 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4156 msiobj_release(&view->hdr);
4157 if (r != ERROR_SUCCESS)
4158 return r;
4160 return ERROR_SUCCESS;
4163 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4165 UINT r;
4166 HKEY source;
4167 LPWSTR buffer;
4168 MSIMEDIADISK *disk;
4169 MSISOURCELISTINFO *info;
4171 r = RegCreateKeyW(hkey, szSourceList, &source);
4172 if (r != ERROR_SUCCESS)
4173 return r;
4175 RegCloseKey(source);
4177 buffer = strrchrW(package->PackagePath, '\\') + 1;
4178 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4179 package->Context, MSICODE_PRODUCT,
4180 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4181 if (r != ERROR_SUCCESS)
4182 return r;
4184 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4185 package->Context, MSICODE_PRODUCT,
4186 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4187 if (r != ERROR_SUCCESS)
4188 return r;
4190 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4191 package->Context, MSICODE_PRODUCT,
4192 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4193 if (r != ERROR_SUCCESS)
4194 return r;
4196 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4198 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4199 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4200 info->options, info->value);
4201 else
4202 MsiSourceListSetInfoW(package->ProductCode, NULL,
4203 info->context, info->options,
4204 info->property, info->value);
4207 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4209 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4210 disk->context, disk->options,
4211 disk->disk_id, disk->volume_label, disk->disk_prompt);
4214 return ERROR_SUCCESS;
4217 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4219 static const WCHAR szARPProductIcon[] =
4220 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4221 static const WCHAR szAssignment[] =
4222 {'A','s','s','i','g','n','m','e','n','t',0};
4223 static const WCHAR szAdvertiseFlags[] =
4224 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4225 static const WCHAR szClients[] =
4226 {'C','l','i','e','n','t','s',0};
4227 static const WCHAR szColon[] = {':',0};
4228 MSIHANDLE hdb, suminfo;
4229 WCHAR *buffer, *ptr, guids[MAX_PATH], packcode[SQUASHED_GUID_SIZE];
4230 DWORD langid, size;
4231 UINT r;
4233 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4234 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4235 msi_free(buffer);
4237 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4238 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4240 /* FIXME */
4241 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4243 buffer = msi_dup_property(package->db, szARPProductIcon);
4244 if (buffer)
4246 LPWSTR path = msi_build_icon_path(package, buffer);
4247 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4248 msi_free(path);
4249 msi_free(buffer);
4252 buffer = msi_dup_property(package->db, szProductVersion);
4253 if (buffer)
4255 DWORD verdword = msi_version_str_to_dword(buffer);
4256 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4257 msi_free(buffer);
4260 msi_reg_set_val_dword(hkey, szAssignment, 0);
4261 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4262 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4263 msi_reg_set_val_str(hkey, szClients, szColon);
4265 hdb = alloc_msihandle(&package->db->hdr);
4266 if (!hdb)
4267 return ERROR_NOT_ENOUGH_MEMORY;
4269 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4270 MsiCloseHandle(hdb);
4271 if (r != ERROR_SUCCESS)
4272 goto done;
4274 size = MAX_PATH;
4275 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4276 NULL, guids, &size);
4277 if (r != ERROR_SUCCESS)
4278 goto done;
4280 ptr = strchrW(guids, ';');
4281 if (ptr) *ptr = 0;
4282 squash_guid(guids, packcode);
4283 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4285 done:
4286 MsiCloseHandle(suminfo);
4287 return ERROR_SUCCESS;
4290 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4292 UINT r;
4293 HKEY hkey;
4294 WCHAR *upgrade, squashed_pc[SQUASHED_GUID_SIZE];
4296 upgrade = msi_dup_property(package->db, szUpgradeCode);
4297 if (!upgrade)
4298 return ERROR_SUCCESS;
4300 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4301 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4302 else
4303 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4305 if (r != ERROR_SUCCESS)
4307 WARN("failed to open upgrade code key\n");
4308 msi_free(upgrade);
4309 return ERROR_SUCCESS;
4311 squash_guid(package->ProductCode, squashed_pc);
4312 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4313 RegCloseKey(hkey);
4314 msi_free(upgrade);
4315 return ERROR_SUCCESS;
4318 static BOOL msi_check_publish(MSIPACKAGE *package)
4320 MSIFEATURE *feature;
4322 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4324 feature->Action = msi_get_feature_action( package, feature );
4325 if (feature->Action == INSTALLSTATE_LOCAL || feature->Action == INSTALLSTATE_SOURCE)
4326 return TRUE;
4329 return FALSE;
4332 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4334 MSIFEATURE *feature;
4336 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4338 feature->Action = msi_get_feature_action( package, feature );
4339 if (feature->Action != INSTALLSTATE_ABSENT)
4340 return FALSE;
4343 return TRUE;
4346 static UINT msi_publish_patches( MSIPACKAGE *package )
4348 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4349 WCHAR patch_squashed[GUID_SIZE];
4350 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4351 LONG res;
4352 MSIPATCHINFO *patch;
4353 UINT r;
4354 WCHAR *p, *all_patches = NULL;
4355 DWORD len = 0;
4357 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4358 if (r != ERROR_SUCCESS)
4359 return ERROR_FUNCTION_FAILED;
4361 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4362 if (res != ERROR_SUCCESS)
4364 r = ERROR_FUNCTION_FAILED;
4365 goto done;
4368 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4369 if (r != ERROR_SUCCESS)
4370 goto done;
4372 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4374 squash_guid( patch->patchcode, patch_squashed );
4375 len += strlenW( patch_squashed ) + 1;
4378 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4379 if (!all_patches)
4380 goto done;
4382 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4384 HKEY patch_key;
4386 squash_guid( patch->patchcode, p );
4387 p += strlenW( p ) + 1;
4389 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4390 (const BYTE *)patch->transforms,
4391 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4392 if (res != ERROR_SUCCESS)
4393 goto done;
4395 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4396 if (r != ERROR_SUCCESS)
4397 goto done;
4399 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4400 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4401 RegCloseKey( patch_key );
4402 if (res != ERROR_SUCCESS)
4403 goto done;
4405 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4407 res = GetLastError();
4408 ERR("Unable to copy patch package %d\n", res);
4409 goto done;
4411 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4412 if (res != ERROR_SUCCESS)
4413 goto done;
4415 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state,
4416 sizeof(patch->state) );
4417 if (res != ERROR_SUCCESS)
4419 RegCloseKey( patch_key );
4420 goto done;
4423 res = RegSetValueExW( patch_key, szUninstallable, 0, REG_DWORD, (const BYTE *)&patch->uninstallable,
4424 sizeof(patch->uninstallable) );
4425 RegCloseKey( patch_key );
4426 if (res != ERROR_SUCCESS)
4427 goto done;
4430 all_patches[len] = 0;
4431 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4432 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4433 if (res != ERROR_SUCCESS)
4434 goto done;
4436 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4437 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4438 if (res != ERROR_SUCCESS)
4439 r = ERROR_FUNCTION_FAILED;
4441 done:
4442 RegCloseKey( product_patches_key );
4443 RegCloseKey( patches_key );
4444 RegCloseKey( product_key );
4445 msi_free( all_patches );
4446 return r;
4449 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4451 UINT rc;
4452 HKEY hukey = NULL, hudkey = NULL;
4453 MSIRECORD *uirow;
4455 if (!list_empty(&package->patches))
4457 rc = msi_publish_patches(package);
4458 if (rc != ERROR_SUCCESS)
4459 goto end;
4462 /* FIXME: also need to publish if the product is in advertise mode */
4463 if (!msi_check_publish(package))
4464 return ERROR_SUCCESS;
4466 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4467 &hukey, TRUE);
4468 if (rc != ERROR_SUCCESS)
4469 goto end;
4471 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4472 NULL, &hudkey, TRUE);
4473 if (rc != ERROR_SUCCESS)
4474 goto end;
4476 rc = msi_publish_upgrade_code(package);
4477 if (rc != ERROR_SUCCESS)
4478 goto end;
4480 rc = msi_publish_product_properties(package, hukey);
4481 if (rc != ERROR_SUCCESS)
4482 goto end;
4484 rc = msi_publish_sourcelist(package, hukey);
4485 if (rc != ERROR_SUCCESS)
4486 goto end;
4488 rc = msi_publish_icons(package);
4490 end:
4491 uirow = MSI_CreateRecord( 1 );
4492 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4493 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4494 msiobj_release( &uirow->hdr );
4496 RegCloseKey(hukey);
4497 RegCloseKey(hudkey);
4498 return rc;
4501 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4503 WCHAR *filename, *ptr, *folder, *ret;
4504 const WCHAR *dirprop;
4506 filename = msi_dup_record_field( row, 2 );
4507 if (filename && (ptr = strchrW( filename, '|' )))
4508 ptr++;
4509 else
4510 ptr = filename;
4512 dirprop = MSI_RecordGetString( row, 3 );
4513 if (dirprop)
4515 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4516 if (!folder) folder = msi_dup_property( package->db, dirprop );
4518 else
4519 folder = msi_dup_property( package->db, szWindowsFolder );
4521 if (!folder)
4523 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4524 msi_free( filename );
4525 return NULL;
4528 ret = msi_build_directory_name( 2, folder, ptr );
4530 msi_free( filename );
4531 msi_free( folder );
4532 return ret;
4535 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4537 MSIPACKAGE *package = param;
4538 LPCWSTR component, section, key, value, identifier;
4539 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4540 MSIRECORD * uirow;
4541 INT action;
4542 MSICOMPONENT *comp;
4544 component = MSI_RecordGetString(row, 8);
4545 comp = msi_get_loaded_component(package,component);
4546 if (!comp)
4547 return ERROR_SUCCESS;
4549 comp->Action = msi_get_component_action( package, comp );
4550 if (comp->Action != INSTALLSTATE_LOCAL)
4552 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4553 return ERROR_SUCCESS;
4556 identifier = MSI_RecordGetString(row,1);
4557 section = MSI_RecordGetString(row,4);
4558 key = MSI_RecordGetString(row,5);
4559 value = MSI_RecordGetString(row,6);
4560 action = MSI_RecordGetInteger(row,7);
4562 deformat_string(package,section,&deformated_section);
4563 deformat_string(package,key,&deformated_key);
4564 deformat_string(package,value,&deformated_value);
4566 fullname = get_ini_file_name(package, row);
4568 if (action == 0)
4570 TRACE("Adding value %s to section %s in %s\n",
4571 debugstr_w(deformated_key), debugstr_w(deformated_section),
4572 debugstr_w(fullname));
4573 WritePrivateProfileStringW(deformated_section, deformated_key,
4574 deformated_value, fullname);
4576 else if (action == 1)
4578 WCHAR returned[10];
4579 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4580 returned, 10, fullname);
4581 if (returned[0] == 0)
4583 TRACE("Adding value %s to section %s in %s\n",
4584 debugstr_w(deformated_key), debugstr_w(deformated_section),
4585 debugstr_w(fullname));
4587 WritePrivateProfileStringW(deformated_section, deformated_key,
4588 deformated_value, fullname);
4591 else if (action == 3)
4592 FIXME("Append to existing section not yet implemented\n");
4594 uirow = MSI_CreateRecord(4);
4595 MSI_RecordSetStringW(uirow,1,identifier);
4596 MSI_RecordSetStringW(uirow,2,deformated_section);
4597 MSI_RecordSetStringW(uirow,3,deformated_key);
4598 MSI_RecordSetStringW(uirow,4,deformated_value);
4599 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4600 msiobj_release( &uirow->hdr );
4602 msi_free(fullname);
4603 msi_free(deformated_key);
4604 msi_free(deformated_value);
4605 msi_free(deformated_section);
4606 return ERROR_SUCCESS;
4609 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4611 static const WCHAR query[] = {
4612 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4613 '`','I','n','i','F','i','l','e','`',0};
4614 MSIQUERY *view;
4615 UINT rc;
4617 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4618 if (rc != ERROR_SUCCESS)
4619 return ERROR_SUCCESS;
4621 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4622 msiobj_release(&view->hdr);
4623 return rc;
4626 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4628 MSIPACKAGE *package = param;
4629 LPCWSTR component, section, key, value, identifier;
4630 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4631 MSICOMPONENT *comp;
4632 MSIRECORD *uirow;
4633 INT action;
4635 component = MSI_RecordGetString( row, 8 );
4636 comp = msi_get_loaded_component( package, component );
4637 if (!comp)
4638 return ERROR_SUCCESS;
4640 comp->Action = msi_get_component_action( package, comp );
4641 if (comp->Action != INSTALLSTATE_ABSENT)
4643 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4644 return ERROR_SUCCESS;
4647 identifier = MSI_RecordGetString( row, 1 );
4648 section = MSI_RecordGetString( row, 4 );
4649 key = MSI_RecordGetString( row, 5 );
4650 value = MSI_RecordGetString( row, 6 );
4651 action = MSI_RecordGetInteger( row, 7 );
4653 deformat_string( package, section, &deformated_section );
4654 deformat_string( package, key, &deformated_key );
4655 deformat_string( package, value, &deformated_value );
4657 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4659 filename = get_ini_file_name( package, row );
4661 TRACE("Removing key %s from section %s in %s\n",
4662 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4664 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4666 WARN("Unable to remove key %u\n", GetLastError());
4668 msi_free( filename );
4670 else
4671 FIXME("Unsupported action %d\n", action);
4674 uirow = MSI_CreateRecord( 4 );
4675 MSI_RecordSetStringW( uirow, 1, identifier );
4676 MSI_RecordSetStringW( uirow, 2, deformated_section );
4677 MSI_RecordSetStringW( uirow, 3, deformated_key );
4678 MSI_RecordSetStringW( uirow, 4, deformated_value );
4679 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4680 msiobj_release( &uirow->hdr );
4682 msi_free( deformated_key );
4683 msi_free( deformated_value );
4684 msi_free( deformated_section );
4685 return ERROR_SUCCESS;
4688 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4690 MSIPACKAGE *package = param;
4691 LPCWSTR component, section, key, value, identifier;
4692 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4693 MSICOMPONENT *comp;
4694 MSIRECORD *uirow;
4695 INT action;
4697 component = MSI_RecordGetString( row, 8 );
4698 comp = msi_get_loaded_component( package, component );
4699 if (!comp)
4700 return ERROR_SUCCESS;
4702 comp->Action = msi_get_component_action( package, comp );
4703 if (comp->Action != INSTALLSTATE_LOCAL)
4705 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4706 return ERROR_SUCCESS;
4709 identifier = MSI_RecordGetString( row, 1 );
4710 section = MSI_RecordGetString( row, 4 );
4711 key = MSI_RecordGetString( row, 5 );
4712 value = MSI_RecordGetString( row, 6 );
4713 action = MSI_RecordGetInteger( row, 7 );
4715 deformat_string( package, section, &deformated_section );
4716 deformat_string( package, key, &deformated_key );
4717 deformat_string( package, value, &deformated_value );
4719 if (action == msidbIniFileActionRemoveLine)
4721 filename = get_ini_file_name( package, row );
4723 TRACE("Removing key %s from section %s in %s\n",
4724 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4726 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4728 WARN("Unable to remove key %u\n", GetLastError());
4730 msi_free( filename );
4732 else
4733 FIXME("Unsupported action %d\n", action);
4735 uirow = MSI_CreateRecord( 4 );
4736 MSI_RecordSetStringW( uirow, 1, identifier );
4737 MSI_RecordSetStringW( uirow, 2, deformated_section );
4738 MSI_RecordSetStringW( uirow, 3, deformated_key );
4739 MSI_RecordSetStringW( uirow, 4, deformated_value );
4740 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4741 msiobj_release( &uirow->hdr );
4743 msi_free( deformated_key );
4744 msi_free( deformated_value );
4745 msi_free( deformated_section );
4746 return ERROR_SUCCESS;
4749 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4751 static const WCHAR query[] = {
4752 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4753 '`','I','n','i','F','i','l','e','`',0};
4754 static const WCHAR remove_query[] = {
4755 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4756 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4757 MSIQUERY *view;
4758 UINT rc;
4760 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4761 if (rc == ERROR_SUCCESS)
4763 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4764 msiobj_release( &view->hdr );
4765 if (rc != ERROR_SUCCESS)
4766 return rc;
4768 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4769 if (rc == ERROR_SUCCESS)
4771 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4772 msiobj_release( &view->hdr );
4773 if (rc != ERROR_SUCCESS)
4774 return rc;
4776 return ERROR_SUCCESS;
4779 static void register_dll( const WCHAR *dll, BOOL unregister )
4781 static const WCHAR regW[] =
4782 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4783 static const WCHAR unregW[] =
4784 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4785 PROCESS_INFORMATION pi;
4786 STARTUPINFOW si;
4787 WCHAR *cmd;
4789 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4791 if (unregister) sprintfW( cmd, unregW, dll );
4792 else sprintfW( cmd, regW, dll );
4794 memset( &si, 0, sizeof(STARTUPINFOW) );
4795 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4797 CloseHandle( pi.hThread );
4798 msi_dialog_check_messages( pi.hProcess );
4799 CloseHandle( pi.hProcess );
4801 msi_free( cmd );
4804 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4806 MSIPACKAGE *package = param;
4807 LPCWSTR filename;
4808 MSIFILE *file;
4809 MSIRECORD *uirow;
4811 filename = MSI_RecordGetString( row, 1 );
4812 file = msi_get_loaded_file( package, filename );
4813 if (!file)
4815 WARN("unable to find file %s\n", debugstr_w(filename));
4816 return ERROR_SUCCESS;
4818 file->Component->Action = msi_get_component_action( package, file->Component );
4819 if (file->Component->Action != INSTALLSTATE_LOCAL)
4821 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4822 return ERROR_SUCCESS;
4825 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4826 register_dll( file->TargetPath, FALSE );
4828 uirow = MSI_CreateRecord( 2 );
4829 MSI_RecordSetStringW( uirow, 1, file->File );
4830 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4831 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4832 msiobj_release( &uirow->hdr );
4834 return ERROR_SUCCESS;
4837 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4839 static const WCHAR query[] = {
4840 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4841 '`','S','e','l','f','R','e','g','`',0};
4842 MSIQUERY *view;
4843 UINT rc;
4845 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4846 if (rc != ERROR_SUCCESS)
4847 return ERROR_SUCCESS;
4849 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4850 msiobj_release(&view->hdr);
4851 return rc;
4854 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4856 MSIPACKAGE *package = param;
4857 LPCWSTR filename;
4858 MSIFILE *file;
4859 MSIRECORD *uirow;
4861 filename = MSI_RecordGetString( row, 1 );
4862 file = msi_get_loaded_file( package, filename );
4863 if (!file)
4865 WARN("unable to find file %s\n", debugstr_w(filename));
4866 return ERROR_SUCCESS;
4868 file->Component->Action = msi_get_component_action( package, file->Component );
4869 if (file->Component->Action != INSTALLSTATE_ABSENT)
4871 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4872 return ERROR_SUCCESS;
4875 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4876 register_dll( file->TargetPath, TRUE );
4878 uirow = MSI_CreateRecord( 2 );
4879 MSI_RecordSetStringW( uirow, 1, file->File );
4880 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4881 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4882 msiobj_release( &uirow->hdr );
4884 return ERROR_SUCCESS;
4887 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4889 static const WCHAR query[] = {
4890 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4891 '`','S','e','l','f','R','e','g','`',0};
4892 MSIQUERY *view;
4893 UINT rc;
4895 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4896 if (rc != ERROR_SUCCESS)
4897 return ERROR_SUCCESS;
4899 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4900 msiobj_release( &view->hdr );
4901 return rc;
4904 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4906 MSIFEATURE *feature;
4907 UINT rc;
4908 HKEY hkey = NULL, userdata = NULL;
4910 if (!msi_check_publish(package))
4911 return ERROR_SUCCESS;
4913 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4914 &hkey, TRUE);
4915 if (rc != ERROR_SUCCESS)
4916 goto end;
4918 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4919 &userdata, TRUE);
4920 if (rc != ERROR_SUCCESS)
4921 goto end;
4923 /* here the guids are base 85 encoded */
4924 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4926 ComponentList *cl;
4927 LPWSTR data = NULL;
4928 GUID clsid;
4929 INT size;
4930 BOOL absent = FALSE;
4931 MSIRECORD *uirow;
4933 if (feature->Level <= 0) continue;
4935 if (feature->Action != INSTALLSTATE_LOCAL &&
4936 feature->Action != INSTALLSTATE_SOURCE &&
4937 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4939 size = 1;
4940 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4942 size += 21;
4944 if (feature->Feature_Parent)
4945 size += strlenW( feature->Feature_Parent )+2;
4947 data = msi_alloc(size * sizeof(WCHAR));
4949 data[0] = 0;
4950 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4952 MSICOMPONENT* component = cl->component;
4953 WCHAR buf[21];
4955 buf[0] = 0;
4956 if (component->ComponentId)
4958 TRACE("From %s\n",debugstr_w(component->ComponentId));
4959 CLSIDFromString(component->ComponentId, &clsid);
4960 encode_base85_guid(&clsid,buf);
4961 TRACE("to %s\n",debugstr_w(buf));
4962 strcatW(data,buf);
4966 if (feature->Feature_Parent)
4968 static const WCHAR sep[] = {'\2',0};
4969 strcatW(data,sep);
4970 strcatW(data,feature->Feature_Parent);
4973 msi_reg_set_val_str( userdata, feature->Feature, data );
4974 msi_free(data);
4976 size = 0;
4977 if (feature->Feature_Parent)
4978 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4979 if (!absent)
4981 size += sizeof(WCHAR);
4982 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4983 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4985 else
4987 size += 2*sizeof(WCHAR);
4988 data = msi_alloc(size);
4989 data[0] = 0x6;
4990 data[1] = 0;
4991 if (feature->Feature_Parent)
4992 strcpyW( &data[1], feature->Feature_Parent );
4993 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4994 (LPBYTE)data,size);
4995 msi_free(data);
4998 /* the UI chunk */
4999 uirow = MSI_CreateRecord( 1 );
5000 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5001 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5002 msiobj_release( &uirow->hdr );
5003 /* FIXME: call msi_ui_progress? */
5006 end:
5007 RegCloseKey(hkey);
5008 RegCloseKey(userdata);
5009 return rc;
5012 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
5014 UINT r;
5015 HKEY hkey;
5016 MSIRECORD *uirow;
5018 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
5020 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
5021 &hkey, FALSE);
5022 if (r == ERROR_SUCCESS)
5024 RegDeleteValueW(hkey, feature->Feature);
5025 RegCloseKey(hkey);
5028 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
5029 &hkey, FALSE);
5030 if (r == ERROR_SUCCESS)
5032 RegDeleteValueW(hkey, feature->Feature);
5033 RegCloseKey(hkey);
5036 uirow = MSI_CreateRecord( 1 );
5037 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5038 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5039 msiobj_release( &uirow->hdr );
5041 return ERROR_SUCCESS;
5044 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5046 MSIFEATURE *feature;
5048 if (!msi_check_unpublish(package))
5049 return ERROR_SUCCESS;
5051 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5053 msi_unpublish_feature(package, feature);
5056 return ERROR_SUCCESS;
5059 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5061 SYSTEMTIME systime;
5062 DWORD size, langid;
5063 WCHAR date[9], *val, *buffer;
5064 const WCHAR *prop, *key;
5066 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5067 static const WCHAR modpath_fmt[] =
5068 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5069 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5070 static const WCHAR szModifyPath[] =
5071 {'M','o','d','i','f','y','P','a','t','h',0};
5072 static const WCHAR szUninstallString[] =
5073 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5074 static const WCHAR szEstimatedSize[] =
5075 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5076 static const WCHAR szDisplayVersion[] =
5077 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5078 static const WCHAR szInstallSource[] =
5079 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5080 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5081 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5082 static const WCHAR szAuthorizedCDFPrefix[] =
5083 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5084 static const WCHAR szARPCONTACT[] =
5085 {'A','R','P','C','O','N','T','A','C','T',0};
5086 static const WCHAR szContact[] =
5087 {'C','o','n','t','a','c','t',0};
5088 static const WCHAR szARPCOMMENTS[] =
5089 {'A','R','P','C','O','M','M','E','N','T','S',0};
5090 static const WCHAR szComments[] =
5091 {'C','o','m','m','e','n','t','s',0};
5092 static const WCHAR szProductName[] =
5093 {'P','r','o','d','u','c','t','N','a','m','e',0};
5094 static const WCHAR szDisplayName[] =
5095 {'D','i','s','p','l','a','y','N','a','m','e',0};
5096 static const WCHAR szARPHELPLINK[] =
5097 {'A','R','P','H','E','L','P','L','I','N','K',0};
5098 static const WCHAR szHelpLink[] =
5099 {'H','e','l','p','L','i','n','k',0};
5100 static const WCHAR szARPHELPTELEPHONE[] =
5101 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5102 static const WCHAR szHelpTelephone[] =
5103 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5104 static const WCHAR szARPINSTALLLOCATION[] =
5105 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5106 static const WCHAR szManufacturer[] =
5107 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5108 static const WCHAR szPublisher[] =
5109 {'P','u','b','l','i','s','h','e','r',0};
5110 static const WCHAR szARPREADME[] =
5111 {'A','R','P','R','E','A','D','M','E',0};
5112 static const WCHAR szReadme[] =
5113 {'R','e','a','d','M','e',0};
5114 static const WCHAR szARPSIZE[] =
5115 {'A','R','P','S','I','Z','E',0};
5116 static const WCHAR szSize[] =
5117 {'S','i','z','e',0};
5118 static const WCHAR szARPURLINFOABOUT[] =
5119 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5120 static const WCHAR szURLInfoAbout[] =
5121 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5122 static const WCHAR szARPURLUPDATEINFO[] =
5123 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5124 static const WCHAR szURLUpdateInfo[] =
5125 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5126 static const WCHAR szARPSYSTEMCOMPONENT[] =
5127 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5128 static const WCHAR szSystemComponent[] =
5129 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5131 static const WCHAR *propval[] = {
5132 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5133 szARPCONTACT, szContact,
5134 szARPCOMMENTS, szComments,
5135 szProductName, szDisplayName,
5136 szARPHELPLINK, szHelpLink,
5137 szARPHELPTELEPHONE, szHelpTelephone,
5138 szARPINSTALLLOCATION, szInstallLocation,
5139 szSourceDir, szInstallSource,
5140 szManufacturer, szPublisher,
5141 szARPREADME, szReadme,
5142 szARPSIZE, szSize,
5143 szARPURLINFOABOUT, szURLInfoAbout,
5144 szARPURLUPDATEINFO, szURLUpdateInfo,
5145 NULL
5147 const WCHAR **p = propval;
5149 while (*p)
5151 prop = *p++;
5152 key = *p++;
5153 val = msi_dup_property(package->db, prop);
5154 msi_reg_set_val_str(hkey, key, val);
5155 msi_free(val);
5158 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5159 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5161 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5163 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5164 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5165 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5166 msi_free(buffer);
5168 /* FIXME: Write real Estimated Size when we have it */
5169 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5171 GetLocalTime(&systime);
5172 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5173 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5175 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5176 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5178 buffer = msi_dup_property(package->db, szProductVersion);
5179 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5180 if (buffer)
5182 DWORD verdword = msi_version_str_to_dword(buffer);
5184 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5185 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5186 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5187 msi_free(buffer);
5190 return ERROR_SUCCESS;
5193 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5195 WCHAR *upgrade_code, squashed_pc[SQUASHED_GUID_SIZE];
5196 MSIRECORD *uirow;
5197 HKEY hkey, props, upgrade_key;
5198 UINT rc;
5200 /* FIXME: also need to publish if the product is in advertise mode */
5201 if (!msi_check_publish(package))
5202 return ERROR_SUCCESS;
5204 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5205 if (rc != ERROR_SUCCESS)
5206 return rc;
5208 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5209 if (rc != ERROR_SUCCESS)
5210 goto done;
5212 rc = msi_publish_install_properties(package, hkey);
5213 if (rc != ERROR_SUCCESS)
5214 goto done;
5216 rc = msi_publish_install_properties(package, props);
5217 if (rc != ERROR_SUCCESS)
5218 goto done;
5220 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5221 if (upgrade_code)
5223 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5224 if (rc == ERROR_SUCCESS)
5226 squash_guid( package->ProductCode, squashed_pc );
5227 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5228 RegCloseKey( upgrade_key );
5230 msi_free( upgrade_code );
5232 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5233 package->delete_on_close = FALSE;
5235 done:
5236 uirow = MSI_CreateRecord( 1 );
5237 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5238 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5239 msiobj_release( &uirow->hdr );
5241 RegCloseKey(hkey);
5242 return ERROR_SUCCESS;
5245 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5247 return execute_script(package, SCRIPT_INSTALL);
5250 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5252 MSIPACKAGE *package = param;
5253 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5254 WCHAR *p, *icon_path;
5256 if (!icon) return ERROR_SUCCESS;
5257 if ((icon_path = msi_build_icon_path( package, icon )))
5259 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5260 DeleteFileW( icon_path );
5261 if ((p = strrchrW( icon_path, '\\' )))
5263 *p = 0;
5264 RemoveDirectoryW( icon_path );
5266 msi_free( icon_path );
5268 return ERROR_SUCCESS;
5271 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5273 static const WCHAR query[]= {
5274 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5275 MSIQUERY *view;
5276 UINT r;
5278 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5279 if (r == ERROR_SUCCESS)
5281 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5282 msiobj_release( &view->hdr );
5283 if (r != ERROR_SUCCESS)
5284 return r;
5286 return ERROR_SUCCESS;
5289 static void remove_product_upgrade_code( MSIPACKAGE *package )
5291 WCHAR *code, product[SQUASHED_GUID_SIZE];
5292 HKEY hkey;
5293 LONG res;
5294 DWORD count;
5296 squash_guid( package->ProductCode, product );
5297 if (!(code = msi_dup_property( package->db, szUpgradeCode )))
5299 WARN( "upgrade code not found\n" );
5300 return;
5302 if (!MSIREG_OpenUpgradeCodesKey( code, &hkey, FALSE ))
5304 RegDeleteValueW( hkey, product );
5305 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5306 RegCloseKey( hkey );
5307 if (!res && !count) MSIREG_DeleteUpgradeCodesKey( code );
5309 if (!MSIREG_OpenUserUpgradeCodesKey( code, &hkey, FALSE ))
5311 RegDeleteValueW( hkey, product );
5312 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5313 RegCloseKey( hkey );
5314 if (!res && !count) MSIREG_DeleteUserUpgradeCodesKey( code );
5316 if (!MSIREG_OpenClassesUpgradeCodesKey( code, &hkey, FALSE ))
5318 RegDeleteValueW( hkey, product );
5319 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5320 RegCloseKey( hkey );
5321 if (!res && !count) MSIREG_DeleteClassesUpgradeCodesKey( code );
5324 msi_free( code );
5327 static UINT ACTION_UnpublishProduct(MSIPACKAGE *package)
5329 MSIPATCHINFO *patch;
5331 MSIREG_DeleteProductKey(package->ProductCode);
5332 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5333 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5335 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5336 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5337 MSIREG_DeleteUserProductKey(package->ProductCode);
5338 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5340 remove_product_upgrade_code( package );
5342 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5344 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5345 if (!strcmpW( package->ProductCode, patch->products ))
5347 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5348 patch->delete_on_close = TRUE;
5350 /* FIXME: remove local patch package if this is the last product */
5352 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5353 package->delete_on_close = TRUE;
5355 msi_unpublish_icons( package );
5356 return ERROR_SUCCESS;
5359 static BOOL is_full_uninstall( MSIPACKAGE *package )
5361 MSIFEATURE *feature;
5363 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5365 if (feature->Action != INSTALLSTATE_ABSENT &&
5366 (feature->Installed != INSTALLSTATE_ABSENT || feature->Action != INSTALLSTATE_UNKNOWN))
5367 return FALSE;
5370 return TRUE;
5373 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5375 UINT rc;
5377 /* first do the same as an InstallExecute */
5378 rc = execute_script(package, SCRIPT_INSTALL);
5379 if (rc != ERROR_SUCCESS)
5380 return rc;
5382 /* then handle commit actions */
5383 rc = execute_script(package, SCRIPT_COMMIT);
5384 if (rc != ERROR_SUCCESS)
5385 return rc;
5387 if (is_full_uninstall(package))
5388 rc = ACTION_UnpublishProduct(package);
5390 return rc;
5393 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5395 static const WCHAR RunOnce[] = {
5396 'S','o','f','t','w','a','r','e','\\',
5397 'M','i','c','r','o','s','o','f','t','\\',
5398 'W','i','n','d','o','w','s','\\',
5399 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5400 'R','u','n','O','n','c','e',0};
5401 static const WCHAR InstallRunOnce[] = {
5402 'S','o','f','t','w','a','r','e','\\',
5403 'M','i','c','r','o','s','o','f','t','\\',
5404 'W','i','n','d','o','w','s','\\',
5405 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5406 'I','n','s','t','a','l','l','e','r','\\',
5407 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5409 static const WCHAR msiexec_fmt[] = {
5410 '%','s',
5411 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5412 '\"','%','s','\"',0};
5413 static const WCHAR install_fmt[] = {
5414 '/','I',' ','\"','%','s','\"',' ',
5415 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5416 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5417 WCHAR buffer[256], sysdir[MAX_PATH], squashed_pc[SQUASHED_GUID_SIZE];
5418 HKEY hkey;
5420 squash_guid( package->ProductCode, squashed_pc );
5422 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5423 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5424 snprintfW( buffer, sizeof(buffer)/sizeof(buffer[0]), msiexec_fmt, sysdir, squashed_pc );
5426 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5427 RegCloseKey(hkey);
5429 TRACE("Reboot command %s\n",debugstr_w(buffer));
5431 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5432 sprintfW( buffer, install_fmt, package->ProductCode, squashed_pc );
5434 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5435 RegCloseKey(hkey);
5437 return ERROR_INSTALL_SUSPEND;
5440 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5442 DWORD attrib;
5443 UINT rc;
5446 * We are currently doing what should be done here in the top level Install
5447 * however for Administrative and uninstalls this step will be needed
5449 if (!package->PackagePath)
5450 return ERROR_SUCCESS;
5452 msi_set_sourcedir_props(package, TRUE);
5454 attrib = GetFileAttributesW(package->db->path);
5455 if (attrib == INVALID_FILE_ATTRIBUTES)
5457 MSIRECORD *record;
5458 LPWSTR prompt;
5459 DWORD size = 0;
5461 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5462 package->Context, MSICODE_PRODUCT,
5463 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5464 if (rc == ERROR_MORE_DATA)
5466 prompt = msi_alloc(size * sizeof(WCHAR));
5467 MsiSourceListGetInfoW(package->ProductCode, NULL,
5468 package->Context, MSICODE_PRODUCT,
5469 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5471 else
5472 prompt = strdupW(package->db->path);
5474 record = MSI_CreateRecord(2);
5475 MSI_RecordSetInteger(record, 1, MSIERR_INSERTDISK);
5476 MSI_RecordSetStringW(record, 2, prompt);
5477 msi_free(prompt);
5478 while(attrib == INVALID_FILE_ATTRIBUTES)
5480 MSI_RecordSetStringW(record, 0, NULL);
5481 rc = MSI_ProcessMessage(package, INSTALLMESSAGE_ERROR, record);
5482 if (rc == IDCANCEL)
5483 return ERROR_INSTALL_USEREXIT;
5484 attrib = GetFileAttributesW(package->db->path);
5486 rc = ERROR_SUCCESS;
5488 else
5489 return ERROR_SUCCESS;
5491 return rc;
5494 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5496 HKEY hkey = 0;
5497 LPWSTR buffer, productid = NULL;
5498 UINT i, rc = ERROR_SUCCESS;
5499 MSIRECORD *uirow;
5501 static const WCHAR szPropKeys[][80] =
5503 {'P','r','o','d','u','c','t','I','D',0},
5504 {'U','S','E','R','N','A','M','E',0},
5505 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5506 {0},
5509 static const WCHAR szRegKeys[][80] =
5511 {'P','r','o','d','u','c','t','I','D',0},
5512 {'R','e','g','O','w','n','e','r',0},
5513 {'R','e','g','C','o','m','p','a','n','y',0},
5514 {0},
5517 if (msi_check_unpublish(package))
5519 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5520 goto end;
5523 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5524 if (!productid)
5525 goto end;
5527 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5528 NULL, &hkey, TRUE);
5529 if (rc != ERROR_SUCCESS)
5530 goto end;
5532 for( i = 0; szPropKeys[i][0]; i++ )
5534 buffer = msi_dup_property( package->db, szPropKeys[i] );
5535 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5536 msi_free( buffer );
5539 end:
5540 uirow = MSI_CreateRecord( 1 );
5541 MSI_RecordSetStringW( uirow, 1, productid );
5542 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5543 msiobj_release( &uirow->hdr );
5545 msi_free(productid);
5546 RegCloseKey(hkey);
5547 return rc;
5550 static UINT iterate_properties(MSIRECORD *record, void *param)
5552 static const WCHAR prop_template[] =
5553 {'P','r','o','p','e','r','t','y','(','S',')',':',' ','[','1',']',' ','=',' ','[','2',']',0};
5554 MSIRECORD *uirow;
5556 uirow = MSI_CloneRecord(record);
5557 if (!uirow) return ERROR_OUTOFMEMORY;
5558 MSI_RecordSetStringW(uirow, 0, prop_template);
5559 MSI_ProcessMessage(param, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow);
5560 msiobj_release(&uirow->hdr);
5562 return ERROR_SUCCESS;
5566 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5568 static const WCHAR prop_query[] =
5569 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','_','P','r','o','p','e','r','t','y','`',0};
5570 WCHAR *productname;
5571 WCHAR *action;
5572 WCHAR *info_template;
5573 MSIQUERY *view;
5574 MSIRECORD *uirow, *uirow_info;
5575 UINT rc;
5577 /* Send COMMONDATA and INFO messages. */
5578 /* FIXME: when should these messages be sent? [see also MsiOpenPackage()] */
5579 uirow = MSI_CreateRecord(3);
5580 if (!uirow) return ERROR_OUTOFMEMORY;
5581 MSI_RecordSetStringW(uirow, 0, NULL);
5582 MSI_RecordSetInteger(uirow, 1, 0);
5583 MSI_RecordSetInteger(uirow, 2, package->num_langids ? package->langids[0] : 0);
5584 MSI_RecordSetInteger(uirow, 3, msi_get_string_table_codepage(package->db->strings));
5585 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5586 /* FIXME: send INSTALLMESSAGE_PROGRESS */
5587 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5589 if (!(needs_ui_sequence(package) && ui_sequence_exists(package)))
5591 uirow_info = MSI_CreateRecord(0);
5592 if (!uirow_info)
5594 msiobj_release(&uirow->hdr);
5595 return ERROR_OUTOFMEMORY;
5597 info_template = msi_get_error_message(package->db, MSIERR_INFO_LOGGINGSTART);
5598 MSI_RecordSetStringW(uirow_info, 0, info_template);
5599 msi_free(info_template);
5600 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow_info);
5601 msiobj_release(&uirow_info->hdr);
5604 MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
5606 productname = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
5607 MSI_RecordSetInteger(uirow, 1, 1);
5608 MSI_RecordSetStringW(uirow, 2, productname);
5609 MSI_RecordSetStringW(uirow, 3, NULL);
5610 MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
5611 msiobj_release(&uirow->hdr);
5613 package->LastActionResult = MSI_NULL_INTEGER;
5615 action = msi_dup_property(package->db, szEXECUTEACTION);
5616 if (!action) action = msi_strdupW(szINSTALL, strlenW(szINSTALL));
5618 /* Perform the action. Top-level actions trigger a sequence. */
5619 if (!strcmpW(action, szINSTALL))
5621 /* Send ACTIONSTART/INFO and INSTALLSTART. */
5622 ui_actionstart(package, szINSTALL, NULL, NULL);
5623 ui_actioninfo(package, szINSTALL, TRUE, 0);
5624 uirow = MSI_CreateRecord(2);
5625 if (!uirow)
5627 rc = ERROR_OUTOFMEMORY;
5628 goto end;
5630 MSI_RecordSetStringW(uirow, 0, NULL);
5631 MSI_RecordSetStringW(uirow, 1, productname);
5632 MSI_RecordSetStringW(uirow, 2, package->ProductCode);
5633 MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLSTART, uirow);
5634 msiobj_release(&uirow->hdr);
5636 /* Perform the installation. Always use the ExecuteSequence. */
5637 package->InWhatSequence |= SEQUENCE_EXEC;
5638 rc = ACTION_ProcessExecSequence(package);
5640 /* Send return value and INSTALLEND. */
5641 ui_actioninfo(package, szINSTALL, FALSE, !rc);
5642 uirow = MSI_CreateRecord(3);
5643 if (!uirow)
5645 rc = ERROR_OUTOFMEMORY;
5646 goto end;
5648 MSI_RecordSetStringW(uirow, 0, NULL);
5649 MSI_RecordSetStringW(uirow, 1, productname);
5650 MSI_RecordSetStringW(uirow, 2, package->ProductCode);
5651 MSI_RecordSetInteger(uirow, 3, !rc);
5652 MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLEND, uirow);
5653 msiobj_release(&uirow->hdr);
5655 else
5656 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
5658 /* Send all set properties. */
5659 if (!MSI_OpenQuery(package->db, &view, prop_query))
5661 MSI_IterateRecords(view, NULL, iterate_properties, package);
5662 msiobj_release(&view->hdr);
5665 /* And finally, toggle the cancel off and on. */
5666 uirow = MSI_CreateRecord(2);
5667 if (!uirow)
5669 rc = ERROR_OUTOFMEMORY;
5670 goto end;
5672 MSI_RecordSetStringW(uirow, 0, NULL);
5673 MSI_RecordSetInteger(uirow, 1, 2);
5674 MSI_RecordSetInteger(uirow, 2, 0);
5675 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5676 MSI_RecordSetInteger(uirow, 2, 1);
5677 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5678 msiobj_release(&uirow->hdr);
5680 end:
5681 msi_free(productname);
5682 msi_free(action);
5683 return rc;
5686 static UINT ACTION_INSTALL(MSIPACKAGE *package)
5688 msi_set_property(package->db, szEXECUTEACTION, szINSTALL, -1);
5689 if (needs_ui_sequence(package) && ui_sequence_exists(package))
5691 package->InWhatSequence |= SEQUENCE_UI;
5692 return ACTION_ProcessUISequence(package);
5694 else
5695 return ACTION_ExecuteAction(package);
5698 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5700 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5701 WCHAR productid_85[21], component_85[21], *ret;
5702 GUID clsid;
5703 DWORD sz;
5705 /* > is used if there is a component GUID and < if not. */
5707 productid_85[0] = 0;
5708 component_85[0] = 0;
5709 CLSIDFromString( package->ProductCode, &clsid );
5711 encode_base85_guid( &clsid, productid_85 );
5712 if (component)
5714 CLSIDFromString( component->ComponentId, &clsid );
5715 encode_base85_guid( &clsid, component_85 );
5718 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5719 debugstr_w(component_85));
5721 sz = 20 + strlenW( feature ) + 20 + 3;
5722 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5723 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5724 return ret;
5727 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5729 MSIPACKAGE *package = param;
5730 LPCWSTR compgroupid, component, feature, qualifier, text;
5731 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5732 HKEY hkey = NULL;
5733 UINT rc;
5734 MSICOMPONENT *comp;
5735 MSIFEATURE *feat;
5736 DWORD sz;
5737 MSIRECORD *uirow;
5738 int len;
5740 feature = MSI_RecordGetString(rec, 5);
5741 feat = msi_get_loaded_feature(package, feature);
5742 if (!feat)
5743 return ERROR_SUCCESS;
5745 feat->Action = msi_get_feature_action( package, feat );
5746 if (feat->Action != INSTALLSTATE_LOCAL &&
5747 feat->Action != INSTALLSTATE_SOURCE &&
5748 feat->Action != INSTALLSTATE_ADVERTISED)
5750 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5751 return ERROR_SUCCESS;
5754 component = MSI_RecordGetString(rec, 3);
5755 comp = msi_get_loaded_component(package, component);
5756 if (!comp)
5757 return ERROR_SUCCESS;
5759 compgroupid = MSI_RecordGetString(rec,1);
5760 qualifier = MSI_RecordGetString(rec,2);
5762 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5763 if (rc != ERROR_SUCCESS)
5764 goto end;
5766 advertise = msi_create_component_advertise_string( package, comp, feature );
5767 text = MSI_RecordGetString( rec, 4 );
5768 if (text)
5770 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5771 strcpyW( p, advertise );
5772 strcatW( p, text );
5773 msi_free( advertise );
5774 advertise = p;
5776 existing = msi_reg_get_val_str( hkey, qualifier );
5778 sz = strlenW( advertise ) + 1;
5779 if (existing)
5781 for (p = existing; *p; p += len)
5783 len = strlenW( p ) + 1;
5784 if (strcmpW( advertise, p )) sz += len;
5787 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5789 rc = ERROR_OUTOFMEMORY;
5790 goto end;
5792 q = output;
5793 if (existing)
5795 for (p = existing; *p; p += len)
5797 len = strlenW( p ) + 1;
5798 if (strcmpW( advertise, p ))
5800 memcpy( q, p, len * sizeof(WCHAR) );
5801 q += len;
5805 strcpyW( q, advertise );
5806 q[strlenW( q ) + 1] = 0;
5808 msi_reg_set_val_multi_str( hkey, qualifier, output );
5810 end:
5811 RegCloseKey(hkey);
5812 msi_free( output );
5813 msi_free( advertise );
5814 msi_free( existing );
5816 /* the UI chunk */
5817 uirow = MSI_CreateRecord( 2 );
5818 MSI_RecordSetStringW( uirow, 1, compgroupid );
5819 MSI_RecordSetStringW( uirow, 2, qualifier);
5820 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5821 msiobj_release( &uirow->hdr );
5822 /* FIXME: call ui_progress? */
5824 return rc;
5828 * At present I am ignorning the advertised components part of this and only
5829 * focusing on the qualified component sets
5831 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5833 static const WCHAR query[] = {
5834 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5835 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5836 MSIQUERY *view;
5837 UINT rc;
5839 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5840 if (rc != ERROR_SUCCESS)
5841 return ERROR_SUCCESS;
5843 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5844 msiobj_release(&view->hdr);
5845 return rc;
5848 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5850 static const WCHAR szInstallerComponents[] = {
5851 'S','o','f','t','w','a','r','e','\\',
5852 'M','i','c','r','o','s','o','f','t','\\',
5853 'I','n','s','t','a','l','l','e','r','\\',
5854 'C','o','m','p','o','n','e','n','t','s','\\',0};
5856 MSIPACKAGE *package = param;
5857 LPCWSTR compgroupid, component, feature, qualifier;
5858 MSICOMPONENT *comp;
5859 MSIFEATURE *feat;
5860 MSIRECORD *uirow;
5861 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5862 LONG res;
5864 feature = MSI_RecordGetString( rec, 5 );
5865 feat = msi_get_loaded_feature( package, feature );
5866 if (!feat)
5867 return ERROR_SUCCESS;
5869 feat->Action = msi_get_feature_action( package, feat );
5870 if (feat->Action != INSTALLSTATE_ABSENT)
5872 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5873 return ERROR_SUCCESS;
5876 component = MSI_RecordGetString( rec, 3 );
5877 comp = msi_get_loaded_component( package, component );
5878 if (!comp)
5879 return ERROR_SUCCESS;
5881 compgroupid = MSI_RecordGetString( rec, 1 );
5882 qualifier = MSI_RecordGetString( rec, 2 );
5884 squash_guid( compgroupid, squashed );
5885 strcpyW( keypath, szInstallerComponents );
5886 strcatW( keypath, squashed );
5888 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5889 if (res != ERROR_SUCCESS)
5891 WARN("Unable to delete component key %d\n", res);
5894 uirow = MSI_CreateRecord( 2 );
5895 MSI_RecordSetStringW( uirow, 1, compgroupid );
5896 MSI_RecordSetStringW( uirow, 2, qualifier );
5897 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5898 msiobj_release( &uirow->hdr );
5900 return ERROR_SUCCESS;
5903 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5905 static const WCHAR query[] = {
5906 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5907 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5908 MSIQUERY *view;
5909 UINT rc;
5911 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5912 if (rc != ERROR_SUCCESS)
5913 return ERROR_SUCCESS;
5915 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5916 msiobj_release( &view->hdr );
5917 return rc;
5920 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5922 static const WCHAR query[] =
5923 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5924 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5925 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5926 MSIPACKAGE *package = param;
5927 MSICOMPONENT *component;
5928 MSIRECORD *row;
5929 MSIFILE *file;
5930 SC_HANDLE hscm = NULL, service = NULL;
5931 LPCWSTR comp, key;
5932 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5933 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5934 DWORD serv_type, start_type, err_control;
5935 BOOL is_vital;
5936 SERVICE_DESCRIPTIONW sd = {NULL};
5937 UINT ret = ERROR_SUCCESS;
5939 comp = MSI_RecordGetString( rec, 12 );
5940 component = msi_get_loaded_component( package, comp );
5941 if (!component)
5943 WARN("service component not found\n");
5944 goto done;
5946 component->Action = msi_get_component_action( package, component );
5947 if (component->Action != INSTALLSTATE_LOCAL)
5949 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5950 goto done;
5952 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5953 if (!hscm)
5955 ERR("Failed to open the SC Manager!\n");
5956 goto done;
5959 start_type = MSI_RecordGetInteger(rec, 5);
5960 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5961 goto done;
5963 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5964 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5965 serv_type = MSI_RecordGetInteger(rec, 4);
5966 err_control = MSI_RecordGetInteger(rec, 6);
5967 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5968 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5969 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5970 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5971 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5972 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5974 /* Should the complete install fail if CreateService fails? */
5975 is_vital = (err_control & msidbServiceInstallErrorControlVital);
5977 /* Remove the msidbServiceInstallErrorControlVital-flag from err_control.
5978 CreateService (under Windows) would fail if not. */
5979 err_control &= ~msidbServiceInstallErrorControlVital;
5981 /* fetch the service path */
5982 row = MSI_QueryGetRecord(package->db, query, comp);
5983 if (!row)
5985 ERR("Query failed\n");
5986 goto done;
5988 if (!(key = MSI_RecordGetString(row, 6)))
5990 msiobj_release(&row->hdr);
5991 goto done;
5993 file = msi_get_loaded_file(package, key);
5994 msiobj_release(&row->hdr);
5995 if (!file)
5997 ERR("Failed to load the service file\n");
5998 goto done;
6001 if (!args || !args[0]) image_path = file->TargetPath;
6002 else
6004 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
6005 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
6007 ret = ERROR_OUTOFMEMORY;
6008 goto done;
6011 strcpyW(image_path, file->TargetPath);
6012 strcatW(image_path, szSpace);
6013 strcatW(image_path, args);
6015 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
6016 start_type, err_control, image_path, load_order,
6017 NULL, depends, serv_name, pass);
6019 if (!service)
6021 if (GetLastError() != ERROR_SERVICE_EXISTS)
6023 WARN("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
6024 if (is_vital)
6025 ret = ERROR_INSTALL_FAILURE;
6029 else if (sd.lpDescription)
6031 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
6032 WARN("failed to set service description %u\n", GetLastError());
6035 if (image_path != file->TargetPath) msi_free(image_path);
6036 done:
6037 if (service) CloseServiceHandle(service);
6038 if (hscm) CloseServiceHandle(hscm);
6039 msi_free(name);
6040 msi_free(disp);
6041 msi_free(sd.lpDescription);
6042 msi_free(load_order);
6043 msi_free(serv_name);
6044 msi_free(pass);
6045 msi_free(depends);
6046 msi_free(args);
6048 return ret;
6051 static UINT ACTION_InstallServices( MSIPACKAGE *package )
6053 static const WCHAR query[] = {
6054 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6055 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
6056 MSIQUERY *view;
6057 UINT rc;
6059 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6060 if (rc != ERROR_SUCCESS)
6061 return ERROR_SUCCESS;
6063 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
6064 msiobj_release(&view->hdr);
6065 return rc;
6068 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
6069 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
6071 LPCWSTR *vector, *temp_vector;
6072 LPWSTR p, q;
6073 DWORD sep_len;
6075 static const WCHAR separator[] = {'[','~',']',0};
6077 *numargs = 0;
6078 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
6080 if (!args)
6081 return NULL;
6083 vector = msi_alloc(sizeof(LPWSTR));
6084 if (!vector)
6085 return NULL;
6087 p = args;
6090 (*numargs)++;
6091 vector[*numargs - 1] = p;
6093 if ((q = strstrW(p, separator)))
6095 *q = '\0';
6097 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
6098 if (!temp_vector)
6100 msi_free(vector);
6101 return NULL;
6103 vector = temp_vector;
6105 p = q + sep_len;
6107 } while (q);
6109 return vector;
6112 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
6114 MSIPACKAGE *package = param;
6115 MSICOMPONENT *comp;
6116 MSIRECORD *uirow;
6117 SC_HANDLE scm = NULL, service = NULL;
6118 LPCWSTR component, *vector = NULL;
6119 LPWSTR name, args, display_name = NULL;
6120 DWORD event, numargs, len, wait, dummy;
6121 UINT r = ERROR_FUNCTION_FAILED;
6122 SERVICE_STATUS_PROCESS status;
6123 ULONGLONG start_time;
6125 component = MSI_RecordGetString(rec, 6);
6126 comp = msi_get_loaded_component(package, component);
6127 if (!comp)
6128 return ERROR_SUCCESS;
6130 event = MSI_RecordGetInteger( rec, 3 );
6131 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6133 comp->Action = msi_get_component_action( package, comp );
6134 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
6135 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
6137 TRACE("not starting %s\n", debugstr_w(name));
6138 msi_free( name );
6139 return ERROR_SUCCESS;
6142 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
6143 wait = MSI_RecordGetInteger(rec, 5);
6145 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
6146 if (!scm)
6148 ERR("Failed to open the service control manager\n");
6149 goto done;
6152 len = 0;
6153 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6154 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6156 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6157 GetServiceDisplayNameW( scm, name, display_name, &len );
6160 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
6161 if (!service)
6163 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6164 goto done;
6167 vector = msi_service_args_to_vector(args, &numargs);
6169 if (!StartServiceW(service, numargs, vector) &&
6170 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6172 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6173 goto done;
6176 r = ERROR_SUCCESS;
6177 if (wait)
6179 /* wait for at most 30 seconds for the service to be up and running */
6180 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6181 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6183 TRACE("failed to query service status (%u)\n", GetLastError());
6184 goto done;
6186 start_time = GetTickCount64();
6187 while (status.dwCurrentState == SERVICE_START_PENDING)
6189 if (GetTickCount64() - start_time > 30000) break;
6190 Sleep(1000);
6191 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6192 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6194 TRACE("failed to query service status (%u)\n", GetLastError());
6195 goto done;
6198 if (status.dwCurrentState != SERVICE_RUNNING)
6200 WARN("service failed to start %u\n", status.dwCurrentState);
6201 r = ERROR_FUNCTION_FAILED;
6205 done:
6206 uirow = MSI_CreateRecord( 2 );
6207 MSI_RecordSetStringW( uirow, 1, display_name );
6208 MSI_RecordSetStringW( uirow, 2, name );
6209 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6210 msiobj_release( &uirow->hdr );
6212 if (service) CloseServiceHandle(service);
6213 if (scm) CloseServiceHandle(scm);
6215 msi_free(name);
6216 msi_free(args);
6217 msi_free(vector);
6218 msi_free(display_name);
6219 return r;
6222 static UINT ACTION_StartServices( MSIPACKAGE *package )
6224 static const WCHAR query[] = {
6225 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6226 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6227 MSIQUERY *view;
6228 UINT rc;
6230 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6231 if (rc != ERROR_SUCCESS)
6232 return ERROR_SUCCESS;
6234 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6235 msiobj_release(&view->hdr);
6236 return rc;
6239 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6241 DWORD i, needed, count;
6242 ENUM_SERVICE_STATUSW *dependencies;
6243 SERVICE_STATUS ss;
6244 SC_HANDLE depserv;
6245 BOOL stopped, ret = FALSE;
6247 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6248 0, &needed, &count))
6249 return TRUE;
6251 if (GetLastError() != ERROR_MORE_DATA)
6252 return FALSE;
6254 dependencies = msi_alloc(needed);
6255 if (!dependencies)
6256 return FALSE;
6258 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6259 needed, &needed, &count))
6260 goto done;
6262 for (i = 0; i < count; i++)
6264 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6265 SERVICE_STOP | SERVICE_QUERY_STATUS);
6266 if (!depserv)
6267 goto done;
6269 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6270 CloseServiceHandle(depserv);
6271 if (!stopped)
6272 goto done;
6275 ret = TRUE;
6277 done:
6278 msi_free(dependencies);
6279 return ret;
6282 static UINT stop_service( LPCWSTR name )
6284 SC_HANDLE scm = NULL, service = NULL;
6285 SERVICE_STATUS status;
6286 SERVICE_STATUS_PROCESS ssp;
6287 DWORD needed;
6289 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6290 if (!scm)
6292 WARN("Failed to open the SCM: %d\n", GetLastError());
6293 goto done;
6296 service = OpenServiceW(scm, name,
6297 SERVICE_STOP |
6298 SERVICE_QUERY_STATUS |
6299 SERVICE_ENUMERATE_DEPENDENTS);
6300 if (!service)
6302 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6303 goto done;
6306 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6307 sizeof(SERVICE_STATUS_PROCESS), &needed))
6309 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6310 goto done;
6313 if (ssp.dwCurrentState == SERVICE_STOPPED)
6314 goto done;
6316 stop_service_dependents(scm, service);
6318 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6319 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6321 done:
6322 if (service) CloseServiceHandle(service);
6323 if (scm) CloseServiceHandle(scm);
6325 return ERROR_SUCCESS;
6328 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6330 MSIPACKAGE *package = param;
6331 MSICOMPONENT *comp;
6332 MSIRECORD *uirow;
6333 LPCWSTR component;
6334 WCHAR *name, *display_name = NULL;
6335 DWORD event, len;
6336 SC_HANDLE scm;
6338 component = MSI_RecordGetString( rec, 6 );
6339 comp = msi_get_loaded_component( package, component );
6340 if (!comp)
6341 return ERROR_SUCCESS;
6343 event = MSI_RecordGetInteger( rec, 3 );
6344 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6346 comp->Action = msi_get_component_action( package, comp );
6347 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6348 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6350 TRACE("not stopping %s\n", debugstr_w(name));
6351 msi_free( name );
6352 return ERROR_SUCCESS;
6355 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6356 if (!scm)
6358 ERR("Failed to open the service control manager\n");
6359 goto done;
6362 len = 0;
6363 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6364 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6366 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6367 GetServiceDisplayNameW( scm, name, display_name, &len );
6369 CloseServiceHandle( scm );
6371 stop_service( name );
6373 done:
6374 uirow = MSI_CreateRecord( 2 );
6375 MSI_RecordSetStringW( uirow, 1, display_name );
6376 MSI_RecordSetStringW( uirow, 2, name );
6377 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6378 msiobj_release( &uirow->hdr );
6380 msi_free( name );
6381 msi_free( display_name );
6382 return ERROR_SUCCESS;
6385 static UINT ACTION_StopServices( MSIPACKAGE *package )
6387 static const WCHAR query[] = {
6388 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6389 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6390 MSIQUERY *view;
6391 UINT rc;
6393 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6394 if (rc != ERROR_SUCCESS)
6395 return ERROR_SUCCESS;
6397 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6398 msiobj_release(&view->hdr);
6399 return rc;
6402 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6404 MSIPACKAGE *package = param;
6405 MSICOMPONENT *comp;
6406 MSIRECORD *uirow;
6407 LPWSTR name = NULL, display_name = NULL;
6408 DWORD event, len;
6409 SC_HANDLE scm = NULL, service = NULL;
6411 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6412 if (!comp)
6413 return ERROR_SUCCESS;
6415 event = MSI_RecordGetInteger( rec, 3 );
6416 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6418 comp->Action = msi_get_component_action( package, comp );
6419 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6420 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6422 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6423 msi_free( name );
6424 return ERROR_SUCCESS;
6426 stop_service( name );
6428 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6429 if (!scm)
6431 WARN("Failed to open the SCM: %d\n", GetLastError());
6432 goto done;
6435 len = 0;
6436 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6437 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6439 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6440 GetServiceDisplayNameW( scm, name, display_name, &len );
6443 service = OpenServiceW( scm, name, DELETE );
6444 if (!service)
6446 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6447 goto done;
6450 if (!DeleteService( service ))
6451 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6453 done:
6454 uirow = MSI_CreateRecord( 2 );
6455 MSI_RecordSetStringW( uirow, 1, display_name );
6456 MSI_RecordSetStringW( uirow, 2, name );
6457 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6458 msiobj_release( &uirow->hdr );
6460 if (service) CloseServiceHandle( service );
6461 if (scm) CloseServiceHandle( scm );
6462 msi_free( name );
6463 msi_free( display_name );
6465 return ERROR_SUCCESS;
6468 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6470 static const WCHAR query[] = {
6471 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6472 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6473 MSIQUERY *view;
6474 UINT rc;
6476 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6477 if (rc != ERROR_SUCCESS)
6478 return ERROR_SUCCESS;
6480 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6481 msiobj_release( &view->hdr );
6482 return rc;
6485 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6487 MSIPACKAGE *package = param;
6488 LPWSTR driver, driver_path, ptr;
6489 WCHAR outpath[MAX_PATH];
6490 MSIFILE *driver_file = NULL, *setup_file = NULL;
6491 MSICOMPONENT *comp;
6492 MSIRECORD *uirow;
6493 LPCWSTR desc, file_key, component;
6494 DWORD len, usage;
6495 UINT r = ERROR_SUCCESS;
6497 static const WCHAR driver_fmt[] = {
6498 'D','r','i','v','e','r','=','%','s',0};
6499 static const WCHAR setup_fmt[] = {
6500 'S','e','t','u','p','=','%','s',0};
6501 static const WCHAR usage_fmt[] = {
6502 'F','i','l','e','U','s','a','g','e','=','1',0};
6504 component = MSI_RecordGetString( rec, 2 );
6505 comp = msi_get_loaded_component( package, component );
6506 if (!comp)
6507 return ERROR_SUCCESS;
6509 comp->Action = msi_get_component_action( package, comp );
6510 if (comp->Action != INSTALLSTATE_LOCAL)
6512 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6513 return ERROR_SUCCESS;
6515 desc = MSI_RecordGetString(rec, 3);
6517 file_key = MSI_RecordGetString( rec, 4 );
6518 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6520 file_key = MSI_RecordGetString( rec, 5 );
6521 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6523 if (!driver_file)
6525 ERR("ODBC Driver entry not found!\n");
6526 return ERROR_FUNCTION_FAILED;
6529 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6530 if (setup_file)
6531 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6532 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6534 driver = msi_alloc(len * sizeof(WCHAR));
6535 if (!driver)
6536 return ERROR_OUTOFMEMORY;
6538 ptr = driver;
6539 lstrcpyW(ptr, desc);
6540 ptr += lstrlenW(ptr) + 1;
6542 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6543 ptr += len + 1;
6545 if (setup_file)
6547 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6548 ptr += len + 1;
6551 lstrcpyW(ptr, usage_fmt);
6552 ptr += lstrlenW(ptr) + 1;
6553 *ptr = '\0';
6555 if (!driver_file->TargetPath)
6557 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6558 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6560 driver_path = strdupW(driver_file->TargetPath);
6561 ptr = strrchrW(driver_path, '\\');
6562 if (ptr) *ptr = '\0';
6564 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6565 NULL, ODBC_INSTALL_COMPLETE, &usage))
6567 ERR("Failed to install SQL driver!\n");
6568 r = ERROR_FUNCTION_FAILED;
6571 uirow = MSI_CreateRecord( 5 );
6572 MSI_RecordSetStringW( uirow, 1, desc );
6573 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6574 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6575 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6576 msiobj_release( &uirow->hdr );
6578 msi_free(driver);
6579 msi_free(driver_path);
6581 return r;
6584 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6586 MSIPACKAGE *package = param;
6587 LPWSTR translator, translator_path, ptr;
6588 WCHAR outpath[MAX_PATH];
6589 MSIFILE *translator_file = NULL, *setup_file = NULL;
6590 MSICOMPONENT *comp;
6591 MSIRECORD *uirow;
6592 LPCWSTR desc, file_key, component;
6593 DWORD len, usage;
6594 UINT r = ERROR_SUCCESS;
6596 static const WCHAR translator_fmt[] = {
6597 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6598 static const WCHAR setup_fmt[] = {
6599 'S','e','t','u','p','=','%','s',0};
6601 component = MSI_RecordGetString( rec, 2 );
6602 comp = msi_get_loaded_component( package, component );
6603 if (!comp)
6604 return ERROR_SUCCESS;
6606 comp->Action = msi_get_component_action( package, comp );
6607 if (comp->Action != INSTALLSTATE_LOCAL)
6609 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6610 return ERROR_SUCCESS;
6612 desc = MSI_RecordGetString(rec, 3);
6614 file_key = MSI_RecordGetString( rec, 4 );
6615 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6617 file_key = MSI_RecordGetString( rec, 5 );
6618 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6620 if (!translator_file)
6622 ERR("ODBC Translator entry not found!\n");
6623 return ERROR_FUNCTION_FAILED;
6626 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6627 if (setup_file)
6628 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6630 translator = msi_alloc(len * sizeof(WCHAR));
6631 if (!translator)
6632 return ERROR_OUTOFMEMORY;
6634 ptr = translator;
6635 lstrcpyW(ptr, desc);
6636 ptr += lstrlenW(ptr) + 1;
6638 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6639 ptr += len + 1;
6641 if (setup_file)
6643 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6644 ptr += len + 1;
6646 *ptr = '\0';
6648 translator_path = strdupW(translator_file->TargetPath);
6649 ptr = strrchrW(translator_path, '\\');
6650 if (ptr) *ptr = '\0';
6652 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6653 NULL, ODBC_INSTALL_COMPLETE, &usage))
6655 ERR("Failed to install SQL translator!\n");
6656 r = ERROR_FUNCTION_FAILED;
6659 uirow = MSI_CreateRecord( 5 );
6660 MSI_RecordSetStringW( uirow, 1, desc );
6661 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6662 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6663 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6664 msiobj_release( &uirow->hdr );
6666 msi_free(translator);
6667 msi_free(translator_path);
6669 return r;
6672 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6674 MSIPACKAGE *package = param;
6675 MSICOMPONENT *comp;
6676 LPWSTR attrs;
6677 LPCWSTR desc, driver, component;
6678 WORD request = ODBC_ADD_SYS_DSN;
6679 INT registration;
6680 DWORD len;
6681 UINT r = ERROR_SUCCESS;
6682 MSIRECORD *uirow;
6684 static const WCHAR attrs_fmt[] = {
6685 'D','S','N','=','%','s',0 };
6687 component = MSI_RecordGetString( rec, 2 );
6688 comp = msi_get_loaded_component( package, component );
6689 if (!comp)
6690 return ERROR_SUCCESS;
6692 comp->Action = msi_get_component_action( package, comp );
6693 if (comp->Action != INSTALLSTATE_LOCAL)
6695 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6696 return ERROR_SUCCESS;
6699 desc = MSI_RecordGetString(rec, 3);
6700 driver = MSI_RecordGetString(rec, 4);
6701 registration = MSI_RecordGetInteger(rec, 5);
6703 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6704 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6706 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6707 attrs = msi_alloc(len * sizeof(WCHAR));
6708 if (!attrs)
6709 return ERROR_OUTOFMEMORY;
6711 len = sprintfW(attrs, attrs_fmt, desc);
6712 attrs[len + 1] = 0;
6714 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6716 ERR("Failed to install SQL data source!\n");
6717 r = ERROR_FUNCTION_FAILED;
6720 uirow = MSI_CreateRecord( 5 );
6721 MSI_RecordSetStringW( uirow, 1, desc );
6722 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6723 MSI_RecordSetInteger( uirow, 3, request );
6724 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6725 msiobj_release( &uirow->hdr );
6727 msi_free(attrs);
6729 return r;
6732 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6734 static const WCHAR driver_query[] = {
6735 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6736 'O','D','B','C','D','r','i','v','e','r',0};
6737 static const WCHAR translator_query[] = {
6738 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6739 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6740 static const WCHAR source_query[] = {
6741 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6742 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6743 MSIQUERY *view;
6744 UINT rc;
6746 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6747 if (rc == ERROR_SUCCESS)
6749 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6750 msiobj_release(&view->hdr);
6751 if (rc != ERROR_SUCCESS)
6752 return rc;
6754 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6755 if (rc == ERROR_SUCCESS)
6757 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6758 msiobj_release(&view->hdr);
6759 if (rc != ERROR_SUCCESS)
6760 return rc;
6762 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6763 if (rc == ERROR_SUCCESS)
6765 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6766 msiobj_release(&view->hdr);
6767 if (rc != ERROR_SUCCESS)
6768 return rc;
6770 return ERROR_SUCCESS;
6773 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6775 MSIPACKAGE *package = param;
6776 MSICOMPONENT *comp;
6777 MSIRECORD *uirow;
6778 DWORD usage;
6779 LPCWSTR desc, component;
6781 component = MSI_RecordGetString( rec, 2 );
6782 comp = msi_get_loaded_component( package, component );
6783 if (!comp)
6784 return ERROR_SUCCESS;
6786 comp->Action = msi_get_component_action( package, comp );
6787 if (comp->Action != INSTALLSTATE_ABSENT)
6789 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6790 return ERROR_SUCCESS;
6793 desc = MSI_RecordGetString( rec, 3 );
6794 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6796 WARN("Failed to remove ODBC driver\n");
6798 else if (!usage)
6800 FIXME("Usage count reached 0\n");
6803 uirow = MSI_CreateRecord( 2 );
6804 MSI_RecordSetStringW( uirow, 1, desc );
6805 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6806 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6807 msiobj_release( &uirow->hdr );
6809 return ERROR_SUCCESS;
6812 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6814 MSIPACKAGE *package = param;
6815 MSICOMPONENT *comp;
6816 MSIRECORD *uirow;
6817 DWORD usage;
6818 LPCWSTR desc, component;
6820 component = MSI_RecordGetString( rec, 2 );
6821 comp = msi_get_loaded_component( package, component );
6822 if (!comp)
6823 return ERROR_SUCCESS;
6825 comp->Action = msi_get_component_action( package, comp );
6826 if (comp->Action != INSTALLSTATE_ABSENT)
6828 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6829 return ERROR_SUCCESS;
6832 desc = MSI_RecordGetString( rec, 3 );
6833 if (!SQLRemoveTranslatorW( desc, &usage ))
6835 WARN("Failed to remove ODBC translator\n");
6837 else if (!usage)
6839 FIXME("Usage count reached 0\n");
6842 uirow = MSI_CreateRecord( 2 );
6843 MSI_RecordSetStringW( uirow, 1, desc );
6844 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6845 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6846 msiobj_release( &uirow->hdr );
6848 return ERROR_SUCCESS;
6851 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6853 MSIPACKAGE *package = param;
6854 MSICOMPONENT *comp;
6855 MSIRECORD *uirow;
6856 LPWSTR attrs;
6857 LPCWSTR desc, driver, component;
6858 WORD request = ODBC_REMOVE_SYS_DSN;
6859 INT registration;
6860 DWORD len;
6862 static const WCHAR attrs_fmt[] = {
6863 'D','S','N','=','%','s',0 };
6865 component = MSI_RecordGetString( rec, 2 );
6866 comp = msi_get_loaded_component( package, component );
6867 if (!comp)
6868 return ERROR_SUCCESS;
6870 comp->Action = msi_get_component_action( package, comp );
6871 if (comp->Action != INSTALLSTATE_ABSENT)
6873 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6874 return ERROR_SUCCESS;
6877 desc = MSI_RecordGetString( rec, 3 );
6878 driver = MSI_RecordGetString( rec, 4 );
6879 registration = MSI_RecordGetInteger( rec, 5 );
6881 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6882 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6884 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6885 attrs = msi_alloc( len * sizeof(WCHAR) );
6886 if (!attrs)
6887 return ERROR_OUTOFMEMORY;
6889 FIXME("Use ODBCSourceAttribute table\n");
6891 len = sprintfW( attrs, attrs_fmt, desc );
6892 attrs[len + 1] = 0;
6894 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6896 WARN("Failed to remove ODBC data source\n");
6898 msi_free( attrs );
6900 uirow = MSI_CreateRecord( 3 );
6901 MSI_RecordSetStringW( uirow, 1, desc );
6902 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6903 MSI_RecordSetInteger( uirow, 3, request );
6904 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6905 msiobj_release( &uirow->hdr );
6907 return ERROR_SUCCESS;
6910 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6912 static const WCHAR driver_query[] = {
6913 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6914 'O','D','B','C','D','r','i','v','e','r',0};
6915 static const WCHAR translator_query[] = {
6916 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6917 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6918 static const WCHAR source_query[] = {
6919 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6920 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6921 MSIQUERY *view;
6922 UINT rc;
6924 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6925 if (rc == ERROR_SUCCESS)
6927 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6928 msiobj_release( &view->hdr );
6929 if (rc != ERROR_SUCCESS)
6930 return rc;
6932 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6933 if (rc == ERROR_SUCCESS)
6935 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6936 msiobj_release( &view->hdr );
6937 if (rc != ERROR_SUCCESS)
6938 return rc;
6940 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6941 if (rc == ERROR_SUCCESS)
6943 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6944 msiobj_release( &view->hdr );
6945 if (rc != ERROR_SUCCESS)
6946 return rc;
6948 return ERROR_SUCCESS;
6951 #define ENV_ACT_SETALWAYS 0x1
6952 #define ENV_ACT_SETABSENT 0x2
6953 #define ENV_ACT_REMOVE 0x4
6954 #define ENV_ACT_REMOVEMATCH 0x8
6956 #define ENV_MOD_MACHINE 0x20000000
6957 #define ENV_MOD_APPEND 0x40000000
6958 #define ENV_MOD_PREFIX 0x80000000
6959 #define ENV_MOD_MASK 0xC0000000
6961 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6963 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6965 LPCWSTR cptr = *name;
6967 static const WCHAR prefix[] = {'[','~',']',0};
6968 static const int prefix_len = 3;
6970 *flags = 0;
6971 while (*cptr)
6973 if (*cptr == '=')
6974 *flags |= ENV_ACT_SETALWAYS;
6975 else if (*cptr == '+')
6976 *flags |= ENV_ACT_SETABSENT;
6977 else if (*cptr == '-')
6978 *flags |= ENV_ACT_REMOVE;
6979 else if (*cptr == '!')
6980 *flags |= ENV_ACT_REMOVEMATCH;
6981 else if (*cptr == '*')
6982 *flags |= ENV_MOD_MACHINE;
6983 else
6984 break;
6986 cptr++;
6987 (*name)++;
6990 if (!*cptr)
6992 ERR("Missing environment variable\n");
6993 return ERROR_FUNCTION_FAILED;
6996 if (*value)
6998 LPCWSTR ptr = *value;
6999 if (!strncmpW(ptr, prefix, prefix_len))
7001 if (ptr[prefix_len] == szSemiColon[0])
7003 *flags |= ENV_MOD_APPEND;
7004 *value += lstrlenW(prefix);
7006 else
7008 *value = NULL;
7011 else if (lstrlenW(*value) >= prefix_len)
7013 ptr += lstrlenW(ptr) - prefix_len;
7014 if (!strcmpW( ptr, prefix ))
7016 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
7018 *flags |= ENV_MOD_PREFIX;
7019 /* the "[~]" will be removed by deformat_string */;
7021 else
7023 *value = NULL;
7029 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
7030 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
7031 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
7032 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
7034 ERR("Invalid flags: %08x\n", *flags);
7035 return ERROR_FUNCTION_FAILED;
7038 if (!*flags)
7039 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
7041 return ERROR_SUCCESS;
7044 static UINT open_env_key( DWORD flags, HKEY *key )
7046 static const WCHAR user_env[] =
7047 {'E','n','v','i','r','o','n','m','e','n','t',0};
7048 static const WCHAR machine_env[] =
7049 {'S','y','s','t','e','m','\\',
7050 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
7051 'C','o','n','t','r','o','l','\\',
7052 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
7053 'E','n','v','i','r','o','n','m','e','n','t',0};
7054 const WCHAR *env;
7055 HKEY root;
7056 LONG res;
7058 if (flags & ENV_MOD_MACHINE)
7060 env = machine_env;
7061 root = HKEY_LOCAL_MACHINE;
7063 else
7065 env = user_env;
7066 root = HKEY_CURRENT_USER;
7069 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
7070 if (res != ERROR_SUCCESS)
7072 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
7073 return ERROR_FUNCTION_FAILED;
7076 return ERROR_SUCCESS;
7079 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
7081 MSIPACKAGE *package = param;
7082 LPCWSTR name, value, component;
7083 WCHAR *data = NULL, *newval = NULL, *deformatted = NULL, *p, *q;
7084 DWORD flags, type, size, len, len_value = 0;
7085 UINT res;
7086 HKEY env = NULL;
7087 MSICOMPONENT *comp;
7088 MSIRECORD *uirow;
7089 int action = 0, found = 0;
7091 component = MSI_RecordGetString(rec, 4);
7092 comp = msi_get_loaded_component(package, component);
7093 if (!comp)
7094 return ERROR_SUCCESS;
7096 comp->Action = msi_get_component_action( package, comp );
7097 if (comp->Action != INSTALLSTATE_LOCAL)
7099 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
7100 return ERROR_SUCCESS;
7102 name = MSI_RecordGetString(rec, 2);
7103 value = MSI_RecordGetString(rec, 3);
7105 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7107 res = env_parse_flags(&name, &value, &flags);
7108 if (res != ERROR_SUCCESS || !value)
7109 goto done;
7111 if (value && !deformat_string(package, value, &deformatted))
7113 res = ERROR_OUTOFMEMORY;
7114 goto done;
7117 if ((value = deformatted))
7119 if (flags & ENV_MOD_PREFIX)
7121 p = strrchrW( value, ';' );
7122 len_value = p - value;
7124 else if (flags & ENV_MOD_APPEND)
7126 value = strchrW( value, ';' ) + 1;
7127 len_value = strlenW( value );
7129 else len_value = strlenW( value );
7132 res = open_env_key( flags, &env );
7133 if (res != ERROR_SUCCESS)
7134 goto done;
7136 if (flags & ENV_MOD_MACHINE)
7137 action |= 0x20000000;
7139 size = 0;
7140 type = REG_SZ;
7141 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
7142 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
7143 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
7144 goto done;
7146 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
7148 action = 0x2;
7150 /* Nothing to do. */
7151 if (!value)
7153 res = ERROR_SUCCESS;
7154 goto done;
7156 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
7157 newval = strdupW(value);
7158 if (!newval)
7160 res = ERROR_OUTOFMEMORY;
7161 goto done;
7164 else
7166 action = 0x1;
7168 /* Contrary to MSDN, +-variable to [~];path works */
7169 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7171 res = ERROR_SUCCESS;
7172 goto done;
7175 if (!(p = q = data = msi_alloc( size )))
7177 msi_free(deformatted);
7178 RegCloseKey(env);
7179 return ERROR_OUTOFMEMORY;
7182 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)data, &size );
7183 if (res != ERROR_SUCCESS)
7184 goto done;
7186 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7188 action = 0x4;
7189 res = RegDeleteValueW(env, name);
7190 if (res != ERROR_SUCCESS)
7191 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7192 goto done;
7195 for (;;)
7197 while (*q && *q != ';') q++;
7198 len = q - p;
7199 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ) &&
7200 (!p[len] || p[len] == ';'))
7202 found = 1;
7203 break;
7205 if (!*q) break;
7206 p = ++q;
7209 if (found)
7211 TRACE("string already set\n");
7212 goto done;
7215 size = (len_value + 1 + strlenW( data ) + 1) * sizeof(WCHAR);
7216 if (!(p = newval = msi_alloc( size )))
7218 res = ERROR_OUTOFMEMORY;
7219 goto done;
7222 if (flags & ENV_MOD_PREFIX)
7224 memcpy( newval, value, len_value * sizeof(WCHAR) );
7225 newval[len_value] = ';';
7226 p = newval + len_value + 1;
7227 action |= 0x80000000;
7230 strcpyW( p, data );
7232 if (flags & ENV_MOD_APPEND)
7234 p += strlenW( data );
7235 *p++ = ';';
7236 memcpy( p, value, (len_value + 1) * sizeof(WCHAR) );
7237 action |= 0x40000000;
7240 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7241 res = RegSetValueExW( env, name, 0, type, (BYTE *)newval, size );
7242 if (res)
7244 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7247 done:
7248 uirow = MSI_CreateRecord( 3 );
7249 MSI_RecordSetStringW( uirow, 1, name );
7250 MSI_RecordSetStringW( uirow, 2, newval );
7251 MSI_RecordSetInteger( uirow, 3, action );
7252 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7253 msiobj_release( &uirow->hdr );
7255 if (env) RegCloseKey(env);
7256 msi_free(deformatted);
7257 msi_free(data);
7258 msi_free(newval);
7259 return res;
7262 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7264 static const WCHAR query[] = {
7265 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7266 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7267 MSIQUERY *view;
7268 UINT rc;
7270 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7271 if (rc != ERROR_SUCCESS)
7272 return ERROR_SUCCESS;
7274 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7275 msiobj_release(&view->hdr);
7276 return rc;
7279 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7281 MSIPACKAGE *package = param;
7282 LPCWSTR name, value, component;
7283 WCHAR *p, *q, *deformatted = NULL, *new_value = NULL;
7284 DWORD flags, type, size, len, len_value = 0, len_new_value;
7285 HKEY env;
7286 MSICOMPONENT *comp;
7287 MSIRECORD *uirow;
7288 int action = 0;
7289 LONG res;
7290 UINT r;
7292 component = MSI_RecordGetString( rec, 4 );
7293 comp = msi_get_loaded_component( package, component );
7294 if (!comp)
7295 return ERROR_SUCCESS;
7297 comp->Action = msi_get_component_action( package, comp );
7298 if (comp->Action != INSTALLSTATE_ABSENT)
7300 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7301 return ERROR_SUCCESS;
7303 name = MSI_RecordGetString( rec, 2 );
7304 value = MSI_RecordGetString( rec, 3 );
7306 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7308 r = env_parse_flags( &name, &value, &flags );
7309 if (r != ERROR_SUCCESS)
7310 return r;
7312 if (!(flags & ENV_ACT_REMOVE))
7314 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7315 return ERROR_SUCCESS;
7318 if (value && !deformat_string( package, value, &deformatted ))
7319 return ERROR_OUTOFMEMORY;
7321 if ((value = deformatted))
7323 if (flags & ENV_MOD_PREFIX)
7325 p = strchrW( value, ';' );
7326 len_value = p - value;
7328 else if (flags & ENV_MOD_APPEND)
7330 value = strchrW( value, ';' ) + 1;
7331 len_value = strlenW( value );
7333 else len_value = strlenW( value );
7336 r = open_env_key( flags, &env );
7337 if (r != ERROR_SUCCESS)
7339 r = ERROR_SUCCESS;
7340 goto done;
7343 if (flags & ENV_MOD_MACHINE)
7344 action |= 0x20000000;
7346 size = 0;
7347 type = REG_SZ;
7348 res = RegQueryValueExW( env, name, NULL, &type, NULL, &size );
7349 if (res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ))
7350 goto done;
7352 if (!(new_value = msi_alloc( size ))) goto done;
7354 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)new_value, &size );
7355 if (res != ERROR_SUCCESS)
7356 goto done;
7358 len_new_value = size / sizeof(WCHAR) - 1;
7359 p = q = new_value;
7360 for (;;)
7362 while (*q && *q != ';') q++;
7363 len = q - p;
7364 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ))
7366 if (*q == ';') q++;
7367 memmove( p, q, (len_new_value - (q - new_value) + 1) * sizeof(WCHAR) );
7368 break;
7370 if (!*q) break;
7371 p = ++q;
7374 if (!new_value[0] || !value)
7376 TRACE("removing %s\n", debugstr_w(name));
7377 res = RegDeleteValueW( env, name );
7378 if (res != ERROR_SUCCESS)
7379 WARN("failed to delete value %s (%d)\n", debugstr_w(name), res);
7381 else
7383 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(new_value));
7384 size = (strlenW( new_value ) + 1) * sizeof(WCHAR);
7385 res = RegSetValueExW( env, name, 0, type, (BYTE *)new_value, size );
7386 if (res != ERROR_SUCCESS)
7387 WARN("failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(new_value), res);
7390 done:
7391 uirow = MSI_CreateRecord( 3 );
7392 MSI_RecordSetStringW( uirow, 1, name );
7393 MSI_RecordSetStringW( uirow, 2, value );
7394 MSI_RecordSetInteger( uirow, 3, action );
7395 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7396 msiobj_release( &uirow->hdr );
7398 if (env) RegCloseKey( env );
7399 msi_free( deformatted );
7400 msi_free( new_value );
7401 return r;
7404 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7406 static const WCHAR query[] = {
7407 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7408 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7409 MSIQUERY *view;
7410 UINT rc;
7412 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7413 if (rc != ERROR_SUCCESS)
7414 return ERROR_SUCCESS;
7416 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7417 msiobj_release( &view->hdr );
7418 return rc;
7421 UINT msi_validate_product_id( MSIPACKAGE *package )
7423 LPWSTR key, template, id;
7424 UINT r = ERROR_SUCCESS;
7426 id = msi_dup_property( package->db, szProductID );
7427 if (id)
7429 msi_free( id );
7430 return ERROR_SUCCESS;
7432 template = msi_dup_property( package->db, szPIDTemplate );
7433 key = msi_dup_property( package->db, szPIDKEY );
7434 if (key && template)
7436 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7437 r = msi_set_property( package->db, szProductID, key, -1 );
7439 msi_free( template );
7440 msi_free( key );
7441 return r;
7444 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7446 return msi_validate_product_id( package );
7449 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7451 TRACE("\n");
7452 package->need_reboot_at_end = 1;
7453 return ERROR_SUCCESS;
7456 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7458 static const WCHAR szAvailableFreeReg[] =
7459 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7460 MSIRECORD *uirow;
7461 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7463 TRACE("%p %d kilobytes\n", package, space);
7465 uirow = MSI_CreateRecord( 1 );
7466 MSI_RecordSetInteger( uirow, 1, space );
7467 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7468 msiobj_release( &uirow->hdr );
7470 return ERROR_SUCCESS;
7473 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7475 TRACE("%p\n", package);
7477 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7478 return ERROR_SUCCESS;
7481 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7483 FIXME("%p\n", package);
7484 return ERROR_SUCCESS;
7487 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7489 static const WCHAR driver_query[] = {
7490 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7491 'O','D','B','C','D','r','i','v','e','r',0};
7492 static const WCHAR translator_query[] = {
7493 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7494 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7495 MSIQUERY *view;
7496 UINT r, count;
7498 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7499 if (r == ERROR_SUCCESS)
7501 count = 0;
7502 r = MSI_IterateRecords( view, &count, NULL, package );
7503 msiobj_release( &view->hdr );
7504 if (r != ERROR_SUCCESS)
7505 return r;
7506 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7508 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7509 if (r == ERROR_SUCCESS)
7511 count = 0;
7512 r = MSI_IterateRecords( view, &count, NULL, package );
7513 msiobj_release( &view->hdr );
7514 if (r != ERROR_SUCCESS)
7515 return r;
7516 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7518 return ERROR_SUCCESS;
7521 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7523 static const WCHAR fmtW[] =
7524 {'m','s','i','e','x','e','c',' ','/','q','n',' ','/','i',' ','%','s',' ',
7525 'R','E','M','O','V','E','=','%','s',0};
7526 MSIPACKAGE *package = param;
7527 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7528 int attrs = MSI_RecordGetInteger( rec, 5 );
7529 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7530 WCHAR *product, *features, *cmd;
7531 STARTUPINFOW si;
7532 PROCESS_INFORMATION info;
7533 BOOL ret;
7535 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7536 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7538 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7540 len += strlenW( product );
7541 if (features)
7542 len += strlenW( features );
7543 else
7544 len += sizeof(szAll) / sizeof(szAll[0]);
7546 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7548 msi_free( product );
7549 msi_free( features );
7550 return ERROR_OUTOFMEMORY;
7552 sprintfW( cmd, fmtW, product, features ? features : szAll );
7553 msi_free( product );
7554 msi_free( features );
7556 memset( &si, 0, sizeof(STARTUPINFOW) );
7557 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7558 msi_free( cmd );
7559 if (!ret) return GetLastError();
7560 CloseHandle( info.hThread );
7562 WaitForSingleObject( info.hProcess, INFINITE );
7563 CloseHandle( info.hProcess );
7564 return ERROR_SUCCESS;
7567 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7569 static const WCHAR query[] = {
7570 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7571 MSIQUERY *view;
7572 UINT r;
7574 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7575 if (r == ERROR_SUCCESS)
7577 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7578 msiobj_release( &view->hdr );
7579 if (r != ERROR_SUCCESS)
7580 return r;
7582 return ERROR_SUCCESS;
7585 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7587 MSIPACKAGE *package = param;
7588 int attributes = MSI_RecordGetInteger( rec, 5 );
7590 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7592 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7593 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7594 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7595 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7596 HKEY hkey;
7597 UINT r;
7599 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7601 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7602 if (r != ERROR_SUCCESS)
7603 return ERROR_SUCCESS;
7605 else
7607 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7608 if (r != ERROR_SUCCESS)
7609 return ERROR_SUCCESS;
7611 RegCloseKey( hkey );
7613 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7614 debugstr_w(upgrade_code), debugstr_w(version_min),
7615 debugstr_w(version_max), debugstr_w(language));
7617 return ERROR_SUCCESS;
7620 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7622 static const WCHAR query[] = {
7623 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7624 'U','p','g','r','a','d','e',0};
7625 MSIQUERY *view;
7626 UINT r;
7628 if (msi_get_property_int( package->db, szInstalled, 0 ))
7630 TRACE("product is installed, skipping action\n");
7631 return ERROR_SUCCESS;
7633 if (msi_get_property_int( package->db, szPreselected, 0 ))
7635 TRACE("Preselected property is set, not migrating feature states\n");
7636 return ERROR_SUCCESS;
7638 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7639 if (r == ERROR_SUCCESS)
7641 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7642 msiobj_release( &view->hdr );
7643 if (r != ERROR_SUCCESS)
7644 return r;
7646 return ERROR_SUCCESS;
7649 static void bind_image( const char *filename, const char *path )
7651 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7653 WARN("failed to bind image %u\n", GetLastError());
7657 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7659 UINT i;
7660 MSIFILE *file;
7661 MSIPACKAGE *package = param;
7662 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7663 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7664 char *filenameA, *pathA;
7665 WCHAR *pathW, **path_list;
7667 if (!(file = msi_get_loaded_file( package, key )))
7669 WARN("file %s not found\n", debugstr_w(key));
7670 return ERROR_SUCCESS;
7672 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7673 path_list = msi_split_string( paths, ';' );
7674 if (!path_list) bind_image( filenameA, NULL );
7675 else
7677 for (i = 0; path_list[i] && path_list[i][0]; i++)
7679 deformat_string( package, path_list[i], &pathW );
7680 if ((pathA = strdupWtoA( pathW )))
7682 bind_image( filenameA, pathA );
7683 msi_free( pathA );
7685 msi_free( pathW );
7688 msi_free( path_list );
7689 msi_free( filenameA );
7690 return ERROR_SUCCESS;
7693 static UINT ACTION_BindImage( MSIPACKAGE *package )
7695 static const WCHAR query[] = {
7696 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7697 'B','i','n','d','I','m','a','g','e',0};
7698 MSIQUERY *view;
7699 UINT r;
7701 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7702 if (r == ERROR_SUCCESS)
7704 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7705 msiobj_release( &view->hdr );
7706 if (r != ERROR_SUCCESS)
7707 return r;
7709 return ERROR_SUCCESS;
7712 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7714 static const WCHAR query[] = {
7715 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7716 MSIQUERY *view;
7717 DWORD count = 0;
7718 UINT r;
7720 r = MSI_OpenQuery( package->db, &view, query, table );
7721 if (r == ERROR_SUCCESS)
7723 r = MSI_IterateRecords(view, &count, NULL, package);
7724 msiobj_release(&view->hdr);
7725 if (r != ERROR_SUCCESS)
7726 return r;
7728 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7729 return ERROR_SUCCESS;
7732 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7734 static const WCHAR table[] = {
7735 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7736 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7739 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7741 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7742 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7745 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7747 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7748 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7751 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7753 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7754 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7757 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7759 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7760 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7763 static const struct
7765 const WCHAR *action;
7766 const UINT description;
7767 const UINT template;
7768 UINT (*handler)(MSIPACKAGE *);
7769 const WCHAR *action_rollback;
7771 StandardActions[] =
7773 { szAllocateRegistrySpace, IDS_DESC_ALLOCATEREGISTRYSPACE, IDS_TEMP_ALLOCATEREGISTRYSPACE, ACTION_AllocateRegistrySpace, NULL },
7774 { szAppSearch, IDS_DESC_APPSEARCH, IDS_TEMP_APPSEARCH, ACTION_AppSearch, NULL },
7775 { szBindImage, IDS_DESC_BINDIMAGE, IDS_TEMP_BINDIMAGE, ACTION_BindImage, NULL },
7776 { szCCPSearch, IDS_DESC_CCPSEARCH, 0, ACTION_CCPSearch, NULL },
7777 { szCostFinalize, IDS_DESC_COSTFINALIZE, 0, ACTION_CostFinalize, NULL },
7778 { szCostInitialize, IDS_DESC_COSTINITIALIZE, 0, ACTION_CostInitialize, NULL },
7779 { szCreateFolders, IDS_DESC_CREATEFOLDERS, IDS_TEMP_CREATEFOLDERS, ACTION_CreateFolders, szRemoveFolders },
7780 { szCreateShortcuts, IDS_DESC_CREATESHORTCUTS, IDS_TEMP_CREATESHORTCUTS, ACTION_CreateShortcuts, szRemoveShortcuts },
7781 { szDeleteServices, IDS_DESC_DELETESERVICES, IDS_TEMP_DELETESERVICES, ACTION_DeleteServices, szInstallServices },
7782 { szDisableRollback, 0, 0, ACTION_DisableRollback, NULL },
7783 { szDuplicateFiles, IDS_DESC_DUPLICATEFILES, IDS_TEMP_DUPLICATEFILES, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7784 { szExecuteAction, 0, 0, ACTION_ExecuteAction, NULL },
7785 { szFileCost, IDS_DESC_FILECOST, 0, ACTION_FileCost, NULL },
7786 { szFindRelatedProducts, IDS_DESC_FINDRELATEDPRODUCTS, IDS_TEMP_FINDRELATEDPRODUCTS, ACTION_FindRelatedProducts, NULL },
7787 { szForceReboot, 0, 0, ACTION_ForceReboot, NULL },
7788 { szInstallAdminPackage, IDS_DESC_INSTALLADMINPACKAGE, IDS_TEMP_INSTALLADMINPACKAGE, ACTION_InstallAdminPackage, NULL },
7789 { szInstallExecute, 0, 0, ACTION_InstallExecute, NULL },
7790 { szInstallExecuteAgain, 0, 0, ACTION_InstallExecute, NULL },
7791 { szInstallFiles, IDS_DESC_INSTALLFILES, IDS_TEMP_INSTALLFILES, ACTION_InstallFiles, szRemoveFiles },
7792 { szInstallFinalize, 0, 0, ACTION_InstallFinalize, NULL },
7793 { szInstallInitialize, 0, 0, ACTION_InstallInitialize, NULL },
7794 { szInstallODBC, IDS_DESC_INSTALLODBC, 0, ACTION_InstallODBC, szRemoveODBC },
7795 { szInstallServices, IDS_DESC_INSTALLSERVICES, IDS_TEMP_INSTALLSERVICES, ACTION_InstallServices, szDeleteServices },
7796 { szInstallSFPCatalogFile, IDS_DESC_INSTALLSFPCATALOGFILE, IDS_TEMP_INSTALLSFPCATALOGFILE, ACTION_InstallSFPCatalogFile, NULL },
7797 { szInstallValidate, IDS_DESC_INSTALLVALIDATE, 0, ACTION_InstallValidate, NULL },
7798 { szIsolateComponents, 0, 0, ACTION_IsolateComponents, NULL },
7799 { szLaunchConditions, IDS_DESC_LAUNCHCONDITIONS, 0, ACTION_LaunchConditions, NULL },
7800 { szMigrateFeatureStates, IDS_DESC_MIGRATEFEATURESTATES, IDS_TEMP_MIGRATEFEATURESTATES, ACTION_MigrateFeatureStates, NULL },
7801 { szMoveFiles, IDS_DESC_MOVEFILES, IDS_TEMP_MOVEFILES, ACTION_MoveFiles, NULL },
7802 { szMsiPublishAssemblies, IDS_DESC_MSIPUBLISHASSEMBLIES, IDS_TEMP_MSIPUBLISHASSEMBLIES, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7803 { szMsiUnpublishAssemblies, IDS_DESC_MSIUNPUBLISHASSEMBLIES, IDS_TEMP_MSIUNPUBLISHASSEMBLIES, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7804 { szPatchFiles, IDS_DESC_PATCHFILES, IDS_TEMP_PATCHFILES, ACTION_PatchFiles, NULL },
7805 { szProcessComponents, IDS_DESC_PROCESSCOMPONENTS, 0, ACTION_ProcessComponents, szProcessComponents },
7806 { szPublishComponents, IDS_DESC_PUBLISHCOMPONENTS, IDS_TEMP_PUBLISHCOMPONENTS, ACTION_PublishComponents, szUnpublishComponents },
7807 { szPublishFeatures, IDS_DESC_PUBLISHFEATURES, IDS_TEMP_PUBLISHFEATURES, ACTION_PublishFeatures, szUnpublishFeatures },
7808 { szPublishProduct, IDS_DESC_PUBLISHPRODUCT, 0, ACTION_PublishProduct, szUnpublishProduct },
7809 { szRegisterClassInfo, IDS_DESC_REGISTERCLASSINFO, IDS_TEMP_REGISTERCLASSINFO, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7810 { szRegisterComPlus, IDS_DESC_REGISTERCOMPLUS, IDS_TEMP_REGISTERCOMPLUS, ACTION_RegisterComPlus, szUnregisterComPlus },
7811 { szRegisterExtensionInfo, IDS_DESC_REGISTEREXTENSIONINFO, 0, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7812 { szRegisterFonts, IDS_DESC_REGISTERFONTS, IDS_TEMP_REGISTERFONTS, ACTION_RegisterFonts, szUnregisterFonts },
7813 { szRegisterMIMEInfo, IDS_DESC_REGISTERMIMEINFO, IDS_TEMP_REGISTERMIMEINFO, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7814 { szRegisterProduct, IDS_DESC_REGISTERPRODUCT, 0, ACTION_RegisterProduct, NULL },
7815 { szRegisterProgIdInfo, IDS_DESC_REGISTERPROGIDINFO, IDS_TEMP_REGISTERPROGIDINFO, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7816 { szRegisterTypeLibraries, IDS_DESC_REGISTERTYPELIBRARIES, IDS_TEMP_REGISTERTYPELIBRARIES, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7817 { szRegisterUser, IDS_DESC_REGISTERUSER, 0, ACTION_RegisterUser, NULL },
7818 { szRemoveDuplicateFiles, IDS_DESC_REMOVEDUPLICATEFILES, IDS_TEMP_REMOVEDUPLICATEFILES, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7819 { szRemoveEnvironmentStrings, IDS_DESC_REMOVEENVIRONMENTSTRINGS, IDS_TEMP_REMOVEENVIRONMENTSTRINGS, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7820 { szRemoveExistingProducts, IDS_DESC_REMOVEEXISTINGPRODUCTS, IDS_TEMP_REMOVEEXISTINGPRODUCTS, ACTION_RemoveExistingProducts, NULL },
7821 { szRemoveFiles, IDS_DESC_REMOVEFILES, IDS_TEMP_REMOVEFILES, ACTION_RemoveFiles, szInstallFiles },
7822 { szRemoveFolders, IDS_DESC_REMOVEFOLDERS, IDS_TEMP_REMOVEFOLDERS, ACTION_RemoveFolders, szCreateFolders },
7823 { szRemoveIniValues, IDS_DESC_REMOVEINIVALUES, IDS_TEMP_REMOVEINIVALUES, ACTION_RemoveIniValues, szWriteIniValues },
7824 { szRemoveODBC, IDS_DESC_REMOVEODBC, 0, ACTION_RemoveODBC, szInstallODBC },
7825 { szRemoveRegistryValues, IDS_DESC_REMOVEREGISTRYVALUES, IDS_TEMP_REMOVEREGISTRYVALUES, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7826 { szRemoveShortcuts, IDS_DESC_REMOVESHORTCUTS, IDS_TEMP_REMOVESHORTCUTS, ACTION_RemoveShortcuts, szCreateShortcuts },
7827 { szResolveSource, 0, 0, ACTION_ResolveSource, NULL },
7828 { szRMCCPSearch, IDS_DESC_RMCCPSEARCH, 0, ACTION_RMCCPSearch, NULL },
7829 { szScheduleReboot, 0, 0, ACTION_ScheduleReboot, NULL },
7830 { szSelfRegModules, IDS_DESC_SELFREGMODULES, IDS_TEMP_SELFREGMODULES, ACTION_SelfRegModules, szSelfUnregModules },
7831 { szSelfUnregModules, IDS_DESC_SELFUNREGMODULES, IDS_TEMP_SELFUNREGMODULES, ACTION_SelfUnregModules, szSelfRegModules },
7832 { szSetODBCFolders, IDS_DESC_SETODBCFOLDERS, 0, ACTION_SetODBCFolders, NULL },
7833 { szStartServices, IDS_DESC_STARTSERVICES, IDS_TEMP_STARTSERVICES, ACTION_StartServices, szStopServices },
7834 { szStopServices, IDS_DESC_STOPSERVICES, IDS_TEMP_STOPSERVICES, ACTION_StopServices, szStartServices },
7835 { szUnpublishComponents, IDS_DESC_UNPUBLISHCOMPONENTS, IDS_TEMP_UNPUBLISHCOMPONENTS, ACTION_UnpublishComponents, szPublishComponents },
7836 { szUnpublishFeatures, IDS_DESC_UNPUBLISHFEATURES, IDS_TEMP_UNPUBLISHFEATURES, ACTION_UnpublishFeatures, szPublishFeatures },
7837 { szUnpublishProduct, IDS_DESC_UNPUBLISHPRODUCT, 0, ACTION_UnpublishProduct, NULL }, /* for rollback only */
7838 { szUnregisterClassInfo, IDS_DESC_UNREGISTERCLASSINFO, IDS_TEMP_UNREGISTERCLASSINFO, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7839 { szUnregisterComPlus, IDS_DESC_UNREGISTERCOMPLUS, IDS_TEMP_UNREGISTERCOMPLUS, ACTION_UnregisterComPlus, szRegisterComPlus },
7840 { szUnregisterExtensionInfo, IDS_DESC_UNREGISTEREXTENSIONINFO, IDS_TEMP_UNREGISTEREXTENSIONINFO, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7841 { szUnregisterFonts, IDS_DESC_UNREGISTERFONTS, IDS_TEMP_UNREGISTERFONTS, ACTION_UnregisterFonts, szRegisterFonts },
7842 { szUnregisterMIMEInfo, IDS_DESC_UNREGISTERMIMEINFO, IDS_TEMP_UNREGISTERMIMEINFO, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7843 { szUnregisterProgIdInfo, IDS_DESC_UNREGISTERPROGIDINFO, IDS_TEMP_UNREGISTERPROGIDINFO, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7844 { szUnregisterTypeLibraries, IDS_DESC_UNREGISTERTYPELIBRARIES, IDS_TEMP_UNREGISTERTYPELIBRARIES, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7845 { szValidateProductID, 0, 0, ACTION_ValidateProductID, NULL },
7846 { szWriteEnvironmentStrings, IDS_DESC_WRITEENVIRONMENTSTRINGS, IDS_TEMP_WRITEENVIRONMENTSTRINGS, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7847 { szWriteIniValues, IDS_DESC_WRITEINIVALUES, IDS_TEMP_WRITEINIVALUES, ACTION_WriteIniValues, szRemoveIniValues },
7848 { szWriteRegistryValues, IDS_DESC_WRITEREGISTRYVALUES, IDS_TEMP_WRITEREGISTRYVALUES, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7849 { szINSTALL, 0, 0, ACTION_INSTALL, NULL },
7850 { 0 }
7853 static UINT ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action)
7855 UINT rc = ERROR_FUNCTION_NOT_CALLED;
7856 UINT i;
7858 i = 0;
7859 while (StandardActions[i].action != NULL)
7861 if (!strcmpW( StandardActions[i].action, action ))
7863 WCHAR description[100] = {0}, template[100] = {0};
7865 if (StandardActions[i].description != 0)
7866 LoadStringW(msi_hInstance, StandardActions[i].description, (LPWSTR)&description, 100);
7867 if (StandardActions[i].template != 0)
7868 LoadStringW(msi_hInstance, StandardActions[i].template, (LPWSTR)&template, 100);
7870 ui_actionstart(package, action, description, template);
7871 if (StandardActions[i].handler)
7873 ui_actioninfo( package, action, TRUE, 0 );
7874 rc = StandardActions[i].handler( package );
7875 ui_actioninfo( package, action, FALSE, !rc );
7877 if (StandardActions[i].action_rollback && !package->need_rollback)
7879 TRACE("scheduling rollback action\n");
7880 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7883 else
7885 FIXME("unhandled standard action %s\n", debugstr_w(action));
7886 rc = ERROR_SUCCESS;
7888 break;
7890 i++;
7892 return rc;
7895 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7897 UINT rc;
7899 TRACE("Performing action (%s)\n", debugstr_w(action));
7901 package->action_progress_increment = 0;
7902 rc = ACTION_HandleStandardAction(package, action);
7904 if (rc == ERROR_FUNCTION_NOT_CALLED)
7905 rc = ACTION_HandleCustomAction(package, action, script);
7907 if (rc == ERROR_FUNCTION_NOT_CALLED)
7908 WARN("unhandled msi action %s\n", debugstr_w(action));
7910 return rc;
7913 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7915 UINT rc = ERROR_SUCCESS;
7916 MSIRECORD *row;
7918 static const WCHAR query[] =
7919 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7920 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7921 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7922 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7923 static const WCHAR ui_query[] =
7924 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7925 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7926 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7927 ' ', '=',' ','%','i',0};
7929 if (needs_ui_sequence(package))
7930 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7931 else
7932 row = MSI_QueryGetRecord(package->db, query, seq);
7934 if (row)
7936 LPCWSTR action, cond;
7938 TRACE("Running the actions\n");
7940 /* check conditions */
7941 cond = MSI_RecordGetString(row, 2);
7943 /* this is a hack to skip errors in the condition code */
7944 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7946 msiobj_release(&row->hdr);
7947 return ERROR_SUCCESS;
7950 action = MSI_RecordGetString(row, 1);
7951 if (!action)
7953 ERR("failed to fetch action\n");
7954 msiobj_release(&row->hdr);
7955 return ERROR_FUNCTION_FAILED;
7958 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7960 msiobj_release(&row->hdr);
7963 return rc;
7966 /****************************************************
7967 * TOP level entry points
7968 *****************************************************/
7970 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7971 LPCWSTR szCommandLine )
7973 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7974 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7975 WCHAR *reinstall = NULL, *productcode, *action;
7976 UINT rc;
7977 DWORD len = 0;
7979 if (szPackagePath)
7981 LPWSTR p, dir;
7982 LPCWSTR file;
7984 dir = strdupW(szPackagePath);
7985 p = strrchrW(dir, '\\');
7986 if (p)
7988 *(++p) = 0;
7989 file = szPackagePath + (p - dir);
7991 else
7993 msi_free(dir);
7994 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7995 GetCurrentDirectoryW(MAX_PATH, dir);
7996 lstrcatW(dir, szBackSlash);
7997 file = szPackagePath;
8000 msi_free( package->PackagePath );
8001 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
8002 if (!package->PackagePath)
8004 msi_free(dir);
8005 return ERROR_OUTOFMEMORY;
8008 lstrcpyW(package->PackagePath, dir);
8009 lstrcatW(package->PackagePath, file);
8010 msi_free(dir);
8012 msi_set_sourcedir_props(package, FALSE);
8015 rc = msi_parse_command_line( package, szCommandLine, FALSE );
8016 if (rc != ERROR_SUCCESS)
8017 return rc;
8019 msi_apply_transforms( package );
8020 msi_apply_patches( package );
8022 if (msi_get_property( package->db, szAction, NULL, &len ))
8023 msi_set_property( package->db, szAction, szINSTALL, -1 );
8024 action = msi_dup_property( package->db, szAction );
8025 CharUpperW(action);
8027 msi_set_original_database_property( package->db, szPackagePath );
8028 msi_parse_command_line( package, szCommandLine, FALSE );
8029 msi_adjust_privilege_properties( package );
8030 msi_set_context( package );
8032 productcode = msi_dup_property( package->db, szProductCode );
8033 if (strcmpiW( productcode, package->ProductCode ))
8035 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
8036 msi_free( package->ProductCode );
8037 package->ProductCode = productcode;
8039 else msi_free( productcode );
8041 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
8043 TRACE("disabling rollback\n");
8044 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
8047 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
8049 /* process the ending type action */
8050 if (rc == ERROR_SUCCESS)
8051 ACTION_PerformActionSequence(package, -1);
8052 else if (rc == ERROR_INSTALL_USEREXIT)
8053 ACTION_PerformActionSequence(package, -2);
8054 else if (rc == ERROR_INSTALL_SUSPEND)
8055 ACTION_PerformActionSequence(package, -4);
8056 else /* failed */
8058 ACTION_PerformActionSequence(package, -3);
8059 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
8061 package->need_rollback = TRUE;
8065 /* finish up running custom actions */
8066 ACTION_FinishCustomActions(package);
8068 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
8070 WARN("installation failed, running rollback script\n");
8071 execute_script( package, SCRIPT_ROLLBACK );
8073 msi_free( reinstall );
8074 msi_free( action );
8076 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
8077 return ERROR_SUCCESS_REBOOT_REQUIRED;
8079 return rc;