dwrite: Avoid repeated method calls during run rendering.
[wine.git] / dlls / msi / action.c
blob3f97c8251196c16be532e5f81134ca3d4fb09a22
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};
161 static INT ui_actionstart(MSIPACKAGE *package, LPCWSTR action, LPCWSTR description, LPCWSTR template)
163 MSIRECORD *row = MSI_CreateRecord(3);
164 INT rc;
165 if (!row) return -1;
166 MSI_RecordSetStringW(row, 1, action);
167 MSI_RecordSetStringW(row, 2, description);
168 MSI_RecordSetStringW(row, 3, template);
169 rc = MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
170 msiobj_release(&row->hdr);
171 return rc;
174 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
175 UINT rc)
177 MSIRECORD *row;
178 WCHAR template[1024];
179 static const WCHAR format[] =
180 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
181 WCHAR message[1024];
182 WCHAR timet[0x100];
184 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
185 LoadStringW(msi_hInstance, start ? IDS_INFO_ACTIONSTART : IDS_INFO_ACTIONENDED, template, 1024);
186 sprintfW(message, template, timet);
188 row = MSI_CreateRecord(2);
189 if (!row) return;
190 MSI_RecordSetStringW(row, 0, message);
191 MSI_RecordSetStringW(row, 1, action);
192 MSI_RecordSetInteger(row, 2, start ? package->LastActionResult : !rc);
193 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
194 msiobj_release(&row->hdr);
197 enum parse_state
199 state_whitespace,
200 state_token,
201 state_quote
204 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
206 enum parse_state state = state_quote;
207 const WCHAR *p;
208 WCHAR *out = value;
209 BOOL ignore, in_quotes = FALSE;
210 int count = 0, len = 0;
212 for (p = str; *p; p++)
214 ignore = FALSE;
215 switch (state)
217 case state_whitespace:
218 switch (*p)
220 case ' ':
221 in_quotes = TRUE;
222 ignore = TRUE;
223 len++;
224 break;
225 case '"':
226 state = state_quote;
227 if (in_quotes && p[1] != '\"') count--;
228 else count++;
229 break;
230 default:
231 state = state_token;
232 in_quotes = TRUE;
233 len++;
234 break;
236 break;
238 case state_token:
239 switch (*p)
241 case '"':
242 state = state_quote;
243 if (in_quotes) count--;
244 else count++;
245 break;
246 case ' ':
247 state = state_whitespace;
248 if (!count) goto done;
249 in_quotes = TRUE;
250 len++;
251 break;
252 default:
253 if (count) in_quotes = TRUE;
254 len++;
255 break;
257 break;
259 case state_quote:
260 switch (*p)
262 case '"':
263 if (in_quotes && p[1] != '\"') count--;
264 else count++;
265 break;
266 case ' ':
267 state = state_whitespace;
268 if (!count || (count > 1 && !len)) goto done;
269 in_quotes = TRUE;
270 len++;
271 break;
272 default:
273 state = state_token;
274 if (count) in_quotes = TRUE;
275 len++;
276 break;
278 break;
280 default: break;
282 if (!ignore) *out++ = *p;
283 if (!count) in_quotes = FALSE;
286 done:
287 if (!len) *value = 0;
288 else *out = 0;
290 *quotes = count;
291 return p - str;
294 static void remove_quotes( WCHAR *str )
296 WCHAR *p = str;
297 int len = strlenW( str );
299 while ((p = strchrW( p, '"' )))
301 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
302 p++;
306 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
307 BOOL preserve_case )
309 LPCWSTR ptr, ptr2;
310 int num_quotes;
311 DWORD len;
312 WCHAR *prop, *val;
313 UINT r;
315 if (!szCommandLine)
316 return ERROR_SUCCESS;
318 ptr = szCommandLine;
319 while (*ptr)
321 while (*ptr == ' ') ptr++;
322 if (!*ptr) break;
324 ptr2 = strchrW( ptr, '=' );
325 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
327 len = ptr2 - ptr;
328 if (!len) return ERROR_INVALID_COMMAND_LINE;
330 while (ptr[len - 1] == ' ') len--;
332 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
333 memcpy( prop, ptr, len * sizeof(WCHAR) );
334 prop[len] = 0;
335 if (!preserve_case) struprW( prop );
337 ptr2++;
338 while (*ptr2 == ' ') ptr2++;
340 num_quotes = 0;
341 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
342 len = parse_prop( ptr2, val, &num_quotes );
343 if (num_quotes % 2)
345 WARN("unbalanced quotes\n");
346 msi_free( val );
347 msi_free( prop );
348 return ERROR_INVALID_COMMAND_LINE;
350 remove_quotes( val );
351 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
353 r = msi_set_property( package->db, prop, val, -1 );
354 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
355 msi_reset_folders( package, TRUE );
357 msi_free( val );
358 msi_free( prop );
360 ptr = ptr2 + len;
363 return ERROR_SUCCESS;
366 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
368 LPCWSTR pc;
369 LPWSTR p, *ret = NULL;
370 UINT count = 0;
372 if (!str)
373 return ret;
375 /* count the number of substrings */
376 for ( pc = str, count = 0; pc; count++ )
378 pc = strchrW( pc, sep );
379 if (pc)
380 pc++;
383 /* allocate space for an array of substring pointers and the substrings */
384 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
385 (lstrlenW(str)+1) * sizeof(WCHAR) );
386 if (!ret)
387 return ret;
389 /* copy the string and set the pointers */
390 p = (LPWSTR) &ret[count+1];
391 lstrcpyW( p, str );
392 for( count = 0; (ret[count] = p); count++ )
394 p = strchrW( p, sep );
395 if (p)
396 *p++ = 0;
399 return ret;
402 static BOOL ui_sequence_exists( MSIPACKAGE *package )
404 static const WCHAR query [] = {
405 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
406 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
407 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',0};
408 MSIQUERY *view;
409 DWORD count = 0;
411 if (!(MSI_DatabaseOpenViewW( package->db, query, &view )))
413 MSI_IterateRecords( view, &count, NULL, package );
414 msiobj_release( &view->hdr );
416 return count != 0;
419 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
421 WCHAR *source, *check, *p, *db;
422 DWORD len;
424 if (!(db = msi_dup_property( package->db, szOriginalDatabase )))
425 return ERROR_OUTOFMEMORY;
427 if (!(p = strrchrW( db, '\\' )) && !(p = strrchrW( db, '/' )))
429 msi_free(db);
430 return ERROR_SUCCESS;
432 len = p - db + 2;
433 source = msi_alloc( len * sizeof(WCHAR) );
434 lstrcpynW( source, db, len );
435 msi_free( db );
437 check = msi_dup_property( package->db, szSourceDir );
438 if (!check || replace)
440 UINT r = msi_set_property( package->db, szSourceDir, source, -1 );
441 if (r == ERROR_SUCCESS)
442 msi_reset_folders( package, TRUE );
444 msi_free( check );
446 check = msi_dup_property( package->db, szSOURCEDIR );
447 if (!check || replace)
448 msi_set_property( package->db, szSOURCEDIR, source, -1 );
450 msi_free( check );
451 msi_free( source );
453 return ERROR_SUCCESS;
456 static BOOL needs_ui_sequence(MSIPACKAGE *package)
458 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
461 UINT msi_set_context(MSIPACKAGE *package)
463 UINT r = msi_locate_product( package->ProductCode, &package->Context );
464 if (r != ERROR_SUCCESS)
466 int num = msi_get_property_int( package->db, szAllUsers, 0 );
467 if (num == 1 || num == 2)
468 package->Context = MSIINSTALLCONTEXT_MACHINE;
469 else
470 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
472 return ERROR_SUCCESS;
475 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
477 UINT rc;
478 LPCWSTR cond, action;
479 MSIPACKAGE *package = param;
481 action = MSI_RecordGetString(row,1);
482 if (!action)
484 ERR("Error is retrieving action name\n");
485 return ERROR_FUNCTION_FAILED;
488 /* check conditions */
489 cond = MSI_RecordGetString(row,2);
491 /* this is a hack to skip errors in the condition code */
492 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
494 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
495 return ERROR_SUCCESS;
498 if (needs_ui_sequence(package))
499 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
500 else
501 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
503 msi_dialog_check_messages( NULL );
505 if (package->CurrentInstallState != ERROR_SUCCESS)
506 rc = package->CurrentInstallState;
508 if (rc == ERROR_FUNCTION_NOT_CALLED)
509 rc = ERROR_SUCCESS;
511 if (rc != ERROR_SUCCESS)
512 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
514 if (package->need_reboot_now)
516 TRACE("action %s asked for immediate reboot, suspending installation\n",
517 debugstr_w(action));
518 rc = ACTION_ForceReboot( package );
520 return rc;
523 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
525 static const WCHAR query[] = {
526 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
527 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
528 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
529 '`','S','e','q','u','e','n','c','e','`',0};
530 MSIQUERY *view;
531 UINT r;
533 TRACE("%p %s\n", package, debugstr_w(table));
535 r = MSI_OpenQuery( package->db, &view, query, table );
536 if (r == ERROR_SUCCESS)
538 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
539 msiobj_release(&view->hdr);
541 return r;
544 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
546 static const WCHAR query[] = {
547 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
548 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
549 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
550 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
551 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
552 static const WCHAR query_validate[] = {
553 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
554 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
555 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
556 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
557 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
558 MSIQUERY *view;
559 INT seq = 0;
560 UINT rc;
562 if (package->script->ExecuteSequenceRun)
564 TRACE("Execute Sequence already Run\n");
565 return ERROR_SUCCESS;
568 package->script->ExecuteSequenceRun = TRUE;
570 /* get the sequence number */
571 if (UIran)
573 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
574 if (!row) return ERROR_FUNCTION_FAILED;
575 seq = MSI_RecordGetInteger(row,1);
576 msiobj_release(&row->hdr);
578 rc = MSI_OpenQuery(package->db, &view, query, seq);
579 if (rc == ERROR_SUCCESS)
581 TRACE("Running the actions\n");
583 msi_set_property( package->db, szSourceDir, NULL, -1 );
584 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
585 msiobj_release(&view->hdr);
587 return rc;
590 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
592 static const WCHAR query[] = {
593 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
594 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
595 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
596 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
597 MSIQUERY *view;
598 UINT rc;
600 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
601 if (rc == ERROR_SUCCESS)
603 TRACE("Running the actions\n");
604 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
605 msiobj_release(&view->hdr);
607 return rc;
610 /********************************************************
611 * ACTION helper functions and functions that perform the actions
612 *******************************************************/
613 static UINT ACTION_HandleCustomAction(MSIPACKAGE *package, LPCWSTR action, UINT script)
615 UINT arc;
616 INT uirc;
618 uirc = ui_actionstart(package, action, NULL, NULL);
619 if (uirc == IDCANCEL)
620 return ERROR_INSTALL_USEREXIT;
621 ui_actioninfo(package, action, TRUE, 0);
622 arc = ACTION_CustomAction( package, action, script );
624 if (arc == ERROR_FUNCTION_NOT_CALLED && needs_ui_sequence(package))
625 arc = ACTION_ShowDialog(package, action);
627 if (arc == ERROR_INSTALL_USEREXIT) /* dialog UI returned -1 */
628 return ERROR_SUCCESS;
630 ui_actioninfo(package, action, FALSE, arc);
631 package->LastActionResult = !arc;
633 return arc;
636 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
638 MSICOMPONENT *comp;
640 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
642 if (!strcmpW( Component, comp->Component )) return comp;
644 return NULL;
647 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
649 MSIFEATURE *feature;
651 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
653 if (!strcmpW( Feature, feature->Feature )) return feature;
655 return NULL;
658 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
660 MSIFILE *file;
662 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
664 if (!strcmpW( key, file->File )) return file;
666 return NULL;
669 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
671 MSIFOLDER *folder;
673 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
675 if (!strcmpW( dir, folder->Directory )) return folder;
677 return NULL;
681 * Recursively create all directories in the path.
682 * shamelessly stolen from setupapi/queue.c
684 BOOL msi_create_full_path( const WCHAR *path )
686 BOOL ret = TRUE;
687 WCHAR *new_path;
688 int len;
690 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
691 strcpyW( new_path, path );
693 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
694 new_path[len - 1] = 0;
696 while (!CreateDirectoryW( new_path, NULL ))
698 WCHAR *slash;
699 DWORD last_error = GetLastError();
700 if (last_error == ERROR_ALREADY_EXISTS) break;
701 if (last_error != ERROR_PATH_NOT_FOUND)
703 ret = FALSE;
704 break;
706 if (!(slash = strrchrW( new_path, '\\' )))
708 ret = FALSE;
709 break;
711 len = slash - new_path;
712 new_path[len] = 0;
713 if (!msi_create_full_path( new_path ))
715 ret = FALSE;
716 break;
718 new_path[len] = '\\';
720 msi_free( new_path );
721 return ret;
724 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
726 MSIRECORD *row;
728 row = MSI_CreateRecord( 4 );
729 MSI_RecordSetInteger( row, 1, a );
730 MSI_RecordSetInteger( row, 2, b );
731 MSI_RecordSetInteger( row, 3, c );
732 MSI_RecordSetInteger( row, 4, d );
733 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
734 msiobj_release( &row->hdr );
736 msi_dialog_check_messages( NULL );
739 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
741 if (!comp->Enabled)
743 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
744 return INSTALLSTATE_UNKNOWN;
746 if (package->need_rollback) return comp->Installed;
747 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
749 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
750 return INSTALLSTATE_UNKNOWN;
752 return comp->ActionRequest;
755 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
757 if (package->need_rollback) return feature->Installed;
758 return feature->ActionRequest;
761 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
763 MSIPACKAGE *package = param;
764 LPCWSTR dir, component, full_path;
765 MSIRECORD *uirow;
766 MSIFOLDER *folder;
767 MSICOMPONENT *comp;
769 component = MSI_RecordGetString(row, 2);
770 if (!component)
771 return ERROR_SUCCESS;
773 comp = msi_get_loaded_component(package, component);
774 if (!comp)
775 return ERROR_SUCCESS;
777 comp->Action = msi_get_component_action( package, comp );
778 if (comp->Action != INSTALLSTATE_LOCAL)
780 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
781 return ERROR_SUCCESS;
784 dir = MSI_RecordGetString(row,1);
785 if (!dir)
787 ERR("Unable to get folder id\n");
788 return ERROR_SUCCESS;
791 uirow = MSI_CreateRecord(1);
792 MSI_RecordSetStringW(uirow, 1, dir);
793 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
794 msiobj_release(&uirow->hdr);
796 full_path = msi_get_target_folder( package, dir );
797 if (!full_path)
799 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
800 return ERROR_SUCCESS;
802 TRACE("folder is %s\n", debugstr_w(full_path));
804 folder = msi_get_loaded_folder( package, dir );
805 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
806 folder->State = FOLDER_STATE_CREATED;
807 return ERROR_SUCCESS;
810 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
812 static const WCHAR query[] = {
813 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
814 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
815 MSIQUERY *view;
816 UINT rc;
818 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
819 if (rc != ERROR_SUCCESS)
820 return ERROR_SUCCESS;
822 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
823 msiobj_release(&view->hdr);
824 return rc;
827 static void remove_persistent_folder( MSIFOLDER *folder )
829 FolderList *fl;
831 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
833 remove_persistent_folder( fl->folder );
835 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
837 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
841 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
843 MSIPACKAGE *package = param;
844 LPCWSTR dir, component, full_path;
845 MSIRECORD *uirow;
846 MSIFOLDER *folder;
847 MSICOMPONENT *comp;
849 component = MSI_RecordGetString(row, 2);
850 if (!component)
851 return ERROR_SUCCESS;
853 comp = msi_get_loaded_component(package, component);
854 if (!comp)
855 return ERROR_SUCCESS;
857 comp->Action = msi_get_component_action( package, comp );
858 if (comp->Action != INSTALLSTATE_ABSENT)
860 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
861 return ERROR_SUCCESS;
864 dir = MSI_RecordGetString( row, 1 );
865 if (!dir)
867 ERR("Unable to get folder id\n");
868 return ERROR_SUCCESS;
871 full_path = msi_get_target_folder( package, dir );
872 if (!full_path)
874 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
875 return ERROR_SUCCESS;
877 TRACE("folder is %s\n", debugstr_w(full_path));
879 uirow = MSI_CreateRecord( 1 );
880 MSI_RecordSetStringW( uirow, 1, dir );
881 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
882 msiobj_release( &uirow->hdr );
884 folder = msi_get_loaded_folder( package, dir );
885 remove_persistent_folder( folder );
886 return ERROR_SUCCESS;
889 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
891 static const WCHAR query[] = {
892 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
893 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
894 MSIQUERY *view;
895 UINT rc;
897 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
898 if (rc != ERROR_SUCCESS)
899 return ERROR_SUCCESS;
901 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
902 msiobj_release( &view->hdr );
903 return rc;
906 static UINT load_component( MSIRECORD *row, LPVOID param )
908 MSIPACKAGE *package = param;
909 MSICOMPONENT *comp;
911 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
912 if (!comp)
913 return ERROR_FUNCTION_FAILED;
915 list_add_tail( &package->components, &comp->entry );
917 /* fill in the data */
918 comp->Component = msi_dup_record_field( row, 1 );
920 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
922 comp->ComponentId = msi_dup_record_field( row, 2 );
923 comp->Directory = msi_dup_record_field( row, 3 );
924 comp->Attributes = MSI_RecordGetInteger(row,4);
925 comp->Condition = msi_dup_record_field( row, 5 );
926 comp->KeyPath = msi_dup_record_field( row, 6 );
928 comp->Installed = INSTALLSTATE_UNKNOWN;
929 comp->Action = INSTALLSTATE_UNKNOWN;
930 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
932 comp->assembly = msi_load_assembly( package, comp );
933 return ERROR_SUCCESS;
936 UINT msi_load_all_components( MSIPACKAGE *package )
938 static const WCHAR query[] = {
939 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
940 '`','C','o','m','p','o','n','e','n','t','`',0};
941 MSIQUERY *view;
942 UINT r;
944 if (!list_empty(&package->components))
945 return ERROR_SUCCESS;
947 r = MSI_DatabaseOpenViewW( package->db, query, &view );
948 if (r != ERROR_SUCCESS)
949 return r;
951 if (!msi_init_assembly_caches( package ))
953 ERR("can't initialize assembly caches\n");
954 msiobj_release( &view->hdr );
955 return ERROR_FUNCTION_FAILED;
958 r = MSI_IterateRecords(view, NULL, load_component, package);
959 msiobj_release(&view->hdr);
960 return r;
963 typedef struct {
964 MSIPACKAGE *package;
965 MSIFEATURE *feature;
966 } _ilfs;
968 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
970 ComponentList *cl;
972 cl = msi_alloc( sizeof (*cl) );
973 if ( !cl )
974 return ERROR_NOT_ENOUGH_MEMORY;
975 cl->component = comp;
976 list_add_tail( &feature->Components, &cl->entry );
978 return ERROR_SUCCESS;
981 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
983 FeatureList *fl;
985 fl = msi_alloc( sizeof(*fl) );
986 if ( !fl )
987 return ERROR_NOT_ENOUGH_MEMORY;
988 fl->feature = child;
989 list_add_tail( &parent->Children, &fl->entry );
991 return ERROR_SUCCESS;
994 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
996 _ilfs* ilfs = param;
997 LPCWSTR component;
998 MSICOMPONENT *comp;
1000 component = MSI_RecordGetString(row,1);
1002 /* check to see if the component is already loaded */
1003 comp = msi_get_loaded_component( ilfs->package, component );
1004 if (!comp)
1006 WARN("ignoring unknown component %s\n", debugstr_w(component));
1007 return ERROR_SUCCESS;
1009 add_feature_component( ilfs->feature, comp );
1010 comp->Enabled = TRUE;
1012 return ERROR_SUCCESS;
1015 static UINT load_feature(MSIRECORD * row, LPVOID param)
1017 static const WCHAR query[] = {
1018 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1019 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1020 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1021 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1022 MSIPACKAGE *package = param;
1023 MSIFEATURE *feature;
1024 MSIQUERY *view;
1025 _ilfs ilfs;
1026 UINT rc;
1028 /* fill in the data */
1030 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1031 if (!feature)
1032 return ERROR_NOT_ENOUGH_MEMORY;
1034 list_init( &feature->Children );
1035 list_init( &feature->Components );
1037 feature->Feature = msi_dup_record_field( row, 1 );
1039 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1041 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1042 feature->Title = msi_dup_record_field( row, 3 );
1043 feature->Description = msi_dup_record_field( row, 4 );
1045 if (!MSI_RecordIsNull(row,5))
1046 feature->Display = MSI_RecordGetInteger(row,5);
1048 feature->Level= MSI_RecordGetInteger(row,6);
1049 feature->Directory = msi_dup_record_field( row, 7 );
1050 feature->Attributes = MSI_RecordGetInteger(row,8);
1052 feature->Installed = INSTALLSTATE_UNKNOWN;
1053 feature->Action = INSTALLSTATE_UNKNOWN;
1054 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1056 list_add_tail( &package->features, &feature->entry );
1058 /* load feature components */
1060 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1061 if (rc != ERROR_SUCCESS)
1062 return ERROR_SUCCESS;
1064 ilfs.package = package;
1065 ilfs.feature = feature;
1067 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1068 msiobj_release(&view->hdr);
1069 return rc;
1072 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1074 MSIPACKAGE *package = param;
1075 MSIFEATURE *parent, *child;
1077 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1078 if (!child)
1079 return ERROR_FUNCTION_FAILED;
1081 if (!child->Feature_Parent)
1082 return ERROR_SUCCESS;
1084 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1085 if (!parent)
1086 return ERROR_FUNCTION_FAILED;
1088 add_feature_child( parent, child );
1089 return ERROR_SUCCESS;
1092 UINT msi_load_all_features( MSIPACKAGE *package )
1094 static const WCHAR query[] = {
1095 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1096 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1097 '`','D','i','s','p','l','a','y','`',0};
1098 MSIQUERY *view;
1099 UINT r;
1101 if (!list_empty(&package->features))
1102 return ERROR_SUCCESS;
1104 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1105 if (r != ERROR_SUCCESS)
1106 return r;
1108 r = MSI_IterateRecords( view, NULL, load_feature, package );
1109 if (r != ERROR_SUCCESS)
1111 msiobj_release( &view->hdr );
1112 return r;
1114 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1115 msiobj_release( &view->hdr );
1116 return r;
1119 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1121 if (!p)
1122 return p;
1123 p = strchrW(p, ch);
1124 if (!p)
1125 return p;
1126 *p = 0;
1127 return p+1;
1130 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1132 static const WCHAR query[] = {
1133 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1134 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1135 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1136 MSIQUERY *view = NULL;
1137 MSIRECORD *row = NULL;
1138 UINT r;
1140 TRACE("%s\n", debugstr_w(file->File));
1142 r = MSI_OpenQuery(package->db, &view, query, file->File);
1143 if (r != ERROR_SUCCESS)
1144 goto done;
1146 r = MSI_ViewExecute(view, NULL);
1147 if (r != ERROR_SUCCESS)
1148 goto done;
1150 r = MSI_ViewFetch(view, &row);
1151 if (r != ERROR_SUCCESS)
1152 goto done;
1154 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1155 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1156 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1157 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1158 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1160 done:
1161 if (view) msiobj_release(&view->hdr);
1162 if (row) msiobj_release(&row->hdr);
1163 return r;
1166 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1168 MSIRECORD *row;
1169 static const WCHAR query[] = {
1170 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1171 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1172 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1174 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1175 if (!row)
1177 WARN("query failed\n");
1178 return ERROR_FUNCTION_FAILED;
1181 file->disk_id = MSI_RecordGetInteger( row, 1 );
1182 msiobj_release( &row->hdr );
1183 return ERROR_SUCCESS;
1186 static UINT load_file(MSIRECORD *row, LPVOID param)
1188 MSIPACKAGE* package = param;
1189 LPCWSTR component;
1190 MSIFILE *file;
1192 /* fill in the data */
1194 file = msi_alloc_zero( sizeof (MSIFILE) );
1195 if (!file)
1196 return ERROR_NOT_ENOUGH_MEMORY;
1198 file->File = msi_dup_record_field( row, 1 );
1200 component = MSI_RecordGetString( row, 2 );
1201 file->Component = msi_get_loaded_component( package, component );
1203 if (!file->Component)
1205 WARN("Component not found: %s\n", debugstr_w(component));
1206 msi_free(file->File);
1207 msi_free(file);
1208 return ERROR_SUCCESS;
1211 file->FileName = msi_dup_record_field( row, 3 );
1212 msi_reduce_to_long_filename( file->FileName );
1214 file->ShortName = msi_dup_record_field( row, 3 );
1215 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1217 file->FileSize = MSI_RecordGetInteger( row, 4 );
1218 file->Version = msi_dup_record_field( row, 5 );
1219 file->Language = msi_dup_record_field( row, 6 );
1220 file->Attributes = MSI_RecordGetInteger( row, 7 );
1221 file->Sequence = MSI_RecordGetInteger( row, 8 );
1223 file->state = msifs_invalid;
1225 /* if the compressed bits are not set in the file attributes,
1226 * then read the information from the package word count property
1228 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1230 file->IsCompressed = FALSE;
1232 else if (file->Attributes &
1233 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1235 file->IsCompressed = TRUE;
1237 else if (file->Attributes & msidbFileAttributesNoncompressed)
1239 file->IsCompressed = FALSE;
1241 else
1243 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1246 load_file_hash(package, file);
1247 load_file_disk_id(package, file);
1249 TRACE("File loaded (file %s sequence %u)\n", debugstr_w(file->File), file->Sequence);
1251 list_add_tail( &package->files, &file->entry );
1253 return ERROR_SUCCESS;
1256 static UINT load_all_files(MSIPACKAGE *package)
1258 static const WCHAR query[] = {
1259 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1260 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1261 '`','S','e','q','u','e','n','c','e','`', 0};
1262 MSIQUERY *view;
1263 UINT rc;
1265 if (!list_empty(&package->files))
1266 return ERROR_SUCCESS;
1268 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1269 if (rc != ERROR_SUCCESS)
1270 return ERROR_SUCCESS;
1272 rc = MSI_IterateRecords(view, NULL, load_file, package);
1273 msiobj_release(&view->hdr);
1274 return rc;
1277 static UINT load_media( MSIRECORD *row, LPVOID param )
1279 MSIPACKAGE *package = param;
1280 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1281 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1283 /* FIXME: load external cabinets and directory sources too */
1284 if (!cabinet || cabinet[0] != '#' || disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
1285 return ERROR_SUCCESS;
1287 return msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1290 static UINT load_all_media( MSIPACKAGE *package )
1292 static const WCHAR query[] = {
1293 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1294 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1295 '`','D','i','s','k','I','d','`',0};
1296 MSIQUERY *view;
1297 UINT r;
1299 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1300 if (r != ERROR_SUCCESS)
1301 return ERROR_SUCCESS;
1303 r = MSI_IterateRecords( view, NULL, load_media, package );
1304 msiobj_release( &view->hdr );
1305 return r;
1308 static UINT load_patch_disk_id( MSIPACKAGE *package, MSIFILEPATCH *patch )
1310 static const WCHAR query[] =
1311 {'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1312 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1313 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','u',0};
1314 MSIRECORD *rec;
1316 if (!(rec = MSI_QueryGetRecord( package->db, query, patch->Sequence )))
1318 WARN("query failed\n");
1319 return ERROR_FUNCTION_FAILED;
1322 patch->disk_id = MSI_RecordGetInteger( rec, 1 );
1323 msiobj_release( &rec->hdr );
1324 return ERROR_SUCCESS;
1327 static UINT load_patch(MSIRECORD *row, LPVOID param)
1329 MSIPACKAGE *package = param;
1330 MSIFILEPATCH *patch;
1331 const WCHAR *file_key;
1333 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1334 if (!patch)
1335 return ERROR_NOT_ENOUGH_MEMORY;
1337 file_key = MSI_RecordGetString( row, 1 );
1338 patch->File = msi_get_loaded_file( package, file_key );
1339 if (!patch->File)
1341 ERR("Failed to find target for patch in File table\n");
1342 msi_free(patch);
1343 return ERROR_FUNCTION_FAILED;
1346 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1347 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1348 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1350 /* FIXME:
1351 * Header field - for patch validation.
1352 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1355 load_patch_disk_id( package, patch );
1357 TRACE("Patch loaded (file %s sequence %u)\n", debugstr_w(patch->File->File), patch->Sequence);
1359 list_add_tail( &package->filepatches, &patch->entry );
1361 return ERROR_SUCCESS;
1364 static UINT load_all_patches(MSIPACKAGE *package)
1366 static const WCHAR query[] = {
1367 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1368 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1369 '`','S','e','q','u','e','n','c','e','`',0};
1370 MSIQUERY *view;
1371 UINT rc;
1373 if (!list_empty(&package->filepatches))
1374 return ERROR_SUCCESS;
1376 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1377 if (rc != ERROR_SUCCESS)
1378 return ERROR_SUCCESS;
1380 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1381 msiobj_release(&view->hdr);
1382 return rc;
1385 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1387 static const WCHAR query[] = {
1388 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1389 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1390 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1391 MSIQUERY *view;
1393 folder->persistent = FALSE;
1394 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1396 if (!MSI_ViewExecute( view, NULL ))
1398 MSIRECORD *rec;
1399 if (!MSI_ViewFetch( view, &rec ))
1401 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1402 folder->persistent = TRUE;
1403 msiobj_release( &rec->hdr );
1406 msiobj_release( &view->hdr );
1408 return ERROR_SUCCESS;
1411 static UINT load_folder( MSIRECORD *row, LPVOID param )
1413 MSIPACKAGE *package = param;
1414 static WCHAR szEmpty[] = { 0 };
1415 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1416 MSIFOLDER *folder;
1418 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1419 list_init( &folder->children );
1420 folder->Directory = msi_dup_record_field( row, 1 );
1421 folder->Parent = msi_dup_record_field( row, 2 );
1422 p = msi_dup_record_field(row, 3);
1424 TRACE("%s\n", debugstr_w(folder->Directory));
1426 /* split src and target dir */
1427 tgt_short = p;
1428 src_short = folder_split_path( p, ':' );
1430 /* split the long and short paths */
1431 tgt_long = folder_split_path( tgt_short, '|' );
1432 src_long = folder_split_path( src_short, '|' );
1434 /* check for no-op dirs */
1435 if (tgt_short && !strcmpW( szDot, tgt_short ))
1436 tgt_short = szEmpty;
1437 if (src_short && !strcmpW( szDot, src_short ))
1438 src_short = szEmpty;
1440 if (!tgt_long)
1441 tgt_long = tgt_short;
1443 if (!src_short) {
1444 src_short = tgt_short;
1445 src_long = tgt_long;
1448 if (!src_long)
1449 src_long = src_short;
1451 /* FIXME: use the target short path too */
1452 folder->TargetDefault = strdupW(tgt_long);
1453 folder->SourceShortPath = strdupW(src_short);
1454 folder->SourceLongPath = strdupW(src_long);
1455 msi_free(p);
1457 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1458 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1459 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1461 load_folder_persistence( package, folder );
1463 list_add_tail( &package->folders, &folder->entry );
1464 return ERROR_SUCCESS;
1467 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1469 FolderList *fl;
1471 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1472 fl->folder = child;
1473 list_add_tail( &parent->children, &fl->entry );
1474 return ERROR_SUCCESS;
1477 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1479 MSIPACKAGE *package = param;
1480 MSIFOLDER *parent, *child;
1482 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1483 return ERROR_FUNCTION_FAILED;
1485 if (!child->Parent) return ERROR_SUCCESS;
1487 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1488 return ERROR_FUNCTION_FAILED;
1490 return add_folder_child( parent, child );
1493 static UINT load_all_folders( MSIPACKAGE *package )
1495 static const WCHAR query[] = {
1496 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1497 '`','D','i','r','e','c','t','o','r','y','`',0};
1498 MSIQUERY *view;
1499 UINT r;
1501 if (!list_empty(&package->folders))
1502 return ERROR_SUCCESS;
1504 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1505 if (r != ERROR_SUCCESS)
1506 return r;
1508 r = MSI_IterateRecords( view, NULL, load_folder, package );
1509 if (r != ERROR_SUCCESS)
1511 msiobj_release( &view->hdr );
1512 return r;
1514 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1515 msiobj_release( &view->hdr );
1516 return r;
1519 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1521 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1522 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1524 load_all_folders( package );
1525 msi_load_all_components( package );
1526 msi_load_all_features( package );
1527 load_all_files( package );
1528 load_all_patches( package );
1529 load_all_media( package );
1531 return ERROR_SUCCESS;
1534 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1536 const WCHAR *action = package->script->Actions[script][index];
1537 ui_actionstart( package, action, NULL, NULL );
1538 TRACE("executing %s\n", debugstr_w(action));
1539 return ACTION_PerformAction( package, action, script );
1542 static UINT execute_script( MSIPACKAGE *package, UINT script )
1544 UINT i, rc = ERROR_SUCCESS;
1546 TRACE("executing script %u\n", script);
1548 if (!package->script)
1550 ERR("no script!\n");
1551 return ERROR_FUNCTION_FAILED;
1553 if (script == SCRIPT_ROLLBACK)
1555 for (i = package->script->ActionCount[script]; i > 0; i--)
1557 rc = execute_script_action( package, script, i - 1 );
1558 if (rc != ERROR_SUCCESS) break;
1561 else
1563 for (i = 0; i < package->script->ActionCount[script]; i++)
1565 rc = execute_script_action( package, script, i );
1566 if (rc != ERROR_SUCCESS) break;
1569 msi_free_action_script(package, script);
1570 return rc;
1573 static UINT ACTION_FileCost(MSIPACKAGE *package)
1575 return ERROR_SUCCESS;
1578 static void get_client_counts( MSIPACKAGE *package )
1580 MSICOMPONENT *comp;
1581 HKEY hkey;
1583 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1585 if (!comp->ComponentId) continue;
1587 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1588 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1590 comp->num_clients = 0;
1591 continue;
1593 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1594 NULL, NULL, NULL, NULL );
1595 RegCloseKey( hkey );
1599 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1601 MSICOMPONENT *comp;
1602 UINT r;
1604 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1606 if (!comp->ComponentId) continue;
1608 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1609 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1610 &comp->Installed );
1611 if (r == ERROR_SUCCESS) continue;
1613 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1614 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1615 &comp->Installed );
1616 if (r == ERROR_SUCCESS) continue;
1618 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1619 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1620 &comp->Installed );
1621 if (r == ERROR_SUCCESS) continue;
1623 comp->Installed = INSTALLSTATE_ABSENT;
1627 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1629 MSIFEATURE *feature;
1631 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1633 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1635 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1636 feature->Installed = INSTALLSTATE_ABSENT;
1637 else
1638 feature->Installed = state;
1642 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1644 return (feature->Level > 0 && feature->Level <= level);
1647 static BOOL process_state_property(MSIPACKAGE* package, int level,
1648 LPCWSTR property, INSTALLSTATE state)
1650 LPWSTR override;
1651 MSIFEATURE *feature;
1652 BOOL remove = !strcmpW(property, szRemove);
1653 BOOL reinstall = !strcmpW(property, szReinstall);
1655 override = msi_dup_property( package->db, property );
1656 if (!override)
1657 return FALSE;
1659 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1661 if (feature->Level <= 0)
1662 continue;
1664 if (reinstall)
1665 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1666 else if (remove)
1667 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1669 if (!strcmpiW( override, szAll ))
1671 feature->Action = state;
1672 feature->ActionRequest = state;
1674 else
1676 LPWSTR ptr = override;
1677 LPWSTR ptr2 = strchrW(override,',');
1679 while (ptr)
1681 int len = ptr2 - ptr;
1683 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1684 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1686 feature->Action = state;
1687 feature->ActionRequest = state;
1688 break;
1690 if (ptr2)
1692 ptr=ptr2+1;
1693 ptr2 = strchrW(ptr,',');
1695 else
1696 break;
1700 msi_free(override);
1701 return TRUE;
1704 static BOOL process_overrides( MSIPACKAGE *package, int level )
1706 static const WCHAR szAddLocal[] =
1707 {'A','D','D','L','O','C','A','L',0};
1708 static const WCHAR szAddSource[] =
1709 {'A','D','D','S','O','U','R','C','E',0};
1710 static const WCHAR szAdvertise[] =
1711 {'A','D','V','E','R','T','I','S','E',0};
1712 BOOL ret = FALSE;
1714 /* all these activation/deactivation things happen in order and things
1715 * later on the list override things earlier on the list.
1717 * 0 INSTALLLEVEL processing
1718 * 1 ADDLOCAL
1719 * 2 REMOVE
1720 * 3 ADDSOURCE
1721 * 4 ADDDEFAULT
1722 * 5 REINSTALL
1723 * 6 ADVERTISE
1724 * 7 COMPADDLOCAL
1725 * 8 COMPADDSOURCE
1726 * 9 FILEADDLOCAL
1727 * 10 FILEADDSOURCE
1728 * 11 FILEADDDEFAULT
1730 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1731 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1732 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1733 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1734 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1736 if (ret && !package->full_reinstall)
1737 msi_set_property( package->db, szPreselected, szOne, -1 );
1739 return ret;
1742 static void disable_children( MSIFEATURE *feature, int level )
1744 FeatureList *fl;
1746 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1748 if (!is_feature_selected( feature, level ))
1750 TRACE("child %s (level %d request %d) follows disabled parent %s (level %d request %d)\n",
1751 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1752 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1754 fl->feature->Level = feature->Level;
1755 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1756 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1758 disable_children( fl->feature, level );
1762 static void follow_parent( MSIFEATURE *feature )
1764 FeatureList *fl;
1766 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1768 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1770 TRACE("child %s (level %d request %d) follows parent %s (level %d request %d)\n",
1771 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1772 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1774 fl->feature->Action = feature->Action;
1775 fl->feature->ActionRequest = feature->ActionRequest;
1777 follow_parent( fl->feature );
1781 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1783 int level;
1784 MSICOMPONENT* component;
1785 MSIFEATURE *feature;
1787 TRACE("Checking Install Level\n");
1789 level = msi_get_property_int(package->db, szInstallLevel, 1);
1791 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1793 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1795 if (!is_feature_selected( feature, level )) continue;
1797 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1799 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1801 feature->Action = INSTALLSTATE_SOURCE;
1802 feature->ActionRequest = INSTALLSTATE_SOURCE;
1804 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1806 feature->Action = INSTALLSTATE_ADVERTISED;
1807 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1809 else
1811 feature->Action = INSTALLSTATE_LOCAL;
1812 feature->ActionRequest = INSTALLSTATE_LOCAL;
1816 /* disable child features of unselected parent or follow parent */
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 /* preselected */
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->Installed == INSTALLSTATE_ABSENT)
1834 feature->Action = INSTALLSTATE_UNKNOWN;
1835 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1837 else
1839 feature->Action = feature->Installed;
1840 feature->ActionRequest = feature->Installed;
1844 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1846 if (feature->Feature_Parent) continue;
1847 disable_children( feature, level );
1848 follow_parent( feature );
1852 /* now we want to set component state based based on feature state */
1853 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1855 ComponentList *cl;
1857 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1858 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1859 feature->ActionRequest, feature->Action);
1861 /* features with components that have compressed files are made local */
1862 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1864 if (cl->component->ForceLocalState &&
1865 feature->ActionRequest == INSTALLSTATE_SOURCE)
1867 feature->Action = INSTALLSTATE_LOCAL;
1868 feature->ActionRequest = INSTALLSTATE_LOCAL;
1869 break;
1873 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1875 component = cl->component;
1877 switch (feature->ActionRequest)
1879 case INSTALLSTATE_ABSENT:
1880 component->anyAbsent = 1;
1881 break;
1882 case INSTALLSTATE_ADVERTISED:
1883 component->hasAdvertisedFeature = 1;
1884 break;
1885 case INSTALLSTATE_SOURCE:
1886 component->hasSourceFeature = 1;
1887 break;
1888 case INSTALLSTATE_LOCAL:
1889 component->hasLocalFeature = 1;
1890 break;
1891 case INSTALLSTATE_DEFAULT:
1892 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1893 component->hasAdvertisedFeature = 1;
1894 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1895 component->hasSourceFeature = 1;
1896 else
1897 component->hasLocalFeature = 1;
1898 break;
1899 default:
1900 break;
1905 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1907 /* check if it's local or source */
1908 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1909 (component->hasLocalFeature || component->hasSourceFeature))
1911 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1912 !component->ForceLocalState)
1914 component->Action = INSTALLSTATE_SOURCE;
1915 component->ActionRequest = INSTALLSTATE_SOURCE;
1917 else
1919 component->Action = INSTALLSTATE_LOCAL;
1920 component->ActionRequest = INSTALLSTATE_LOCAL;
1922 continue;
1925 /* if any feature is local, the component must be local too */
1926 if (component->hasLocalFeature)
1928 component->Action = INSTALLSTATE_LOCAL;
1929 component->ActionRequest = INSTALLSTATE_LOCAL;
1930 continue;
1932 if (component->hasSourceFeature)
1934 component->Action = INSTALLSTATE_SOURCE;
1935 component->ActionRequest = INSTALLSTATE_SOURCE;
1936 continue;
1938 if (component->hasAdvertisedFeature)
1940 component->Action = INSTALLSTATE_ADVERTISED;
1941 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1942 continue;
1944 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1945 if (component->anyAbsent && component->ComponentId)
1947 component->Action = INSTALLSTATE_ABSENT;
1948 component->ActionRequest = INSTALLSTATE_ABSENT;
1952 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1954 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1956 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1957 component->Action = INSTALLSTATE_LOCAL;
1958 component->ActionRequest = INSTALLSTATE_LOCAL;
1961 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1962 component->Installed == INSTALLSTATE_SOURCE &&
1963 component->hasSourceFeature)
1965 component->Action = INSTALLSTATE_UNKNOWN;
1966 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1969 TRACE("component %s (installed %d request %d action %d)\n",
1970 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1972 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1973 component->num_clients++;
1974 else if (component->Action == INSTALLSTATE_ABSENT)
1975 component->num_clients--;
1978 return ERROR_SUCCESS;
1981 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1983 MSIPACKAGE *package = param;
1984 LPCWSTR name;
1985 MSIFEATURE *feature;
1987 name = MSI_RecordGetString( row, 1 );
1989 feature = msi_get_loaded_feature( package, name );
1990 if (!feature)
1991 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1992 else
1994 LPCWSTR Condition;
1995 Condition = MSI_RecordGetString(row,3);
1997 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1999 int level = MSI_RecordGetInteger(row,2);
2000 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2001 feature->Level = level;
2004 return ERROR_SUCCESS;
2007 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2009 static const WCHAR name[] = {'\\',0};
2010 VS_FIXEDFILEINFO *ptr, *ret;
2011 LPVOID version;
2012 DWORD versize, handle;
2013 UINT sz;
2015 versize = GetFileVersionInfoSizeW( filename, &handle );
2016 if (!versize)
2017 return NULL;
2019 version = msi_alloc( versize );
2020 if (!version)
2021 return NULL;
2023 GetFileVersionInfoW( filename, 0, versize, version );
2025 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2027 msi_free( version );
2028 return NULL;
2031 ret = msi_alloc( sz );
2032 memcpy( ret, ptr, sz );
2034 msi_free( version );
2035 return ret;
2038 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2040 DWORD ms, ls;
2042 msi_parse_version_string( version, &ms, &ls );
2044 if (fi->dwFileVersionMS > ms) return 1;
2045 else if (fi->dwFileVersionMS < ms) return -1;
2046 else if (fi->dwFileVersionLS > ls) return 1;
2047 else if (fi->dwFileVersionLS < ls) return -1;
2048 return 0;
2051 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2053 DWORD ms1, ms2;
2055 msi_parse_version_string( ver1, &ms1, NULL );
2056 msi_parse_version_string( ver2, &ms2, NULL );
2058 if (ms1 > ms2) return 1;
2059 else if (ms1 < ms2) return -1;
2060 return 0;
2063 DWORD msi_get_disk_file_size( LPCWSTR filename )
2065 HANDLE file;
2066 DWORD size;
2068 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2069 if (file == INVALID_HANDLE_VALUE)
2070 return INVALID_FILE_SIZE;
2072 size = GetFileSize( file, NULL );
2073 CloseHandle( file );
2074 return size;
2077 BOOL msi_file_hash_matches( MSIFILE *file )
2079 UINT r;
2080 MSIFILEHASHINFO hash;
2082 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2083 r = msi_get_filehash( file->TargetPath, &hash );
2084 if (r != ERROR_SUCCESS)
2085 return FALSE;
2087 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2090 static WCHAR *create_temp_dir( MSIDATABASE *db )
2092 static UINT id;
2093 WCHAR *ret;
2095 if (!db->tempfolder)
2097 WCHAR tmp[MAX_PATH];
2098 UINT len = sizeof(tmp)/sizeof(tmp[0]);
2100 if (msi_get_property( db, szTempFolder, tmp, &len ) ||
2101 GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY)
2103 GetTempPathW( MAX_PATH, tmp );
2105 if (!(db->tempfolder = strdupW( tmp ))) return NULL;
2108 if ((ret = msi_alloc( (strlenW( db->tempfolder ) + 20) * sizeof(WCHAR) )))
2110 for (;;)
2112 if (!GetTempFileNameW( db->tempfolder, szMsi, ++id, ret ))
2114 msi_free( ret );
2115 return NULL;
2117 if (CreateDirectoryW( ret, NULL )) break;
2121 return ret;
2125 * msi_build_directory_name()
2127 * This function is to save messing round with directory names
2128 * It handles adding backslashes between path segments,
2129 * and can add \ at the end of the directory name if told to.
2131 * It takes a variable number of arguments.
2132 * It always allocates a new string for the result, so make sure
2133 * to free the return value when finished with it.
2135 * The first arg is the number of path segments that follow.
2136 * The arguments following count are a list of path segments.
2137 * A path segment may be NULL.
2139 * Path segments will be added with a \ separating them.
2140 * A \ will not be added after the last segment, however if the
2141 * last segment is NULL, then the last character will be a \
2143 WCHAR *msi_build_directory_name( DWORD count, ... )
2145 DWORD sz = 1, i;
2146 WCHAR *dir;
2147 va_list va;
2149 va_start( va, count );
2150 for (i = 0; i < count; i++)
2152 const WCHAR *str = va_arg( va, const WCHAR * );
2153 if (str) sz += strlenW( str ) + 1;
2155 va_end( va );
2157 dir = msi_alloc( sz * sizeof(WCHAR) );
2158 dir[0] = 0;
2160 va_start( va, count );
2161 for (i = 0; i < count; i++)
2163 const WCHAR *str = va_arg( va, const WCHAR * );
2164 if (!str) continue;
2165 strcatW( dir, str );
2166 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2168 va_end( va );
2169 return dir;
2172 BOOL msi_is_global_assembly( MSICOMPONENT *comp )
2174 return comp->assembly && !comp->assembly->application;
2177 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2179 msi_free( file->TargetPath );
2180 if (msi_is_global_assembly( file->Component ))
2182 MSIASSEMBLY *assembly = file->Component->assembly;
2184 if (!assembly->tempdir) assembly->tempdir = create_temp_dir( package->db );
2185 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2187 else
2189 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2190 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2193 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2196 static UINT calculate_file_cost( MSIPACKAGE *package )
2198 VS_FIXEDFILEINFO *file_version;
2199 WCHAR *font_version;
2200 MSIFILE *file;
2202 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2204 MSICOMPONENT *comp = file->Component;
2205 DWORD file_size;
2207 if (!comp->Enabled) continue;
2209 if (file->IsCompressed)
2210 comp->ForceLocalState = TRUE;
2212 set_target_path( package, file );
2214 if ((comp->assembly && !comp->assembly->installed) ||
2215 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2217 comp->Cost += file->FileSize;
2218 continue;
2220 file_size = msi_get_disk_file_size( file->TargetPath );
2221 TRACE("%s (size %u)\n", debugstr_w(file->TargetPath), file_size);
2223 if (file->Version)
2225 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2227 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2229 comp->Cost += file->FileSize - file_size;
2231 msi_free( file_version );
2232 continue;
2234 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2236 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2238 comp->Cost += file->FileSize - file_size;
2240 msi_free( font_version );
2241 continue;
2244 if (file_size != file->FileSize)
2246 comp->Cost += file->FileSize - file_size;
2249 return ERROR_SUCCESS;
2252 WCHAR *msi_normalize_path( const WCHAR *in )
2254 const WCHAR *p = in;
2255 WCHAR *q, *ret;
2256 int n, len = strlenW( in ) + 2;
2258 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2260 len = 0;
2261 while (1)
2263 /* copy until the end of the string or a space */
2264 while (*p != ' ' && (*q = *p))
2266 p++, len++;
2267 /* reduce many backslashes to one */
2268 if (*p != '\\' || *q != '\\')
2269 q++;
2272 /* quit at the end of the string */
2273 if (!*p)
2274 break;
2276 /* count the number of spaces */
2277 n = 0;
2278 while (p[n] == ' ')
2279 n++;
2281 /* if it's leading or trailing space, skip it */
2282 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2283 p += n;
2284 else /* copy n spaces */
2285 while (n && (*q++ = *p++)) n--;
2287 while (q - ret > 0 && q[-1] == ' ') q--;
2288 if (q - ret > 0 && q[-1] != '\\')
2290 q[0] = '\\';
2291 q[1] = 0;
2293 return ret;
2296 static WCHAR *get_install_location( MSIPACKAGE *package )
2298 HKEY hkey;
2299 WCHAR *path;
2301 if (!package->ProductCode) return NULL;
2302 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2303 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2305 msi_free( path );
2306 path = NULL;
2308 RegCloseKey( hkey );
2309 return path;
2312 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2314 FolderList *fl;
2315 MSIFOLDER *folder, *parent, *child;
2316 WCHAR *path, *normalized_path;
2318 TRACE("resolving %s\n", debugstr_w(name));
2320 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2322 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2324 if (!(path = get_install_location( package )) &&
2325 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2327 path = msi_dup_property( package->db, szRootDrive );
2330 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2332 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2334 parent = msi_get_loaded_folder( package, folder->Parent );
2335 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2337 else
2338 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2340 normalized_path = msi_normalize_path( path );
2341 msi_free( path );
2342 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2344 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2345 msi_free( normalized_path );
2346 return;
2348 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2349 msi_free( folder->ResolvedTarget );
2350 folder->ResolvedTarget = normalized_path;
2352 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2354 child = fl->folder;
2355 msi_resolve_target_folder( package, child->Directory, load_prop );
2357 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2360 static ULONGLONG get_volume_space_required( MSIPACKAGE *package )
2362 MSICOMPONENT *comp;
2363 ULONGLONG ret = 0;
2365 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2367 if (comp->Action == INSTALLSTATE_LOCAL) ret += comp->Cost;
2369 return ret;
2372 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2374 static const WCHAR query[] =
2375 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2376 '`','C','o','n','d','i','t','i','o','n','`',0};
2377 static const WCHAR szOutOfDiskSpace[] =
2378 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2379 static const WCHAR szPrimaryFolder[] =
2380 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2381 static const WCHAR szPrimaryVolumePath[] =
2382 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2383 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2384 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2385 'A','v','a','i','l','a','b','l','e',0};
2386 static const WCHAR szPrimaryVolumeSpaceRequired[] =
2387 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2388 'R','e','q','u','i','r','e','d',0};
2389 static const WCHAR szPrimaryVolumeSpaceRemaining[] =
2390 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2391 'R','e','m','a','i','n','i','n','g',0};
2392 static const WCHAR szOutOfNoRbDiskSpace[] =
2393 {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0};
2394 MSICOMPONENT *comp;
2395 MSIQUERY *view;
2396 WCHAR *level, *primary_key, *primary_folder;
2397 UINT rc;
2399 TRACE("Building directory properties\n");
2400 msi_resolve_target_folder( package, szTargetDir, TRUE );
2402 TRACE("Evaluating component conditions\n");
2403 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2405 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2407 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2408 comp->Enabled = FALSE;
2410 else
2411 comp->Enabled = TRUE;
2413 get_client_counts( package );
2415 /* read components states from the registry */
2416 ACTION_GetComponentInstallStates(package);
2417 ACTION_GetFeatureInstallStates(package);
2419 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2421 TRACE("Evaluating feature conditions\n");
2423 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2424 if (rc == ERROR_SUCCESS)
2426 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2427 msiobj_release( &view->hdr );
2428 if (rc != ERROR_SUCCESS)
2429 return rc;
2433 TRACE("Calculating file cost\n");
2434 calculate_file_cost( package );
2436 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2437 /* set default run level if not set */
2438 level = msi_dup_property( package->db, szInstallLevel );
2439 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2440 msi_free(level);
2442 if ((rc = MSI_SetFeatureStates( package ))) return rc;
2444 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2446 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2448 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2449 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2451 static const WCHAR fmtW[] = {'%','l','u',0};
2452 ULARGE_INTEGER free;
2453 ULONGLONG required;
2454 WCHAR buf[21];
2456 primary_folder[2] = 0;
2457 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2459 sprintfW( buf, fmtW, free.QuadPart / 512 );
2460 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2462 required = get_volume_space_required( package );
2463 sprintfW( buf, fmtW, required / 512 );
2464 msi_set_property( package->db, szPrimaryVolumeSpaceRequired, buf, -1 );
2466 sprintfW( buf, fmtW, (free.QuadPart - required) / 512 );
2467 msi_set_property( package->db, szPrimaryVolumeSpaceRemaining, buf, -1 );
2468 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2470 msi_free( primary_folder );
2472 msi_free( primary_key );
2475 /* FIXME: check volume disk space */
2476 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2477 msi_set_property( package->db, szOutOfNoRbDiskSpace, szZero, -1 );
2479 return ERROR_SUCCESS;
2482 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size )
2484 BYTE *data;
2486 if (!value)
2488 *size = sizeof(WCHAR);
2489 *type = REG_SZ;
2490 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2491 return data;
2493 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2495 if (value[1]=='x')
2497 LPWSTR ptr;
2498 CHAR byte[5];
2499 LPWSTR deformated = NULL;
2500 int count;
2502 deformat_string(package, &value[2], &deformated);
2504 /* binary value type */
2505 ptr = deformated;
2506 *type = REG_BINARY;
2507 if (strlenW(ptr)%2)
2508 *size = (strlenW(ptr)/2)+1;
2509 else
2510 *size = strlenW(ptr)/2;
2512 data = msi_alloc(*size);
2514 byte[0] = '0';
2515 byte[1] = 'x';
2516 byte[4] = 0;
2517 count = 0;
2518 /* if uneven pad with a zero in front */
2519 if (strlenW(ptr)%2)
2521 byte[2]= '0';
2522 byte[3]= *ptr;
2523 ptr++;
2524 data[count] = (BYTE)strtol(byte,NULL,0);
2525 count ++;
2526 TRACE("Uneven byte count\n");
2528 while (*ptr)
2530 byte[2]= *ptr;
2531 ptr++;
2532 byte[3]= *ptr;
2533 ptr++;
2534 data[count] = (BYTE)strtol(byte,NULL,0);
2535 count ++;
2537 msi_free(deformated);
2539 TRACE("Data %i bytes(%i)\n",*size,count);
2541 else
2543 LPWSTR deformated;
2544 LPWSTR p;
2545 DWORD d = 0;
2546 deformat_string(package, &value[1], &deformated);
2548 *type=REG_DWORD;
2549 *size = sizeof(DWORD);
2550 data = msi_alloc(*size);
2551 p = deformated;
2552 if (*p == '-')
2553 p++;
2554 while (*p)
2556 if ( (*p < '0') || (*p > '9') )
2557 break;
2558 d *= 10;
2559 d += (*p - '0');
2560 p++;
2562 if (deformated[0] == '-')
2563 d = -d;
2564 *(LPDWORD)data = d;
2565 TRACE("DWORD %i\n",*(LPDWORD)data);
2567 msi_free(deformated);
2570 else
2572 const WCHAR *ptr = value;
2574 *type = REG_SZ;
2575 if (value[0] == '#')
2577 ptr++; len--;
2578 if (value[1] == '%')
2580 ptr++; len--;
2581 *type = REG_EXPAND_SZ;
2584 data = (BYTE *)msi_strdupW( ptr, len );
2585 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2586 *size = (len + 1) * sizeof(WCHAR);
2588 return data;
2591 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2593 const WCHAR *ret;
2595 switch (root)
2597 case -1:
2598 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2600 *root_key = HKEY_LOCAL_MACHINE;
2601 ret = szHLM;
2603 else
2605 *root_key = HKEY_CURRENT_USER;
2606 ret = szHCU;
2608 break;
2609 case 0:
2610 *root_key = HKEY_CLASSES_ROOT;
2611 ret = szHCR;
2612 break;
2613 case 1:
2614 *root_key = HKEY_CURRENT_USER;
2615 ret = szHCU;
2616 break;
2617 case 2:
2618 *root_key = HKEY_LOCAL_MACHINE;
2619 ret = szHLM;
2620 break;
2621 case 3:
2622 *root_key = HKEY_USERS;
2623 ret = szHU;
2624 break;
2625 default:
2626 ERR("Unknown root %i\n", root);
2627 return NULL;
2630 return ret;
2633 static inline REGSAM get_registry_view( const MSICOMPONENT *comp )
2635 REGSAM view = 0;
2636 if (is_wow64 || is_64bit)
2637 view |= (comp->Attributes & msidbComponentAttributes64bit) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
2638 return view;
2641 static HKEY open_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, BOOL create, REGSAM access )
2643 WCHAR *subkey, *p, *q;
2644 HKEY hkey, ret = NULL;
2645 LONG res;
2647 access |= get_registry_view( comp );
2649 if (!(subkey = strdupW( path ))) return NULL;
2650 p = subkey;
2651 if ((q = strchrW( p, '\\' ))) *q = 0;
2652 if (create)
2653 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2654 else
2655 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2656 if (res)
2658 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2659 msi_free( subkey );
2660 return NULL;
2662 if (q && q[1])
2664 ret = open_key( comp, hkey, q + 1, create, access );
2665 RegCloseKey( hkey );
2667 else ret = hkey;
2668 msi_free( subkey );
2669 return ret;
2672 static BOOL is_special_entry( const WCHAR *name )
2674 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2677 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2679 const WCHAR *p = str;
2680 WCHAR **ret;
2681 int i = 0;
2683 *count = 0;
2684 if (!str) return NULL;
2685 while ((p - str) < len)
2687 p += strlenW( p ) + 1;
2688 (*count)++;
2690 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2691 p = str;
2692 while ((p - str) < len)
2694 if (!(ret[i] = strdupW( p )))
2696 for (; i >= 0; i--) msi_free( ret[i] );
2697 msi_free( ret );
2698 return NULL;
2700 p += strlenW( p ) + 1;
2701 i++;
2703 return ret;
2706 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2707 WCHAR **right, DWORD right_count, DWORD *size )
2709 WCHAR *ret, *p;
2710 unsigned int i;
2712 *size = sizeof(WCHAR);
2713 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2714 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2716 if (!(ret = p = msi_alloc( *size ))) return NULL;
2718 for (i = 0; i < left_count; i++)
2720 strcpyW( p, left[i] );
2721 p += strlenW( p ) + 1;
2723 for (i = 0; i < right_count; i++)
2725 strcpyW( p, right[i] );
2726 p += strlenW( p ) + 1;
2728 *p = 0;
2729 return ret;
2732 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2733 WCHAR **new, DWORD new_count )
2735 DWORD ret = old_count;
2736 unsigned int i, j, k;
2738 for (i = 0; i < new_count; i++)
2740 for (j = 0; j < old_count; j++)
2742 if (old[j] && !strcmpW( new[i], old[j] ))
2744 msi_free( old[j] );
2745 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2746 old[k] = NULL;
2747 ret--;
2751 return ret;
2754 enum join_op
2756 JOIN_OP_APPEND,
2757 JOIN_OP_PREPEND,
2758 JOIN_OP_REPLACE
2761 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2762 WCHAR **new, DWORD new_count, DWORD *size )
2764 switch (op)
2766 case JOIN_OP_APPEND:
2767 old_count = remove_duplicate_values( old, old_count, new, new_count );
2768 return flatten_multi_string_values( old, old_count, new, new_count, size );
2770 case JOIN_OP_PREPEND:
2771 old_count = remove_duplicate_values( old, old_count, new, new_count );
2772 return flatten_multi_string_values( new, new_count, old, old_count, size );
2774 case JOIN_OP_REPLACE:
2775 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2777 default:
2778 ERR("unhandled join op %u\n", op);
2779 return NULL;
2783 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2784 BYTE *new_value, DWORD new_size, DWORD *size )
2786 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2787 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2788 enum join_op op = JOIN_OP_REPLACE;
2789 WCHAR **old = NULL, **new = NULL;
2790 BYTE *ret;
2792 if (new_size / sizeof(WCHAR) - 1 > 1)
2794 new_ptr = (const WCHAR *)new_value;
2795 new_len = new_size / sizeof(WCHAR) - 1;
2797 if (!new_ptr[0] && new_ptr[new_len - 1])
2799 op = JOIN_OP_APPEND;
2800 new_len--;
2801 new_ptr++;
2803 else if (new_ptr[0] && !new_ptr[new_len - 1])
2805 op = JOIN_OP_PREPEND;
2806 new_len--;
2808 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2810 op = JOIN_OP_REPLACE;
2811 new_len -= 2;
2812 new_ptr++;
2814 new = split_multi_string_values( new_ptr, new_len, &new_count );
2816 if (old_size / sizeof(WCHAR) - 1 > 1)
2818 old_ptr = (const WCHAR *)old_value;
2819 old_len = old_size / sizeof(WCHAR) - 1;
2820 old = split_multi_string_values( old_ptr, old_len, &old_count );
2822 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2823 for (i = 0; i < old_count; i++) msi_free( old[i] );
2824 for (i = 0; i < new_count; i++) msi_free( new[i] );
2825 msi_free( old );
2826 msi_free( new );
2827 return ret;
2830 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2832 BYTE *ret;
2833 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2834 if (!(ret = msi_alloc( *size ))) return NULL;
2835 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2836 return ret;
2839 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2841 MSIPACKAGE *package = param;
2842 BYTE *new_value, *old_value = NULL;
2843 HKEY root_key, hkey;
2844 DWORD type, old_type, new_size, old_size = 0;
2845 LPWSTR deformated, uikey;
2846 const WCHAR *szRoot, *component, *name, *key, *str;
2847 MSICOMPONENT *comp;
2848 MSIRECORD * uirow;
2849 INT root;
2850 BOOL check_first = FALSE;
2851 int len;
2853 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2855 component = MSI_RecordGetString(row, 6);
2856 comp = msi_get_loaded_component(package,component);
2857 if (!comp)
2858 return ERROR_SUCCESS;
2860 comp->Action = msi_get_component_action( package, comp );
2861 if (comp->Action != INSTALLSTATE_LOCAL)
2863 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2864 return ERROR_SUCCESS;
2867 name = MSI_RecordGetString(row, 4);
2868 if( MSI_RecordIsNull(row,5) && name )
2870 /* null values can have special meanings */
2871 if (name[0]=='-' && name[1] == 0)
2872 return ERROR_SUCCESS;
2873 if ((name[0] == '+' || name[0] == '*') && !name[1])
2874 check_first = TRUE;
2877 root = MSI_RecordGetInteger(row,2);
2878 key = MSI_RecordGetString(row, 3);
2880 szRoot = get_root_key( package, root, &root_key );
2881 if (!szRoot)
2882 return ERROR_SUCCESS;
2884 deformat_string(package, key , &deformated);
2885 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2886 strcpyW(uikey,szRoot);
2887 strcatW(uikey,deformated);
2889 if (!(hkey = open_key( comp, root_key, deformated, TRUE, KEY_QUERY_VALUE | KEY_SET_VALUE )))
2891 ERR("Could not create key %s\n", debugstr_w(deformated));
2892 msi_free(uikey);
2893 msi_free(deformated);
2894 return ERROR_FUNCTION_FAILED;
2896 msi_free( deformated );
2897 str = msi_record_get_string( row, 5, NULL );
2898 len = deformat_string( package, str, &deformated );
2899 new_value = parse_value( package, deformated, len, &type, &new_size );
2901 msi_free( deformated );
2902 deformat_string(package, name, &deformated);
2904 if (!is_special_entry( name ))
2906 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2907 if (type == REG_MULTI_SZ)
2909 BYTE *new;
2910 if (old_value && old_type != REG_MULTI_SZ)
2912 msi_free( old_value );
2913 old_value = NULL;
2914 old_size = 0;
2916 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2917 msi_free( new_value );
2918 new_value = new;
2920 if (!check_first)
2922 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2923 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2925 else if (!old_value)
2927 if (deformated || new_size)
2929 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2930 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2933 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2935 RegCloseKey(hkey);
2937 uirow = MSI_CreateRecord(3);
2938 MSI_RecordSetStringW(uirow,2,deformated);
2939 MSI_RecordSetStringW(uirow,1,uikey);
2940 if (type == REG_SZ || type == REG_EXPAND_SZ)
2941 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2942 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
2943 msiobj_release( &uirow->hdr );
2945 msi_free(new_value);
2946 msi_free(old_value);
2947 msi_free(deformated);
2948 msi_free(uikey);
2950 return ERROR_SUCCESS;
2953 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2955 static const WCHAR query[] = {
2956 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2957 '`','R','e','g','i','s','t','r','y','`',0};
2958 MSIQUERY *view;
2959 UINT rc;
2961 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2962 if (rc != ERROR_SUCCESS)
2963 return ERROR_SUCCESS;
2965 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2966 msiobj_release(&view->hdr);
2967 return rc;
2970 static void delete_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2972 REGSAM access = 0;
2973 WCHAR *subkey, *p;
2974 HKEY hkey;
2975 LONG res;
2977 access |= get_registry_view( comp );
2979 if (!(subkey = strdupW( path ))) return;
2982 if ((p = strrchrW( subkey, '\\' )))
2984 *p = 0;
2985 if (!p[1]) continue; /* trailing backslash */
2986 hkey = open_key( comp, root, subkey, FALSE, access | READ_CONTROL );
2987 if (!hkey) break;
2988 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2989 RegCloseKey( hkey );
2991 else
2992 res = RegDeleteKeyExW( root, subkey, access, 0 );
2993 if (res)
2995 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
2996 break;
2998 } while (p);
2999 msi_free( subkey );
3002 static void delete_value( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, const WCHAR *value )
3004 LONG res;
3005 HKEY hkey;
3006 DWORD num_subkeys, num_values;
3008 if ((hkey = open_key( comp, root, path, FALSE, KEY_SET_VALUE | KEY_QUERY_VALUE )))
3010 if ((res = RegDeleteValueW( hkey, value )))
3011 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
3013 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
3014 NULL, NULL, NULL, NULL );
3015 RegCloseKey( hkey );
3016 if (!res && !num_subkeys && !num_values)
3018 TRACE("removing empty key %s\n", debugstr_w(path));
3019 delete_key( comp, root, path );
3024 static void delete_tree( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
3026 LONG res;
3027 HKEY hkey;
3029 if (!(hkey = open_key( comp, root, path, FALSE, KEY_ALL_ACCESS ))) return;
3030 res = RegDeleteTreeW( hkey, NULL );
3031 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3032 delete_key( comp, root, path );
3033 RegCloseKey( hkey );
3036 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3038 MSIPACKAGE *package = param;
3039 LPCWSTR component, name, key_str, root_key_str;
3040 LPWSTR deformated_key, deformated_name, ui_key_str;
3041 MSICOMPONENT *comp;
3042 MSIRECORD *uirow;
3043 BOOL delete_key = FALSE;
3044 HKEY hkey_root;
3045 UINT size;
3046 INT root;
3048 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3050 component = MSI_RecordGetString( row, 6 );
3051 comp = msi_get_loaded_component( package, component );
3052 if (!comp)
3053 return ERROR_SUCCESS;
3055 comp->Action = msi_get_component_action( package, comp );
3056 if (comp->Action != INSTALLSTATE_ABSENT)
3058 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3059 return ERROR_SUCCESS;
3062 name = MSI_RecordGetString( row, 4 );
3063 if (MSI_RecordIsNull( row, 5 ) && name )
3065 if (name[0] == '+' && !name[1])
3066 return ERROR_SUCCESS;
3067 if ((name[0] == '-' || name[0] == '*') && !name[1])
3069 delete_key = TRUE;
3070 name = NULL;
3074 root = MSI_RecordGetInteger( row, 2 );
3075 key_str = MSI_RecordGetString( row, 3 );
3077 root_key_str = get_root_key( package, root, &hkey_root );
3078 if (!root_key_str)
3079 return ERROR_SUCCESS;
3081 deformat_string( package, key_str, &deformated_key );
3082 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3083 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3084 strcpyW( ui_key_str, root_key_str );
3085 strcatW( ui_key_str, deformated_key );
3087 deformat_string( package, name, &deformated_name );
3089 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3090 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3091 msi_free( deformated_key );
3093 uirow = MSI_CreateRecord( 2 );
3094 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3095 MSI_RecordSetStringW( uirow, 2, deformated_name );
3096 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3097 msiobj_release( &uirow->hdr );
3099 msi_free( ui_key_str );
3100 msi_free( deformated_name );
3101 return ERROR_SUCCESS;
3104 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3106 MSIPACKAGE *package = param;
3107 LPCWSTR component, name, key_str, root_key_str;
3108 LPWSTR deformated_key, deformated_name, ui_key_str;
3109 MSICOMPONENT *comp;
3110 MSIRECORD *uirow;
3111 BOOL delete_key = FALSE;
3112 HKEY hkey_root;
3113 UINT size;
3114 INT root;
3116 component = MSI_RecordGetString( row, 5 );
3117 comp = msi_get_loaded_component( package, component );
3118 if (!comp)
3119 return ERROR_SUCCESS;
3121 comp->Action = msi_get_component_action( package, comp );
3122 if (comp->Action != INSTALLSTATE_LOCAL)
3124 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3125 return ERROR_SUCCESS;
3128 if ((name = MSI_RecordGetString( row, 4 )))
3130 if (name[0] == '-' && !name[1])
3132 delete_key = TRUE;
3133 name = NULL;
3137 root = MSI_RecordGetInteger( row, 2 );
3138 key_str = MSI_RecordGetString( row, 3 );
3140 root_key_str = get_root_key( package, root, &hkey_root );
3141 if (!root_key_str)
3142 return ERROR_SUCCESS;
3144 deformat_string( package, key_str, &deformated_key );
3145 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3146 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3147 strcpyW( ui_key_str, root_key_str );
3148 strcatW( ui_key_str, deformated_key );
3150 deformat_string( package, name, &deformated_name );
3152 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3153 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3154 msi_free( deformated_key );
3156 uirow = MSI_CreateRecord( 2 );
3157 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3158 MSI_RecordSetStringW( uirow, 2, deformated_name );
3159 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3160 msiobj_release( &uirow->hdr );
3162 msi_free( ui_key_str );
3163 msi_free( deformated_name );
3164 return ERROR_SUCCESS;
3167 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3169 static const WCHAR registry_query[] = {
3170 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3171 '`','R','e','g','i','s','t','r','y','`',0};
3172 static const WCHAR remove_registry_query[] = {
3173 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3174 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3175 MSIQUERY *view;
3176 UINT rc;
3178 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3179 if (rc == ERROR_SUCCESS)
3181 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3182 msiobj_release( &view->hdr );
3183 if (rc != ERROR_SUCCESS)
3184 return rc;
3186 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3187 if (rc == ERROR_SUCCESS)
3189 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3190 msiobj_release( &view->hdr );
3191 if (rc != ERROR_SUCCESS)
3192 return rc;
3194 return ERROR_SUCCESS;
3197 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3199 return ERROR_SUCCESS;
3203 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3205 static const WCHAR query[]= {
3206 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3207 '`','R','e','g','i','s','t','r','y','`',0};
3208 MSICOMPONENT *comp;
3209 DWORD total = 0, count = 0;
3210 MSIQUERY *view;
3211 MSIFEATURE *feature;
3212 MSIFILE *file;
3213 UINT rc;
3215 TRACE("InstallValidate\n");
3217 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3218 if (rc == ERROR_SUCCESS)
3220 rc = MSI_IterateRecords( view, &count, NULL, package );
3221 msiobj_release( &view->hdr );
3222 if (rc != ERROR_SUCCESS)
3223 return rc;
3224 total += count * REG_PROGRESS_VALUE;
3226 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3227 total += COMPONENT_PROGRESS_VALUE;
3229 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3230 total += file->FileSize;
3232 msi_ui_progress( package, 0, total, 0, 0 );
3234 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3236 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3237 debugstr_w(feature->Feature), feature->Installed,
3238 feature->ActionRequest, feature->Action);
3240 return ERROR_SUCCESS;
3243 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3245 MSIPACKAGE* package = param;
3246 LPCWSTR cond = NULL;
3247 LPCWSTR message = NULL;
3248 UINT r;
3250 static const WCHAR title[]=
3251 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3253 cond = MSI_RecordGetString(row,1);
3255 r = MSI_EvaluateConditionW(package,cond);
3256 if (r == MSICONDITION_FALSE)
3258 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3260 LPWSTR deformated;
3261 message = MSI_RecordGetString(row,2);
3262 deformat_string(package,message,&deformated);
3263 MessageBoxW(NULL,deformated,title,MB_OK);
3264 msi_free(deformated);
3267 return ERROR_INSTALL_FAILURE;
3270 return ERROR_SUCCESS;
3273 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3275 static const WCHAR query[] = {
3276 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3277 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3278 MSIQUERY *view;
3279 UINT rc;
3281 TRACE("Checking launch conditions\n");
3283 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3284 if (rc != ERROR_SUCCESS)
3285 return ERROR_SUCCESS;
3287 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3288 msiobj_release(&view->hdr);
3289 return rc;
3292 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3295 if (!cmp->KeyPath)
3296 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3298 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3300 static const WCHAR query[] = {
3301 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3302 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3303 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3304 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3305 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3306 MSIRECORD *row;
3307 UINT root, len;
3308 LPWSTR deformated, buffer, deformated_name;
3309 LPCWSTR key, name;
3311 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3312 if (!row)
3313 return NULL;
3315 root = MSI_RecordGetInteger(row,2);
3316 key = MSI_RecordGetString(row, 3);
3317 name = MSI_RecordGetString(row, 4);
3318 deformat_string(package, key , &deformated);
3319 deformat_string(package, name, &deformated_name);
3321 len = strlenW(deformated) + 6;
3322 if (deformated_name)
3323 len+=strlenW(deformated_name);
3325 buffer = msi_alloc( len *sizeof(WCHAR));
3327 if (deformated_name)
3328 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3329 else
3330 sprintfW(buffer,fmt,root,deformated);
3332 msi_free(deformated);
3333 msi_free(deformated_name);
3334 msiobj_release(&row->hdr);
3336 return buffer;
3338 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3340 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3341 return NULL;
3343 else
3345 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3347 if (file)
3348 return strdupW( file->TargetPath );
3350 return NULL;
3353 static HKEY openSharedDLLsKey(void)
3355 HKEY hkey=0;
3356 static const WCHAR path[] =
3357 {'S','o','f','t','w','a','r','e','\\',
3358 'M','i','c','r','o','s','o','f','t','\\',
3359 'W','i','n','d','o','w','s','\\',
3360 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3361 'S','h','a','r','e','d','D','L','L','s',0};
3363 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3364 return hkey;
3367 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3369 HKEY hkey;
3370 DWORD count=0;
3371 DWORD type;
3372 DWORD sz = sizeof(count);
3373 DWORD rc;
3375 hkey = openSharedDLLsKey();
3376 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3377 if (rc != ERROR_SUCCESS)
3378 count = 0;
3379 RegCloseKey(hkey);
3380 return count;
3383 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3385 HKEY hkey;
3387 hkey = openSharedDLLsKey();
3388 if (count > 0)
3389 msi_reg_set_val_dword( hkey, path, count );
3390 else
3391 RegDeleteValueW(hkey,path);
3392 RegCloseKey(hkey);
3393 return count;
3396 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3398 MSIFEATURE *feature;
3399 INT count = 0;
3400 BOOL write = FALSE;
3402 /* only refcount DLLs */
3403 if (comp->KeyPath == NULL ||
3404 comp->assembly ||
3405 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3406 comp->Attributes & msidbComponentAttributesODBCDataSource)
3407 write = FALSE;
3408 else
3410 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3411 write = (count > 0);
3413 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3414 write = TRUE;
3417 /* increment counts */
3418 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3420 ComponentList *cl;
3422 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3423 continue;
3425 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3427 if ( cl->component == comp )
3428 count++;
3432 /* decrement counts */
3433 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3435 ComponentList *cl;
3437 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3438 continue;
3440 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3442 if ( cl->component == comp )
3443 count--;
3447 /* ref count all the files in the component */
3448 if (write)
3450 MSIFILE *file;
3452 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3454 if (file->Component == comp)
3455 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3459 /* add a count for permanent */
3460 if (comp->Attributes & msidbComponentAttributesPermanent)
3461 count ++;
3463 comp->RefCount = count;
3465 if (write)
3466 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3469 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3471 if (comp->assembly)
3473 const WCHAR prefixW[] = {'<','\\',0};
3474 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3475 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3477 if (keypath)
3479 strcpyW( keypath, prefixW );
3480 strcatW( keypath, comp->assembly->display_name );
3482 return keypath;
3484 return resolve_keypath( package, comp );
3487 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3489 WCHAR squashed_pc[SQUASHED_GUID_SIZE], squashed_cc[SQUASHED_GUID_SIZE];
3490 UINT rc;
3491 MSICOMPONENT *comp;
3492 HKEY hkey;
3494 TRACE("\n");
3496 squash_guid( package->ProductCode, squashed_pc );
3497 msi_set_sourcedir_props(package, FALSE);
3499 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3501 MSIRECORD *uirow;
3502 INSTALLSTATE action;
3504 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3505 if (!comp->ComponentId)
3506 continue;
3508 squash_guid( comp->ComponentId, squashed_cc );
3509 msi_free( comp->FullKeypath );
3510 comp->FullKeypath = build_full_keypath( package, comp );
3512 ACTION_RefCountComponent( package, comp );
3514 if (package->need_rollback) action = comp->Installed;
3515 else action = comp->ActionRequest;
3517 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3518 debugstr_w(comp->Component), debugstr_w(squashed_cc),
3519 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3521 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3523 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3524 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3525 else
3526 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3528 if (rc != ERROR_SUCCESS)
3529 continue;
3531 if (comp->Attributes & msidbComponentAttributesPermanent)
3533 static const WCHAR szPermKey[] =
3534 { '0','0','0','0','0','0','0','0','0','0','0','0',
3535 '0','0','0','0','0','0','0','0','0','0','0','0',
3536 '0','0','0','0','0','0','0','0',0 };
3538 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3540 if (action == INSTALLSTATE_LOCAL)
3541 msi_reg_set_val_str( hkey, squashed_pc, comp->FullKeypath );
3542 else
3544 MSIFILE *file;
3545 MSIRECORD *row;
3546 LPWSTR ptr, ptr2;
3547 WCHAR source[MAX_PATH];
3548 WCHAR base[MAX_PATH];
3549 LPWSTR sourcepath;
3551 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3552 static const WCHAR query[] = {
3553 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3554 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3555 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3556 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3557 '`','D','i','s','k','I','d','`',0};
3559 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3560 continue;
3562 if (!(row = MSI_QueryGetRecord(package->db, query, file->Sequence)))
3563 return ERROR_FUNCTION_FAILED;
3565 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3566 ptr2 = strrchrW(source, '\\') + 1;
3567 msiobj_release(&row->hdr);
3569 lstrcpyW(base, package->PackagePath);
3570 ptr = strrchrW(base, '\\');
3571 *(ptr + 1) = '\0';
3573 sourcepath = msi_resolve_file_source(package, file);
3574 ptr = sourcepath + lstrlenW(base);
3575 lstrcpyW(ptr2, ptr);
3576 msi_free(sourcepath);
3578 msi_reg_set_val_str( hkey, squashed_pc, source );
3580 RegCloseKey(hkey);
3582 else if (action == INSTALLSTATE_ABSENT)
3584 if (comp->num_clients <= 0)
3586 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3587 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3588 else
3589 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3591 if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
3593 else
3595 LONG res;
3597 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3598 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE );
3599 else
3600 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );
3602 if (rc != ERROR_SUCCESS)
3604 WARN( "failed to open component key %u\n", rc );
3605 continue;
3607 res = RegDeleteValueW( hkey, squashed_pc );
3608 RegCloseKey(hkey);
3609 if (res) WARN( "failed to delete component value %d\n", res );
3613 /* UI stuff */
3614 uirow = MSI_CreateRecord(3);
3615 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3616 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3617 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3618 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3619 msiobj_release( &uirow->hdr );
3621 return ERROR_SUCCESS;
3624 typedef struct {
3625 CLSID clsid;
3626 LPWSTR source;
3628 LPWSTR path;
3629 ITypeLib *ptLib;
3630 } typelib_struct;
3632 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3633 LPWSTR lpszName, LONG_PTR lParam)
3635 TLIBATTR *attr;
3636 typelib_struct *tl_struct = (typelib_struct*) lParam;
3637 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3638 int sz;
3639 HRESULT res;
3641 if (!IS_INTRESOURCE(lpszName))
3643 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3644 return TRUE;
3647 sz = strlenW(tl_struct->source)+4;
3648 sz *= sizeof(WCHAR);
3650 if ((INT_PTR)lpszName == 1)
3651 tl_struct->path = strdupW(tl_struct->source);
3652 else
3654 tl_struct->path = msi_alloc(sz);
3655 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3658 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3659 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3660 if (FAILED(res))
3662 msi_free(tl_struct->path);
3663 tl_struct->path = NULL;
3665 return TRUE;
3668 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3669 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3671 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3672 return FALSE;
3675 msi_free(tl_struct->path);
3676 tl_struct->path = NULL;
3678 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3679 ITypeLib_Release(tl_struct->ptLib);
3681 return TRUE;
3684 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3686 MSIPACKAGE* package = param;
3687 LPCWSTR component;
3688 MSICOMPONENT *comp;
3689 MSIFILE *file;
3690 typelib_struct tl_struct;
3691 ITypeLib *tlib;
3692 HMODULE module;
3693 HRESULT hr;
3695 component = MSI_RecordGetString(row,3);
3696 comp = msi_get_loaded_component(package,component);
3697 if (!comp)
3698 return ERROR_SUCCESS;
3700 comp->Action = msi_get_component_action( package, comp );
3701 if (comp->Action != INSTALLSTATE_LOCAL)
3703 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3704 return ERROR_SUCCESS;
3707 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3709 TRACE("component has no key path\n");
3710 return ERROR_SUCCESS;
3712 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3714 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3715 if (module)
3717 LPCWSTR guid;
3718 guid = MSI_RecordGetString(row,1);
3719 CLSIDFromString( guid, &tl_struct.clsid);
3720 tl_struct.source = strdupW( file->TargetPath );
3721 tl_struct.path = NULL;
3723 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3724 (LONG_PTR)&tl_struct);
3726 if (tl_struct.path)
3728 LPCWSTR helpid, help_path = NULL;
3729 HRESULT res;
3731 helpid = MSI_RecordGetString(row,6);
3733 if (helpid) help_path = msi_get_target_folder( package, helpid );
3734 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3736 if (FAILED(res))
3737 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3738 else
3739 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3741 ITypeLib_Release(tl_struct.ptLib);
3742 msi_free(tl_struct.path);
3744 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3746 FreeLibrary(module);
3747 msi_free(tl_struct.source);
3749 else
3751 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3752 if (FAILED(hr))
3754 ERR("Failed to load type library: %08x\n", hr);
3755 return ERROR_INSTALL_FAILURE;
3758 ITypeLib_Release(tlib);
3761 return ERROR_SUCCESS;
3764 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3766 static const WCHAR query[] = {
3767 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3768 '`','T','y','p','e','L','i','b','`',0};
3769 MSIQUERY *view;
3770 UINT rc;
3772 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3773 if (rc != ERROR_SUCCESS)
3774 return ERROR_SUCCESS;
3776 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3777 msiobj_release(&view->hdr);
3778 return rc;
3781 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3783 MSIPACKAGE *package = param;
3784 LPCWSTR component, guid;
3785 MSICOMPONENT *comp;
3786 GUID libid;
3787 UINT version;
3788 LCID language;
3789 SYSKIND syskind;
3790 HRESULT hr;
3792 component = MSI_RecordGetString( row, 3 );
3793 comp = msi_get_loaded_component( package, component );
3794 if (!comp)
3795 return ERROR_SUCCESS;
3797 comp->Action = msi_get_component_action( package, comp );
3798 if (comp->Action != INSTALLSTATE_ABSENT)
3800 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3801 return ERROR_SUCCESS;
3803 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3805 guid = MSI_RecordGetString( row, 1 );
3806 CLSIDFromString( guid, &libid );
3807 version = MSI_RecordGetInteger( row, 4 );
3808 language = MSI_RecordGetInteger( row, 2 );
3810 #ifdef _WIN64
3811 syskind = SYS_WIN64;
3812 #else
3813 syskind = SYS_WIN32;
3814 #endif
3816 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3817 if (FAILED(hr))
3819 WARN("Failed to unregister typelib: %08x\n", hr);
3822 return ERROR_SUCCESS;
3825 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3827 static const WCHAR query[] = {
3828 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3829 '`','T','y','p','e','L','i','b','`',0};
3830 MSIQUERY *view;
3831 UINT rc;
3833 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3834 if (rc != ERROR_SUCCESS)
3835 return ERROR_SUCCESS;
3837 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3838 msiobj_release( &view->hdr );
3839 return rc;
3842 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3844 static const WCHAR szlnk[] = {'.','l','n','k',0};
3845 LPCWSTR directory, extension, link_folder;
3846 LPWSTR link_file, filename;
3848 directory = MSI_RecordGetString( row, 2 );
3849 link_folder = msi_get_target_folder( package, directory );
3850 if (!link_folder)
3852 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3853 return NULL;
3855 /* may be needed because of a bug somewhere else */
3856 msi_create_full_path( link_folder );
3858 filename = msi_dup_record_field( row, 3 );
3859 msi_reduce_to_long_filename( filename );
3861 extension = strrchrW( filename, '.' );
3862 if (!extension || strcmpiW( extension, szlnk ))
3864 int len = strlenW( filename );
3865 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3866 memcpy( filename + len, szlnk, sizeof(szlnk) );
3868 link_file = msi_build_directory_name( 2, link_folder, filename );
3869 msi_free( filename );
3871 return link_file;
3874 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3876 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3877 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3878 WCHAR *folder, *dest, *path;
3880 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3881 folder = msi_dup_property( package->db, szWindowsFolder );
3882 else
3884 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3885 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3886 msi_free( appdata );
3888 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3889 msi_create_full_path( dest );
3890 path = msi_build_directory_name( 2, dest, icon_name );
3891 msi_free( folder );
3892 msi_free( dest );
3893 return path;
3896 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3898 MSIPACKAGE *package = param;
3899 LPWSTR link_file, deformated, path;
3900 LPCWSTR component, target;
3901 MSICOMPONENT *comp;
3902 IShellLinkW *sl = NULL;
3903 IPersistFile *pf = NULL;
3904 HRESULT res;
3906 component = MSI_RecordGetString(row, 4);
3907 comp = msi_get_loaded_component(package, component);
3908 if (!comp)
3909 return ERROR_SUCCESS;
3911 comp->Action = msi_get_component_action( package, comp );
3912 if (comp->Action != INSTALLSTATE_LOCAL)
3914 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3915 return ERROR_SUCCESS;
3917 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3919 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3920 &IID_IShellLinkW, (LPVOID *) &sl );
3922 if (FAILED( res ))
3924 ERR("CLSID_ShellLink not available\n");
3925 goto err;
3928 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3929 if (FAILED( res ))
3931 ERR("QueryInterface(IID_IPersistFile) failed\n");
3932 goto err;
3935 target = MSI_RecordGetString(row, 5);
3936 if (strchrW(target, '['))
3938 deformat_string( package, target, &path );
3939 TRACE("target path is %s\n", debugstr_w(path));
3940 IShellLinkW_SetPath( sl, path );
3941 msi_free( path );
3943 else
3945 FIXME("poorly handled shortcut format, advertised shortcut\n");
3946 path = resolve_keypath( package, comp );
3947 IShellLinkW_SetPath( sl, path );
3948 msi_free( path );
3951 if (!MSI_RecordIsNull(row,6))
3953 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3954 deformat_string(package, arguments, &deformated);
3955 IShellLinkW_SetArguments(sl,deformated);
3956 msi_free(deformated);
3959 if (!MSI_RecordIsNull(row,7))
3961 LPCWSTR description = MSI_RecordGetString(row, 7);
3962 IShellLinkW_SetDescription(sl, description);
3965 if (!MSI_RecordIsNull(row,8))
3966 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3968 if (!MSI_RecordIsNull(row,9))
3970 INT index;
3971 LPCWSTR icon = MSI_RecordGetString(row, 9);
3973 path = msi_build_icon_path(package, icon);
3974 index = MSI_RecordGetInteger(row,10);
3976 /* no value means 0 */
3977 if (index == MSI_NULL_INTEGER)
3978 index = 0;
3980 IShellLinkW_SetIconLocation(sl, path, index);
3981 msi_free(path);
3984 if (!MSI_RecordIsNull(row,11))
3985 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3987 if (!MSI_RecordIsNull(row,12))
3989 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3990 full_path = msi_get_target_folder( package, wkdir );
3991 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3993 link_file = get_link_file(package, row);
3995 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3996 IPersistFile_Save(pf, link_file, FALSE);
3997 msi_free(link_file);
3999 err:
4000 if (pf)
4001 IPersistFile_Release( pf );
4002 if (sl)
4003 IShellLinkW_Release( sl );
4005 return ERROR_SUCCESS;
4008 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
4010 static const WCHAR query[] = {
4011 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4012 '`','S','h','o','r','t','c','u','t','`',0};
4013 MSIQUERY *view;
4014 HRESULT res;
4015 UINT rc;
4017 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4018 if (rc != ERROR_SUCCESS)
4019 return ERROR_SUCCESS;
4021 res = CoInitialize( NULL );
4023 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4024 msiobj_release(&view->hdr);
4026 if (SUCCEEDED(res)) CoUninitialize();
4027 return rc;
4030 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4032 MSIPACKAGE *package = param;
4033 LPWSTR link_file;
4034 LPCWSTR component;
4035 MSICOMPONENT *comp;
4037 component = MSI_RecordGetString( row, 4 );
4038 comp = msi_get_loaded_component( package, component );
4039 if (!comp)
4040 return ERROR_SUCCESS;
4042 comp->Action = msi_get_component_action( package, comp );
4043 if (comp->Action != INSTALLSTATE_ABSENT)
4045 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4046 return ERROR_SUCCESS;
4048 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
4050 link_file = get_link_file( package, row );
4052 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4053 if (!DeleteFileW( link_file ))
4055 WARN("Failed to remove shortcut file %u\n", GetLastError());
4057 msi_free( link_file );
4059 return ERROR_SUCCESS;
4062 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4064 static const WCHAR query[] = {
4065 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4066 '`','S','h','o','r','t','c','u','t','`',0};
4067 MSIQUERY *view;
4068 UINT rc;
4070 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4071 if (rc != ERROR_SUCCESS)
4072 return ERROR_SUCCESS;
4074 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4075 msiobj_release( &view->hdr );
4076 return rc;
4079 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4081 MSIPACKAGE* package = param;
4082 HANDLE the_file;
4083 LPWSTR FilePath;
4084 LPCWSTR FileName;
4085 CHAR buffer[1024];
4086 DWORD sz;
4087 UINT rc;
4089 FileName = MSI_RecordGetString(row,1);
4090 if (!FileName)
4092 ERR("Unable to get FileName\n");
4093 return ERROR_SUCCESS;
4096 FilePath = msi_build_icon_path(package, FileName);
4098 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4100 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4101 FILE_ATTRIBUTE_NORMAL, NULL);
4103 if (the_file == INVALID_HANDLE_VALUE)
4105 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4106 msi_free(FilePath);
4107 return ERROR_SUCCESS;
4112 DWORD write;
4113 sz = 1024;
4114 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4115 if (rc != ERROR_SUCCESS)
4117 ERR("Failed to get stream\n");
4118 DeleteFileW(FilePath);
4119 break;
4121 WriteFile(the_file,buffer,sz,&write,NULL);
4122 } while (sz == 1024);
4124 msi_free(FilePath);
4125 CloseHandle(the_file);
4127 return ERROR_SUCCESS;
4130 static UINT msi_publish_icons(MSIPACKAGE *package)
4132 static const WCHAR query[]= {
4133 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4134 '`','I','c','o','n','`',0};
4135 MSIQUERY *view;
4136 UINT r;
4138 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4139 if (r == ERROR_SUCCESS)
4141 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4142 msiobj_release(&view->hdr);
4143 if (r != ERROR_SUCCESS)
4144 return r;
4146 return ERROR_SUCCESS;
4149 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4151 UINT r;
4152 HKEY source;
4153 LPWSTR buffer;
4154 MSIMEDIADISK *disk;
4155 MSISOURCELISTINFO *info;
4157 r = RegCreateKeyW(hkey, szSourceList, &source);
4158 if (r != ERROR_SUCCESS)
4159 return r;
4161 RegCloseKey(source);
4163 buffer = strrchrW(package->PackagePath, '\\') + 1;
4164 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4165 package->Context, MSICODE_PRODUCT,
4166 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4167 if (r != ERROR_SUCCESS)
4168 return r;
4170 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4171 package->Context, MSICODE_PRODUCT,
4172 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4173 if (r != ERROR_SUCCESS)
4174 return r;
4176 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4177 package->Context, MSICODE_PRODUCT,
4178 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4179 if (r != ERROR_SUCCESS)
4180 return r;
4182 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4184 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4185 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4186 info->options, info->value);
4187 else
4188 MsiSourceListSetInfoW(package->ProductCode, NULL,
4189 info->context, info->options,
4190 info->property, info->value);
4193 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4195 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4196 disk->context, disk->options,
4197 disk->disk_id, disk->volume_label, disk->disk_prompt);
4200 return ERROR_SUCCESS;
4203 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4205 static const WCHAR szARPProductIcon[] =
4206 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4207 static const WCHAR szAssignment[] =
4208 {'A','s','s','i','g','n','m','e','n','t',0};
4209 static const WCHAR szAdvertiseFlags[] =
4210 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4211 static const WCHAR szClients[] =
4212 {'C','l','i','e','n','t','s',0};
4213 static const WCHAR szColon[] = {':',0};
4214 MSIHANDLE hdb, suminfo;
4215 WCHAR *buffer, *ptr, guids[MAX_PATH], packcode[SQUASHED_GUID_SIZE];
4216 DWORD langid, size;
4217 UINT r;
4219 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4220 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4221 msi_free(buffer);
4223 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4224 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4226 /* FIXME */
4227 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4229 buffer = msi_dup_property(package->db, szARPProductIcon);
4230 if (buffer)
4232 LPWSTR path = msi_build_icon_path(package, buffer);
4233 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4234 msi_free(path);
4235 msi_free(buffer);
4238 buffer = msi_dup_property(package->db, szProductVersion);
4239 if (buffer)
4241 DWORD verdword = msi_version_str_to_dword(buffer);
4242 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4243 msi_free(buffer);
4246 msi_reg_set_val_dword(hkey, szAssignment, 0);
4247 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4248 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4249 msi_reg_set_val_str(hkey, szClients, szColon);
4251 hdb = alloc_msihandle(&package->db->hdr);
4252 if (!hdb)
4253 return ERROR_NOT_ENOUGH_MEMORY;
4255 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4256 MsiCloseHandle(hdb);
4257 if (r != ERROR_SUCCESS)
4258 goto done;
4260 size = MAX_PATH;
4261 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4262 NULL, guids, &size);
4263 if (r != ERROR_SUCCESS)
4264 goto done;
4266 ptr = strchrW(guids, ';');
4267 if (ptr) *ptr = 0;
4268 squash_guid(guids, packcode);
4269 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4271 done:
4272 MsiCloseHandle(suminfo);
4273 return ERROR_SUCCESS;
4276 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4278 UINT r;
4279 HKEY hkey;
4280 WCHAR *upgrade, squashed_pc[SQUASHED_GUID_SIZE];
4282 upgrade = msi_dup_property(package->db, szUpgradeCode);
4283 if (!upgrade)
4284 return ERROR_SUCCESS;
4286 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4287 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4288 else
4289 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4291 if (r != ERROR_SUCCESS)
4293 WARN("failed to open upgrade code key\n");
4294 msi_free(upgrade);
4295 return ERROR_SUCCESS;
4297 squash_guid(package->ProductCode, squashed_pc);
4298 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4299 RegCloseKey(hkey);
4300 msi_free(upgrade);
4301 return ERROR_SUCCESS;
4304 static BOOL msi_check_publish(MSIPACKAGE *package)
4306 MSIFEATURE *feature;
4308 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4310 feature->Action = msi_get_feature_action( package, feature );
4311 if (feature->Action == INSTALLSTATE_LOCAL)
4312 return TRUE;
4315 return FALSE;
4318 static BOOL msi_check_unpublish(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_ABSENT)
4326 return FALSE;
4329 return TRUE;
4332 static UINT msi_publish_patches( MSIPACKAGE *package )
4334 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4335 WCHAR patch_squashed[GUID_SIZE];
4336 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4337 LONG res;
4338 MSIPATCHINFO *patch;
4339 UINT r;
4340 WCHAR *p, *all_patches = NULL;
4341 DWORD len = 0;
4343 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4344 if (r != ERROR_SUCCESS)
4345 return ERROR_FUNCTION_FAILED;
4347 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4348 if (res != ERROR_SUCCESS)
4350 r = ERROR_FUNCTION_FAILED;
4351 goto done;
4354 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4355 if (r != ERROR_SUCCESS)
4356 goto done;
4358 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4360 squash_guid( patch->patchcode, patch_squashed );
4361 len += strlenW( patch_squashed ) + 1;
4364 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4365 if (!all_patches)
4366 goto done;
4368 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4370 HKEY patch_key;
4372 squash_guid( patch->patchcode, p );
4373 p += strlenW( p ) + 1;
4375 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4376 (const BYTE *)patch->transforms,
4377 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4378 if (res != ERROR_SUCCESS)
4379 goto done;
4381 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4382 if (r != ERROR_SUCCESS)
4383 goto done;
4385 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4386 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4387 RegCloseKey( patch_key );
4388 if (res != ERROR_SUCCESS)
4389 goto done;
4391 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4393 res = GetLastError();
4394 ERR("Unable to copy patch package %d\n", res);
4395 goto done;
4397 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4398 if (res != ERROR_SUCCESS)
4399 goto done;
4401 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state,
4402 sizeof(patch->state) );
4403 if (res != ERROR_SUCCESS)
4405 RegCloseKey( patch_key );
4406 goto done;
4409 res = RegSetValueExW( patch_key, szUninstallable, 0, REG_DWORD, (const BYTE *)&patch->uninstallable,
4410 sizeof(patch->uninstallable) );
4411 RegCloseKey( patch_key );
4412 if (res != ERROR_SUCCESS)
4413 goto done;
4416 all_patches[len] = 0;
4417 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4418 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4419 if (res != ERROR_SUCCESS)
4420 goto done;
4422 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4423 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4424 if (res != ERROR_SUCCESS)
4425 r = ERROR_FUNCTION_FAILED;
4427 done:
4428 RegCloseKey( product_patches_key );
4429 RegCloseKey( patches_key );
4430 RegCloseKey( product_key );
4431 msi_free( all_patches );
4432 return r;
4435 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4437 UINT rc;
4438 HKEY hukey = NULL, hudkey = NULL;
4439 MSIRECORD *uirow;
4441 if (!list_empty(&package->patches))
4443 rc = msi_publish_patches(package);
4444 if (rc != ERROR_SUCCESS)
4445 goto end;
4448 /* FIXME: also need to publish if the product is in advertise mode */
4449 if (!msi_check_publish(package))
4450 return ERROR_SUCCESS;
4452 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4453 &hukey, TRUE);
4454 if (rc != ERROR_SUCCESS)
4455 goto end;
4457 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4458 NULL, &hudkey, TRUE);
4459 if (rc != ERROR_SUCCESS)
4460 goto end;
4462 rc = msi_publish_upgrade_code(package);
4463 if (rc != ERROR_SUCCESS)
4464 goto end;
4466 rc = msi_publish_product_properties(package, hukey);
4467 if (rc != ERROR_SUCCESS)
4468 goto end;
4470 rc = msi_publish_sourcelist(package, hukey);
4471 if (rc != ERROR_SUCCESS)
4472 goto end;
4474 rc = msi_publish_icons(package);
4476 end:
4477 uirow = MSI_CreateRecord( 1 );
4478 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4479 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4480 msiobj_release( &uirow->hdr );
4482 RegCloseKey(hukey);
4483 RegCloseKey(hudkey);
4484 return rc;
4487 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4489 WCHAR *filename, *ptr, *folder, *ret;
4490 const WCHAR *dirprop;
4492 filename = msi_dup_record_field( row, 2 );
4493 if (filename && (ptr = strchrW( filename, '|' )))
4494 ptr++;
4495 else
4496 ptr = filename;
4498 dirprop = MSI_RecordGetString( row, 3 );
4499 if (dirprop)
4501 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4502 if (!folder) folder = msi_dup_property( package->db, dirprop );
4504 else
4505 folder = msi_dup_property( package->db, szWindowsFolder );
4507 if (!folder)
4509 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4510 msi_free( filename );
4511 return NULL;
4514 ret = msi_build_directory_name( 2, folder, ptr );
4516 msi_free( filename );
4517 msi_free( folder );
4518 return ret;
4521 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4523 MSIPACKAGE *package = param;
4524 LPCWSTR component, section, key, value, identifier;
4525 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4526 MSIRECORD * uirow;
4527 INT action;
4528 MSICOMPONENT *comp;
4530 component = MSI_RecordGetString(row, 8);
4531 comp = msi_get_loaded_component(package,component);
4532 if (!comp)
4533 return ERROR_SUCCESS;
4535 comp->Action = msi_get_component_action( package, comp );
4536 if (comp->Action != INSTALLSTATE_LOCAL)
4538 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4539 return ERROR_SUCCESS;
4542 identifier = MSI_RecordGetString(row,1);
4543 section = MSI_RecordGetString(row,4);
4544 key = MSI_RecordGetString(row,5);
4545 value = MSI_RecordGetString(row,6);
4546 action = MSI_RecordGetInteger(row,7);
4548 deformat_string(package,section,&deformated_section);
4549 deformat_string(package,key,&deformated_key);
4550 deformat_string(package,value,&deformated_value);
4552 fullname = get_ini_file_name(package, row);
4554 if (action == 0)
4556 TRACE("Adding value %s to section %s in %s\n",
4557 debugstr_w(deformated_key), debugstr_w(deformated_section),
4558 debugstr_w(fullname));
4559 WritePrivateProfileStringW(deformated_section, deformated_key,
4560 deformated_value, fullname);
4562 else if (action == 1)
4564 WCHAR returned[10];
4565 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4566 returned, 10, fullname);
4567 if (returned[0] == 0)
4569 TRACE("Adding value %s to section %s in %s\n",
4570 debugstr_w(deformated_key), debugstr_w(deformated_section),
4571 debugstr_w(fullname));
4573 WritePrivateProfileStringW(deformated_section, deformated_key,
4574 deformated_value, fullname);
4577 else if (action == 3)
4578 FIXME("Append to existing section not yet implemented\n");
4580 uirow = MSI_CreateRecord(4);
4581 MSI_RecordSetStringW(uirow,1,identifier);
4582 MSI_RecordSetStringW(uirow,2,deformated_section);
4583 MSI_RecordSetStringW(uirow,3,deformated_key);
4584 MSI_RecordSetStringW(uirow,4,deformated_value);
4585 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4586 msiobj_release( &uirow->hdr );
4588 msi_free(fullname);
4589 msi_free(deformated_key);
4590 msi_free(deformated_value);
4591 msi_free(deformated_section);
4592 return ERROR_SUCCESS;
4595 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4597 static const WCHAR query[] = {
4598 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4599 '`','I','n','i','F','i','l','e','`',0};
4600 MSIQUERY *view;
4601 UINT rc;
4603 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4604 if (rc != ERROR_SUCCESS)
4605 return ERROR_SUCCESS;
4607 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4608 msiobj_release(&view->hdr);
4609 return rc;
4612 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4614 MSIPACKAGE *package = param;
4615 LPCWSTR component, section, key, value, identifier;
4616 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4617 MSICOMPONENT *comp;
4618 MSIRECORD *uirow;
4619 INT action;
4621 component = MSI_RecordGetString( row, 8 );
4622 comp = msi_get_loaded_component( package, component );
4623 if (!comp)
4624 return ERROR_SUCCESS;
4626 comp->Action = msi_get_component_action( package, comp );
4627 if (comp->Action != INSTALLSTATE_ABSENT)
4629 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4630 return ERROR_SUCCESS;
4633 identifier = MSI_RecordGetString( row, 1 );
4634 section = MSI_RecordGetString( row, 4 );
4635 key = MSI_RecordGetString( row, 5 );
4636 value = MSI_RecordGetString( row, 6 );
4637 action = MSI_RecordGetInteger( row, 7 );
4639 deformat_string( package, section, &deformated_section );
4640 deformat_string( package, key, &deformated_key );
4641 deformat_string( package, value, &deformated_value );
4643 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4645 filename = get_ini_file_name( package, row );
4647 TRACE("Removing key %s from section %s in %s\n",
4648 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4650 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4652 WARN("Unable to remove key %u\n", GetLastError());
4654 msi_free( filename );
4656 else
4657 FIXME("Unsupported action %d\n", action);
4660 uirow = MSI_CreateRecord( 4 );
4661 MSI_RecordSetStringW( uirow, 1, identifier );
4662 MSI_RecordSetStringW( uirow, 2, deformated_section );
4663 MSI_RecordSetStringW( uirow, 3, deformated_key );
4664 MSI_RecordSetStringW( uirow, 4, deformated_value );
4665 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4666 msiobj_release( &uirow->hdr );
4668 msi_free( deformated_key );
4669 msi_free( deformated_value );
4670 msi_free( deformated_section );
4671 return ERROR_SUCCESS;
4674 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4676 MSIPACKAGE *package = param;
4677 LPCWSTR component, section, key, value, identifier;
4678 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4679 MSICOMPONENT *comp;
4680 MSIRECORD *uirow;
4681 INT action;
4683 component = MSI_RecordGetString( row, 8 );
4684 comp = msi_get_loaded_component( package, component );
4685 if (!comp)
4686 return ERROR_SUCCESS;
4688 comp->Action = msi_get_component_action( package, comp );
4689 if (comp->Action != INSTALLSTATE_LOCAL)
4691 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4692 return ERROR_SUCCESS;
4695 identifier = MSI_RecordGetString( row, 1 );
4696 section = MSI_RecordGetString( row, 4 );
4697 key = MSI_RecordGetString( row, 5 );
4698 value = MSI_RecordGetString( row, 6 );
4699 action = MSI_RecordGetInteger( row, 7 );
4701 deformat_string( package, section, &deformated_section );
4702 deformat_string( package, key, &deformated_key );
4703 deformat_string( package, value, &deformated_value );
4705 if (action == msidbIniFileActionRemoveLine)
4707 filename = get_ini_file_name( package, row );
4709 TRACE("Removing key %s from section %s in %s\n",
4710 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4712 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4714 WARN("Unable to remove key %u\n", GetLastError());
4716 msi_free( filename );
4718 else
4719 FIXME("Unsupported action %d\n", action);
4721 uirow = MSI_CreateRecord( 4 );
4722 MSI_RecordSetStringW( uirow, 1, identifier );
4723 MSI_RecordSetStringW( uirow, 2, deformated_section );
4724 MSI_RecordSetStringW( uirow, 3, deformated_key );
4725 MSI_RecordSetStringW( uirow, 4, deformated_value );
4726 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4727 msiobj_release( &uirow->hdr );
4729 msi_free( deformated_key );
4730 msi_free( deformated_value );
4731 msi_free( deformated_section );
4732 return ERROR_SUCCESS;
4735 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4737 static const WCHAR query[] = {
4738 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4739 '`','I','n','i','F','i','l','e','`',0};
4740 static const WCHAR remove_query[] = {
4741 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4742 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4743 MSIQUERY *view;
4744 UINT rc;
4746 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4747 if (rc == ERROR_SUCCESS)
4749 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4750 msiobj_release( &view->hdr );
4751 if (rc != ERROR_SUCCESS)
4752 return rc;
4754 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4755 if (rc == ERROR_SUCCESS)
4757 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4758 msiobj_release( &view->hdr );
4759 if (rc != ERROR_SUCCESS)
4760 return rc;
4762 return ERROR_SUCCESS;
4765 static void register_dll( const WCHAR *dll, BOOL unregister )
4767 static const WCHAR regW[] =
4768 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4769 static const WCHAR unregW[] =
4770 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4771 PROCESS_INFORMATION pi;
4772 STARTUPINFOW si;
4773 WCHAR *cmd;
4775 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4777 if (unregister) sprintfW( cmd, unregW, dll );
4778 else sprintfW( cmd, regW, dll );
4780 memset( &si, 0, sizeof(STARTUPINFOW) );
4781 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4783 CloseHandle( pi.hThread );
4784 msi_dialog_check_messages( pi.hProcess );
4785 CloseHandle( pi.hProcess );
4787 msi_free( cmd );
4790 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4792 MSIPACKAGE *package = param;
4793 LPCWSTR filename;
4794 MSIFILE *file;
4795 MSIRECORD *uirow;
4797 filename = MSI_RecordGetString( row, 1 );
4798 file = msi_get_loaded_file( package, filename );
4799 if (!file)
4801 WARN("unable to find file %s\n", debugstr_w(filename));
4802 return ERROR_SUCCESS;
4804 file->Component->Action = msi_get_component_action( package, file->Component );
4805 if (file->Component->Action != INSTALLSTATE_LOCAL)
4807 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4808 return ERROR_SUCCESS;
4811 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4812 register_dll( file->TargetPath, FALSE );
4814 uirow = MSI_CreateRecord( 2 );
4815 MSI_RecordSetStringW( uirow, 1, file->File );
4816 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4817 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4818 msiobj_release( &uirow->hdr );
4820 return ERROR_SUCCESS;
4823 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4825 static const WCHAR query[] = {
4826 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4827 '`','S','e','l','f','R','e','g','`',0};
4828 MSIQUERY *view;
4829 UINT rc;
4831 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4832 if (rc != ERROR_SUCCESS)
4833 return ERROR_SUCCESS;
4835 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4836 msiobj_release(&view->hdr);
4837 return rc;
4840 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4842 MSIPACKAGE *package = param;
4843 LPCWSTR filename;
4844 MSIFILE *file;
4845 MSIRECORD *uirow;
4847 filename = MSI_RecordGetString( row, 1 );
4848 file = msi_get_loaded_file( package, filename );
4849 if (!file)
4851 WARN("unable to find file %s\n", debugstr_w(filename));
4852 return ERROR_SUCCESS;
4854 file->Component->Action = msi_get_component_action( package, file->Component );
4855 if (file->Component->Action != INSTALLSTATE_ABSENT)
4857 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4858 return ERROR_SUCCESS;
4861 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4862 register_dll( file->TargetPath, TRUE );
4864 uirow = MSI_CreateRecord( 2 );
4865 MSI_RecordSetStringW( uirow, 1, file->File );
4866 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4867 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4868 msiobj_release( &uirow->hdr );
4870 return ERROR_SUCCESS;
4873 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4875 static const WCHAR query[] = {
4876 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4877 '`','S','e','l','f','R','e','g','`',0};
4878 MSIQUERY *view;
4879 UINT rc;
4881 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4882 if (rc != ERROR_SUCCESS)
4883 return ERROR_SUCCESS;
4885 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4886 msiobj_release( &view->hdr );
4887 return rc;
4890 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4892 MSIFEATURE *feature;
4893 UINT rc;
4894 HKEY hkey = NULL, userdata = NULL;
4896 if (!msi_check_publish(package))
4897 return ERROR_SUCCESS;
4899 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4900 &hkey, TRUE);
4901 if (rc != ERROR_SUCCESS)
4902 goto end;
4904 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4905 &userdata, TRUE);
4906 if (rc != ERROR_SUCCESS)
4907 goto end;
4909 /* here the guids are base 85 encoded */
4910 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4912 ComponentList *cl;
4913 LPWSTR data = NULL;
4914 GUID clsid;
4915 INT size;
4916 BOOL absent = FALSE;
4917 MSIRECORD *uirow;
4919 if (feature->Level <= 0) continue;
4921 if (feature->Action != INSTALLSTATE_LOCAL &&
4922 feature->Action != INSTALLSTATE_SOURCE &&
4923 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4925 size = 1;
4926 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4928 size += 21;
4930 if (feature->Feature_Parent)
4931 size += strlenW( feature->Feature_Parent )+2;
4933 data = msi_alloc(size * sizeof(WCHAR));
4935 data[0] = 0;
4936 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4938 MSICOMPONENT* component = cl->component;
4939 WCHAR buf[21];
4941 buf[0] = 0;
4942 if (component->ComponentId)
4944 TRACE("From %s\n",debugstr_w(component->ComponentId));
4945 CLSIDFromString(component->ComponentId, &clsid);
4946 encode_base85_guid(&clsid,buf);
4947 TRACE("to %s\n",debugstr_w(buf));
4948 strcatW(data,buf);
4952 if (feature->Feature_Parent)
4954 static const WCHAR sep[] = {'\2',0};
4955 strcatW(data,sep);
4956 strcatW(data,feature->Feature_Parent);
4959 msi_reg_set_val_str( userdata, feature->Feature, data );
4960 msi_free(data);
4962 size = 0;
4963 if (feature->Feature_Parent)
4964 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4965 if (!absent)
4967 size += sizeof(WCHAR);
4968 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4969 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4971 else
4973 size += 2*sizeof(WCHAR);
4974 data = msi_alloc(size);
4975 data[0] = 0x6;
4976 data[1] = 0;
4977 if (feature->Feature_Parent)
4978 strcpyW( &data[1], feature->Feature_Parent );
4979 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4980 (LPBYTE)data,size);
4981 msi_free(data);
4984 /* the UI chunk */
4985 uirow = MSI_CreateRecord( 1 );
4986 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4987 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4988 msiobj_release( &uirow->hdr );
4989 /* FIXME: call msi_ui_progress? */
4992 end:
4993 RegCloseKey(hkey);
4994 RegCloseKey(userdata);
4995 return rc;
4998 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
5000 UINT r;
5001 HKEY hkey;
5002 MSIRECORD *uirow;
5004 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
5006 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
5007 &hkey, FALSE);
5008 if (r == ERROR_SUCCESS)
5010 RegDeleteValueW(hkey, feature->Feature);
5011 RegCloseKey(hkey);
5014 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
5015 &hkey, FALSE);
5016 if (r == ERROR_SUCCESS)
5018 RegDeleteValueW(hkey, feature->Feature);
5019 RegCloseKey(hkey);
5022 uirow = MSI_CreateRecord( 1 );
5023 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5024 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5025 msiobj_release( &uirow->hdr );
5027 return ERROR_SUCCESS;
5030 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5032 MSIFEATURE *feature;
5034 if (!msi_check_unpublish(package))
5035 return ERROR_SUCCESS;
5037 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5039 msi_unpublish_feature(package, feature);
5042 return ERROR_SUCCESS;
5045 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5047 SYSTEMTIME systime;
5048 DWORD size, langid;
5049 WCHAR date[9], *val, *buffer;
5050 const WCHAR *prop, *key;
5052 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5053 static const WCHAR modpath_fmt[] =
5054 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5055 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5056 static const WCHAR szModifyPath[] =
5057 {'M','o','d','i','f','y','P','a','t','h',0};
5058 static const WCHAR szUninstallString[] =
5059 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5060 static const WCHAR szEstimatedSize[] =
5061 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5062 static const WCHAR szDisplayVersion[] =
5063 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5064 static const WCHAR szInstallSource[] =
5065 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5066 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5067 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5068 static const WCHAR szAuthorizedCDFPrefix[] =
5069 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5070 static const WCHAR szARPCONTACT[] =
5071 {'A','R','P','C','O','N','T','A','C','T',0};
5072 static const WCHAR szContact[] =
5073 {'C','o','n','t','a','c','t',0};
5074 static const WCHAR szARPCOMMENTS[] =
5075 {'A','R','P','C','O','M','M','E','N','T','S',0};
5076 static const WCHAR szComments[] =
5077 {'C','o','m','m','e','n','t','s',0};
5078 static const WCHAR szProductName[] =
5079 {'P','r','o','d','u','c','t','N','a','m','e',0};
5080 static const WCHAR szDisplayName[] =
5081 {'D','i','s','p','l','a','y','N','a','m','e',0};
5082 static const WCHAR szARPHELPLINK[] =
5083 {'A','R','P','H','E','L','P','L','I','N','K',0};
5084 static const WCHAR szHelpLink[] =
5085 {'H','e','l','p','L','i','n','k',0};
5086 static const WCHAR szARPHELPTELEPHONE[] =
5087 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5088 static const WCHAR szHelpTelephone[] =
5089 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5090 static const WCHAR szARPINSTALLLOCATION[] =
5091 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5092 static const WCHAR szManufacturer[] =
5093 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5094 static const WCHAR szPublisher[] =
5095 {'P','u','b','l','i','s','h','e','r',0};
5096 static const WCHAR szARPREADME[] =
5097 {'A','R','P','R','E','A','D','M','E',0};
5098 static const WCHAR szReadme[] =
5099 {'R','e','a','d','M','e',0};
5100 static const WCHAR szARPSIZE[] =
5101 {'A','R','P','S','I','Z','E',0};
5102 static const WCHAR szSize[] =
5103 {'S','i','z','e',0};
5104 static const WCHAR szARPURLINFOABOUT[] =
5105 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5106 static const WCHAR szURLInfoAbout[] =
5107 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5108 static const WCHAR szARPURLUPDATEINFO[] =
5109 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5110 static const WCHAR szURLUpdateInfo[] =
5111 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5112 static const WCHAR szARPSYSTEMCOMPONENT[] =
5113 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5114 static const WCHAR szSystemComponent[] =
5115 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5117 static const WCHAR *propval[] = {
5118 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5119 szARPCONTACT, szContact,
5120 szARPCOMMENTS, szComments,
5121 szProductName, szDisplayName,
5122 szARPHELPLINK, szHelpLink,
5123 szARPHELPTELEPHONE, szHelpTelephone,
5124 szARPINSTALLLOCATION, szInstallLocation,
5125 szSourceDir, szInstallSource,
5126 szManufacturer, szPublisher,
5127 szARPREADME, szReadme,
5128 szARPSIZE, szSize,
5129 szARPURLINFOABOUT, szURLInfoAbout,
5130 szARPURLUPDATEINFO, szURLUpdateInfo,
5131 NULL
5133 const WCHAR **p = propval;
5135 while (*p)
5137 prop = *p++;
5138 key = *p++;
5139 val = msi_dup_property(package->db, prop);
5140 msi_reg_set_val_str(hkey, key, val);
5141 msi_free(val);
5144 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5145 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5147 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5149 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5150 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5151 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5152 msi_free(buffer);
5154 /* FIXME: Write real Estimated Size when we have it */
5155 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5157 GetLocalTime(&systime);
5158 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5159 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5161 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5162 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5164 buffer = msi_dup_property(package->db, szProductVersion);
5165 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5166 if (buffer)
5168 DWORD verdword = msi_version_str_to_dword(buffer);
5170 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5171 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5172 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5173 msi_free(buffer);
5176 return ERROR_SUCCESS;
5179 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5181 WCHAR *upgrade_code, squashed_pc[SQUASHED_GUID_SIZE];
5182 MSIRECORD *uirow;
5183 HKEY hkey, props, upgrade_key;
5184 UINT rc;
5186 /* FIXME: also need to publish if the product is in advertise mode */
5187 if (!msi_check_publish(package))
5188 return ERROR_SUCCESS;
5190 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5191 if (rc != ERROR_SUCCESS)
5192 return rc;
5194 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5195 if (rc != ERROR_SUCCESS)
5196 goto done;
5198 rc = msi_publish_install_properties(package, hkey);
5199 if (rc != ERROR_SUCCESS)
5200 goto done;
5202 rc = msi_publish_install_properties(package, props);
5203 if (rc != ERROR_SUCCESS)
5204 goto done;
5206 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5207 if (upgrade_code)
5209 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5210 if (rc == ERROR_SUCCESS)
5212 squash_guid( package->ProductCode, squashed_pc );
5213 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5214 RegCloseKey( upgrade_key );
5216 msi_free( upgrade_code );
5218 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5219 package->delete_on_close = FALSE;
5221 done:
5222 uirow = MSI_CreateRecord( 1 );
5223 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5224 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5225 msiobj_release( &uirow->hdr );
5227 RegCloseKey(hkey);
5228 return ERROR_SUCCESS;
5231 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5233 return execute_script(package, SCRIPT_INSTALL);
5236 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5238 MSIPACKAGE *package = param;
5239 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5240 WCHAR *p, *icon_path;
5242 if (!icon) return ERROR_SUCCESS;
5243 if ((icon_path = msi_build_icon_path( package, icon )))
5245 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5246 DeleteFileW( icon_path );
5247 if ((p = strrchrW( icon_path, '\\' )))
5249 *p = 0;
5250 RemoveDirectoryW( icon_path );
5252 msi_free( icon_path );
5254 return ERROR_SUCCESS;
5257 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5259 static const WCHAR query[]= {
5260 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5261 MSIQUERY *view;
5262 UINT r;
5264 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5265 if (r == ERROR_SUCCESS)
5267 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5268 msiobj_release( &view->hdr );
5269 if (r != ERROR_SUCCESS)
5270 return r;
5272 return ERROR_SUCCESS;
5275 static void remove_product_upgrade_code( MSIPACKAGE *package )
5277 WCHAR *code, product[SQUASHED_GUID_SIZE];
5278 HKEY hkey;
5279 LONG res;
5280 DWORD count;
5282 squash_guid( package->ProductCode, product );
5283 if (!(code = msi_dup_property( package->db, szUpgradeCode )))
5285 WARN( "upgrade code not found\n" );
5286 return;
5288 if (!MSIREG_OpenUpgradeCodesKey( code, &hkey, FALSE ))
5290 RegDeleteValueW( hkey, product );
5291 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5292 RegCloseKey( hkey );
5293 if (!res && !count) MSIREG_DeleteUpgradeCodesKey( code );
5295 if (!MSIREG_OpenUserUpgradeCodesKey( code, &hkey, FALSE ))
5297 RegDeleteValueW( hkey, product );
5298 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5299 RegCloseKey( hkey );
5300 if (!res && !count) MSIREG_DeleteUserUpgradeCodesKey( code );
5302 if (!MSIREG_OpenClassesUpgradeCodesKey( 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_DeleteClassesUpgradeCodesKey( code );
5310 msi_free( code );
5313 static UINT ACTION_UnpublishProduct(MSIPACKAGE *package)
5315 MSIPATCHINFO *patch;
5317 MSIREG_DeleteProductKey(package->ProductCode);
5318 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5319 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5321 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5322 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5323 MSIREG_DeleteUserProductKey(package->ProductCode);
5324 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5326 remove_product_upgrade_code( package );
5328 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5330 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5331 if (!strcmpW( package->ProductCode, patch->products ))
5333 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5334 patch->delete_on_close = TRUE;
5336 /* FIXME: remove local patch package if this is the last product */
5338 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5339 package->delete_on_close = TRUE;
5341 msi_unpublish_icons( package );
5342 return ERROR_SUCCESS;
5345 static BOOL is_full_uninstall( MSIPACKAGE *package )
5347 WCHAR **features, *remove = msi_dup_property( package->db, szRemove );
5348 MSIFEATURE *feature;
5349 BOOL ret = TRUE;
5350 UINT i;
5352 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5354 if (feature->Action == INSTALLSTATE_LOCAL) ret = FALSE;
5357 features = msi_split_string( remove, ',' );
5358 for (i = 0; features && features[i]; i++)
5360 if (!strcmpW( features[i], szAll )) ret = TRUE;
5363 msi_free(features);
5364 msi_free(remove);
5365 return ret;
5368 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5370 UINT rc;
5372 /* first do the same as an InstallExecute */
5373 rc = execute_script(package, SCRIPT_INSTALL);
5374 if (rc != ERROR_SUCCESS)
5375 return rc;
5377 /* then handle commit actions */
5378 rc = execute_script(package, SCRIPT_COMMIT);
5379 if (rc != ERROR_SUCCESS)
5380 return rc;
5382 if (is_full_uninstall(package))
5383 rc = ACTION_UnpublishProduct(package);
5385 return rc;
5388 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5390 static const WCHAR RunOnce[] = {
5391 'S','o','f','t','w','a','r','e','\\',
5392 'M','i','c','r','o','s','o','f','t','\\',
5393 'W','i','n','d','o','w','s','\\',
5394 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5395 'R','u','n','O','n','c','e',0};
5396 static const WCHAR InstallRunOnce[] = {
5397 'S','o','f','t','w','a','r','e','\\',
5398 'M','i','c','r','o','s','o','f','t','\\',
5399 'W','i','n','d','o','w','s','\\',
5400 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5401 'I','n','s','t','a','l','l','e','r','\\',
5402 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5404 static const WCHAR msiexec_fmt[] = {
5405 '%','s',
5406 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5407 '\"','%','s','\"',0};
5408 static const WCHAR install_fmt[] = {
5409 '/','I',' ','\"','%','s','\"',' ',
5410 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5411 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5412 WCHAR buffer[256], sysdir[MAX_PATH], squashed_pc[SQUASHED_GUID_SIZE];
5413 HKEY hkey;
5415 squash_guid( package->ProductCode, squashed_pc );
5417 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5418 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5419 snprintfW( buffer, sizeof(buffer)/sizeof(buffer[0]), msiexec_fmt, sysdir, squashed_pc );
5421 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5422 RegCloseKey(hkey);
5424 TRACE("Reboot command %s\n",debugstr_w(buffer));
5426 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5427 sprintfW( buffer, install_fmt, package->ProductCode, squashed_pc );
5429 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5430 RegCloseKey(hkey);
5432 return ERROR_INSTALL_SUSPEND;
5435 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5437 static const WCHAR query[] =
5438 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5439 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5440 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5441 MSIRECORD *rec, *row;
5442 DWORD i, size = 0;
5443 va_list va;
5444 const WCHAR *str;
5445 WCHAR *data;
5447 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5449 rec = MSI_CreateRecord( count + 2 );
5450 str = MSI_RecordGetString( row, 1 );
5451 MSI_RecordSetStringW( rec, 0, str );
5452 msiobj_release( &row->hdr );
5453 MSI_RecordSetInteger( rec, 1, error );
5455 va_start( va, count );
5456 for (i = 0; i < count; i++)
5458 str = va_arg( va, const WCHAR *);
5459 MSI_RecordSetStringW( rec, i + 2, str );
5461 va_end( va );
5463 MSI_FormatRecordW( package, rec, NULL, &size );
5464 size++;
5465 data = msi_alloc( size * sizeof(WCHAR) );
5466 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5467 else data[0] = 0;
5468 msiobj_release( &rec->hdr );
5469 return data;
5472 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5474 DWORD attrib;
5475 UINT rc;
5478 * We are currently doing what should be done here in the top level Install
5479 * however for Administrative and uninstalls this step will be needed
5481 if (!package->PackagePath)
5482 return ERROR_SUCCESS;
5484 msi_set_sourcedir_props(package, TRUE);
5486 attrib = GetFileAttributesW(package->db->path);
5487 if (attrib == INVALID_FILE_ATTRIBUTES)
5489 LPWSTR prompt, msg;
5490 DWORD size = 0;
5492 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5493 package->Context, MSICODE_PRODUCT,
5494 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5495 if (rc == ERROR_MORE_DATA)
5497 prompt = msi_alloc(size * sizeof(WCHAR));
5498 MsiSourceListGetInfoW(package->ProductCode, NULL,
5499 package->Context, MSICODE_PRODUCT,
5500 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5502 else
5503 prompt = strdupW(package->db->path);
5505 msg = msi_build_error_string(package, 1302, 1, prompt);
5506 msi_free(prompt);
5507 while(attrib == INVALID_FILE_ATTRIBUTES)
5509 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5510 if (rc == IDCANCEL)
5512 msi_free(msg);
5513 return ERROR_INSTALL_USEREXIT;
5515 attrib = GetFileAttributesW(package->db->path);
5517 msi_free(msg);
5518 rc = ERROR_SUCCESS;
5520 else
5521 return ERROR_SUCCESS;
5523 return rc;
5526 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5528 HKEY hkey = 0;
5529 LPWSTR buffer, productid = NULL;
5530 UINT i, rc = ERROR_SUCCESS;
5531 MSIRECORD *uirow;
5533 static const WCHAR szPropKeys[][80] =
5535 {'P','r','o','d','u','c','t','I','D',0},
5536 {'U','S','E','R','N','A','M','E',0},
5537 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5538 {0},
5541 static const WCHAR szRegKeys[][80] =
5543 {'P','r','o','d','u','c','t','I','D',0},
5544 {'R','e','g','O','w','n','e','r',0},
5545 {'R','e','g','C','o','m','p','a','n','y',0},
5546 {0},
5549 if (msi_check_unpublish(package))
5551 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5552 goto end;
5555 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5556 if (!productid)
5557 goto end;
5559 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5560 NULL, &hkey, TRUE);
5561 if (rc != ERROR_SUCCESS)
5562 goto end;
5564 for( i = 0; szPropKeys[i][0]; i++ )
5566 buffer = msi_dup_property( package->db, szPropKeys[i] );
5567 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5568 msi_free( buffer );
5571 end:
5572 uirow = MSI_CreateRecord( 1 );
5573 MSI_RecordSetStringW( uirow, 1, productid );
5574 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5575 msiobj_release( &uirow->hdr );
5577 msi_free(productid);
5578 RegCloseKey(hkey);
5579 return rc;
5583 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5585 UINT rc;
5587 package->script->InWhatSequence |= SEQUENCE_EXEC;
5588 rc = ACTION_ProcessExecSequence(package,FALSE);
5589 return rc;
5592 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5594 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5595 WCHAR productid_85[21], component_85[21], *ret;
5596 GUID clsid;
5597 DWORD sz;
5599 /* > is used if there is a component GUID and < if not. */
5601 productid_85[0] = 0;
5602 component_85[0] = 0;
5603 CLSIDFromString( package->ProductCode, &clsid );
5605 encode_base85_guid( &clsid, productid_85 );
5606 if (component)
5608 CLSIDFromString( component->ComponentId, &clsid );
5609 encode_base85_guid( &clsid, component_85 );
5612 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5613 debugstr_w(component_85));
5615 sz = 20 + strlenW( feature ) + 20 + 3;
5616 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5617 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5618 return ret;
5621 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5623 MSIPACKAGE *package = param;
5624 LPCWSTR compgroupid, component, feature, qualifier, text;
5625 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5626 HKEY hkey = NULL;
5627 UINT rc;
5628 MSICOMPONENT *comp;
5629 MSIFEATURE *feat;
5630 DWORD sz;
5631 MSIRECORD *uirow;
5632 int len;
5634 feature = MSI_RecordGetString(rec, 5);
5635 feat = msi_get_loaded_feature(package, feature);
5636 if (!feat)
5637 return ERROR_SUCCESS;
5639 feat->Action = msi_get_feature_action( package, feat );
5640 if (feat->Action != INSTALLSTATE_LOCAL &&
5641 feat->Action != INSTALLSTATE_SOURCE &&
5642 feat->Action != INSTALLSTATE_ADVERTISED)
5644 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5645 return ERROR_SUCCESS;
5648 component = MSI_RecordGetString(rec, 3);
5649 comp = msi_get_loaded_component(package, component);
5650 if (!comp)
5651 return ERROR_SUCCESS;
5653 compgroupid = MSI_RecordGetString(rec,1);
5654 qualifier = MSI_RecordGetString(rec,2);
5656 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5657 if (rc != ERROR_SUCCESS)
5658 goto end;
5660 advertise = msi_create_component_advertise_string( package, comp, feature );
5661 text = MSI_RecordGetString( rec, 4 );
5662 if (text)
5664 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5665 strcpyW( p, advertise );
5666 strcatW( p, text );
5667 msi_free( advertise );
5668 advertise = p;
5670 existing = msi_reg_get_val_str( hkey, qualifier );
5672 sz = strlenW( advertise ) + 1;
5673 if (existing)
5675 for (p = existing; *p; p += len)
5677 len = strlenW( p ) + 1;
5678 if (strcmpW( advertise, p )) sz += len;
5681 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5683 rc = ERROR_OUTOFMEMORY;
5684 goto end;
5686 q = output;
5687 if (existing)
5689 for (p = existing; *p; p += len)
5691 len = strlenW( p ) + 1;
5692 if (strcmpW( advertise, p ))
5694 memcpy( q, p, len * sizeof(WCHAR) );
5695 q += len;
5699 strcpyW( q, advertise );
5700 q[strlenW( q ) + 1] = 0;
5702 msi_reg_set_val_multi_str( hkey, qualifier, output );
5704 end:
5705 RegCloseKey(hkey);
5706 msi_free( output );
5707 msi_free( advertise );
5708 msi_free( existing );
5710 /* the UI chunk */
5711 uirow = MSI_CreateRecord( 2 );
5712 MSI_RecordSetStringW( uirow, 1, compgroupid );
5713 MSI_RecordSetStringW( uirow, 2, qualifier);
5714 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5715 msiobj_release( &uirow->hdr );
5716 /* FIXME: call ui_progress? */
5718 return rc;
5722 * At present I am ignorning the advertised components part of this and only
5723 * focusing on the qualified component sets
5725 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5727 static const WCHAR query[] = {
5728 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5729 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5730 MSIQUERY *view;
5731 UINT rc;
5733 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5734 if (rc != ERROR_SUCCESS)
5735 return ERROR_SUCCESS;
5737 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5738 msiobj_release(&view->hdr);
5739 return rc;
5742 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5744 static const WCHAR szInstallerComponents[] = {
5745 'S','o','f','t','w','a','r','e','\\',
5746 'M','i','c','r','o','s','o','f','t','\\',
5747 'I','n','s','t','a','l','l','e','r','\\',
5748 'C','o','m','p','o','n','e','n','t','s','\\',0};
5750 MSIPACKAGE *package = param;
5751 LPCWSTR compgroupid, component, feature, qualifier;
5752 MSICOMPONENT *comp;
5753 MSIFEATURE *feat;
5754 MSIRECORD *uirow;
5755 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5756 LONG res;
5758 feature = MSI_RecordGetString( rec, 5 );
5759 feat = msi_get_loaded_feature( package, feature );
5760 if (!feat)
5761 return ERROR_SUCCESS;
5763 feat->Action = msi_get_feature_action( package, feat );
5764 if (feat->Action != INSTALLSTATE_ABSENT)
5766 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5767 return ERROR_SUCCESS;
5770 component = MSI_RecordGetString( rec, 3 );
5771 comp = msi_get_loaded_component( package, component );
5772 if (!comp)
5773 return ERROR_SUCCESS;
5775 compgroupid = MSI_RecordGetString( rec, 1 );
5776 qualifier = MSI_RecordGetString( rec, 2 );
5778 squash_guid( compgroupid, squashed );
5779 strcpyW( keypath, szInstallerComponents );
5780 strcatW( keypath, squashed );
5782 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5783 if (res != ERROR_SUCCESS)
5785 WARN("Unable to delete component key %d\n", res);
5788 uirow = MSI_CreateRecord( 2 );
5789 MSI_RecordSetStringW( uirow, 1, compgroupid );
5790 MSI_RecordSetStringW( uirow, 2, qualifier );
5791 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5792 msiobj_release( &uirow->hdr );
5794 return ERROR_SUCCESS;
5797 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5799 static const WCHAR query[] = {
5800 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5801 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5802 MSIQUERY *view;
5803 UINT rc;
5805 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5806 if (rc != ERROR_SUCCESS)
5807 return ERROR_SUCCESS;
5809 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5810 msiobj_release( &view->hdr );
5811 return rc;
5814 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5816 static const WCHAR query[] =
5817 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5818 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5819 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5820 MSIPACKAGE *package = param;
5821 MSICOMPONENT *component;
5822 MSIRECORD *row;
5823 MSIFILE *file;
5824 SC_HANDLE hscm = NULL, service = NULL;
5825 LPCWSTR comp, key;
5826 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5827 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5828 DWORD serv_type, start_type, err_control;
5829 BOOL is_vital;
5830 SERVICE_DESCRIPTIONW sd = {NULL};
5831 UINT ret = ERROR_SUCCESS;
5833 comp = MSI_RecordGetString( rec, 12 );
5834 component = msi_get_loaded_component( package, comp );
5835 if (!component)
5837 WARN("service component not found\n");
5838 goto done;
5840 component->Action = msi_get_component_action( package, component );
5841 if (component->Action != INSTALLSTATE_LOCAL)
5843 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5844 goto done;
5846 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5847 if (!hscm)
5849 ERR("Failed to open the SC Manager!\n");
5850 goto done;
5853 start_type = MSI_RecordGetInteger(rec, 5);
5854 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5855 goto done;
5857 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5858 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5859 serv_type = MSI_RecordGetInteger(rec, 4);
5860 err_control = MSI_RecordGetInteger(rec, 6);
5861 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5862 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5863 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5864 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5865 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5866 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5868 /* Should the complete install fail if CreateService fails? */
5869 is_vital = (err_control & msidbServiceInstallErrorControlVital);
5871 /* Remove the msidbServiceInstallErrorControlVital-flag from err_control.
5872 CreateService (under Windows) would fail if not. */
5873 err_control &= ~msidbServiceInstallErrorControlVital;
5875 /* fetch the service path */
5876 row = MSI_QueryGetRecord(package->db, query, comp);
5877 if (!row)
5879 ERR("Query failed\n");
5880 goto done;
5882 if (!(key = MSI_RecordGetString(row, 6)))
5884 msiobj_release(&row->hdr);
5885 goto done;
5887 file = msi_get_loaded_file(package, key);
5888 msiobj_release(&row->hdr);
5889 if (!file)
5891 ERR("Failed to load the service file\n");
5892 goto done;
5895 if (!args || !args[0]) image_path = file->TargetPath;
5896 else
5898 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5899 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5901 ret = ERROR_OUTOFMEMORY;
5902 goto done;
5905 strcpyW(image_path, file->TargetPath);
5906 strcatW(image_path, szSpace);
5907 strcatW(image_path, args);
5909 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5910 start_type, err_control, image_path, load_order,
5911 NULL, depends, serv_name, pass);
5913 if (!service)
5915 if (GetLastError() != ERROR_SERVICE_EXISTS)
5917 WARN("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5918 if (is_vital)
5919 ret = ERROR_INSTALL_FAILURE;
5923 else if (sd.lpDescription)
5925 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5926 WARN("failed to set service description %u\n", GetLastError());
5929 if (image_path != file->TargetPath) msi_free(image_path);
5930 done:
5931 if (service) CloseServiceHandle(service);
5932 if (hscm) CloseServiceHandle(hscm);
5933 msi_free(name);
5934 msi_free(disp);
5935 msi_free(sd.lpDescription);
5936 msi_free(load_order);
5937 msi_free(serv_name);
5938 msi_free(pass);
5939 msi_free(depends);
5940 msi_free(args);
5942 return ret;
5945 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5947 static const WCHAR query[] = {
5948 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5949 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5950 MSIQUERY *view;
5951 UINT rc;
5953 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5954 if (rc != ERROR_SUCCESS)
5955 return ERROR_SUCCESS;
5957 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5958 msiobj_release(&view->hdr);
5959 return rc;
5962 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5963 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5965 LPCWSTR *vector, *temp_vector;
5966 LPWSTR p, q;
5967 DWORD sep_len;
5969 static const WCHAR separator[] = {'[','~',']',0};
5971 *numargs = 0;
5972 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5974 if (!args)
5975 return NULL;
5977 vector = msi_alloc(sizeof(LPWSTR));
5978 if (!vector)
5979 return NULL;
5981 p = args;
5984 (*numargs)++;
5985 vector[*numargs - 1] = p;
5987 if ((q = strstrW(p, separator)))
5989 *q = '\0';
5991 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5992 if (!temp_vector)
5994 msi_free(vector);
5995 return NULL;
5997 vector = temp_vector;
5999 p = q + sep_len;
6001 } while (q);
6003 return vector;
6006 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
6008 MSIPACKAGE *package = param;
6009 MSICOMPONENT *comp;
6010 MSIRECORD *uirow;
6011 SC_HANDLE scm = NULL, service = NULL;
6012 LPCWSTR component, *vector = NULL;
6013 LPWSTR name, args, display_name = NULL;
6014 DWORD event, numargs, len, wait, dummy;
6015 UINT r = ERROR_FUNCTION_FAILED;
6016 SERVICE_STATUS_PROCESS status;
6017 ULONGLONG start_time;
6019 component = MSI_RecordGetString(rec, 6);
6020 comp = msi_get_loaded_component(package, component);
6021 if (!comp)
6022 return ERROR_SUCCESS;
6024 event = MSI_RecordGetInteger( rec, 3 );
6025 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6027 comp->Action = msi_get_component_action( package, comp );
6028 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
6029 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
6031 TRACE("not starting %s\n", debugstr_w(name));
6032 msi_free( name );
6033 return ERROR_SUCCESS;
6036 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
6037 wait = MSI_RecordGetInteger(rec, 5);
6039 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
6040 if (!scm)
6042 ERR("Failed to open the service control manager\n");
6043 goto done;
6046 len = 0;
6047 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6048 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6050 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6051 GetServiceDisplayNameW( scm, name, display_name, &len );
6054 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
6055 if (!service)
6057 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6058 goto done;
6061 vector = msi_service_args_to_vector(args, &numargs);
6063 if (!StartServiceW(service, numargs, vector) &&
6064 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6066 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6067 goto done;
6070 r = ERROR_SUCCESS;
6071 if (wait)
6073 /* wait for at most 30 seconds for the service to be up and running */
6074 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6075 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6077 TRACE("failed to query service status (%u)\n", GetLastError());
6078 goto done;
6080 start_time = GetTickCount64();
6081 while (status.dwCurrentState == SERVICE_START_PENDING)
6083 if (GetTickCount64() - start_time > 30000) break;
6084 Sleep(1000);
6085 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6086 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6088 TRACE("failed to query service status (%u)\n", GetLastError());
6089 goto done;
6092 if (status.dwCurrentState != SERVICE_RUNNING)
6094 WARN("service failed to start %u\n", status.dwCurrentState);
6095 r = ERROR_FUNCTION_FAILED;
6099 done:
6100 uirow = MSI_CreateRecord( 2 );
6101 MSI_RecordSetStringW( uirow, 1, display_name );
6102 MSI_RecordSetStringW( uirow, 2, name );
6103 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6104 msiobj_release( &uirow->hdr );
6106 if (service) CloseServiceHandle(service);
6107 if (scm) CloseServiceHandle(scm);
6109 msi_free(name);
6110 msi_free(args);
6111 msi_free(vector);
6112 msi_free(display_name);
6113 return r;
6116 static UINT ACTION_StartServices( MSIPACKAGE *package )
6118 static const WCHAR query[] = {
6119 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6120 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6121 MSIQUERY *view;
6122 UINT rc;
6124 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6125 if (rc != ERROR_SUCCESS)
6126 return ERROR_SUCCESS;
6128 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6129 msiobj_release(&view->hdr);
6130 return rc;
6133 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6135 DWORD i, needed, count;
6136 ENUM_SERVICE_STATUSW *dependencies;
6137 SERVICE_STATUS ss;
6138 SC_HANDLE depserv;
6139 BOOL stopped, ret = FALSE;
6141 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6142 0, &needed, &count))
6143 return TRUE;
6145 if (GetLastError() != ERROR_MORE_DATA)
6146 return FALSE;
6148 dependencies = msi_alloc(needed);
6149 if (!dependencies)
6150 return FALSE;
6152 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6153 needed, &needed, &count))
6154 goto done;
6156 for (i = 0; i < count; i++)
6158 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6159 SERVICE_STOP | SERVICE_QUERY_STATUS);
6160 if (!depserv)
6161 goto done;
6163 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6164 CloseServiceHandle(depserv);
6165 if (!stopped)
6166 goto done;
6169 ret = TRUE;
6171 done:
6172 msi_free(dependencies);
6173 return ret;
6176 static UINT stop_service( LPCWSTR name )
6178 SC_HANDLE scm = NULL, service = NULL;
6179 SERVICE_STATUS status;
6180 SERVICE_STATUS_PROCESS ssp;
6181 DWORD needed;
6183 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6184 if (!scm)
6186 WARN("Failed to open the SCM: %d\n", GetLastError());
6187 goto done;
6190 service = OpenServiceW(scm, name,
6191 SERVICE_STOP |
6192 SERVICE_QUERY_STATUS |
6193 SERVICE_ENUMERATE_DEPENDENTS);
6194 if (!service)
6196 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6197 goto done;
6200 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6201 sizeof(SERVICE_STATUS_PROCESS), &needed))
6203 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6204 goto done;
6207 if (ssp.dwCurrentState == SERVICE_STOPPED)
6208 goto done;
6210 stop_service_dependents(scm, service);
6212 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6213 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6215 done:
6216 if (service) CloseServiceHandle(service);
6217 if (scm) CloseServiceHandle(scm);
6219 return ERROR_SUCCESS;
6222 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6224 MSIPACKAGE *package = param;
6225 MSICOMPONENT *comp;
6226 MSIRECORD *uirow;
6227 LPCWSTR component;
6228 WCHAR *name, *display_name = NULL;
6229 DWORD event, len;
6230 SC_HANDLE scm;
6232 component = MSI_RecordGetString( rec, 6 );
6233 comp = msi_get_loaded_component( package, component );
6234 if (!comp)
6235 return ERROR_SUCCESS;
6237 event = MSI_RecordGetInteger( rec, 3 );
6238 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6240 comp->Action = msi_get_component_action( package, comp );
6241 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6242 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6244 TRACE("not stopping %s\n", debugstr_w(name));
6245 msi_free( name );
6246 return ERROR_SUCCESS;
6249 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6250 if (!scm)
6252 ERR("Failed to open the service control manager\n");
6253 goto done;
6256 len = 0;
6257 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6258 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6260 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6261 GetServiceDisplayNameW( scm, name, display_name, &len );
6263 CloseServiceHandle( scm );
6265 stop_service( name );
6267 done:
6268 uirow = MSI_CreateRecord( 2 );
6269 MSI_RecordSetStringW( uirow, 1, display_name );
6270 MSI_RecordSetStringW( uirow, 2, name );
6271 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6272 msiobj_release( &uirow->hdr );
6274 msi_free( name );
6275 msi_free( display_name );
6276 return ERROR_SUCCESS;
6279 static UINT ACTION_StopServices( MSIPACKAGE *package )
6281 static const WCHAR query[] = {
6282 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6283 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6284 MSIQUERY *view;
6285 UINT rc;
6287 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6288 if (rc != ERROR_SUCCESS)
6289 return ERROR_SUCCESS;
6291 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6292 msiobj_release(&view->hdr);
6293 return rc;
6296 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6298 MSIPACKAGE *package = param;
6299 MSICOMPONENT *comp;
6300 MSIRECORD *uirow;
6301 LPWSTR name = NULL, display_name = NULL;
6302 DWORD event, len;
6303 SC_HANDLE scm = NULL, service = NULL;
6305 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6306 if (!comp)
6307 return ERROR_SUCCESS;
6309 event = MSI_RecordGetInteger( rec, 3 );
6310 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6312 comp->Action = msi_get_component_action( package, comp );
6313 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6314 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6316 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6317 msi_free( name );
6318 return ERROR_SUCCESS;
6320 stop_service( name );
6322 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6323 if (!scm)
6325 WARN("Failed to open the SCM: %d\n", GetLastError());
6326 goto done;
6329 len = 0;
6330 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6331 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6333 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6334 GetServiceDisplayNameW( scm, name, display_name, &len );
6337 service = OpenServiceW( scm, name, DELETE );
6338 if (!service)
6340 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6341 goto done;
6344 if (!DeleteService( service ))
6345 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6347 done:
6348 uirow = MSI_CreateRecord( 2 );
6349 MSI_RecordSetStringW( uirow, 1, display_name );
6350 MSI_RecordSetStringW( uirow, 2, name );
6351 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6352 msiobj_release( &uirow->hdr );
6354 if (service) CloseServiceHandle( service );
6355 if (scm) CloseServiceHandle( scm );
6356 msi_free( name );
6357 msi_free( display_name );
6359 return ERROR_SUCCESS;
6362 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6364 static const WCHAR query[] = {
6365 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6366 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6367 MSIQUERY *view;
6368 UINT rc;
6370 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6371 if (rc != ERROR_SUCCESS)
6372 return ERROR_SUCCESS;
6374 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6375 msiobj_release( &view->hdr );
6376 return rc;
6379 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6381 MSIPACKAGE *package = param;
6382 LPWSTR driver, driver_path, ptr;
6383 WCHAR outpath[MAX_PATH];
6384 MSIFILE *driver_file = NULL, *setup_file = NULL;
6385 MSICOMPONENT *comp;
6386 MSIRECORD *uirow;
6387 LPCWSTR desc, file_key, component;
6388 DWORD len, usage;
6389 UINT r = ERROR_SUCCESS;
6391 static const WCHAR driver_fmt[] = {
6392 'D','r','i','v','e','r','=','%','s',0};
6393 static const WCHAR setup_fmt[] = {
6394 'S','e','t','u','p','=','%','s',0};
6395 static const WCHAR usage_fmt[] = {
6396 'F','i','l','e','U','s','a','g','e','=','1',0};
6398 component = MSI_RecordGetString( rec, 2 );
6399 comp = msi_get_loaded_component( package, component );
6400 if (!comp)
6401 return ERROR_SUCCESS;
6403 comp->Action = msi_get_component_action( package, comp );
6404 if (comp->Action != INSTALLSTATE_LOCAL)
6406 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6407 return ERROR_SUCCESS;
6409 desc = MSI_RecordGetString(rec, 3);
6411 file_key = MSI_RecordGetString( rec, 4 );
6412 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6414 file_key = MSI_RecordGetString( rec, 5 );
6415 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6417 if (!driver_file)
6419 ERR("ODBC Driver entry not found!\n");
6420 return ERROR_FUNCTION_FAILED;
6423 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6424 if (setup_file)
6425 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6426 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6428 driver = msi_alloc(len * sizeof(WCHAR));
6429 if (!driver)
6430 return ERROR_OUTOFMEMORY;
6432 ptr = driver;
6433 lstrcpyW(ptr, desc);
6434 ptr += lstrlenW(ptr) + 1;
6436 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6437 ptr += len + 1;
6439 if (setup_file)
6441 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6442 ptr += len + 1;
6445 lstrcpyW(ptr, usage_fmt);
6446 ptr += lstrlenW(ptr) + 1;
6447 *ptr = '\0';
6449 if (!driver_file->TargetPath)
6451 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6452 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6454 driver_path = strdupW(driver_file->TargetPath);
6455 ptr = strrchrW(driver_path, '\\');
6456 if (ptr) *ptr = '\0';
6458 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6459 NULL, ODBC_INSTALL_COMPLETE, &usage))
6461 ERR("Failed to install SQL driver!\n");
6462 r = ERROR_FUNCTION_FAILED;
6465 uirow = MSI_CreateRecord( 5 );
6466 MSI_RecordSetStringW( uirow, 1, desc );
6467 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6468 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6469 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6470 msiobj_release( &uirow->hdr );
6472 msi_free(driver);
6473 msi_free(driver_path);
6475 return r;
6478 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6480 MSIPACKAGE *package = param;
6481 LPWSTR translator, translator_path, ptr;
6482 WCHAR outpath[MAX_PATH];
6483 MSIFILE *translator_file = NULL, *setup_file = NULL;
6484 MSICOMPONENT *comp;
6485 MSIRECORD *uirow;
6486 LPCWSTR desc, file_key, component;
6487 DWORD len, usage;
6488 UINT r = ERROR_SUCCESS;
6490 static const WCHAR translator_fmt[] = {
6491 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6492 static const WCHAR setup_fmt[] = {
6493 'S','e','t','u','p','=','%','s',0};
6495 component = MSI_RecordGetString( rec, 2 );
6496 comp = msi_get_loaded_component( package, component );
6497 if (!comp)
6498 return ERROR_SUCCESS;
6500 comp->Action = msi_get_component_action( package, comp );
6501 if (comp->Action != INSTALLSTATE_LOCAL)
6503 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6504 return ERROR_SUCCESS;
6506 desc = MSI_RecordGetString(rec, 3);
6508 file_key = MSI_RecordGetString( rec, 4 );
6509 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6511 file_key = MSI_RecordGetString( rec, 5 );
6512 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6514 if (!translator_file)
6516 ERR("ODBC Translator entry not found!\n");
6517 return ERROR_FUNCTION_FAILED;
6520 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6521 if (setup_file)
6522 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6524 translator = msi_alloc(len * sizeof(WCHAR));
6525 if (!translator)
6526 return ERROR_OUTOFMEMORY;
6528 ptr = translator;
6529 lstrcpyW(ptr, desc);
6530 ptr += lstrlenW(ptr) + 1;
6532 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6533 ptr += len + 1;
6535 if (setup_file)
6537 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6538 ptr += len + 1;
6540 *ptr = '\0';
6542 translator_path = strdupW(translator_file->TargetPath);
6543 ptr = strrchrW(translator_path, '\\');
6544 if (ptr) *ptr = '\0';
6546 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6547 NULL, ODBC_INSTALL_COMPLETE, &usage))
6549 ERR("Failed to install SQL translator!\n");
6550 r = ERROR_FUNCTION_FAILED;
6553 uirow = MSI_CreateRecord( 5 );
6554 MSI_RecordSetStringW( uirow, 1, desc );
6555 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6556 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6557 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6558 msiobj_release( &uirow->hdr );
6560 msi_free(translator);
6561 msi_free(translator_path);
6563 return r;
6566 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6568 MSIPACKAGE *package = param;
6569 MSICOMPONENT *comp;
6570 LPWSTR attrs;
6571 LPCWSTR desc, driver, component;
6572 WORD request = ODBC_ADD_SYS_DSN;
6573 INT registration;
6574 DWORD len;
6575 UINT r = ERROR_SUCCESS;
6576 MSIRECORD *uirow;
6578 static const WCHAR attrs_fmt[] = {
6579 'D','S','N','=','%','s',0 };
6581 component = MSI_RecordGetString( rec, 2 );
6582 comp = msi_get_loaded_component( package, component );
6583 if (!comp)
6584 return ERROR_SUCCESS;
6586 comp->Action = msi_get_component_action( package, comp );
6587 if (comp->Action != INSTALLSTATE_LOCAL)
6589 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6590 return ERROR_SUCCESS;
6593 desc = MSI_RecordGetString(rec, 3);
6594 driver = MSI_RecordGetString(rec, 4);
6595 registration = MSI_RecordGetInteger(rec, 5);
6597 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6598 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6600 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6601 attrs = msi_alloc(len * sizeof(WCHAR));
6602 if (!attrs)
6603 return ERROR_OUTOFMEMORY;
6605 len = sprintfW(attrs, attrs_fmt, desc);
6606 attrs[len + 1] = 0;
6608 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6610 ERR("Failed to install SQL data source!\n");
6611 r = ERROR_FUNCTION_FAILED;
6614 uirow = MSI_CreateRecord( 5 );
6615 MSI_RecordSetStringW( uirow, 1, desc );
6616 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6617 MSI_RecordSetInteger( uirow, 3, request );
6618 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6619 msiobj_release( &uirow->hdr );
6621 msi_free(attrs);
6623 return r;
6626 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6628 static const WCHAR driver_query[] = {
6629 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6630 'O','D','B','C','D','r','i','v','e','r',0};
6631 static const WCHAR translator_query[] = {
6632 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6633 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6634 static const WCHAR source_query[] = {
6635 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6636 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6637 MSIQUERY *view;
6638 UINT rc;
6640 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6641 if (rc == ERROR_SUCCESS)
6643 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6644 msiobj_release(&view->hdr);
6645 if (rc != ERROR_SUCCESS)
6646 return rc;
6648 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6649 if (rc == ERROR_SUCCESS)
6651 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6652 msiobj_release(&view->hdr);
6653 if (rc != ERROR_SUCCESS)
6654 return rc;
6656 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6657 if (rc == ERROR_SUCCESS)
6659 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6660 msiobj_release(&view->hdr);
6661 if (rc != ERROR_SUCCESS)
6662 return rc;
6664 return ERROR_SUCCESS;
6667 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6669 MSIPACKAGE *package = param;
6670 MSICOMPONENT *comp;
6671 MSIRECORD *uirow;
6672 DWORD usage;
6673 LPCWSTR desc, component;
6675 component = MSI_RecordGetString( rec, 2 );
6676 comp = msi_get_loaded_component( package, component );
6677 if (!comp)
6678 return ERROR_SUCCESS;
6680 comp->Action = msi_get_component_action( package, comp );
6681 if (comp->Action != INSTALLSTATE_ABSENT)
6683 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6684 return ERROR_SUCCESS;
6687 desc = MSI_RecordGetString( rec, 3 );
6688 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6690 WARN("Failed to remove ODBC driver\n");
6692 else if (!usage)
6694 FIXME("Usage count reached 0\n");
6697 uirow = MSI_CreateRecord( 2 );
6698 MSI_RecordSetStringW( uirow, 1, desc );
6699 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6700 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6701 msiobj_release( &uirow->hdr );
6703 return ERROR_SUCCESS;
6706 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6708 MSIPACKAGE *package = param;
6709 MSICOMPONENT *comp;
6710 MSIRECORD *uirow;
6711 DWORD usage;
6712 LPCWSTR desc, component;
6714 component = MSI_RecordGetString( rec, 2 );
6715 comp = msi_get_loaded_component( package, component );
6716 if (!comp)
6717 return ERROR_SUCCESS;
6719 comp->Action = msi_get_component_action( package, comp );
6720 if (comp->Action != INSTALLSTATE_ABSENT)
6722 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6723 return ERROR_SUCCESS;
6726 desc = MSI_RecordGetString( rec, 3 );
6727 if (!SQLRemoveTranslatorW( desc, &usage ))
6729 WARN("Failed to remove ODBC translator\n");
6731 else if (!usage)
6733 FIXME("Usage count reached 0\n");
6736 uirow = MSI_CreateRecord( 2 );
6737 MSI_RecordSetStringW( uirow, 1, desc );
6738 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6739 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6740 msiobj_release( &uirow->hdr );
6742 return ERROR_SUCCESS;
6745 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6747 MSIPACKAGE *package = param;
6748 MSICOMPONENT *comp;
6749 MSIRECORD *uirow;
6750 LPWSTR attrs;
6751 LPCWSTR desc, driver, component;
6752 WORD request = ODBC_REMOVE_SYS_DSN;
6753 INT registration;
6754 DWORD len;
6756 static const WCHAR attrs_fmt[] = {
6757 'D','S','N','=','%','s',0 };
6759 component = MSI_RecordGetString( rec, 2 );
6760 comp = msi_get_loaded_component( package, component );
6761 if (!comp)
6762 return ERROR_SUCCESS;
6764 comp->Action = msi_get_component_action( package, comp );
6765 if (comp->Action != INSTALLSTATE_ABSENT)
6767 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6768 return ERROR_SUCCESS;
6771 desc = MSI_RecordGetString( rec, 3 );
6772 driver = MSI_RecordGetString( rec, 4 );
6773 registration = MSI_RecordGetInteger( rec, 5 );
6775 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6776 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6778 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6779 attrs = msi_alloc( len * sizeof(WCHAR) );
6780 if (!attrs)
6781 return ERROR_OUTOFMEMORY;
6783 FIXME("Use ODBCSourceAttribute table\n");
6785 len = sprintfW( attrs, attrs_fmt, desc );
6786 attrs[len + 1] = 0;
6788 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6790 WARN("Failed to remove ODBC data source\n");
6792 msi_free( attrs );
6794 uirow = MSI_CreateRecord( 3 );
6795 MSI_RecordSetStringW( uirow, 1, desc );
6796 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6797 MSI_RecordSetInteger( uirow, 3, request );
6798 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6799 msiobj_release( &uirow->hdr );
6801 return ERROR_SUCCESS;
6804 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6806 static const WCHAR driver_query[] = {
6807 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6808 'O','D','B','C','D','r','i','v','e','r',0};
6809 static const WCHAR translator_query[] = {
6810 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6811 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6812 static const WCHAR source_query[] = {
6813 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6814 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6815 MSIQUERY *view;
6816 UINT rc;
6818 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6819 if (rc == ERROR_SUCCESS)
6821 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6822 msiobj_release( &view->hdr );
6823 if (rc != ERROR_SUCCESS)
6824 return rc;
6826 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6827 if (rc == ERROR_SUCCESS)
6829 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6830 msiobj_release( &view->hdr );
6831 if (rc != ERROR_SUCCESS)
6832 return rc;
6834 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6835 if (rc == ERROR_SUCCESS)
6837 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6838 msiobj_release( &view->hdr );
6839 if (rc != ERROR_SUCCESS)
6840 return rc;
6842 return ERROR_SUCCESS;
6845 #define ENV_ACT_SETALWAYS 0x1
6846 #define ENV_ACT_SETABSENT 0x2
6847 #define ENV_ACT_REMOVE 0x4
6848 #define ENV_ACT_REMOVEMATCH 0x8
6850 #define ENV_MOD_MACHINE 0x20000000
6851 #define ENV_MOD_APPEND 0x40000000
6852 #define ENV_MOD_PREFIX 0x80000000
6853 #define ENV_MOD_MASK 0xC0000000
6855 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6857 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6859 LPCWSTR cptr = *name;
6861 static const WCHAR prefix[] = {'[','~',']',0};
6862 static const int prefix_len = 3;
6864 *flags = 0;
6865 while (*cptr)
6867 if (*cptr == '=')
6868 *flags |= ENV_ACT_SETALWAYS;
6869 else if (*cptr == '+')
6870 *flags |= ENV_ACT_SETABSENT;
6871 else if (*cptr == '-')
6872 *flags |= ENV_ACT_REMOVE;
6873 else if (*cptr == '!')
6874 *flags |= ENV_ACT_REMOVEMATCH;
6875 else if (*cptr == '*')
6876 *flags |= ENV_MOD_MACHINE;
6877 else
6878 break;
6880 cptr++;
6881 (*name)++;
6884 if (!*cptr)
6886 ERR("Missing environment variable\n");
6887 return ERROR_FUNCTION_FAILED;
6890 if (*value)
6892 LPCWSTR ptr = *value;
6893 if (!strncmpW(ptr, prefix, prefix_len))
6895 if (ptr[prefix_len] == szSemiColon[0])
6897 *flags |= ENV_MOD_APPEND;
6898 *value += lstrlenW(prefix);
6900 else
6902 *value = NULL;
6905 else if (lstrlenW(*value) >= prefix_len)
6907 ptr += lstrlenW(ptr) - prefix_len;
6908 if (!strcmpW( ptr, prefix ))
6910 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6912 *flags |= ENV_MOD_PREFIX;
6913 /* the "[~]" will be removed by deformat_string */;
6915 else
6917 *value = NULL;
6923 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6924 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6925 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6926 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6928 ERR("Invalid flags: %08x\n", *flags);
6929 return ERROR_FUNCTION_FAILED;
6932 if (!*flags)
6933 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6935 return ERROR_SUCCESS;
6938 static UINT open_env_key( DWORD flags, HKEY *key )
6940 static const WCHAR user_env[] =
6941 {'E','n','v','i','r','o','n','m','e','n','t',0};
6942 static const WCHAR machine_env[] =
6943 {'S','y','s','t','e','m','\\',
6944 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6945 'C','o','n','t','r','o','l','\\',
6946 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6947 'E','n','v','i','r','o','n','m','e','n','t',0};
6948 const WCHAR *env;
6949 HKEY root;
6950 LONG res;
6952 if (flags & ENV_MOD_MACHINE)
6954 env = machine_env;
6955 root = HKEY_LOCAL_MACHINE;
6957 else
6959 env = user_env;
6960 root = HKEY_CURRENT_USER;
6963 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6964 if (res != ERROR_SUCCESS)
6966 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6967 return ERROR_FUNCTION_FAILED;
6970 return ERROR_SUCCESS;
6973 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6975 MSIPACKAGE *package = param;
6976 LPCWSTR name, value, component;
6977 WCHAR *data = NULL, *newval = NULL, *deformatted = NULL, *p, *q;
6978 DWORD flags, type, size, len, len_value = 0;
6979 UINT res;
6980 HKEY env = NULL;
6981 MSICOMPONENT *comp;
6982 MSIRECORD *uirow;
6983 int action = 0, found = 0;
6985 component = MSI_RecordGetString(rec, 4);
6986 comp = msi_get_loaded_component(package, component);
6987 if (!comp)
6988 return ERROR_SUCCESS;
6990 comp->Action = msi_get_component_action( package, comp );
6991 if (comp->Action != INSTALLSTATE_LOCAL)
6993 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6994 return ERROR_SUCCESS;
6996 name = MSI_RecordGetString(rec, 2);
6997 value = MSI_RecordGetString(rec, 3);
6999 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7001 res = env_parse_flags(&name, &value, &flags);
7002 if (res != ERROR_SUCCESS || !value)
7003 goto done;
7005 if (value && !deformat_string(package, value, &deformatted))
7007 res = ERROR_OUTOFMEMORY;
7008 goto done;
7011 if ((value = deformatted))
7013 if (flags & ENV_MOD_PREFIX)
7015 p = strrchrW( value, ';' );
7016 len_value = p - value;
7018 else if (flags & ENV_MOD_APPEND)
7020 value = strchrW( value, ';' ) + 1;
7021 len_value = strlenW( value );
7023 else len_value = strlenW( value );
7026 res = open_env_key( flags, &env );
7027 if (res != ERROR_SUCCESS)
7028 goto done;
7030 if (flags & ENV_MOD_MACHINE)
7031 action |= 0x20000000;
7033 size = 0;
7034 type = REG_SZ;
7035 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
7036 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
7037 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
7038 goto done;
7040 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
7042 action = 0x2;
7044 /* Nothing to do. */
7045 if (!value)
7047 res = ERROR_SUCCESS;
7048 goto done;
7050 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
7051 newval = strdupW(value);
7052 if (!newval)
7054 res = ERROR_OUTOFMEMORY;
7055 goto done;
7058 else
7060 action = 0x1;
7062 /* Contrary to MSDN, +-variable to [~];path works */
7063 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7065 res = ERROR_SUCCESS;
7066 goto done;
7069 if (!(p = q = data = msi_alloc( size )))
7071 msi_free(deformatted);
7072 RegCloseKey(env);
7073 return ERROR_OUTOFMEMORY;
7076 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)data, &size );
7077 if (res != ERROR_SUCCESS)
7078 goto done;
7080 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7082 action = 0x4;
7083 res = RegDeleteValueW(env, name);
7084 if (res != ERROR_SUCCESS)
7085 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7086 goto done;
7089 for (;;)
7091 while (*q && *q != ';') q++;
7092 len = q - p;
7093 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ) &&
7094 (!p[len] || p[len] == ';'))
7096 found = 1;
7097 break;
7099 if (!*q) break;
7100 p = ++q;
7103 if (found)
7105 TRACE("string already set\n");
7106 goto done;
7109 size = (len_value + 1 + strlenW( data ) + 1) * sizeof(WCHAR);
7110 if (!(p = newval = msi_alloc( size )))
7112 res = ERROR_OUTOFMEMORY;
7113 goto done;
7116 if (flags & ENV_MOD_PREFIX)
7118 memcpy( newval, value, len_value * sizeof(WCHAR) );
7119 newval[len_value] = ';';
7120 p = newval + len_value + 1;
7121 action |= 0x80000000;
7124 strcpyW( p, data );
7126 if (flags & ENV_MOD_APPEND)
7128 p += strlenW( data );
7129 *p++ = ';';
7130 memcpy( p, value, (len_value + 1) * sizeof(WCHAR) );
7131 action |= 0x40000000;
7134 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7135 res = RegSetValueExW( env, name, 0, type, (BYTE *)newval, size );
7136 if (res)
7138 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7141 done:
7142 uirow = MSI_CreateRecord( 3 );
7143 MSI_RecordSetStringW( uirow, 1, name );
7144 MSI_RecordSetStringW( uirow, 2, newval );
7145 MSI_RecordSetInteger( uirow, 3, action );
7146 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7147 msiobj_release( &uirow->hdr );
7149 if (env) RegCloseKey(env);
7150 msi_free(deformatted);
7151 msi_free(data);
7152 msi_free(newval);
7153 return res;
7156 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7158 static const WCHAR query[] = {
7159 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7160 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7161 MSIQUERY *view;
7162 UINT rc;
7164 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7165 if (rc != ERROR_SUCCESS)
7166 return ERROR_SUCCESS;
7168 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7169 msiobj_release(&view->hdr);
7170 return rc;
7173 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7175 MSIPACKAGE *package = param;
7176 LPCWSTR name, value, component;
7177 WCHAR *p, *q, *deformatted = NULL, *new_value = NULL;
7178 DWORD flags, type, size, len, len_value = 0, len_new_value;
7179 HKEY env;
7180 MSICOMPONENT *comp;
7181 MSIRECORD *uirow;
7182 int action = 0;
7183 LONG res;
7184 UINT r;
7186 component = MSI_RecordGetString( rec, 4 );
7187 comp = msi_get_loaded_component( package, component );
7188 if (!comp)
7189 return ERROR_SUCCESS;
7191 comp->Action = msi_get_component_action( package, comp );
7192 if (comp->Action != INSTALLSTATE_ABSENT)
7194 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7195 return ERROR_SUCCESS;
7197 name = MSI_RecordGetString( rec, 2 );
7198 value = MSI_RecordGetString( rec, 3 );
7200 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7202 r = env_parse_flags( &name, &value, &flags );
7203 if (r != ERROR_SUCCESS)
7204 return r;
7206 if (!(flags & ENV_ACT_REMOVE))
7208 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7209 return ERROR_SUCCESS;
7212 if (value && !deformat_string( package, value, &deformatted ))
7213 return ERROR_OUTOFMEMORY;
7215 if ((value = deformatted))
7217 if (flags & ENV_MOD_PREFIX)
7219 p = strchrW( value, ';' );
7220 len_value = p - value;
7222 else if (flags & ENV_MOD_APPEND)
7224 value = strchrW( value, ';' ) + 1;
7225 len_value = strlenW( value );
7227 else len_value = strlenW( value );
7230 r = open_env_key( flags, &env );
7231 if (r != ERROR_SUCCESS)
7233 r = ERROR_SUCCESS;
7234 goto done;
7237 if (flags & ENV_MOD_MACHINE)
7238 action |= 0x20000000;
7240 size = 0;
7241 type = REG_SZ;
7242 res = RegQueryValueExW( env, name, NULL, &type, NULL, &size );
7243 if (res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ))
7244 goto done;
7246 if (!(new_value = msi_alloc( size ))) goto done;
7248 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)new_value, &size );
7249 if (res != ERROR_SUCCESS)
7250 goto done;
7252 len_new_value = size / sizeof(WCHAR) - 1;
7253 p = q = new_value;
7254 for (;;)
7256 while (*q && *q != ';') q++;
7257 len = q - p;
7258 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ))
7260 if (*q == ';') q++;
7261 memmove( p, q, (len_new_value - (q - new_value) + 1) * sizeof(WCHAR) );
7262 break;
7264 if (!*q) break;
7265 p = ++q;
7268 if (!new_value[0] || !value)
7270 TRACE("removing %s\n", debugstr_w(name));
7271 res = RegDeleteValueW( env, name );
7272 if (res != ERROR_SUCCESS)
7273 WARN("failed to delete value %s (%d)\n", debugstr_w(name), res);
7275 else
7277 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(new_value));
7278 size = (strlenW( new_value ) + 1) * sizeof(WCHAR);
7279 res = RegSetValueExW( env, name, 0, type, (BYTE *)new_value, size );
7280 if (res != ERROR_SUCCESS)
7281 WARN("failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(new_value), res);
7284 done:
7285 uirow = MSI_CreateRecord( 3 );
7286 MSI_RecordSetStringW( uirow, 1, name );
7287 MSI_RecordSetStringW( uirow, 2, value );
7288 MSI_RecordSetInteger( uirow, 3, action );
7289 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7290 msiobj_release( &uirow->hdr );
7292 if (env) RegCloseKey( env );
7293 msi_free( deformatted );
7294 msi_free( new_value );
7295 return r;
7298 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7300 static const WCHAR query[] = {
7301 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7302 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7303 MSIQUERY *view;
7304 UINT rc;
7306 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7307 if (rc != ERROR_SUCCESS)
7308 return ERROR_SUCCESS;
7310 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7311 msiobj_release( &view->hdr );
7312 return rc;
7315 UINT msi_validate_product_id( MSIPACKAGE *package )
7317 LPWSTR key, template, id;
7318 UINT r = ERROR_SUCCESS;
7320 id = msi_dup_property( package->db, szProductID );
7321 if (id)
7323 msi_free( id );
7324 return ERROR_SUCCESS;
7326 template = msi_dup_property( package->db, szPIDTemplate );
7327 key = msi_dup_property( package->db, szPIDKEY );
7328 if (key && template)
7330 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7331 r = msi_set_property( package->db, szProductID, key, -1 );
7333 msi_free( template );
7334 msi_free( key );
7335 return r;
7338 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7340 return msi_validate_product_id( package );
7343 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7345 TRACE("\n");
7346 package->need_reboot_at_end = 1;
7347 return ERROR_SUCCESS;
7350 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7352 static const WCHAR szAvailableFreeReg[] =
7353 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7354 MSIRECORD *uirow;
7355 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7357 TRACE("%p %d kilobytes\n", package, space);
7359 uirow = MSI_CreateRecord( 1 );
7360 MSI_RecordSetInteger( uirow, 1, space );
7361 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7362 msiobj_release( &uirow->hdr );
7364 return ERROR_SUCCESS;
7367 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7369 TRACE("%p\n", package);
7371 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7372 return ERROR_SUCCESS;
7375 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7377 FIXME("%p\n", package);
7378 return ERROR_SUCCESS;
7381 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7383 static const WCHAR driver_query[] = {
7384 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7385 'O','D','B','C','D','r','i','v','e','r',0};
7386 static const WCHAR translator_query[] = {
7387 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7388 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7389 MSIQUERY *view;
7390 UINT r, count;
7392 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7393 if (r == ERROR_SUCCESS)
7395 count = 0;
7396 r = MSI_IterateRecords( view, &count, NULL, package );
7397 msiobj_release( &view->hdr );
7398 if (r != ERROR_SUCCESS)
7399 return r;
7400 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7402 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7403 if (r == ERROR_SUCCESS)
7405 count = 0;
7406 r = MSI_IterateRecords( view, &count, NULL, package );
7407 msiobj_release( &view->hdr );
7408 if (r != ERROR_SUCCESS)
7409 return r;
7410 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7412 return ERROR_SUCCESS;
7415 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7417 static const WCHAR fmtW[] =
7418 {'m','s','i','e','x','e','c',' ','/','q','n',' ','/','i',' ','%','s',' ',
7419 'R','E','M','O','V','E','=','%','s',0};
7420 MSIPACKAGE *package = param;
7421 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7422 int attrs = MSI_RecordGetInteger( rec, 5 );
7423 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7424 WCHAR *product, *features, *cmd;
7425 STARTUPINFOW si;
7426 PROCESS_INFORMATION info;
7427 BOOL ret;
7429 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7430 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7432 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7434 len += strlenW( product );
7435 if (features)
7436 len += strlenW( features );
7437 else
7438 len += sizeof(szAll) / sizeof(szAll[0]);
7440 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7442 msi_free( product );
7443 msi_free( features );
7444 return ERROR_OUTOFMEMORY;
7446 sprintfW( cmd, fmtW, product, features ? features : szAll );
7447 msi_free( product );
7448 msi_free( features );
7450 memset( &si, 0, sizeof(STARTUPINFOW) );
7451 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7452 msi_free( cmd );
7453 if (!ret) return GetLastError();
7454 CloseHandle( info.hThread );
7456 WaitForSingleObject( info.hProcess, INFINITE );
7457 CloseHandle( info.hProcess );
7458 return ERROR_SUCCESS;
7461 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7463 static const WCHAR query[] = {
7464 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7465 MSIQUERY *view;
7466 UINT r;
7468 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7469 if (r == ERROR_SUCCESS)
7471 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7472 msiobj_release( &view->hdr );
7473 if (r != ERROR_SUCCESS)
7474 return r;
7476 return ERROR_SUCCESS;
7479 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7481 MSIPACKAGE *package = param;
7482 int attributes = MSI_RecordGetInteger( rec, 5 );
7484 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7486 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7487 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7488 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7489 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7490 HKEY hkey;
7491 UINT r;
7493 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7495 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7496 if (r != ERROR_SUCCESS)
7497 return ERROR_SUCCESS;
7499 else
7501 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7502 if (r != ERROR_SUCCESS)
7503 return ERROR_SUCCESS;
7505 RegCloseKey( hkey );
7507 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7508 debugstr_w(upgrade_code), debugstr_w(version_min),
7509 debugstr_w(version_max), debugstr_w(language));
7511 return ERROR_SUCCESS;
7514 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7516 static const WCHAR query[] = {
7517 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7518 'U','p','g','r','a','d','e',0};
7519 MSIQUERY *view;
7520 UINT r;
7522 if (msi_get_property_int( package->db, szInstalled, 0 ))
7524 TRACE("product is installed, skipping action\n");
7525 return ERROR_SUCCESS;
7527 if (msi_get_property_int( package->db, szPreselected, 0 ))
7529 TRACE("Preselected property is set, not migrating feature states\n");
7530 return ERROR_SUCCESS;
7532 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7533 if (r == ERROR_SUCCESS)
7535 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7536 msiobj_release( &view->hdr );
7537 if (r != ERROR_SUCCESS)
7538 return r;
7540 return ERROR_SUCCESS;
7543 static void bind_image( const char *filename, const char *path )
7545 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7547 WARN("failed to bind image %u\n", GetLastError());
7551 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7553 UINT i;
7554 MSIFILE *file;
7555 MSIPACKAGE *package = param;
7556 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7557 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7558 char *filenameA, *pathA;
7559 WCHAR *pathW, **path_list;
7561 if (!(file = msi_get_loaded_file( package, key )))
7563 WARN("file %s not found\n", debugstr_w(key));
7564 return ERROR_SUCCESS;
7566 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7567 path_list = msi_split_string( paths, ';' );
7568 if (!path_list) bind_image( filenameA, NULL );
7569 else
7571 for (i = 0; path_list[i] && path_list[i][0]; i++)
7573 deformat_string( package, path_list[i], &pathW );
7574 if ((pathA = strdupWtoA( pathW )))
7576 bind_image( filenameA, pathA );
7577 msi_free( pathA );
7579 msi_free( pathW );
7582 msi_free( path_list );
7583 msi_free( filenameA );
7584 return ERROR_SUCCESS;
7587 static UINT ACTION_BindImage( MSIPACKAGE *package )
7589 static const WCHAR query[] = {
7590 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7591 'B','i','n','d','I','m','a','g','e',0};
7592 MSIQUERY *view;
7593 UINT r;
7595 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7596 if (r == ERROR_SUCCESS)
7598 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7599 msiobj_release( &view->hdr );
7600 if (r != ERROR_SUCCESS)
7601 return r;
7603 return ERROR_SUCCESS;
7606 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7608 static const WCHAR query[] = {
7609 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7610 MSIQUERY *view;
7611 DWORD count = 0;
7612 UINT r;
7614 r = MSI_OpenQuery( package->db, &view, query, table );
7615 if (r == ERROR_SUCCESS)
7617 r = MSI_IterateRecords(view, &count, NULL, package);
7618 msiobj_release(&view->hdr);
7619 if (r != ERROR_SUCCESS)
7620 return r;
7622 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7623 return ERROR_SUCCESS;
7626 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7628 static const WCHAR table[] = {
7629 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7630 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7633 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7635 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7636 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7639 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7641 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7642 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7645 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7647 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7648 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7651 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7653 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7654 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7657 static const struct
7659 const WCHAR *action;
7660 const UINT description;
7661 const UINT template;
7662 UINT (*handler)(MSIPACKAGE *);
7663 const WCHAR *action_rollback;
7665 StandardActions[] =
7667 { szAllocateRegistrySpace, IDS_DESC_ALLOCATEREGISTRYSPACE, IDS_TEMP_ALLOCATEREGISTRYSPACE, ACTION_AllocateRegistrySpace, NULL },
7668 { szAppSearch, IDS_DESC_APPSEARCH, IDS_TEMP_APPSEARCH, ACTION_AppSearch, NULL },
7669 { szBindImage, IDS_DESC_BINDIMAGE, IDS_TEMP_BINDIMAGE, ACTION_BindImage, NULL },
7670 { szCCPSearch, IDS_DESC_CCPSEARCH, 0, ACTION_CCPSearch, NULL },
7671 { szCostFinalize, IDS_DESC_COSTFINALIZE, 0, ACTION_CostFinalize, NULL },
7672 { szCostInitialize, IDS_DESC_COSTINITIALIZE, 0, ACTION_CostInitialize, NULL },
7673 { szCreateFolders, IDS_DESC_CREATEFOLDERS, IDS_TEMP_CREATEFOLDERS, ACTION_CreateFolders, szRemoveFolders },
7674 { szCreateShortcuts, IDS_DESC_CREATESHORTCUTS, IDS_TEMP_CREATESHORTCUTS, ACTION_CreateShortcuts, szRemoveShortcuts },
7675 { szDeleteServices, IDS_DESC_DELETESERVICES, IDS_TEMP_DELETESERVICES, ACTION_DeleteServices, szInstallServices },
7676 { szDisableRollback, 0, 0, ACTION_DisableRollback, NULL },
7677 { szDuplicateFiles, IDS_DESC_DUPLICATEFILES, IDS_TEMP_DUPLICATEFILES, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7678 { szExecuteAction, 0, 0, ACTION_ExecuteAction, NULL },
7679 { szFileCost, IDS_DESC_FILECOST, 0, ACTION_FileCost, NULL },
7680 { szFindRelatedProducts, IDS_DESC_FINDRELATEDPRODUCTS, IDS_TEMP_FINDRELATEDPRODUCTS, ACTION_FindRelatedProducts, NULL },
7681 { szForceReboot, 0, 0, ACTION_ForceReboot, NULL },
7682 { szInstallAdminPackage, IDS_DESC_INSTALLADMINPACKAGE, IDS_TEMP_INSTALLADMINPACKAGE, ACTION_InstallAdminPackage, NULL },
7683 { szInstallExecute, 0, 0, ACTION_InstallExecute, NULL },
7684 { szInstallExecuteAgain, 0, 0, ACTION_InstallExecute, NULL },
7685 { szInstallFiles, IDS_DESC_INSTALLFILES, IDS_TEMP_INSTALLFILES, ACTION_InstallFiles, szRemoveFiles },
7686 { szInstallFinalize, 0, 0, ACTION_InstallFinalize, NULL },
7687 { szInstallInitialize, 0, 0, ACTION_InstallInitialize, NULL },
7688 { szInstallODBC, IDS_DESC_INSTALLODBC, 0, ACTION_InstallODBC, szRemoveODBC },
7689 { szInstallServices, IDS_DESC_INSTALLSERVICES, IDS_TEMP_INSTALLSERVICES, ACTION_InstallServices, szDeleteServices },
7690 { szInstallSFPCatalogFile, IDS_DESC_INSTALLSFPCATALOGFILE, IDS_TEMP_INSTALLSFPCATALOGFILE, ACTION_InstallSFPCatalogFile, NULL },
7691 { szInstallValidate, IDS_DESC_INSTALLVALIDATE, 0, ACTION_InstallValidate, NULL },
7692 { szIsolateComponents, 0, 0, ACTION_IsolateComponents, NULL },
7693 { szLaunchConditions, IDS_DESC_LAUNCHCONDITIONS, 0, ACTION_LaunchConditions, NULL },
7694 { szMigrateFeatureStates, IDS_DESC_MIGRATEFEATURESTATES, IDS_TEMP_MIGRATEFEATURESTATES, ACTION_MigrateFeatureStates, NULL },
7695 { szMoveFiles, IDS_DESC_MOVEFILES, IDS_TEMP_MOVEFILES, ACTION_MoveFiles, NULL },
7696 { szMsiPublishAssemblies, IDS_DESC_MSIPUBLISHASSEMBLIES, IDS_TEMP_MSIPUBLISHASSEMBLIES, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7697 { szMsiUnpublishAssemblies, IDS_DESC_MSIUNPUBLISHASSEMBLIES, IDS_TEMP_MSIUNPUBLISHASSEMBLIES, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7698 { szPatchFiles, IDS_DESC_PATCHFILES, IDS_TEMP_PATCHFILES, ACTION_PatchFiles, NULL },
7699 { szProcessComponents, IDS_DESC_PROCESSCOMPONENTS, 0, ACTION_ProcessComponents, szProcessComponents },
7700 { szPublishComponents, IDS_DESC_PUBLISHCOMPONENTS, IDS_TEMP_PUBLISHCOMPONENTS, ACTION_PublishComponents, szUnpublishComponents },
7701 { szPublishFeatures, IDS_DESC_PUBLISHFEATURES, IDS_TEMP_PUBLISHFEATURES, ACTION_PublishFeatures, szUnpublishFeatures },
7702 { szPublishProduct, IDS_DESC_PUBLISHPRODUCT, 0, ACTION_PublishProduct, szUnpublishProduct },
7703 { szRegisterClassInfo, IDS_DESC_REGISTERCLASSINFO, IDS_TEMP_REGISTERCLASSINFO, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7704 { szRegisterComPlus, IDS_DESC_REGISTERCOMPLUS, IDS_TEMP_REGISTERCOMPLUS, ACTION_RegisterComPlus, szUnregisterComPlus },
7705 { szRegisterExtensionInfo, IDS_DESC_REGISTEREXTENSIONINFO, 0, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7706 { szRegisterFonts, IDS_DESC_REGISTERFONTS, IDS_TEMP_REGISTERFONTS, ACTION_RegisterFonts, szUnregisterFonts },
7707 { szRegisterMIMEInfo, IDS_DESC_REGISTERMIMEINFO, IDS_TEMP_REGISTERMIMEINFO, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7708 { szRegisterProduct, IDS_DESC_REGISTERPRODUCT, 0, ACTION_RegisterProduct, NULL },
7709 { szRegisterProgIdInfo, IDS_DESC_REGISTERPROGIDINFO, IDS_TEMP_REGISTERPROGIDINFO, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7710 { szRegisterTypeLibraries, IDS_DESC_REGISTERTYPELIBRARIES, IDS_TEMP_REGISTERTYPELIBRARIES, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7711 { szRegisterUser, IDS_DESC_REGISTERUSER, 0, ACTION_RegisterUser, NULL },
7712 { szRemoveDuplicateFiles, IDS_DESC_REMOVEDUPLICATEFILES, IDS_TEMP_REMOVEDUPLICATEFILES, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7713 { szRemoveEnvironmentStrings, IDS_DESC_REMOVEENVIRONMENTSTRINGS, IDS_TEMP_REMOVEENVIRONMENTSTRINGS, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7714 { szRemoveExistingProducts, IDS_DESC_REMOVEEXISTINGPRODUCTS, IDS_TEMP_REMOVEEXISTINGPRODUCTS, ACTION_RemoveExistingProducts, NULL },
7715 { szRemoveFiles, IDS_DESC_REMOVEFILES, IDS_TEMP_REMOVEFILES, ACTION_RemoveFiles, szInstallFiles },
7716 { szRemoveFolders, IDS_DESC_REMOVEFOLDERS, IDS_TEMP_REMOVEFOLDERS, ACTION_RemoveFolders, szCreateFolders },
7717 { szRemoveIniValues, IDS_DESC_REMOVEINIVALUES, IDS_TEMP_REMOVEINIVALUES, ACTION_RemoveIniValues, szWriteIniValues },
7718 { szRemoveODBC, IDS_DESC_REMOVEODBC, 0, ACTION_RemoveODBC, szInstallODBC },
7719 { szRemoveRegistryValues, IDS_DESC_REMOVEREGISTRYVALUES, IDS_TEMP_REMOVEREGISTRYVALUES, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7720 { szRemoveShortcuts, IDS_DESC_REMOVESHORTCUTS, IDS_TEMP_REMOVESHORTCUTS, ACTION_RemoveShortcuts, szCreateShortcuts },
7721 { szResolveSource, 0, 0, ACTION_ResolveSource, NULL },
7722 { szRMCCPSearch, IDS_DESC_RMCCPSEARCH, 0, ACTION_RMCCPSearch, NULL },
7723 { szScheduleReboot, 0, 0, ACTION_ScheduleReboot, NULL },
7724 { szSelfRegModules, IDS_DESC_SELFREGMODULES, IDS_TEMP_SELFREGMODULES, ACTION_SelfRegModules, szSelfUnregModules },
7725 { szSelfUnregModules, IDS_DESC_SELFUNREGMODULES, IDS_TEMP_SELFUNREGMODULES, ACTION_SelfUnregModules, szSelfRegModules },
7726 { szSetODBCFolders, IDS_DESC_SETODBCFOLDERS, 0, ACTION_SetODBCFolders, NULL },
7727 { szStartServices, IDS_DESC_STARTSERVICES, IDS_TEMP_STARTSERVICES, ACTION_StartServices, szStopServices },
7728 { szStopServices, IDS_DESC_STOPSERVICES, IDS_TEMP_STOPSERVICES, ACTION_StopServices, szStartServices },
7729 { szUnpublishComponents, IDS_DESC_UNPUBLISHCOMPONENTS, IDS_TEMP_UNPUBLISHCOMPONENTS, ACTION_UnpublishComponents, szPublishComponents },
7730 { szUnpublishFeatures, IDS_DESC_UNPUBLISHFEATURES, IDS_TEMP_UNPUBLISHFEATURES, ACTION_UnpublishFeatures, szPublishFeatures },
7731 { szUnpublishProduct, IDS_DESC_UNPUBLISHPRODUCT, 0, ACTION_UnpublishProduct, NULL }, /* for rollback only */
7732 { szUnregisterClassInfo, IDS_DESC_UNREGISTERCLASSINFO, IDS_TEMP_UNREGISTERCLASSINFO, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7733 { szUnregisterComPlus, IDS_DESC_UNREGISTERCOMPLUS, IDS_TEMP_UNREGISTERCOMPLUS, ACTION_UnregisterComPlus, szRegisterComPlus },
7734 { szUnregisterExtensionInfo, IDS_DESC_UNREGISTEREXTENSIONINFO, IDS_TEMP_UNREGISTEREXTENSIONINFO, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7735 { szUnregisterFonts, IDS_DESC_UNREGISTERFONTS, IDS_TEMP_UNREGISTERFONTS, ACTION_UnregisterFonts, szRegisterFonts },
7736 { szUnregisterMIMEInfo, IDS_DESC_UNREGISTERMIMEINFO, IDS_TEMP_UNREGISTERMIMEINFO, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7737 { szUnregisterProgIdInfo, IDS_DESC_UNREGISTERPROGIDINFO, IDS_TEMP_UNREGISTERPROGIDINFO, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7738 { szUnregisterTypeLibraries, IDS_DESC_UNREGISTERTYPELIBRARIES, IDS_TEMP_UNREGISTERTYPELIBRARIES, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7739 { szValidateProductID, 0, 0, ACTION_ValidateProductID, NULL },
7740 { szWriteEnvironmentStrings, IDS_DESC_WRITEENVIRONMENTSTRINGS, IDS_TEMP_WRITEENVIRONMENTSTRINGS, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7741 { szWriteIniValues, IDS_DESC_WRITEINIVALUES, IDS_TEMP_WRITEINIVALUES, ACTION_WriteIniValues, szRemoveIniValues },
7742 { szWriteRegistryValues, IDS_DESC_WRITEREGISTRYVALUES, IDS_TEMP_WRITEREGISTRYVALUES, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7743 { 0 }
7746 static UINT ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action)
7748 UINT rc = ERROR_FUNCTION_NOT_CALLED;
7749 UINT i;
7751 i = 0;
7752 while (StandardActions[i].action != NULL)
7754 if (!strcmpW( StandardActions[i].action, action ))
7756 WCHAR description[100] = {0}, template[100] = {0};
7758 if (StandardActions[i].description != 0)
7759 LoadStringW(msi_hInstance, StandardActions[i].description, (LPWSTR)&description, 100);
7760 if (StandardActions[i].template != 0)
7761 LoadStringW(msi_hInstance, StandardActions[i].template, (LPWSTR)&template, 100);
7763 ui_actionstart(package, action, description, template);
7764 if (StandardActions[i].handler)
7766 ui_actioninfo( package, action, TRUE, 0 );
7767 rc = StandardActions[i].handler( package );
7768 ui_actioninfo( package, action, FALSE, rc );
7769 package->LastActionResult = !rc;
7771 if (StandardActions[i].action_rollback && !package->need_rollback)
7773 TRACE("scheduling rollback action\n");
7774 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7777 else
7779 FIXME("unhandled standard action %s\n", debugstr_w(action));
7780 rc = ERROR_SUCCESS;
7782 break;
7784 i++;
7786 return rc;
7789 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7791 UINT rc;
7793 TRACE("Performing action (%s)\n", debugstr_w(action));
7795 rc = ACTION_HandleStandardAction(package, action);
7797 if (rc == ERROR_FUNCTION_NOT_CALLED)
7798 rc = ACTION_HandleCustomAction(package, action, script);
7800 if (rc == ERROR_FUNCTION_NOT_CALLED)
7801 WARN("unhandled msi action %s\n", debugstr_w(action));
7803 return rc;
7806 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7808 UINT rc;
7810 TRACE("Performing action (%s)\n", debugstr_w(action));
7812 package->action_progress_increment = 0;
7813 rc = ACTION_HandleStandardAction(package, action);
7815 if (rc == ERROR_FUNCTION_NOT_CALLED)
7816 rc = ACTION_HandleCustomAction(package, action, script);
7818 if (rc == ERROR_FUNCTION_NOT_CALLED)
7819 WARN("unhandled msi action %s\n", debugstr_w(action));
7821 return rc;
7824 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7826 UINT rc = ERROR_SUCCESS;
7827 MSIRECORD *row;
7829 static const WCHAR query[] =
7830 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7831 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7832 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7833 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7834 static const WCHAR ui_query[] =
7835 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7836 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7837 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7838 ' ', '=',' ','%','i',0};
7840 if (needs_ui_sequence(package))
7841 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7842 else
7843 row = MSI_QueryGetRecord(package->db, query, seq);
7845 if (row)
7847 LPCWSTR action, cond;
7849 TRACE("Running the actions\n");
7851 /* check conditions */
7852 cond = MSI_RecordGetString(row, 2);
7854 /* this is a hack to skip errors in the condition code */
7855 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7857 msiobj_release(&row->hdr);
7858 return ERROR_SUCCESS;
7861 action = MSI_RecordGetString(row, 1);
7862 if (!action)
7864 ERR("failed to fetch action\n");
7865 msiobj_release(&row->hdr);
7866 return ERROR_FUNCTION_FAILED;
7869 if (needs_ui_sequence(package))
7870 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7871 else
7872 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7874 msiobj_release(&row->hdr);
7877 return rc;
7880 /****************************************************
7881 * TOP level entry points
7882 *****************************************************/
7884 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7885 LPCWSTR szCommandLine )
7887 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7888 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7889 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7890 WCHAR *reinstall, *remove, *patch, *productcode;
7891 BOOL ui_exists;
7892 UINT rc;
7894 msi_set_property( package->db, szAction, szInstall, -1 );
7896 package->script->InWhatSequence = SEQUENCE_INSTALL;
7898 if (szPackagePath)
7900 LPWSTR p, dir;
7901 LPCWSTR file;
7903 dir = strdupW(szPackagePath);
7904 p = strrchrW(dir, '\\');
7905 if (p)
7907 *(++p) = 0;
7908 file = szPackagePath + (p - dir);
7910 else
7912 msi_free(dir);
7913 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7914 GetCurrentDirectoryW(MAX_PATH, dir);
7915 lstrcatW(dir, szBackSlash);
7916 file = szPackagePath;
7919 msi_free( package->PackagePath );
7920 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7921 if (!package->PackagePath)
7923 msi_free(dir);
7924 return ERROR_OUTOFMEMORY;
7927 lstrcpyW(package->PackagePath, dir);
7928 lstrcatW(package->PackagePath, file);
7929 msi_free(dir);
7931 msi_set_sourcedir_props(package, FALSE);
7934 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7935 if (rc != ERROR_SUCCESS)
7936 return rc;
7938 msi_apply_transforms( package );
7939 msi_apply_patches( package );
7941 patch = msi_dup_property( package->db, szPatch );
7942 remove = msi_dup_property( package->db, szRemove );
7943 reinstall = msi_dup_property( package->db, szReinstall );
7944 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7946 TRACE("setting REINSTALL property to ALL\n");
7947 msi_set_property( package->db, szReinstall, szAll, -1 );
7948 package->full_reinstall = 1;
7951 msi_set_original_database_property( package->db, szPackagePath );
7952 msi_parse_command_line( package, szCommandLine, FALSE );
7953 msi_adjust_privilege_properties( package );
7954 msi_set_context( package );
7956 productcode = msi_dup_property( package->db, szProductCode );
7957 if (strcmpiW( productcode, package->ProductCode ))
7959 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
7960 msi_free( package->ProductCode );
7961 package->ProductCode = productcode;
7963 else msi_free( productcode );
7965 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7967 TRACE("disabling rollback\n");
7968 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7971 if (needs_ui_sequence( package))
7973 package->script->InWhatSequence |= SEQUENCE_UI;
7974 rc = ACTION_ProcessUISequence(package);
7975 ui_exists = ui_sequence_exists(package);
7976 if (rc == ERROR_SUCCESS || !ui_exists)
7978 package->script->InWhatSequence |= SEQUENCE_EXEC;
7979 rc = ACTION_ProcessExecSequence(package, ui_exists);
7982 else
7983 rc = ACTION_ProcessExecSequence(package, FALSE);
7985 /* process the ending type action */
7986 if (rc == ERROR_SUCCESS)
7987 ACTION_PerformActionSequence(package, -1);
7988 else if (rc == ERROR_INSTALL_USEREXIT)
7989 ACTION_PerformActionSequence(package, -2);
7990 else if (rc == ERROR_INSTALL_SUSPEND)
7991 ACTION_PerformActionSequence(package, -4);
7992 else /* failed */
7994 ACTION_PerformActionSequence(package, -3);
7995 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7997 package->need_rollback = TRUE;
8001 /* finish up running custom actions */
8002 ACTION_FinishCustomActions(package);
8004 if (package->need_rollback && !reinstall)
8006 WARN("installation failed, running rollback script\n");
8007 execute_script( package, SCRIPT_ROLLBACK );
8009 msi_free( reinstall );
8010 msi_free( remove );
8011 msi_free( patch );
8013 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
8014 return ERROR_SUCCESS_REBOOT_REQUIRED;
8016 return rc;