msi: Correctly parse double quotes in the token value.
[wine/multimedia.git] / dlls / msi / action.c
blobf2e8e7113f69356863cea7568763a8d598c3244f
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 "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "shlwapi.h"
39 #include "imagehlp.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
48 static const WCHAR szCreateFolders[] =
49 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
50 static const WCHAR szCostFinalize[] =
51 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
52 static const WCHAR szWriteRegistryValues[] =
53 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
54 static const WCHAR szFileCost[] =
55 {'F','i','l','e','C','o','s','t',0};
56 static const WCHAR szInstallInitialize[] =
57 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
58 static const WCHAR szInstallValidate[] =
59 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
60 static const WCHAR szLaunchConditions[] =
61 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
62 static const WCHAR szProcessComponents[] =
63 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
64 static const WCHAR szRegisterTypeLibraries[] =
65 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
66 static const WCHAR szCreateShortcuts[] =
67 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
68 static const WCHAR szPublishProduct[] =
69 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
70 static const WCHAR szWriteIniValues[] =
71 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
72 static const WCHAR szSelfRegModules[] =
73 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
74 static const WCHAR szPublishFeatures[] =
75 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
76 static const WCHAR szRegisterProduct[] =
77 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
78 static const WCHAR szInstallExecute[] =
79 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
80 static const WCHAR szInstallExecuteAgain[] =
81 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
82 static const WCHAR szInstallFinalize[] =
83 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
84 static const WCHAR szForceReboot[] =
85 {'F','o','r','c','e','R','e','b','o','o','t',0};
86 static const WCHAR szResolveSource[] =
87 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
88 static const WCHAR szAllocateRegistrySpace[] =
89 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
90 static const WCHAR szBindImage[] =
91 {'B','i','n','d','I','m','a','g','e',0};
92 static const WCHAR szDeleteServices[] =
93 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
94 static const WCHAR szDisableRollback[] =
95 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
96 static const WCHAR szExecuteAction[] =
97 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
98 static const WCHAR szInstallAdminPackage[] =
99 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
100 static const WCHAR szInstallSFPCatalogFile[] =
101 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
102 static const WCHAR szIsolateComponents[] =
103 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
104 static const WCHAR szMigrateFeatureStates[] =
105 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
106 static const WCHAR szMsiUnpublishAssemblies[] =
107 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
108 static const WCHAR szInstallODBC[] =
109 {'I','n','s','t','a','l','l','O','D','B','C',0};
110 static const WCHAR szInstallServices[] =
111 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
112 static const WCHAR szPublishComponents[] =
113 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
114 static const WCHAR szRegisterComPlus[] =
115 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
116 static const WCHAR szRegisterUser[] =
117 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
118 static const WCHAR szRemoveEnvironmentStrings[] =
119 {'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};
120 static const WCHAR szRemoveExistingProducts[] =
121 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
122 static const WCHAR szRemoveFolders[] =
123 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
124 static const WCHAR szRemoveIniValues[] =
125 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
126 static const WCHAR szRemoveODBC[] =
127 {'R','e','m','o','v','e','O','D','B','C',0};
128 static const WCHAR szRemoveRegistryValues[] =
129 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
130 static const WCHAR szRemoveShortcuts[] =
131 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
132 static const WCHAR szRMCCPSearch[] =
133 {'R','M','C','C','P','S','e','a','r','c','h',0};
134 static const WCHAR szScheduleReboot[] =
135 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
136 static const WCHAR szSelfUnregModules[] =
137 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
138 static const WCHAR szSetODBCFolders[] =
139 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
140 static const WCHAR szStartServices[] =
141 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
142 static const WCHAR szStopServices[] =
143 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
144 static const WCHAR szUnpublishComponents[] =
145 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
146 static const WCHAR szUnpublishFeatures[] =
147 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
148 static const WCHAR szUnregisterComPlus[] =
149 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
150 static const WCHAR szUnregisterTypeLibraries[] =
151 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
152 static const WCHAR szValidateProductID[] =
153 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
154 static const WCHAR szWriteEnvironmentStrings[] =
155 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
157 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
159 static const WCHAR Query_t[] =
160 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
161 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
162 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
163 ' ','\'','%','s','\'',0};
164 MSIRECORD * row;
166 row = MSI_QueryGetRecord( package->db, Query_t, action );
167 if (!row)
168 return;
169 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
170 msiobj_release(&row->hdr);
173 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
174 UINT rc)
176 MSIRECORD * row;
177 static const WCHAR template_s[]=
178 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
179 '%','s', '.',0};
180 static const WCHAR template_e[]=
181 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
182 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
183 '%','i','.',0};
184 static const WCHAR format[] =
185 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
186 WCHAR message[1024];
187 WCHAR timet[0x100];
189 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
190 if (start)
191 sprintfW(message,template_s,timet,action);
192 else
193 sprintfW(message,template_e,timet,action,rc);
195 row = MSI_CreateRecord(1);
196 MSI_RecordSetStringW(row,1,message);
198 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
199 msiobj_release(&row->hdr);
202 enum parse_state
204 state_whitespace,
205 state_token,
206 state_quote
209 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
211 enum parse_state state = state_quote;
212 const WCHAR *p;
213 WCHAR *out = value;
214 BOOL ignore, in_quotes = FALSE;
215 int count = 0, len = 0;
217 for (p = str; *p; p++)
219 ignore = FALSE;
220 switch (state)
222 case state_whitespace:
223 switch (*p)
225 case ' ':
226 in_quotes = TRUE;
227 ignore = TRUE;
228 len++;
229 break;
230 case '"':
231 state = state_quote;
232 if (in_quotes && p[1] != '\"') count--;
233 else count++;
234 break;
235 default:
236 state = state_token;
237 in_quotes = TRUE;
238 len++;
239 break;
241 break;
243 case state_token:
244 switch (*p)
246 case '"':
247 state = state_quote;
248 if (in_quotes && p[1] != '\"') count--;
249 else count++;
250 break;
251 case ' ':
252 state = state_whitespace;
253 if (!count) goto done;
254 in_quotes = TRUE;
255 len++;
256 break;
257 default:
258 if (!count) in_quotes = FALSE;
259 else in_quotes = TRUE;
260 len++;
261 break;
263 break;
265 case state_quote:
266 switch (*p)
268 case '"':
269 if (in_quotes && p[1] != '\"') count--;
270 else count++;
271 break;
272 case ' ':
273 state = state_whitespace;
274 if (!count || (count > 1 && !len)) goto done;
275 in_quotes = TRUE;
276 len++;
277 break;
278 default:
279 state = state_token;
280 if (!count) in_quotes = FALSE;
281 else in_quotes = TRUE;
282 len++;
283 break;
285 break;
287 default: break;
289 if (!ignore) *out++ = *p;
292 done:
293 if (!len) *value = 0;
294 else *out = 0;
296 *quotes = count;
297 return p - str;
300 static void remove_quotes( WCHAR *str )
302 WCHAR *p = str;
303 int len = strlenW( str );
305 while ((p = strchrW( p, '"' )))
307 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
308 p++;
312 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
313 BOOL preserve_case )
315 LPCWSTR ptr, ptr2;
316 int num_quotes;
317 DWORD len;
318 WCHAR *prop, *val;
319 UINT r;
321 if (!szCommandLine)
322 return ERROR_SUCCESS;
324 ptr = szCommandLine;
325 while (*ptr)
327 while (*ptr == ' ') ptr++;
328 if (!*ptr) break;
330 ptr2 = strchrW( ptr, '=' );
331 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
333 len = ptr2 - ptr;
334 if (!len) return ERROR_INVALID_COMMAND_LINE;
336 while (ptr[len - 1] == ' ') len--;
338 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
339 memcpy( prop, ptr, len * sizeof(WCHAR) );
340 prop[len] = 0;
341 if (!preserve_case) struprW( prop );
343 ptr2++;
344 while (*ptr2 == ' ') ptr2++;
346 num_quotes = 0;
347 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
348 len = parse_prop( ptr2, val, &num_quotes );
349 if (num_quotes % 2)
351 WARN("unbalanced quotes\n");
352 msi_free( val );
353 msi_free( prop );
354 return ERROR_INVALID_COMMAND_LINE;
356 remove_quotes( val );
357 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
359 r = msi_set_property( package->db, prop, val, -1 );
360 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
361 msi_reset_folders( package, TRUE );
363 msi_free( val );
364 msi_free( prop );
366 ptr = ptr2 + len;
369 return ERROR_SUCCESS;
372 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
374 LPCWSTR pc;
375 LPWSTR p, *ret = NULL;
376 UINT count = 0;
378 if (!str)
379 return ret;
381 /* count the number of substrings */
382 for ( pc = str, count = 0; pc; count++ )
384 pc = strchrW( pc, sep );
385 if (pc)
386 pc++;
389 /* allocate space for an array of substring pointers and the substrings */
390 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
391 (lstrlenW(str)+1) * sizeof(WCHAR) );
392 if (!ret)
393 return ret;
395 /* copy the string and set the pointers */
396 p = (LPWSTR) &ret[count+1];
397 lstrcpyW( p, str );
398 for( count = 0; (ret[count] = p); count++ )
400 p = strchrW( p, sep );
401 if (p)
402 *p++ = 0;
405 return ret;
408 static BOOL ui_sequence_exists( MSIPACKAGE *package )
410 static const WCHAR query [] = {
411 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
412 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
413 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',0};
414 MSIQUERY *view;
415 DWORD count = 0;
417 if (!(MSI_DatabaseOpenViewW( package->db, query, &view )))
419 MSI_IterateRecords( view, &count, NULL, package );
420 msiobj_release( &view->hdr );
422 return count != 0;
425 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
427 WCHAR *source, *check, *p, *db;
428 DWORD len;
430 if (!(db = msi_dup_property( package->db, szOriginalDatabase )))
431 return ERROR_OUTOFMEMORY;
433 if (!(p = strrchrW( db, '\\' )) && !(p = strrchrW( db, '/' )))
435 msi_free(db);
436 return ERROR_SUCCESS;
438 len = p - db + 2;
439 source = msi_alloc( len * sizeof(WCHAR) );
440 lstrcpynW( source, db, len );
441 msi_free( db );
443 check = msi_dup_property( package->db, szSourceDir );
444 if (!check || replace)
446 UINT r = msi_set_property( package->db, szSourceDir, source, -1 );
447 if (r == ERROR_SUCCESS)
448 msi_reset_folders( package, TRUE );
450 msi_free( check );
452 check = msi_dup_property( package->db, szSOURCEDIR );
453 if (!check || replace)
454 msi_set_property( package->db, szSOURCEDIR, source, -1 );
456 msi_free( check );
457 msi_free( source );
459 return ERROR_SUCCESS;
462 static BOOL needs_ui_sequence(MSIPACKAGE *package)
464 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
467 UINT msi_set_context(MSIPACKAGE *package)
469 UINT r = msi_locate_product( package->ProductCode, &package->Context );
470 if (r != ERROR_SUCCESS)
472 int num = msi_get_property_int( package->db, szAllUsers, 0 );
473 if (num == 1 || num == 2)
474 package->Context = MSIINSTALLCONTEXT_MACHINE;
475 else
476 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
478 return ERROR_SUCCESS;
481 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
483 UINT rc;
484 LPCWSTR cond, action;
485 MSIPACKAGE *package = param;
487 action = MSI_RecordGetString(row,1);
488 if (!action)
490 ERR("Error is retrieving action name\n");
491 return ERROR_FUNCTION_FAILED;
494 /* check conditions */
495 cond = MSI_RecordGetString(row,2);
497 /* this is a hack to skip errors in the condition code */
498 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
500 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
501 return ERROR_SUCCESS;
504 if (needs_ui_sequence(package))
505 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
506 else
507 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
509 msi_dialog_check_messages( NULL );
511 if (package->CurrentInstallState != ERROR_SUCCESS)
512 rc = package->CurrentInstallState;
514 if (rc == ERROR_FUNCTION_NOT_CALLED)
515 rc = ERROR_SUCCESS;
517 if (rc != ERROR_SUCCESS)
518 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
520 if (package->need_reboot_now)
522 TRACE("action %s asked for immediate reboot, suspending installation\n",
523 debugstr_w(action));
524 rc = ACTION_ForceReboot( package );
526 return rc;
529 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
531 static const WCHAR query[] = {
532 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
533 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
534 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
535 '`','S','e','q','u','e','n','c','e','`',0};
536 MSIQUERY *view;
537 UINT r;
539 TRACE("%p %s\n", package, debugstr_w(table));
541 r = MSI_OpenQuery( package->db, &view, query, table );
542 if (r == ERROR_SUCCESS)
544 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
545 msiobj_release(&view->hdr);
547 return r;
550 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
552 static const WCHAR query[] = {
553 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
554 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
555 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
556 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
557 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
558 static const WCHAR query_validate[] = {
559 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
560 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
561 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
562 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
563 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
564 MSIQUERY *view;
565 INT seq = 0;
566 UINT rc;
568 if (package->script->ExecuteSequenceRun)
570 TRACE("Execute Sequence already Run\n");
571 return ERROR_SUCCESS;
574 package->script->ExecuteSequenceRun = TRUE;
576 /* get the sequence number */
577 if (UIran)
579 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
580 if (!row) return ERROR_FUNCTION_FAILED;
581 seq = MSI_RecordGetInteger(row,1);
582 msiobj_release(&row->hdr);
584 rc = MSI_OpenQuery(package->db, &view, query, seq);
585 if (rc == ERROR_SUCCESS)
587 TRACE("Running the actions\n");
589 msi_set_property( package->db, szSourceDir, NULL, -1 );
590 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
591 msiobj_release(&view->hdr);
593 return rc;
596 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
598 static const WCHAR query[] = {
599 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
600 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
601 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
602 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
603 MSIQUERY *view;
604 UINT rc;
606 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
607 if (rc == ERROR_SUCCESS)
609 TRACE("Running the actions\n");
610 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
611 msiobj_release(&view->hdr);
613 return rc;
616 /********************************************************
617 * ACTION helper functions and functions that perform the actions
618 *******************************************************/
619 static BOOL ACTION_HandleCustomAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc, UINT script )
621 BOOL ret=FALSE;
622 UINT arc;
624 arc = ACTION_CustomAction( package, action, script );
625 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
627 *rc = arc;
628 ret = TRUE;
630 return ret;
633 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
635 MSICOMPONENT *comp;
637 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
639 if (!strcmpW( Component, comp->Component )) return comp;
641 return NULL;
644 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
646 MSIFEATURE *feature;
648 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
650 if (!strcmpW( Feature, feature->Feature )) return feature;
652 return NULL;
655 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
657 MSIFILE *file;
659 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
661 if (!strcmpW( key, file->File )) return file;
663 return NULL;
666 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
668 MSIFOLDER *folder;
670 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
672 if (!strcmpW( dir, folder->Directory )) return folder;
674 return NULL;
678 * Recursively create all directories in the path.
679 * shamelessly stolen from setupapi/queue.c
681 BOOL msi_create_full_path( const WCHAR *path )
683 BOOL ret = TRUE;
684 WCHAR *new_path;
685 int len;
687 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
688 strcpyW( new_path, path );
690 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
691 new_path[len - 1] = 0;
693 while (!CreateDirectoryW( new_path, NULL ))
695 WCHAR *slash;
696 DWORD last_error = GetLastError();
697 if (last_error == ERROR_ALREADY_EXISTS) break;
698 if (last_error != ERROR_PATH_NOT_FOUND)
700 ret = FALSE;
701 break;
703 if (!(slash = strrchrW( new_path, '\\' )))
705 ret = FALSE;
706 break;
708 len = slash - new_path;
709 new_path[len] = 0;
710 if (!msi_create_full_path( new_path ))
712 ret = FALSE;
713 break;
715 new_path[len] = '\\';
717 msi_free( new_path );
718 return ret;
721 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
723 MSIRECORD *row;
725 row = MSI_CreateRecord( 4 );
726 MSI_RecordSetInteger( row, 1, a );
727 MSI_RecordSetInteger( row, 2, b );
728 MSI_RecordSetInteger( row, 3, c );
729 MSI_RecordSetInteger( row, 4, d );
730 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
731 msiobj_release( &row->hdr );
733 msi_dialog_check_messages( NULL );
736 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
738 static const WCHAR query[] =
739 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
740 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
741 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
742 WCHAR message[1024];
743 MSIRECORD *row = 0;
744 DWORD size;
746 if (!package->LastAction || strcmpW( package->LastAction, action ))
748 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
750 if (MSI_RecordIsNull( row, 3 ))
752 msiobj_release( &row->hdr );
753 return;
755 /* update the cached action format */
756 msi_free( package->ActionFormat );
757 package->ActionFormat = msi_dup_record_field( row, 3 );
758 msi_free( package->LastAction );
759 package->LastAction = strdupW( action );
760 msiobj_release( &row->hdr );
762 size = 1024;
763 MSI_RecordSetStringW( record, 0, package->ActionFormat );
764 MSI_FormatRecordW( package, record, message, &size );
765 row = MSI_CreateRecord( 1 );
766 MSI_RecordSetStringW( row, 1, message );
767 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
768 msiobj_release( &row->hdr );
771 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
773 if (!comp->Enabled)
775 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
776 return INSTALLSTATE_UNKNOWN;
778 if (package->need_rollback) return comp->Installed;
779 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
781 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
782 return INSTALLSTATE_UNKNOWN;
784 return comp->ActionRequest;
787 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
789 if (package->need_rollback) return feature->Installed;
790 return feature->ActionRequest;
793 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
795 MSIPACKAGE *package = param;
796 LPCWSTR dir, component, full_path;
797 MSIRECORD *uirow;
798 MSIFOLDER *folder;
799 MSICOMPONENT *comp;
801 component = MSI_RecordGetString(row, 2);
802 if (!component)
803 return ERROR_SUCCESS;
805 comp = msi_get_loaded_component(package, component);
806 if (!comp)
807 return ERROR_SUCCESS;
809 comp->Action = msi_get_component_action( package, comp );
810 if (comp->Action != INSTALLSTATE_LOCAL)
812 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
813 return ERROR_SUCCESS;
816 dir = MSI_RecordGetString(row,1);
817 if (!dir)
819 ERR("Unable to get folder id\n");
820 return ERROR_SUCCESS;
823 uirow = MSI_CreateRecord(1);
824 MSI_RecordSetStringW(uirow, 1, dir);
825 msi_ui_actiondata(package, szCreateFolders, uirow);
826 msiobj_release(&uirow->hdr);
828 full_path = msi_get_target_folder( package, dir );
829 if (!full_path)
831 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
832 return ERROR_SUCCESS;
834 TRACE("folder is %s\n", debugstr_w(full_path));
836 folder = msi_get_loaded_folder( package, dir );
837 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
838 folder->State = FOLDER_STATE_CREATED;
839 return ERROR_SUCCESS;
842 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
844 static const WCHAR query[] = {
845 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
846 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
847 MSIQUERY *view;
848 UINT rc;
850 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
851 if (rc != ERROR_SUCCESS)
852 return ERROR_SUCCESS;
854 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
855 msiobj_release(&view->hdr);
856 return rc;
859 static void remove_persistent_folder( MSIFOLDER *folder )
861 FolderList *fl;
863 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
865 remove_persistent_folder( fl->folder );
867 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
869 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
873 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
875 MSIPACKAGE *package = param;
876 LPCWSTR dir, component, full_path;
877 MSIRECORD *uirow;
878 MSIFOLDER *folder;
879 MSICOMPONENT *comp;
881 component = MSI_RecordGetString(row, 2);
882 if (!component)
883 return ERROR_SUCCESS;
885 comp = msi_get_loaded_component(package, component);
886 if (!comp)
887 return ERROR_SUCCESS;
889 comp->Action = msi_get_component_action( package, comp );
890 if (comp->Action != INSTALLSTATE_ABSENT)
892 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
893 return ERROR_SUCCESS;
896 dir = MSI_RecordGetString( row, 1 );
897 if (!dir)
899 ERR("Unable to get folder id\n");
900 return ERROR_SUCCESS;
903 full_path = msi_get_target_folder( package, dir );
904 if (!full_path)
906 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
907 return ERROR_SUCCESS;
909 TRACE("folder is %s\n", debugstr_w(full_path));
911 uirow = MSI_CreateRecord( 1 );
912 MSI_RecordSetStringW( uirow, 1, dir );
913 msi_ui_actiondata( package, szRemoveFolders, uirow );
914 msiobj_release( &uirow->hdr );
916 folder = msi_get_loaded_folder( package, dir );
917 remove_persistent_folder( folder );
918 return ERROR_SUCCESS;
921 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
923 static const WCHAR query[] = {
924 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
925 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
926 MSIQUERY *view;
927 UINT rc;
929 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
930 if (rc != ERROR_SUCCESS)
931 return ERROR_SUCCESS;
933 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
934 msiobj_release( &view->hdr );
935 return rc;
938 static UINT load_component( MSIRECORD *row, LPVOID param )
940 MSIPACKAGE *package = param;
941 MSICOMPONENT *comp;
943 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
944 if (!comp)
945 return ERROR_FUNCTION_FAILED;
947 list_add_tail( &package->components, &comp->entry );
949 /* fill in the data */
950 comp->Component = msi_dup_record_field( row, 1 );
952 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
954 comp->ComponentId = msi_dup_record_field( row, 2 );
955 comp->Directory = msi_dup_record_field( row, 3 );
956 comp->Attributes = MSI_RecordGetInteger(row,4);
957 comp->Condition = msi_dup_record_field( row, 5 );
958 comp->KeyPath = msi_dup_record_field( row, 6 );
960 comp->Installed = INSTALLSTATE_UNKNOWN;
961 comp->Action = INSTALLSTATE_UNKNOWN;
962 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
964 comp->assembly = msi_load_assembly( package, comp );
965 return ERROR_SUCCESS;
968 UINT msi_load_all_components( MSIPACKAGE *package )
970 static const WCHAR query[] = {
971 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
972 '`','C','o','m','p','o','n','e','n','t','`',0};
973 MSIQUERY *view;
974 UINT r;
976 if (!list_empty(&package->components))
977 return ERROR_SUCCESS;
979 r = MSI_DatabaseOpenViewW( package->db, query, &view );
980 if (r != ERROR_SUCCESS)
981 return r;
983 if (!msi_init_assembly_caches( package ))
985 ERR("can't initialize assembly caches\n");
986 msiobj_release( &view->hdr );
987 return ERROR_FUNCTION_FAILED;
990 r = MSI_IterateRecords(view, NULL, load_component, package);
991 msiobj_release(&view->hdr);
992 return r;
995 typedef struct {
996 MSIPACKAGE *package;
997 MSIFEATURE *feature;
998 } _ilfs;
1000 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1002 ComponentList *cl;
1004 cl = msi_alloc( sizeof (*cl) );
1005 if ( !cl )
1006 return ERROR_NOT_ENOUGH_MEMORY;
1007 cl->component = comp;
1008 list_add_tail( &feature->Components, &cl->entry );
1010 return ERROR_SUCCESS;
1013 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1015 FeatureList *fl;
1017 fl = msi_alloc( sizeof(*fl) );
1018 if ( !fl )
1019 return ERROR_NOT_ENOUGH_MEMORY;
1020 fl->feature = child;
1021 list_add_tail( &parent->Children, &fl->entry );
1023 return ERROR_SUCCESS;
1026 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1028 _ilfs* ilfs = param;
1029 LPCWSTR component;
1030 MSICOMPONENT *comp;
1032 component = MSI_RecordGetString(row,1);
1034 /* check to see if the component is already loaded */
1035 comp = msi_get_loaded_component( ilfs->package, component );
1036 if (!comp)
1038 WARN("ignoring unknown component %s\n", debugstr_w(component));
1039 return ERROR_SUCCESS;
1041 add_feature_component( ilfs->feature, comp );
1042 comp->Enabled = TRUE;
1044 return ERROR_SUCCESS;
1047 static UINT load_feature(MSIRECORD * row, LPVOID param)
1049 static const WCHAR query[] = {
1050 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1051 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1052 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1053 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1054 MSIPACKAGE *package = param;
1055 MSIFEATURE *feature;
1056 MSIQUERY *view;
1057 _ilfs ilfs;
1058 UINT rc;
1060 /* fill in the data */
1062 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1063 if (!feature)
1064 return ERROR_NOT_ENOUGH_MEMORY;
1066 list_init( &feature->Children );
1067 list_init( &feature->Components );
1069 feature->Feature = msi_dup_record_field( row, 1 );
1071 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1073 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1074 feature->Title = msi_dup_record_field( row, 3 );
1075 feature->Description = msi_dup_record_field( row, 4 );
1077 if (!MSI_RecordIsNull(row,5))
1078 feature->Display = MSI_RecordGetInteger(row,5);
1080 feature->Level= MSI_RecordGetInteger(row,6);
1081 feature->Directory = msi_dup_record_field( row, 7 );
1082 feature->Attributes = MSI_RecordGetInteger(row,8);
1084 feature->Installed = INSTALLSTATE_UNKNOWN;
1085 feature->Action = INSTALLSTATE_UNKNOWN;
1086 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1088 list_add_tail( &package->features, &feature->entry );
1090 /* load feature components */
1092 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1093 if (rc != ERROR_SUCCESS)
1094 return ERROR_SUCCESS;
1096 ilfs.package = package;
1097 ilfs.feature = feature;
1099 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1100 msiobj_release(&view->hdr);
1101 return rc;
1104 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1106 MSIPACKAGE *package = param;
1107 MSIFEATURE *parent, *child;
1109 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1110 if (!child)
1111 return ERROR_FUNCTION_FAILED;
1113 if (!child->Feature_Parent)
1114 return ERROR_SUCCESS;
1116 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1117 if (!parent)
1118 return ERROR_FUNCTION_FAILED;
1120 add_feature_child( parent, child );
1121 return ERROR_SUCCESS;
1124 UINT msi_load_all_features( MSIPACKAGE *package )
1126 static const WCHAR query[] = {
1127 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1128 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1129 '`','D','i','s','p','l','a','y','`',0};
1130 MSIQUERY *view;
1131 UINT r;
1133 if (!list_empty(&package->features))
1134 return ERROR_SUCCESS;
1136 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1137 if (r != ERROR_SUCCESS)
1138 return r;
1140 r = MSI_IterateRecords( view, NULL, load_feature, package );
1141 if (r != ERROR_SUCCESS)
1143 msiobj_release( &view->hdr );
1144 return r;
1146 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1147 msiobj_release( &view->hdr );
1148 return r;
1151 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1153 if (!p)
1154 return p;
1155 p = strchrW(p, ch);
1156 if (!p)
1157 return p;
1158 *p = 0;
1159 return p+1;
1162 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1164 static const WCHAR query[] = {
1165 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1166 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1167 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1168 MSIQUERY *view = NULL;
1169 MSIRECORD *row = NULL;
1170 UINT r;
1172 TRACE("%s\n", debugstr_w(file->File));
1174 r = MSI_OpenQuery(package->db, &view, query, file->File);
1175 if (r != ERROR_SUCCESS)
1176 goto done;
1178 r = MSI_ViewExecute(view, NULL);
1179 if (r != ERROR_SUCCESS)
1180 goto done;
1182 r = MSI_ViewFetch(view, &row);
1183 if (r != ERROR_SUCCESS)
1184 goto done;
1186 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1187 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1188 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1189 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1190 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1192 done:
1193 if (view) msiobj_release(&view->hdr);
1194 if (row) msiobj_release(&row->hdr);
1195 return r;
1198 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1200 MSIRECORD *row;
1201 static const WCHAR query[] = {
1202 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1203 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1204 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1206 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1207 if (!row)
1209 WARN("query failed\n");
1210 return ERROR_FUNCTION_FAILED;
1213 file->disk_id = MSI_RecordGetInteger( row, 1 );
1214 msiobj_release( &row->hdr );
1215 return ERROR_SUCCESS;
1218 static UINT load_file(MSIRECORD *row, LPVOID param)
1220 MSIPACKAGE* package = param;
1221 LPCWSTR component;
1222 MSIFILE *file;
1224 /* fill in the data */
1226 file = msi_alloc_zero( sizeof (MSIFILE) );
1227 if (!file)
1228 return ERROR_NOT_ENOUGH_MEMORY;
1230 file->File = msi_dup_record_field( row, 1 );
1232 component = MSI_RecordGetString( row, 2 );
1233 file->Component = msi_get_loaded_component( package, component );
1235 if (!file->Component)
1237 WARN("Component not found: %s\n", debugstr_w(component));
1238 msi_free(file->File);
1239 msi_free(file);
1240 return ERROR_SUCCESS;
1243 file->FileName = msi_dup_record_field( row, 3 );
1244 msi_reduce_to_long_filename( file->FileName );
1246 file->ShortName = msi_dup_record_field( row, 3 );
1247 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1249 file->FileSize = MSI_RecordGetInteger( row, 4 );
1250 file->Version = msi_dup_record_field( row, 5 );
1251 file->Language = msi_dup_record_field( row, 6 );
1252 file->Attributes = MSI_RecordGetInteger( row, 7 );
1253 file->Sequence = MSI_RecordGetInteger( row, 8 );
1255 file->state = msifs_invalid;
1257 /* if the compressed bits are not set in the file attributes,
1258 * then read the information from the package word count property
1260 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1262 file->IsCompressed = FALSE;
1264 else if (file->Attributes &
1265 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1267 file->IsCompressed = TRUE;
1269 else if (file->Attributes & msidbFileAttributesNoncompressed)
1271 file->IsCompressed = FALSE;
1273 else
1275 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1278 load_file_hash(package, file);
1279 load_file_disk_id(package, file);
1281 TRACE("File loaded (file %s sequence %u)\n", debugstr_w(file->File), file->Sequence);
1283 list_add_tail( &package->files, &file->entry );
1285 return ERROR_SUCCESS;
1288 static UINT load_all_files(MSIPACKAGE *package)
1290 static const WCHAR query[] = {
1291 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1292 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1293 '`','S','e','q','u','e','n','c','e','`', 0};
1294 MSIQUERY *view;
1295 UINT rc;
1297 if (!list_empty(&package->files))
1298 return ERROR_SUCCESS;
1300 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1301 if (rc != ERROR_SUCCESS)
1302 return ERROR_SUCCESS;
1304 rc = MSI_IterateRecords(view, NULL, load_file, package);
1305 msiobj_release(&view->hdr);
1306 return rc;
1309 static UINT load_media( MSIRECORD *row, LPVOID param )
1311 MSIPACKAGE *package = param;
1312 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1313 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1315 /* FIXME: load external cabinets and directory sources too */
1316 if (!cabinet || cabinet[0] != '#' || disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
1317 return ERROR_SUCCESS;
1319 return msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1322 static UINT load_all_media( MSIPACKAGE *package )
1324 static const WCHAR query[] = {
1325 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1326 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1327 '`','D','i','s','k','I','d','`',0};
1328 MSIQUERY *view;
1329 UINT r;
1331 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1332 if (r != ERROR_SUCCESS)
1333 return ERROR_SUCCESS;
1335 r = MSI_IterateRecords( view, NULL, load_media, package );
1336 msiobj_release( &view->hdr );
1337 return r;
1340 static UINT load_patch_disk_id( MSIPACKAGE *package, MSIFILEPATCH *patch )
1342 static const WCHAR query[] =
1343 {'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1344 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1345 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','u',0};
1346 MSIRECORD *rec;
1348 if (!(rec = MSI_QueryGetRecord( package->db, query, patch->Sequence )))
1350 WARN("query failed\n");
1351 return ERROR_FUNCTION_FAILED;
1354 patch->disk_id = MSI_RecordGetInteger( rec, 1 );
1355 msiobj_release( &rec->hdr );
1356 return ERROR_SUCCESS;
1359 static UINT load_patch(MSIRECORD *row, LPVOID param)
1361 MSIPACKAGE *package = param;
1362 MSIFILEPATCH *patch;
1363 const WCHAR *file_key;
1365 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1366 if (!patch)
1367 return ERROR_NOT_ENOUGH_MEMORY;
1369 file_key = MSI_RecordGetString( row, 1 );
1370 patch->File = msi_get_loaded_file( package, file_key );
1371 if (!patch->File)
1373 ERR("Failed to find target for patch in File table\n");
1374 msi_free(patch);
1375 return ERROR_FUNCTION_FAILED;
1378 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1379 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1380 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1382 /* FIXME:
1383 * Header field - for patch validation.
1384 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1387 load_patch_disk_id( package, patch );
1389 TRACE("Patch loaded (file %s sequence %u)\n", debugstr_w(patch->File->File), patch->Sequence);
1391 list_add_tail( &package->filepatches, &patch->entry );
1393 return ERROR_SUCCESS;
1396 static UINT load_all_patches(MSIPACKAGE *package)
1398 static const WCHAR query[] = {
1399 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1400 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1401 '`','S','e','q','u','e','n','c','e','`',0};
1402 MSIQUERY *view;
1403 UINT rc;
1405 if (!list_empty(&package->filepatches))
1406 return ERROR_SUCCESS;
1408 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1409 if (rc != ERROR_SUCCESS)
1410 return ERROR_SUCCESS;
1412 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1413 msiobj_release(&view->hdr);
1414 return rc;
1417 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1419 static const WCHAR query[] = {
1420 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1421 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1422 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1423 MSIQUERY *view;
1425 folder->persistent = FALSE;
1426 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1428 if (!MSI_ViewExecute( view, NULL ))
1430 MSIRECORD *rec;
1431 if (!MSI_ViewFetch( view, &rec ))
1433 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1434 folder->persistent = TRUE;
1435 msiobj_release( &rec->hdr );
1438 msiobj_release( &view->hdr );
1440 return ERROR_SUCCESS;
1443 static UINT load_folder( MSIRECORD *row, LPVOID param )
1445 MSIPACKAGE *package = param;
1446 static WCHAR szEmpty[] = { 0 };
1447 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1448 MSIFOLDER *folder;
1450 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1451 list_init( &folder->children );
1452 folder->Directory = msi_dup_record_field( row, 1 );
1453 folder->Parent = msi_dup_record_field( row, 2 );
1454 p = msi_dup_record_field(row, 3);
1456 TRACE("%s\n", debugstr_w(folder->Directory));
1458 /* split src and target dir */
1459 tgt_short = p;
1460 src_short = folder_split_path( p, ':' );
1462 /* split the long and short paths */
1463 tgt_long = folder_split_path( tgt_short, '|' );
1464 src_long = folder_split_path( src_short, '|' );
1466 /* check for no-op dirs */
1467 if (tgt_short && !strcmpW( szDot, tgt_short ))
1468 tgt_short = szEmpty;
1469 if (src_short && !strcmpW( szDot, src_short ))
1470 src_short = szEmpty;
1472 if (!tgt_long)
1473 tgt_long = tgt_short;
1475 if (!src_short) {
1476 src_short = tgt_short;
1477 src_long = tgt_long;
1480 if (!src_long)
1481 src_long = src_short;
1483 /* FIXME: use the target short path too */
1484 folder->TargetDefault = strdupW(tgt_long);
1485 folder->SourceShortPath = strdupW(src_short);
1486 folder->SourceLongPath = strdupW(src_long);
1487 msi_free(p);
1489 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1490 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1491 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1493 load_folder_persistence( package, folder );
1495 list_add_tail( &package->folders, &folder->entry );
1496 return ERROR_SUCCESS;
1499 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1501 FolderList *fl;
1503 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1504 fl->folder = child;
1505 list_add_tail( &parent->children, &fl->entry );
1506 return ERROR_SUCCESS;
1509 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1511 MSIPACKAGE *package = param;
1512 MSIFOLDER *parent, *child;
1514 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1515 return ERROR_FUNCTION_FAILED;
1517 if (!child->Parent) return ERROR_SUCCESS;
1519 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1520 return ERROR_FUNCTION_FAILED;
1522 return add_folder_child( parent, child );
1525 static UINT load_all_folders( MSIPACKAGE *package )
1527 static const WCHAR query[] = {
1528 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1529 '`','D','i','r','e','c','t','o','r','y','`',0};
1530 MSIQUERY *view;
1531 UINT r;
1533 if (!list_empty(&package->folders))
1534 return ERROR_SUCCESS;
1536 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1537 if (r != ERROR_SUCCESS)
1538 return r;
1540 r = MSI_IterateRecords( view, NULL, load_folder, package );
1541 if (r != ERROR_SUCCESS)
1543 msiobj_release( &view->hdr );
1544 return r;
1546 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1547 msiobj_release( &view->hdr );
1548 return r;
1551 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1553 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1554 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1556 load_all_folders( package );
1557 msi_load_all_components( package );
1558 msi_load_all_features( package );
1559 load_all_files( package );
1560 load_all_patches( package );
1561 load_all_media( package );
1563 return ERROR_SUCCESS;
1566 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1568 const WCHAR *action = package->script->Actions[script][index];
1569 ui_actionstart( package, action );
1570 TRACE("executing %s\n", debugstr_w(action));
1571 return ACTION_PerformAction( package, action, script );
1574 static UINT execute_script( MSIPACKAGE *package, UINT script )
1576 UINT i, rc = ERROR_SUCCESS;
1578 TRACE("executing script %u\n", script);
1580 if (!package->script)
1582 ERR("no script!\n");
1583 return ERROR_FUNCTION_FAILED;
1585 if (script == SCRIPT_ROLLBACK)
1587 for (i = package->script->ActionCount[script]; i > 0; i--)
1589 rc = execute_script_action( package, script, i - 1 );
1590 if (rc != ERROR_SUCCESS) break;
1593 else
1595 for (i = 0; i < package->script->ActionCount[script]; i++)
1597 rc = execute_script_action( package, script, i );
1598 if (rc != ERROR_SUCCESS) break;
1601 msi_free_action_script(package, script);
1602 return rc;
1605 static UINT ACTION_FileCost(MSIPACKAGE *package)
1607 return ERROR_SUCCESS;
1610 static void get_client_counts( MSIPACKAGE *package )
1612 MSICOMPONENT *comp;
1613 HKEY hkey;
1615 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1617 if (!comp->ComponentId) continue;
1619 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1620 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1622 comp->num_clients = 0;
1623 continue;
1625 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1626 NULL, NULL, NULL, NULL );
1627 RegCloseKey( hkey );
1631 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1633 MSICOMPONENT *comp;
1634 UINT r;
1636 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1638 if (!comp->ComponentId) continue;
1640 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1641 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1642 &comp->Installed );
1643 if (r == ERROR_SUCCESS) continue;
1645 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1646 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1647 &comp->Installed );
1648 if (r == ERROR_SUCCESS) continue;
1650 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1651 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1652 &comp->Installed );
1653 if (r == ERROR_SUCCESS) continue;
1655 comp->Installed = INSTALLSTATE_ABSENT;
1659 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1661 MSIFEATURE *feature;
1663 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1665 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1667 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1668 feature->Installed = INSTALLSTATE_ABSENT;
1669 else
1670 feature->Installed = state;
1674 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1676 return (feature->Level > 0 && feature->Level <= level);
1679 static BOOL process_state_property(MSIPACKAGE* package, int level,
1680 LPCWSTR property, INSTALLSTATE state)
1682 LPWSTR override;
1683 MSIFEATURE *feature;
1684 BOOL remove = !strcmpW(property, szRemove);
1685 BOOL reinstall = !strcmpW(property, szReinstall);
1687 override = msi_dup_property( package->db, property );
1688 if (!override)
1689 return FALSE;
1691 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1693 if (feature->Level <= 0)
1694 continue;
1696 if (reinstall)
1697 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1698 else if (remove)
1699 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1701 if (!strcmpiW( override, szAll ))
1703 feature->Action = state;
1704 feature->ActionRequest = state;
1706 else
1708 LPWSTR ptr = override;
1709 LPWSTR ptr2 = strchrW(override,',');
1711 while (ptr)
1713 int len = ptr2 - ptr;
1715 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1716 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1718 feature->Action = state;
1719 feature->ActionRequest = state;
1720 break;
1722 if (ptr2)
1724 ptr=ptr2+1;
1725 ptr2 = strchrW(ptr,',');
1727 else
1728 break;
1732 msi_free(override);
1733 return TRUE;
1736 static BOOL process_overrides( MSIPACKAGE *package, int level )
1738 static const WCHAR szAddLocal[] =
1739 {'A','D','D','L','O','C','A','L',0};
1740 static const WCHAR szAddSource[] =
1741 {'A','D','D','S','O','U','R','C','E',0};
1742 static const WCHAR szAdvertise[] =
1743 {'A','D','V','E','R','T','I','S','E',0};
1744 BOOL ret = FALSE;
1746 /* all these activation/deactivation things happen in order and things
1747 * later on the list override things earlier on the list.
1749 * 0 INSTALLLEVEL processing
1750 * 1 ADDLOCAL
1751 * 2 REMOVE
1752 * 3 ADDSOURCE
1753 * 4 ADDDEFAULT
1754 * 5 REINSTALL
1755 * 6 ADVERTISE
1756 * 7 COMPADDLOCAL
1757 * 8 COMPADDSOURCE
1758 * 9 FILEADDLOCAL
1759 * 10 FILEADDSOURCE
1760 * 11 FILEADDDEFAULT
1762 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1763 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1764 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1765 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1766 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1768 if (ret && !package->full_reinstall)
1769 msi_set_property( package->db, szPreselected, szOne, -1 );
1771 return ret;
1774 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1776 int level;
1777 MSICOMPONENT* component;
1778 MSIFEATURE *feature;
1780 TRACE("Checking Install Level\n");
1782 level = msi_get_property_int(package->db, szInstallLevel, 1);
1784 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1786 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1788 if (!is_feature_selected( feature, level )) continue;
1790 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1792 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1794 feature->Action = INSTALLSTATE_SOURCE;
1795 feature->ActionRequest = INSTALLSTATE_SOURCE;
1797 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1799 feature->Action = INSTALLSTATE_ADVERTISED;
1800 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1802 else
1804 feature->Action = INSTALLSTATE_LOCAL;
1805 feature->ActionRequest = INSTALLSTATE_LOCAL;
1809 /* disable child features of unselected parent or follow parent */
1810 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1812 FeatureList *fl;
1814 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1816 if (!is_feature_selected( feature, level ))
1818 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1819 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1821 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1823 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1824 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1825 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1826 fl->feature->Action = feature->Action;
1827 fl->feature->ActionRequest = feature->ActionRequest;
1832 else /* preselected */
1834 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1836 if (!is_feature_selected( feature, level )) continue;
1838 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1840 if (feature->Installed == INSTALLSTATE_ABSENT)
1842 feature->Action = INSTALLSTATE_UNKNOWN;
1843 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1845 else
1847 feature->Action = feature->Installed;
1848 feature->ActionRequest = feature->Installed;
1852 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1854 FeatureList *fl;
1856 if (!is_feature_selected( feature, level )) continue;
1858 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1860 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1861 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1863 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1864 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1865 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1866 fl->feature->Action = feature->Action;
1867 fl->feature->ActionRequest = feature->ActionRequest;
1873 /* now we want to set component state based based on feature state */
1874 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1876 ComponentList *cl;
1878 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1879 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1880 feature->ActionRequest, feature->Action);
1882 /* features with components that have compressed files are made local */
1883 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1885 if (cl->component->ForceLocalState &&
1886 feature->ActionRequest == INSTALLSTATE_SOURCE)
1888 feature->Action = INSTALLSTATE_LOCAL;
1889 feature->ActionRequest = INSTALLSTATE_LOCAL;
1890 break;
1894 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1896 component = cl->component;
1898 switch (feature->ActionRequest)
1900 case INSTALLSTATE_ABSENT:
1901 component->anyAbsent = 1;
1902 break;
1903 case INSTALLSTATE_ADVERTISED:
1904 component->hasAdvertisedFeature = 1;
1905 break;
1906 case INSTALLSTATE_SOURCE:
1907 component->hasSourceFeature = 1;
1908 break;
1909 case INSTALLSTATE_LOCAL:
1910 component->hasLocalFeature = 1;
1911 break;
1912 case INSTALLSTATE_DEFAULT:
1913 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1914 component->hasAdvertisedFeature = 1;
1915 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1916 component->hasSourceFeature = 1;
1917 else
1918 component->hasLocalFeature = 1;
1919 break;
1920 default:
1921 break;
1926 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1928 /* check if it's local or source */
1929 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1930 (component->hasLocalFeature || component->hasSourceFeature))
1932 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1933 !component->ForceLocalState)
1935 component->Action = INSTALLSTATE_SOURCE;
1936 component->ActionRequest = INSTALLSTATE_SOURCE;
1938 else
1940 component->Action = INSTALLSTATE_LOCAL;
1941 component->ActionRequest = INSTALLSTATE_LOCAL;
1943 continue;
1946 /* if any feature is local, the component must be local too */
1947 if (component->hasLocalFeature)
1949 component->Action = INSTALLSTATE_LOCAL;
1950 component->ActionRequest = INSTALLSTATE_LOCAL;
1951 continue;
1953 if (component->hasSourceFeature)
1955 component->Action = INSTALLSTATE_SOURCE;
1956 component->ActionRequest = INSTALLSTATE_SOURCE;
1957 continue;
1959 if (component->hasAdvertisedFeature)
1961 component->Action = INSTALLSTATE_ADVERTISED;
1962 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1963 continue;
1965 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1966 if (component->anyAbsent && component->ComponentId)
1968 component->Action = INSTALLSTATE_ABSENT;
1969 component->ActionRequest = INSTALLSTATE_ABSENT;
1973 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1975 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1977 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1978 component->Action = INSTALLSTATE_LOCAL;
1979 component->ActionRequest = INSTALLSTATE_LOCAL;
1982 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1983 component->Installed == INSTALLSTATE_SOURCE &&
1984 component->hasSourceFeature)
1986 component->Action = INSTALLSTATE_UNKNOWN;
1987 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1990 TRACE("component %s (installed %d request %d action %d)\n",
1991 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1993 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1994 component->num_clients++;
1995 else if (component->Action == INSTALLSTATE_ABSENT)
1996 component->num_clients--;
1999 return ERROR_SUCCESS;
2002 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2004 MSIPACKAGE *package = param;
2005 LPCWSTR name;
2006 MSIFEATURE *feature;
2008 name = MSI_RecordGetString( row, 1 );
2010 feature = msi_get_loaded_feature( package, name );
2011 if (!feature)
2012 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2013 else
2015 LPCWSTR Condition;
2016 Condition = MSI_RecordGetString(row,3);
2018 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2020 int level = MSI_RecordGetInteger(row,2);
2021 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2022 feature->Level = level;
2025 return ERROR_SUCCESS;
2028 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2030 static const WCHAR name[] = {'\\',0};
2031 VS_FIXEDFILEINFO *ptr, *ret;
2032 LPVOID version;
2033 DWORD versize, handle;
2034 UINT sz;
2036 versize = GetFileVersionInfoSizeW( filename, &handle );
2037 if (!versize)
2038 return NULL;
2040 version = msi_alloc( versize );
2041 if (!version)
2042 return NULL;
2044 GetFileVersionInfoW( filename, 0, versize, version );
2046 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2048 msi_free( version );
2049 return NULL;
2052 ret = msi_alloc( sz );
2053 memcpy( ret, ptr, sz );
2055 msi_free( version );
2056 return ret;
2059 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2061 DWORD ms, ls;
2063 msi_parse_version_string( version, &ms, &ls );
2065 if (fi->dwFileVersionMS > ms) return 1;
2066 else if (fi->dwFileVersionMS < ms) return -1;
2067 else if (fi->dwFileVersionLS > ls) return 1;
2068 else if (fi->dwFileVersionLS < ls) return -1;
2069 return 0;
2072 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2074 DWORD ms1, ms2;
2076 msi_parse_version_string( ver1, &ms1, NULL );
2077 msi_parse_version_string( ver2, &ms2, NULL );
2079 if (ms1 > ms2) return 1;
2080 else if (ms1 < ms2) return -1;
2081 return 0;
2084 DWORD msi_get_disk_file_size( LPCWSTR filename )
2086 HANDLE file;
2087 DWORD size;
2089 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2090 if (file == INVALID_HANDLE_VALUE)
2091 return INVALID_FILE_SIZE;
2093 size = GetFileSize( file, NULL );
2094 CloseHandle( file );
2095 return size;
2098 BOOL msi_file_hash_matches( MSIFILE *file )
2100 UINT r;
2101 MSIFILEHASHINFO hash;
2103 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2104 r = msi_get_filehash( file->TargetPath, &hash );
2105 if (r != ERROR_SUCCESS)
2106 return FALSE;
2108 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2111 static WCHAR *create_temp_dir( MSIDATABASE *db )
2113 static UINT id;
2114 WCHAR *ret;
2116 if (!db->tempfolder)
2118 WCHAR tmp[MAX_PATH];
2119 UINT len = sizeof(tmp)/sizeof(tmp[0]);
2121 if (msi_get_property( db, szTempFolder, tmp, &len ) ||
2122 GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY)
2124 GetTempPathW( MAX_PATH, tmp );
2126 if (!(db->tempfolder = strdupW( tmp ))) return NULL;
2129 if ((ret = msi_alloc( (strlenW( db->tempfolder ) + 20) * sizeof(WCHAR) )))
2131 for (;;)
2133 if (!GetTempFileNameW( db->tempfolder, szMsi, ++id, ret ))
2135 msi_free( ret );
2136 return NULL;
2138 if (CreateDirectoryW( ret, NULL )) break;
2142 return ret;
2146 * msi_build_directory_name()
2148 * This function is to save messing round with directory names
2149 * It handles adding backslashes between path segments,
2150 * and can add \ at the end of the directory name if told to.
2152 * It takes a variable number of arguments.
2153 * It always allocates a new string for the result, so make sure
2154 * to free the return value when finished with it.
2156 * The first arg is the number of path segments that follow.
2157 * The arguments following count are a list of path segments.
2158 * A path segment may be NULL.
2160 * Path segments will be added with a \ separating them.
2161 * A \ will not be added after the last segment, however if the
2162 * last segment is NULL, then the last character will be a \
2164 WCHAR *msi_build_directory_name( DWORD count, ... )
2166 DWORD sz = 1, i;
2167 WCHAR *dir;
2168 va_list va;
2170 va_start( va, count );
2171 for (i = 0; i < count; i++)
2173 const WCHAR *str = va_arg( va, const WCHAR * );
2174 if (str) sz += strlenW( str ) + 1;
2176 va_end( va );
2178 dir = msi_alloc( sz * sizeof(WCHAR) );
2179 dir[0] = 0;
2181 va_start( va, count );
2182 for (i = 0; i < count; i++)
2184 const WCHAR *str = va_arg( va, const WCHAR * );
2185 if (!str) continue;
2186 strcatW( dir, str );
2187 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2189 va_end( va );
2190 return dir;
2193 BOOL msi_is_global_assembly( MSICOMPONENT *comp )
2195 return comp->assembly && !comp->assembly->application;
2198 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2200 msi_free( file->TargetPath );
2201 if (msi_is_global_assembly( file->Component ))
2203 MSIASSEMBLY *assembly = file->Component->assembly;
2205 if (!assembly->tempdir) assembly->tempdir = create_temp_dir( package->db );
2206 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2208 else
2210 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2211 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2214 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2217 static UINT calculate_file_cost( MSIPACKAGE *package )
2219 VS_FIXEDFILEINFO *file_version;
2220 WCHAR *font_version;
2221 MSIFILE *file;
2223 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2225 MSICOMPONENT *comp = file->Component;
2226 DWORD file_size;
2228 if (!comp->Enabled) continue;
2230 if (file->IsCompressed)
2231 comp->ForceLocalState = TRUE;
2233 set_target_path( package, file );
2235 if ((comp->assembly && !comp->assembly->installed) ||
2236 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2238 comp->Cost += file->FileSize;
2239 continue;
2241 file_size = msi_get_disk_file_size( file->TargetPath );
2242 TRACE("%s (size %u)\n", debugstr_w(file->TargetPath), file_size);
2244 if (file->Version)
2246 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2248 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2250 comp->Cost += file->FileSize - file_size;
2252 msi_free( file_version );
2253 continue;
2255 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2257 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2259 comp->Cost += file->FileSize - file_size;
2261 msi_free( font_version );
2262 continue;
2265 if (file_size != file->FileSize)
2267 comp->Cost += file->FileSize - file_size;
2270 return ERROR_SUCCESS;
2273 WCHAR *msi_normalize_path( const WCHAR *in )
2275 const WCHAR *p = in;
2276 WCHAR *q, *ret;
2277 int n, len = strlenW( in ) + 2;
2279 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2281 len = 0;
2282 while (1)
2284 /* copy until the end of the string or a space */
2285 while (*p != ' ' && (*q = *p))
2287 p++, len++;
2288 /* reduce many backslashes to one */
2289 if (*p != '\\' || *q != '\\')
2290 q++;
2293 /* quit at the end of the string */
2294 if (!*p)
2295 break;
2297 /* count the number of spaces */
2298 n = 0;
2299 while (p[n] == ' ')
2300 n++;
2302 /* if it's leading or trailing space, skip it */
2303 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2304 p += n;
2305 else /* copy n spaces */
2306 while (n && (*q++ = *p++)) n--;
2308 while (q - ret > 0 && q[-1] == ' ') q--;
2309 if (q - ret > 0 && q[-1] != '\\')
2311 q[0] = '\\';
2312 q[1] = 0;
2314 return ret;
2317 static WCHAR *get_install_location( MSIPACKAGE *package )
2319 HKEY hkey;
2320 WCHAR *path;
2322 if (!package->ProductCode) return NULL;
2323 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2324 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2326 msi_free( path );
2327 path = NULL;
2329 RegCloseKey( hkey );
2330 return path;
2333 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2335 FolderList *fl;
2336 MSIFOLDER *folder, *parent, *child;
2337 WCHAR *path, *normalized_path;
2339 TRACE("resolving %s\n", debugstr_w(name));
2341 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2343 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2345 if (!(path = get_install_location( package )) &&
2346 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2348 path = msi_dup_property( package->db, szRootDrive );
2351 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2353 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2355 parent = msi_get_loaded_folder( package, folder->Parent );
2356 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2358 else
2359 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2361 normalized_path = msi_normalize_path( path );
2362 msi_free( path );
2363 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2365 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2366 msi_free( normalized_path );
2367 return;
2369 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2370 msi_free( folder->ResolvedTarget );
2371 folder->ResolvedTarget = normalized_path;
2373 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2375 child = fl->folder;
2376 msi_resolve_target_folder( package, child->Directory, load_prop );
2378 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2381 static ULONGLONG get_volume_space_required( MSIPACKAGE *package )
2383 MSICOMPONENT *comp;
2384 ULONGLONG ret = 0;
2386 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2388 if (comp->Action == INSTALLSTATE_LOCAL) ret += comp->Cost;
2390 return ret;
2393 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2395 static const WCHAR query[] =
2396 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2397 '`','C','o','n','d','i','t','i','o','n','`',0};
2398 static const WCHAR szOutOfDiskSpace[] =
2399 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2400 static const WCHAR szPrimaryFolder[] =
2401 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2402 static const WCHAR szPrimaryVolumePath[] =
2403 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2404 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2405 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2406 'A','v','a','i','l','a','b','l','e',0};
2407 static const WCHAR szPrimaryVolumeSpaceRequired[] =
2408 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2409 'R','e','q','u','i','r','e','d',0};
2410 static const WCHAR szPrimaryVolumeSpaceRemaining[] =
2411 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2412 'R','e','m','a','i','n','i','n','g',0};
2413 static const WCHAR szOutOfNoRbDiskSpace[] =
2414 {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0};
2415 MSICOMPONENT *comp;
2416 MSIQUERY *view;
2417 WCHAR *level, *primary_key, *primary_folder;
2418 UINT rc;
2420 TRACE("Building directory properties\n");
2421 msi_resolve_target_folder( package, szTargetDir, TRUE );
2423 TRACE("Evaluating component conditions\n");
2424 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2426 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2428 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2429 comp->Enabled = FALSE;
2431 else
2432 comp->Enabled = TRUE;
2434 get_client_counts( package );
2436 /* read components states from the registry */
2437 ACTION_GetComponentInstallStates(package);
2438 ACTION_GetFeatureInstallStates(package);
2440 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2442 TRACE("Evaluating feature conditions\n");
2444 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2445 if (rc == ERROR_SUCCESS)
2447 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2448 msiobj_release( &view->hdr );
2449 if (rc != ERROR_SUCCESS)
2450 return rc;
2454 TRACE("Calculating file cost\n");
2455 calculate_file_cost( package );
2457 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2458 /* set default run level if not set */
2459 level = msi_dup_property( package->db, szInstallLevel );
2460 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2461 msi_free(level);
2463 if ((rc = MSI_SetFeatureStates( package ))) return rc;
2465 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2467 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2469 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2470 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2472 static const WCHAR fmtW[] = {'%','l','u',0};
2473 ULARGE_INTEGER free;
2474 ULONGLONG required;
2475 WCHAR buf[21];
2477 primary_folder[2] = 0;
2478 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2480 sprintfW( buf, fmtW, free.QuadPart / 512 );
2481 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2483 required = get_volume_space_required( package );
2484 sprintfW( buf, fmtW, required / 512 );
2485 msi_set_property( package->db, szPrimaryVolumeSpaceRequired, buf, -1 );
2487 sprintfW( buf, fmtW, (free.QuadPart - required) / 512 );
2488 msi_set_property( package->db, szPrimaryVolumeSpaceRemaining, buf, -1 );
2489 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2491 msi_free( primary_folder );
2493 msi_free( primary_key );
2496 /* FIXME: check volume disk space */
2497 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2498 msi_set_property( package->db, szOutOfNoRbDiskSpace, szZero, -1 );
2500 return ERROR_SUCCESS;
2503 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size )
2505 BYTE *data;
2507 if (!value)
2509 *size = sizeof(WCHAR);
2510 *type = REG_SZ;
2511 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2512 return data;
2514 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2516 if (value[1]=='x')
2518 LPWSTR ptr;
2519 CHAR byte[5];
2520 LPWSTR deformated = NULL;
2521 int count;
2523 deformat_string(package, &value[2], &deformated);
2525 /* binary value type */
2526 ptr = deformated;
2527 *type = REG_BINARY;
2528 if (strlenW(ptr)%2)
2529 *size = (strlenW(ptr)/2)+1;
2530 else
2531 *size = strlenW(ptr)/2;
2533 data = msi_alloc(*size);
2535 byte[0] = '0';
2536 byte[1] = 'x';
2537 byte[4] = 0;
2538 count = 0;
2539 /* if uneven pad with a zero in front */
2540 if (strlenW(ptr)%2)
2542 byte[2]= '0';
2543 byte[3]= *ptr;
2544 ptr++;
2545 data[count] = (BYTE)strtol(byte,NULL,0);
2546 count ++;
2547 TRACE("Uneven byte count\n");
2549 while (*ptr)
2551 byte[2]= *ptr;
2552 ptr++;
2553 byte[3]= *ptr;
2554 ptr++;
2555 data[count] = (BYTE)strtol(byte,NULL,0);
2556 count ++;
2558 msi_free(deformated);
2560 TRACE("Data %i bytes(%i)\n",*size,count);
2562 else
2564 LPWSTR deformated;
2565 LPWSTR p;
2566 DWORD d = 0;
2567 deformat_string(package, &value[1], &deformated);
2569 *type=REG_DWORD;
2570 *size = sizeof(DWORD);
2571 data = msi_alloc(*size);
2572 p = deformated;
2573 if (*p == '-')
2574 p++;
2575 while (*p)
2577 if ( (*p < '0') || (*p > '9') )
2578 break;
2579 d *= 10;
2580 d += (*p - '0');
2581 p++;
2583 if (deformated[0] == '-')
2584 d = -d;
2585 *(LPDWORD)data = d;
2586 TRACE("DWORD %i\n",*(LPDWORD)data);
2588 msi_free(deformated);
2591 else
2593 const WCHAR *ptr = value;
2595 *type = REG_SZ;
2596 if (value[0] == '#')
2598 ptr++; len--;
2599 if (value[1] == '%')
2601 ptr++; len--;
2602 *type = REG_EXPAND_SZ;
2605 data = (BYTE *)msi_strdupW( ptr, len );
2606 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2607 *size = (len + 1) * sizeof(WCHAR);
2609 return data;
2612 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2614 const WCHAR *ret;
2616 switch (root)
2618 case -1:
2619 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2621 *root_key = HKEY_LOCAL_MACHINE;
2622 ret = szHLM;
2624 else
2626 *root_key = HKEY_CURRENT_USER;
2627 ret = szHCU;
2629 break;
2630 case 0:
2631 *root_key = HKEY_CLASSES_ROOT;
2632 ret = szHCR;
2633 break;
2634 case 1:
2635 *root_key = HKEY_CURRENT_USER;
2636 ret = szHCU;
2637 break;
2638 case 2:
2639 *root_key = HKEY_LOCAL_MACHINE;
2640 ret = szHLM;
2641 break;
2642 case 3:
2643 *root_key = HKEY_USERS;
2644 ret = szHU;
2645 break;
2646 default:
2647 ERR("Unknown root %i\n", root);
2648 return NULL;
2651 return ret;
2654 static inline REGSAM get_registry_view( const MSICOMPONENT *comp )
2656 REGSAM view = 0;
2657 if (is_wow64 || is_64bit)
2658 view |= (comp->Attributes & msidbComponentAttributes64bit) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
2659 return view;
2662 static HKEY open_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, BOOL create, REGSAM access )
2664 WCHAR *subkey, *p, *q;
2665 HKEY hkey, ret = NULL;
2666 LONG res;
2668 access |= get_registry_view( comp );
2670 if (!(subkey = strdupW( path ))) return NULL;
2671 p = subkey;
2672 if ((q = strchrW( p, '\\' ))) *q = 0;
2673 if (create)
2674 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2675 else
2676 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2677 if (res)
2679 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2680 msi_free( subkey );
2681 return NULL;
2683 if (q && q[1])
2685 ret = open_key( comp, hkey, q + 1, create, access );
2686 RegCloseKey( hkey );
2688 else ret = hkey;
2689 msi_free( subkey );
2690 return ret;
2693 static BOOL is_special_entry( const WCHAR *name )
2695 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2698 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2700 const WCHAR *p = str;
2701 WCHAR **ret;
2702 int i = 0;
2704 *count = 0;
2705 if (!str) return NULL;
2706 while ((p - str) < len)
2708 p += strlenW( p ) + 1;
2709 (*count)++;
2711 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2712 p = str;
2713 while ((p - str) < len)
2715 if (!(ret[i] = strdupW( p )))
2717 for (; i >= 0; i--) msi_free( ret[i] );
2718 msi_free( ret );
2719 return NULL;
2721 p += strlenW( p ) + 1;
2722 i++;
2724 return ret;
2727 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2728 WCHAR **right, DWORD right_count, DWORD *size )
2730 WCHAR *ret, *p;
2731 unsigned int i;
2733 *size = sizeof(WCHAR);
2734 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2735 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2737 if (!(ret = p = msi_alloc( *size ))) return NULL;
2739 for (i = 0; i < left_count; i++)
2741 strcpyW( p, left[i] );
2742 p += strlenW( p ) + 1;
2744 for (i = 0; i < right_count; i++)
2746 strcpyW( p, right[i] );
2747 p += strlenW( p ) + 1;
2749 *p = 0;
2750 return ret;
2753 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2754 WCHAR **new, DWORD new_count )
2756 DWORD ret = old_count;
2757 unsigned int i, j, k;
2759 for (i = 0; i < new_count; i++)
2761 for (j = 0; j < old_count; j++)
2763 if (old[j] && !strcmpW( new[i], old[j] ))
2765 msi_free( old[j] );
2766 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2767 old[k] = NULL;
2768 ret--;
2772 return ret;
2775 enum join_op
2777 JOIN_OP_APPEND,
2778 JOIN_OP_PREPEND,
2779 JOIN_OP_REPLACE
2782 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2783 WCHAR **new, DWORD new_count, DWORD *size )
2785 switch (op)
2787 case JOIN_OP_APPEND:
2788 old_count = remove_duplicate_values( old, old_count, new, new_count );
2789 return flatten_multi_string_values( old, old_count, new, new_count, size );
2791 case JOIN_OP_PREPEND:
2792 old_count = remove_duplicate_values( old, old_count, new, new_count );
2793 return flatten_multi_string_values( new, new_count, old, old_count, size );
2795 case JOIN_OP_REPLACE:
2796 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2798 default:
2799 ERR("unhandled join op %u\n", op);
2800 return NULL;
2804 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2805 BYTE *new_value, DWORD new_size, DWORD *size )
2807 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2808 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2809 enum join_op op = JOIN_OP_REPLACE;
2810 WCHAR **old = NULL, **new = NULL;
2811 BYTE *ret;
2813 if (new_size / sizeof(WCHAR) - 1 > 1)
2815 new_ptr = (const WCHAR *)new_value;
2816 new_len = new_size / sizeof(WCHAR) - 1;
2818 if (!new_ptr[0] && new_ptr[new_len - 1])
2820 op = JOIN_OP_APPEND;
2821 new_len--;
2822 new_ptr++;
2824 else if (new_ptr[0] && !new_ptr[new_len - 1])
2826 op = JOIN_OP_PREPEND;
2827 new_len--;
2829 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2831 op = JOIN_OP_REPLACE;
2832 new_len -= 2;
2833 new_ptr++;
2835 new = split_multi_string_values( new_ptr, new_len, &new_count );
2837 if (old_size / sizeof(WCHAR) - 1 > 1)
2839 old_ptr = (const WCHAR *)old_value;
2840 old_len = old_size / sizeof(WCHAR) - 1;
2841 old = split_multi_string_values( old_ptr, old_len, &old_count );
2843 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2844 for (i = 0; i < old_count; i++) msi_free( old[i] );
2845 for (i = 0; i < new_count; i++) msi_free( new[i] );
2846 msi_free( old );
2847 msi_free( new );
2848 return ret;
2851 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2853 BYTE *ret;
2854 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2855 if (!(ret = msi_alloc( *size ))) return NULL;
2856 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2857 return ret;
2860 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2862 MSIPACKAGE *package = param;
2863 BYTE *new_value, *old_value = NULL;
2864 HKEY root_key, hkey;
2865 DWORD type, old_type, new_size, old_size = 0;
2866 LPWSTR deformated, uikey;
2867 const WCHAR *szRoot, *component, *name, *key, *str;
2868 MSICOMPONENT *comp;
2869 MSIRECORD * uirow;
2870 INT root;
2871 BOOL check_first = FALSE;
2872 int len;
2874 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2876 component = MSI_RecordGetString(row, 6);
2877 comp = msi_get_loaded_component(package,component);
2878 if (!comp)
2879 return ERROR_SUCCESS;
2881 comp->Action = msi_get_component_action( package, comp );
2882 if (comp->Action != INSTALLSTATE_LOCAL)
2884 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2885 return ERROR_SUCCESS;
2888 name = MSI_RecordGetString(row, 4);
2889 if( MSI_RecordIsNull(row,5) && name )
2891 /* null values can have special meanings */
2892 if (name[0]=='-' && name[1] == 0)
2893 return ERROR_SUCCESS;
2894 if ((name[0] == '+' || name[0] == '*') && !name[1])
2895 check_first = TRUE;
2898 root = MSI_RecordGetInteger(row,2);
2899 key = MSI_RecordGetString(row, 3);
2901 szRoot = get_root_key( package, root, &root_key );
2902 if (!szRoot)
2903 return ERROR_SUCCESS;
2905 deformat_string(package, key , &deformated);
2906 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2907 strcpyW(uikey,szRoot);
2908 strcatW(uikey,deformated);
2910 if (!(hkey = open_key( comp, root_key, deformated, TRUE, KEY_QUERY_VALUE | KEY_SET_VALUE )))
2912 ERR("Could not create key %s\n", debugstr_w(deformated));
2913 msi_free(uikey);
2914 msi_free(deformated);
2915 return ERROR_FUNCTION_FAILED;
2917 msi_free( deformated );
2918 str = msi_record_get_string( row, 5, NULL );
2919 len = deformat_string( package, str, &deformated );
2920 new_value = parse_value( package, deformated, len, &type, &new_size );
2922 msi_free( deformated );
2923 deformat_string(package, name, &deformated);
2925 if (!is_special_entry( name ))
2927 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2928 if (type == REG_MULTI_SZ)
2930 BYTE *new;
2931 if (old_value && old_type != REG_MULTI_SZ)
2933 msi_free( old_value );
2934 old_value = NULL;
2935 old_size = 0;
2937 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2938 msi_free( new_value );
2939 new_value = new;
2941 if (!check_first)
2943 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2944 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2946 else if (!old_value)
2948 if (deformated || new_size)
2950 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2951 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2954 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2956 RegCloseKey(hkey);
2958 uirow = MSI_CreateRecord(3);
2959 MSI_RecordSetStringW(uirow,2,deformated);
2960 MSI_RecordSetStringW(uirow,1,uikey);
2961 if (type == REG_SZ || type == REG_EXPAND_SZ)
2962 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2963 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2964 msiobj_release( &uirow->hdr );
2966 msi_free(new_value);
2967 msi_free(old_value);
2968 msi_free(deformated);
2969 msi_free(uikey);
2971 return ERROR_SUCCESS;
2974 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2976 static const WCHAR query[] = {
2977 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2978 '`','R','e','g','i','s','t','r','y','`',0};
2979 MSIQUERY *view;
2980 UINT rc;
2982 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2983 if (rc != ERROR_SUCCESS)
2984 return ERROR_SUCCESS;
2986 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2987 msiobj_release(&view->hdr);
2988 return rc;
2991 static void delete_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2993 REGSAM access = 0;
2994 WCHAR *subkey, *p;
2995 HKEY hkey;
2996 LONG res;
2998 access |= get_registry_view( comp );
3000 if (!(subkey = strdupW( path ))) return;
3003 if ((p = strrchrW( subkey, '\\' )))
3005 *p = 0;
3006 if (!p[1]) continue; /* trailing backslash */
3007 hkey = open_key( comp, root, subkey, FALSE, access );
3008 if (!hkey) break;
3009 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
3010 RegCloseKey( hkey );
3012 else
3013 res = RegDeleteKeyExW( root, subkey, access, 0 );
3014 if (res)
3016 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
3017 break;
3019 } while (p);
3020 msi_free( subkey );
3023 static void delete_value( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, const WCHAR *value )
3025 LONG res;
3026 HKEY hkey;
3027 DWORD num_subkeys, num_values;
3029 if ((hkey = open_key( comp, root, path, FALSE, KEY_SET_VALUE | KEY_QUERY_VALUE )))
3031 if ((res = RegDeleteValueW( hkey, value )))
3032 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
3034 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
3035 NULL, NULL, NULL, NULL );
3036 RegCloseKey( hkey );
3037 if (!res && !num_subkeys && !num_values)
3039 TRACE("removing empty key %s\n", debugstr_w(path));
3040 delete_key( comp, root, path );
3045 static void delete_tree( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
3047 LONG res;
3048 HKEY hkey;
3050 if (!(hkey = open_key( comp, root, path, FALSE, KEY_ALL_ACCESS ))) return;
3051 res = RegDeleteTreeW( hkey, NULL );
3052 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3053 delete_key( comp, root, path );
3054 RegCloseKey( hkey );
3057 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3059 MSIPACKAGE *package = param;
3060 LPCWSTR component, name, key_str, root_key_str;
3061 LPWSTR deformated_key, deformated_name, ui_key_str;
3062 MSICOMPONENT *comp;
3063 MSIRECORD *uirow;
3064 BOOL delete_key = FALSE;
3065 HKEY hkey_root;
3066 UINT size;
3067 INT root;
3069 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3071 component = MSI_RecordGetString( row, 6 );
3072 comp = msi_get_loaded_component( package, component );
3073 if (!comp)
3074 return ERROR_SUCCESS;
3076 comp->Action = msi_get_component_action( package, comp );
3077 if (comp->Action != INSTALLSTATE_ABSENT)
3079 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3080 return ERROR_SUCCESS;
3083 name = MSI_RecordGetString( row, 4 );
3084 if (MSI_RecordIsNull( row, 5 ) && name )
3086 if (name[0] == '+' && !name[1])
3087 return ERROR_SUCCESS;
3088 if ((name[0] == '-' || name[0] == '*') && !name[1])
3090 delete_key = TRUE;
3091 name = NULL;
3095 root = MSI_RecordGetInteger( row, 2 );
3096 key_str = MSI_RecordGetString( row, 3 );
3098 root_key_str = get_root_key( package, root, &hkey_root );
3099 if (!root_key_str)
3100 return ERROR_SUCCESS;
3102 deformat_string( package, key_str, &deformated_key );
3103 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3104 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3105 strcpyW( ui_key_str, root_key_str );
3106 strcatW( ui_key_str, deformated_key );
3108 deformat_string( package, name, &deformated_name );
3110 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3111 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3112 msi_free( deformated_key );
3114 uirow = MSI_CreateRecord( 2 );
3115 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3116 MSI_RecordSetStringW( uirow, 2, deformated_name );
3117 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3118 msiobj_release( &uirow->hdr );
3120 msi_free( ui_key_str );
3121 msi_free( deformated_name );
3122 return ERROR_SUCCESS;
3125 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3127 MSIPACKAGE *package = param;
3128 LPCWSTR component, name, key_str, root_key_str;
3129 LPWSTR deformated_key, deformated_name, ui_key_str;
3130 MSICOMPONENT *comp;
3131 MSIRECORD *uirow;
3132 BOOL delete_key = FALSE;
3133 HKEY hkey_root;
3134 UINT size;
3135 INT root;
3137 component = MSI_RecordGetString( row, 5 );
3138 comp = msi_get_loaded_component( package, component );
3139 if (!comp)
3140 return ERROR_SUCCESS;
3142 comp->Action = msi_get_component_action( package, comp );
3143 if (comp->Action != INSTALLSTATE_LOCAL)
3145 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3146 return ERROR_SUCCESS;
3149 if ((name = MSI_RecordGetString( row, 4 )))
3151 if (name[0] == '-' && !name[1])
3153 delete_key = TRUE;
3154 name = NULL;
3158 root = MSI_RecordGetInteger( row, 2 );
3159 key_str = MSI_RecordGetString( row, 3 );
3161 root_key_str = get_root_key( package, root, &hkey_root );
3162 if (!root_key_str)
3163 return ERROR_SUCCESS;
3165 deformat_string( package, key_str, &deformated_key );
3166 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3167 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3168 strcpyW( ui_key_str, root_key_str );
3169 strcatW( ui_key_str, deformated_key );
3171 deformat_string( package, name, &deformated_name );
3173 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3174 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3175 msi_free( deformated_key );
3177 uirow = MSI_CreateRecord( 2 );
3178 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3179 MSI_RecordSetStringW( uirow, 2, deformated_name );
3180 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3181 msiobj_release( &uirow->hdr );
3183 msi_free( ui_key_str );
3184 msi_free( deformated_name );
3185 return ERROR_SUCCESS;
3188 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3190 static const WCHAR registry_query[] = {
3191 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3192 '`','R','e','g','i','s','t','r','y','`',0};
3193 static const WCHAR remove_registry_query[] = {
3194 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3195 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3196 MSIQUERY *view;
3197 UINT rc;
3199 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3200 if (rc == ERROR_SUCCESS)
3202 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3203 msiobj_release( &view->hdr );
3204 if (rc != ERROR_SUCCESS)
3205 return rc;
3207 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3208 if (rc == ERROR_SUCCESS)
3210 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3211 msiobj_release( &view->hdr );
3212 if (rc != ERROR_SUCCESS)
3213 return rc;
3215 return ERROR_SUCCESS;
3218 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3220 return ERROR_SUCCESS;
3224 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3226 static const WCHAR query[]= {
3227 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3228 '`','R','e','g','i','s','t','r','y','`',0};
3229 MSICOMPONENT *comp;
3230 DWORD total = 0, count = 0;
3231 MSIQUERY *view;
3232 MSIFEATURE *feature;
3233 MSIFILE *file;
3234 UINT rc;
3236 TRACE("InstallValidate\n");
3238 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3239 if (rc == ERROR_SUCCESS)
3241 rc = MSI_IterateRecords( view, &count, NULL, package );
3242 msiobj_release( &view->hdr );
3243 if (rc != ERROR_SUCCESS)
3244 return rc;
3245 total += count * REG_PROGRESS_VALUE;
3247 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3248 total += COMPONENT_PROGRESS_VALUE;
3250 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3251 total += file->FileSize;
3253 msi_ui_progress( package, 0, total, 0, 0 );
3255 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3257 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3258 debugstr_w(feature->Feature), feature->Installed,
3259 feature->ActionRequest, feature->Action);
3261 return ERROR_SUCCESS;
3264 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3266 MSIPACKAGE* package = param;
3267 LPCWSTR cond = NULL;
3268 LPCWSTR message = NULL;
3269 UINT r;
3271 static const WCHAR title[]=
3272 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3274 cond = MSI_RecordGetString(row,1);
3276 r = MSI_EvaluateConditionW(package,cond);
3277 if (r == MSICONDITION_FALSE)
3279 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3281 LPWSTR deformated;
3282 message = MSI_RecordGetString(row,2);
3283 deformat_string(package,message,&deformated);
3284 MessageBoxW(NULL,deformated,title,MB_OK);
3285 msi_free(deformated);
3288 return ERROR_INSTALL_FAILURE;
3291 return ERROR_SUCCESS;
3294 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3296 static const WCHAR query[] = {
3297 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3298 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3299 MSIQUERY *view;
3300 UINT rc;
3302 TRACE("Checking launch conditions\n");
3304 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3305 if (rc != ERROR_SUCCESS)
3306 return ERROR_SUCCESS;
3308 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3309 msiobj_release(&view->hdr);
3310 return rc;
3313 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3316 if (!cmp->KeyPath)
3317 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3319 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3321 static const WCHAR query[] = {
3322 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3323 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3324 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3325 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3326 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3327 MSIRECORD *row;
3328 UINT root, len;
3329 LPWSTR deformated, buffer, deformated_name;
3330 LPCWSTR key, name;
3332 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3333 if (!row)
3334 return NULL;
3336 root = MSI_RecordGetInteger(row,2);
3337 key = MSI_RecordGetString(row, 3);
3338 name = MSI_RecordGetString(row, 4);
3339 deformat_string(package, key , &deformated);
3340 deformat_string(package, name, &deformated_name);
3342 len = strlenW(deformated) + 6;
3343 if (deformated_name)
3344 len+=strlenW(deformated_name);
3346 buffer = msi_alloc( len *sizeof(WCHAR));
3348 if (deformated_name)
3349 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3350 else
3351 sprintfW(buffer,fmt,root,deformated);
3353 msi_free(deformated);
3354 msi_free(deformated_name);
3355 msiobj_release(&row->hdr);
3357 return buffer;
3359 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3361 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3362 return NULL;
3364 else
3366 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3368 if (file)
3369 return strdupW( file->TargetPath );
3371 return NULL;
3374 static HKEY openSharedDLLsKey(void)
3376 HKEY hkey=0;
3377 static const WCHAR path[] =
3378 {'S','o','f','t','w','a','r','e','\\',
3379 'M','i','c','r','o','s','o','f','t','\\',
3380 'W','i','n','d','o','w','s','\\',
3381 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3382 'S','h','a','r','e','d','D','L','L','s',0};
3384 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3385 return hkey;
3388 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3390 HKEY hkey;
3391 DWORD count=0;
3392 DWORD type;
3393 DWORD sz = sizeof(count);
3394 DWORD rc;
3396 hkey = openSharedDLLsKey();
3397 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3398 if (rc != ERROR_SUCCESS)
3399 count = 0;
3400 RegCloseKey(hkey);
3401 return count;
3404 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3406 HKEY hkey;
3408 hkey = openSharedDLLsKey();
3409 if (count > 0)
3410 msi_reg_set_val_dword( hkey, path, count );
3411 else
3412 RegDeleteValueW(hkey,path);
3413 RegCloseKey(hkey);
3414 return count;
3417 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3419 MSIFEATURE *feature;
3420 INT count = 0;
3421 BOOL write = FALSE;
3423 /* only refcount DLLs */
3424 if (comp->KeyPath == NULL ||
3425 comp->assembly ||
3426 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3427 comp->Attributes & msidbComponentAttributesODBCDataSource)
3428 write = FALSE;
3429 else
3431 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3432 write = (count > 0);
3434 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3435 write = TRUE;
3438 /* increment counts */
3439 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3441 ComponentList *cl;
3443 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3444 continue;
3446 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3448 if ( cl->component == comp )
3449 count++;
3453 /* decrement counts */
3454 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3456 ComponentList *cl;
3458 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3459 continue;
3461 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3463 if ( cl->component == comp )
3464 count--;
3468 /* ref count all the files in the component */
3469 if (write)
3471 MSIFILE *file;
3473 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3475 if (file->Component == comp)
3476 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3480 /* add a count for permanent */
3481 if (comp->Attributes & msidbComponentAttributesPermanent)
3482 count ++;
3484 comp->RefCount = count;
3486 if (write)
3487 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3490 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3492 if (comp->assembly)
3494 const WCHAR prefixW[] = {'<','\\',0};
3495 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3496 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3498 if (keypath)
3500 strcpyW( keypath, prefixW );
3501 strcatW( keypath, comp->assembly->display_name );
3503 return keypath;
3505 return resolve_keypath( package, comp );
3508 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3510 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3511 UINT rc;
3512 MSICOMPONENT *comp;
3513 HKEY hkey;
3515 TRACE("\n");
3517 squash_guid(package->ProductCode,squished_pc);
3518 msi_set_sourcedir_props(package, FALSE);
3520 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3522 MSIRECORD *uirow;
3523 INSTALLSTATE action;
3525 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3526 if (!comp->ComponentId)
3527 continue;
3529 squash_guid( comp->ComponentId, squished_cc );
3530 msi_free( comp->FullKeypath );
3531 comp->FullKeypath = build_full_keypath( package, comp );
3533 ACTION_RefCountComponent( package, comp );
3535 if (package->need_rollback) action = comp->Installed;
3536 else action = comp->ActionRequest;
3538 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3539 debugstr_w(comp->Component), debugstr_w(squished_cc),
3540 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3542 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3544 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3545 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3546 else
3547 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3549 if (rc != ERROR_SUCCESS)
3550 continue;
3552 if (comp->Attributes & msidbComponentAttributesPermanent)
3554 static const WCHAR szPermKey[] =
3555 { '0','0','0','0','0','0','0','0','0','0','0','0',
3556 '0','0','0','0','0','0','0','0','0','0','0','0',
3557 '0','0','0','0','0','0','0','0',0 };
3559 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3561 if (action == INSTALLSTATE_LOCAL)
3562 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3563 else
3565 MSIFILE *file;
3566 MSIRECORD *row;
3567 LPWSTR ptr, ptr2;
3568 WCHAR source[MAX_PATH];
3569 WCHAR base[MAX_PATH];
3570 LPWSTR sourcepath;
3572 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3573 static const WCHAR query[] = {
3574 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3575 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3576 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3577 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3578 '`','D','i','s','k','I','d','`',0};
3580 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3581 continue;
3583 if (!(row = MSI_QueryGetRecord(package->db, query, file->Sequence)))
3584 return ERROR_FUNCTION_FAILED;
3586 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3587 ptr2 = strrchrW(source, '\\') + 1;
3588 msiobj_release(&row->hdr);
3590 lstrcpyW(base, package->PackagePath);
3591 ptr = strrchrW(base, '\\');
3592 *(ptr + 1) = '\0';
3594 sourcepath = msi_resolve_file_source(package, file);
3595 ptr = sourcepath + lstrlenW(base);
3596 lstrcpyW(ptr2, ptr);
3597 msi_free(sourcepath);
3599 msi_reg_set_val_str(hkey, squished_pc, source);
3601 RegCloseKey(hkey);
3603 else if (action == INSTALLSTATE_ABSENT)
3605 if (comp->num_clients <= 0)
3607 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3608 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3609 else
3610 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3612 if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
3614 else
3616 LONG res;
3618 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3619 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE );
3620 else
3621 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );
3623 if (rc != ERROR_SUCCESS)
3625 WARN( "failed to open component key %u\n", rc );
3626 continue;
3628 res = RegDeleteValueW( hkey, squished_pc );
3629 RegCloseKey(hkey);
3630 if (res) WARN( "failed to delete component value %d\n", res );
3634 /* UI stuff */
3635 uirow = MSI_CreateRecord(3);
3636 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3637 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3638 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3639 msi_ui_actiondata( package, szProcessComponents, uirow );
3640 msiobj_release( &uirow->hdr );
3642 return ERROR_SUCCESS;
3645 typedef struct {
3646 CLSID clsid;
3647 LPWSTR source;
3649 LPWSTR path;
3650 ITypeLib *ptLib;
3651 } typelib_struct;
3653 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3654 LPWSTR lpszName, LONG_PTR lParam)
3656 TLIBATTR *attr;
3657 typelib_struct *tl_struct = (typelib_struct*) lParam;
3658 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3659 int sz;
3660 HRESULT res;
3662 if (!IS_INTRESOURCE(lpszName))
3664 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3665 return TRUE;
3668 sz = strlenW(tl_struct->source)+4;
3669 sz *= sizeof(WCHAR);
3671 if ((INT_PTR)lpszName == 1)
3672 tl_struct->path = strdupW(tl_struct->source);
3673 else
3675 tl_struct->path = msi_alloc(sz);
3676 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3679 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3680 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3681 if (FAILED(res))
3683 msi_free(tl_struct->path);
3684 tl_struct->path = NULL;
3686 return TRUE;
3689 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3690 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3692 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3693 return FALSE;
3696 msi_free(tl_struct->path);
3697 tl_struct->path = NULL;
3699 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3700 ITypeLib_Release(tl_struct->ptLib);
3702 return TRUE;
3705 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3707 MSIPACKAGE* package = param;
3708 LPCWSTR component;
3709 MSICOMPONENT *comp;
3710 MSIFILE *file;
3711 typelib_struct tl_struct;
3712 ITypeLib *tlib;
3713 HMODULE module;
3714 HRESULT hr;
3716 component = MSI_RecordGetString(row,3);
3717 comp = msi_get_loaded_component(package,component);
3718 if (!comp)
3719 return ERROR_SUCCESS;
3721 comp->Action = msi_get_component_action( package, comp );
3722 if (comp->Action != INSTALLSTATE_LOCAL)
3724 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3725 return ERROR_SUCCESS;
3728 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3730 TRACE("component has no key path\n");
3731 return ERROR_SUCCESS;
3733 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3735 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3736 if (module)
3738 LPCWSTR guid;
3739 guid = MSI_RecordGetString(row,1);
3740 CLSIDFromString( guid, &tl_struct.clsid);
3741 tl_struct.source = strdupW( file->TargetPath );
3742 tl_struct.path = NULL;
3744 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3745 (LONG_PTR)&tl_struct);
3747 if (tl_struct.path)
3749 LPCWSTR helpid, help_path = NULL;
3750 HRESULT res;
3752 helpid = MSI_RecordGetString(row,6);
3754 if (helpid) help_path = msi_get_target_folder( package, helpid );
3755 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3757 if (FAILED(res))
3758 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3759 else
3760 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3762 ITypeLib_Release(tl_struct.ptLib);
3763 msi_free(tl_struct.path);
3765 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3767 FreeLibrary(module);
3768 msi_free(tl_struct.source);
3770 else
3772 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3773 if (FAILED(hr))
3775 ERR("Failed to load type library: %08x\n", hr);
3776 return ERROR_INSTALL_FAILURE;
3779 ITypeLib_Release(tlib);
3782 return ERROR_SUCCESS;
3785 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3787 static const WCHAR query[] = {
3788 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3789 '`','T','y','p','e','L','i','b','`',0};
3790 MSIQUERY *view;
3791 UINT rc;
3793 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3794 if (rc != ERROR_SUCCESS)
3795 return ERROR_SUCCESS;
3797 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3798 msiobj_release(&view->hdr);
3799 return rc;
3802 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3804 MSIPACKAGE *package = param;
3805 LPCWSTR component, guid;
3806 MSICOMPONENT *comp;
3807 GUID libid;
3808 UINT version;
3809 LCID language;
3810 SYSKIND syskind;
3811 HRESULT hr;
3813 component = MSI_RecordGetString( row, 3 );
3814 comp = msi_get_loaded_component( package, component );
3815 if (!comp)
3816 return ERROR_SUCCESS;
3818 comp->Action = msi_get_component_action( package, comp );
3819 if (comp->Action != INSTALLSTATE_ABSENT)
3821 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3822 return ERROR_SUCCESS;
3824 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3826 guid = MSI_RecordGetString( row, 1 );
3827 CLSIDFromString( guid, &libid );
3828 version = MSI_RecordGetInteger( row, 4 );
3829 language = MSI_RecordGetInteger( row, 2 );
3831 #ifdef _WIN64
3832 syskind = SYS_WIN64;
3833 #else
3834 syskind = SYS_WIN32;
3835 #endif
3837 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3838 if (FAILED(hr))
3840 WARN("Failed to unregister typelib: %08x\n", hr);
3843 return ERROR_SUCCESS;
3846 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3848 static const WCHAR query[] = {
3849 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3850 '`','T','y','p','e','L','i','b','`',0};
3851 MSIQUERY *view;
3852 UINT rc;
3854 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3855 if (rc != ERROR_SUCCESS)
3856 return ERROR_SUCCESS;
3858 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3859 msiobj_release( &view->hdr );
3860 return rc;
3863 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3865 static const WCHAR szlnk[] = {'.','l','n','k',0};
3866 LPCWSTR directory, extension, link_folder;
3867 LPWSTR link_file, filename;
3869 directory = MSI_RecordGetString( row, 2 );
3870 link_folder = msi_get_target_folder( package, directory );
3871 if (!link_folder)
3873 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3874 return NULL;
3876 /* may be needed because of a bug somewhere else */
3877 msi_create_full_path( link_folder );
3879 filename = msi_dup_record_field( row, 3 );
3880 msi_reduce_to_long_filename( filename );
3882 extension = strrchrW( filename, '.' );
3883 if (!extension || strcmpiW( extension, szlnk ))
3885 int len = strlenW( filename );
3886 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3887 memcpy( filename + len, szlnk, sizeof(szlnk) );
3889 link_file = msi_build_directory_name( 2, link_folder, filename );
3890 msi_free( filename );
3892 return link_file;
3895 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3897 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3898 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3899 WCHAR *folder, *dest, *path;
3901 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3902 folder = msi_dup_property( package->db, szWindowsFolder );
3903 else
3905 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3906 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3907 msi_free( appdata );
3909 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3910 msi_create_full_path( dest );
3911 path = msi_build_directory_name( 2, dest, icon_name );
3912 msi_free( folder );
3913 msi_free( dest );
3914 return path;
3917 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3919 MSIPACKAGE *package = param;
3920 LPWSTR link_file, deformated, path;
3921 LPCWSTR component, target;
3922 MSICOMPONENT *comp;
3923 IShellLinkW *sl = NULL;
3924 IPersistFile *pf = NULL;
3925 HRESULT res;
3927 component = MSI_RecordGetString(row, 4);
3928 comp = msi_get_loaded_component(package, component);
3929 if (!comp)
3930 return ERROR_SUCCESS;
3932 comp->Action = msi_get_component_action( package, comp );
3933 if (comp->Action != INSTALLSTATE_LOCAL)
3935 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3936 return ERROR_SUCCESS;
3938 msi_ui_actiondata( package, szCreateShortcuts, row );
3940 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3941 &IID_IShellLinkW, (LPVOID *) &sl );
3943 if (FAILED( res ))
3945 ERR("CLSID_ShellLink not available\n");
3946 goto err;
3949 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3950 if (FAILED( res ))
3952 ERR("QueryInterface(IID_IPersistFile) failed\n");
3953 goto err;
3956 target = MSI_RecordGetString(row, 5);
3957 if (strchrW(target, '['))
3959 deformat_string( package, target, &path );
3960 TRACE("target path is %s\n", debugstr_w(path));
3961 IShellLinkW_SetPath( sl, path );
3962 msi_free( path );
3964 else
3966 FIXME("poorly handled shortcut format, advertised shortcut\n");
3967 path = resolve_keypath( package, comp );
3968 IShellLinkW_SetPath( sl, path );
3969 msi_free( path );
3972 if (!MSI_RecordIsNull(row,6))
3974 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3975 deformat_string(package, arguments, &deformated);
3976 IShellLinkW_SetArguments(sl,deformated);
3977 msi_free(deformated);
3980 if (!MSI_RecordIsNull(row,7))
3982 LPCWSTR description = MSI_RecordGetString(row, 7);
3983 IShellLinkW_SetDescription(sl, description);
3986 if (!MSI_RecordIsNull(row,8))
3987 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3989 if (!MSI_RecordIsNull(row,9))
3991 INT index;
3992 LPCWSTR icon = MSI_RecordGetString(row, 9);
3994 path = msi_build_icon_path(package, icon);
3995 index = MSI_RecordGetInteger(row,10);
3997 /* no value means 0 */
3998 if (index == MSI_NULL_INTEGER)
3999 index = 0;
4001 IShellLinkW_SetIconLocation(sl, path, index);
4002 msi_free(path);
4005 if (!MSI_RecordIsNull(row,11))
4006 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
4008 if (!MSI_RecordIsNull(row,12))
4010 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
4011 full_path = msi_get_target_folder( package, wkdir );
4012 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
4014 link_file = get_link_file(package, row);
4016 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
4017 IPersistFile_Save(pf, link_file, FALSE);
4018 msi_free(link_file);
4020 err:
4021 if (pf)
4022 IPersistFile_Release( pf );
4023 if (sl)
4024 IShellLinkW_Release( sl );
4026 return ERROR_SUCCESS;
4029 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
4031 static const WCHAR query[] = {
4032 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4033 '`','S','h','o','r','t','c','u','t','`',0};
4034 MSIQUERY *view;
4035 HRESULT res;
4036 UINT rc;
4038 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4039 if (rc != ERROR_SUCCESS)
4040 return ERROR_SUCCESS;
4042 res = CoInitialize( NULL );
4044 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4045 msiobj_release(&view->hdr);
4047 if (SUCCEEDED(res)) CoUninitialize();
4048 return rc;
4051 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4053 MSIPACKAGE *package = param;
4054 LPWSTR link_file;
4055 LPCWSTR component;
4056 MSICOMPONENT *comp;
4058 component = MSI_RecordGetString( row, 4 );
4059 comp = msi_get_loaded_component( package, component );
4060 if (!comp)
4061 return ERROR_SUCCESS;
4063 comp->Action = msi_get_component_action( package, comp );
4064 if (comp->Action != INSTALLSTATE_ABSENT)
4066 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4067 return ERROR_SUCCESS;
4069 msi_ui_actiondata( package, szRemoveShortcuts, row );
4071 link_file = get_link_file( package, row );
4073 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4074 if (!DeleteFileW( link_file ))
4076 WARN("Failed to remove shortcut file %u\n", GetLastError());
4078 msi_free( link_file );
4080 return ERROR_SUCCESS;
4083 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4085 static const WCHAR query[] = {
4086 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4087 '`','S','h','o','r','t','c','u','t','`',0};
4088 MSIQUERY *view;
4089 UINT rc;
4091 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4092 if (rc != ERROR_SUCCESS)
4093 return ERROR_SUCCESS;
4095 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4096 msiobj_release( &view->hdr );
4097 return rc;
4100 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4102 MSIPACKAGE* package = param;
4103 HANDLE the_file;
4104 LPWSTR FilePath;
4105 LPCWSTR FileName;
4106 CHAR buffer[1024];
4107 DWORD sz;
4108 UINT rc;
4110 FileName = MSI_RecordGetString(row,1);
4111 if (!FileName)
4113 ERR("Unable to get FileName\n");
4114 return ERROR_SUCCESS;
4117 FilePath = msi_build_icon_path(package, FileName);
4119 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4121 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4122 FILE_ATTRIBUTE_NORMAL, NULL);
4124 if (the_file == INVALID_HANDLE_VALUE)
4126 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4127 msi_free(FilePath);
4128 return ERROR_SUCCESS;
4133 DWORD write;
4134 sz = 1024;
4135 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4136 if (rc != ERROR_SUCCESS)
4138 ERR("Failed to get stream\n");
4139 CloseHandle(the_file);
4140 DeleteFileW(FilePath);
4141 break;
4143 WriteFile(the_file,buffer,sz,&write,NULL);
4144 } while (sz == 1024);
4146 msi_free(FilePath);
4147 CloseHandle(the_file);
4149 return ERROR_SUCCESS;
4152 static UINT msi_publish_icons(MSIPACKAGE *package)
4154 static const WCHAR query[]= {
4155 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4156 '`','I','c','o','n','`',0};
4157 MSIQUERY *view;
4158 UINT r;
4160 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4161 if (r == ERROR_SUCCESS)
4163 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4164 msiobj_release(&view->hdr);
4165 if (r != ERROR_SUCCESS)
4166 return r;
4168 return ERROR_SUCCESS;
4171 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4173 UINT r;
4174 HKEY source;
4175 LPWSTR buffer;
4176 MSIMEDIADISK *disk;
4177 MSISOURCELISTINFO *info;
4179 r = RegCreateKeyW(hkey, szSourceList, &source);
4180 if (r != ERROR_SUCCESS)
4181 return r;
4183 RegCloseKey(source);
4185 buffer = strrchrW(package->PackagePath, '\\') + 1;
4186 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4187 package->Context, MSICODE_PRODUCT,
4188 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4189 if (r != ERROR_SUCCESS)
4190 return r;
4192 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4193 package->Context, MSICODE_PRODUCT,
4194 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4195 if (r != ERROR_SUCCESS)
4196 return r;
4198 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4199 package->Context, MSICODE_PRODUCT,
4200 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4201 if (r != ERROR_SUCCESS)
4202 return r;
4204 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4206 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4207 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4208 info->options, info->value);
4209 else
4210 MsiSourceListSetInfoW(package->ProductCode, NULL,
4211 info->context, info->options,
4212 info->property, info->value);
4215 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4217 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4218 disk->context, disk->options,
4219 disk->disk_id, disk->volume_label, disk->disk_prompt);
4222 return ERROR_SUCCESS;
4225 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4227 MSIHANDLE hdb, suminfo;
4228 WCHAR guids[MAX_PATH];
4229 WCHAR packcode[SQUISH_GUID_SIZE];
4230 LPWSTR buffer;
4231 LPWSTR ptr;
4232 DWORD langid;
4233 DWORD size;
4234 UINT r;
4236 static const WCHAR szARPProductIcon[] =
4237 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4238 static const WCHAR szAssignment[] =
4239 {'A','s','s','i','g','n','m','e','n','t',0};
4240 static const WCHAR szAdvertiseFlags[] =
4241 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4242 static const WCHAR szClients[] =
4243 {'C','l','i','e','n','t','s',0};
4244 static const WCHAR szColon[] = {':',0};
4246 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4247 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4248 msi_free(buffer);
4250 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4251 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4253 /* FIXME */
4254 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4256 buffer = msi_dup_property(package->db, szARPProductIcon);
4257 if (buffer)
4259 LPWSTR path = msi_build_icon_path(package, buffer);
4260 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4261 msi_free(path);
4262 msi_free(buffer);
4265 buffer = msi_dup_property(package->db, szProductVersion);
4266 if (buffer)
4268 DWORD verdword = msi_version_str_to_dword(buffer);
4269 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4270 msi_free(buffer);
4273 msi_reg_set_val_dword(hkey, szAssignment, 0);
4274 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4275 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4276 msi_reg_set_val_str(hkey, szClients, szColon);
4278 hdb = alloc_msihandle(&package->db->hdr);
4279 if (!hdb)
4280 return ERROR_NOT_ENOUGH_MEMORY;
4282 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4283 MsiCloseHandle(hdb);
4284 if (r != ERROR_SUCCESS)
4285 goto done;
4287 size = MAX_PATH;
4288 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4289 NULL, guids, &size);
4290 if (r != ERROR_SUCCESS)
4291 goto done;
4293 ptr = strchrW(guids, ';');
4294 if (ptr) *ptr = 0;
4295 squash_guid(guids, packcode);
4296 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4298 done:
4299 MsiCloseHandle(suminfo);
4300 return ERROR_SUCCESS;
4303 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4305 UINT r;
4306 HKEY hkey;
4307 LPWSTR upgrade;
4308 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4310 upgrade = msi_dup_property(package->db, szUpgradeCode);
4311 if (!upgrade)
4312 return ERROR_SUCCESS;
4314 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4315 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4316 else
4317 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4319 if (r != ERROR_SUCCESS)
4321 WARN("failed to open upgrade code key\n");
4322 msi_free(upgrade);
4323 return ERROR_SUCCESS;
4325 squash_guid(package->ProductCode, squashed_pc);
4326 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4327 RegCloseKey(hkey);
4328 msi_free(upgrade);
4329 return ERROR_SUCCESS;
4332 static BOOL msi_check_publish(MSIPACKAGE *package)
4334 MSIFEATURE *feature;
4336 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4338 feature->Action = msi_get_feature_action( package, feature );
4339 if (feature->Action == INSTALLSTATE_LOCAL)
4340 return TRUE;
4343 return FALSE;
4346 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4348 MSIFEATURE *feature;
4350 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4352 feature->Action = msi_get_feature_action( package, feature );
4353 if (feature->Action != INSTALLSTATE_ABSENT)
4354 return FALSE;
4357 return TRUE;
4360 static UINT msi_publish_patches( MSIPACKAGE *package )
4362 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4363 WCHAR patch_squashed[GUID_SIZE];
4364 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4365 LONG res;
4366 MSIPATCHINFO *patch;
4367 UINT r;
4368 WCHAR *p, *all_patches = NULL;
4369 DWORD len = 0;
4371 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4372 if (r != ERROR_SUCCESS)
4373 return ERROR_FUNCTION_FAILED;
4375 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4376 if (res != ERROR_SUCCESS)
4378 r = ERROR_FUNCTION_FAILED;
4379 goto done;
4382 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4383 if (r != ERROR_SUCCESS)
4384 goto done;
4386 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4388 squash_guid( patch->patchcode, patch_squashed );
4389 len += strlenW( patch_squashed ) + 1;
4392 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4393 if (!all_patches)
4394 goto done;
4396 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4398 HKEY patch_key;
4400 squash_guid( patch->patchcode, p );
4401 p += strlenW( p ) + 1;
4403 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4404 (const BYTE *)patch->transforms,
4405 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4406 if (res != ERROR_SUCCESS)
4407 goto done;
4409 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4410 if (r != ERROR_SUCCESS)
4411 goto done;
4413 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4414 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4415 RegCloseKey( patch_key );
4416 if (res != ERROR_SUCCESS)
4417 goto done;
4419 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4421 res = GetLastError();
4422 ERR("Unable to copy patch package %d\n", res);
4423 goto done;
4425 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4426 if (res != ERROR_SUCCESS)
4427 goto done;
4429 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4430 RegCloseKey( patch_key );
4431 if (res != ERROR_SUCCESS)
4432 goto done;
4435 all_patches[len] = 0;
4436 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4437 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4438 if (res != ERROR_SUCCESS)
4439 goto done;
4441 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4442 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4443 if (res != ERROR_SUCCESS)
4444 r = ERROR_FUNCTION_FAILED;
4446 done:
4447 RegCloseKey( product_patches_key );
4448 RegCloseKey( patches_key );
4449 RegCloseKey( product_key );
4450 msi_free( all_patches );
4451 return r;
4454 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4456 UINT rc;
4457 HKEY hukey = NULL, hudkey = NULL;
4458 MSIRECORD *uirow;
4460 if (!list_empty(&package->patches))
4462 rc = msi_publish_patches(package);
4463 if (rc != ERROR_SUCCESS)
4464 goto end;
4467 /* FIXME: also need to publish if the product is in advertise mode */
4468 if (!msi_check_publish(package))
4469 return ERROR_SUCCESS;
4471 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4472 &hukey, TRUE);
4473 if (rc != ERROR_SUCCESS)
4474 goto end;
4476 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4477 NULL, &hudkey, TRUE);
4478 if (rc != ERROR_SUCCESS)
4479 goto end;
4481 rc = msi_publish_upgrade_code(package);
4482 if (rc != ERROR_SUCCESS)
4483 goto end;
4485 rc = msi_publish_product_properties(package, hukey);
4486 if (rc != ERROR_SUCCESS)
4487 goto end;
4489 rc = msi_publish_sourcelist(package, hukey);
4490 if (rc != ERROR_SUCCESS)
4491 goto end;
4493 rc = msi_publish_icons(package);
4495 end:
4496 uirow = MSI_CreateRecord( 1 );
4497 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4498 msi_ui_actiondata( package, szPublishProduct, uirow );
4499 msiobj_release( &uirow->hdr );
4501 RegCloseKey(hukey);
4502 RegCloseKey(hudkey);
4503 return rc;
4506 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4508 WCHAR *filename, *ptr, *folder, *ret;
4509 const WCHAR *dirprop;
4511 filename = msi_dup_record_field( row, 2 );
4512 if (filename && (ptr = strchrW( filename, '|' )))
4513 ptr++;
4514 else
4515 ptr = filename;
4517 dirprop = MSI_RecordGetString( row, 3 );
4518 if (dirprop)
4520 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4521 if (!folder) folder = msi_dup_property( package->db, dirprop );
4523 else
4524 folder = msi_dup_property( package->db, szWindowsFolder );
4526 if (!folder)
4528 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4529 msi_free( filename );
4530 return NULL;
4533 ret = msi_build_directory_name( 2, folder, ptr );
4535 msi_free( filename );
4536 msi_free( folder );
4537 return ret;
4540 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4542 MSIPACKAGE *package = param;
4543 LPCWSTR component, section, key, value, identifier;
4544 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4545 MSIRECORD * uirow;
4546 INT action;
4547 MSICOMPONENT *comp;
4549 component = MSI_RecordGetString(row, 8);
4550 comp = msi_get_loaded_component(package,component);
4551 if (!comp)
4552 return ERROR_SUCCESS;
4554 comp->Action = msi_get_component_action( package, comp );
4555 if (comp->Action != INSTALLSTATE_LOCAL)
4557 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4558 return ERROR_SUCCESS;
4561 identifier = MSI_RecordGetString(row,1);
4562 section = MSI_RecordGetString(row,4);
4563 key = MSI_RecordGetString(row,5);
4564 value = MSI_RecordGetString(row,6);
4565 action = MSI_RecordGetInteger(row,7);
4567 deformat_string(package,section,&deformated_section);
4568 deformat_string(package,key,&deformated_key);
4569 deformat_string(package,value,&deformated_value);
4571 fullname = get_ini_file_name(package, row);
4573 if (action == 0)
4575 TRACE("Adding value %s to section %s in %s\n",
4576 debugstr_w(deformated_key), debugstr_w(deformated_section),
4577 debugstr_w(fullname));
4578 WritePrivateProfileStringW(deformated_section, deformated_key,
4579 deformated_value, fullname);
4581 else if (action == 1)
4583 WCHAR returned[10];
4584 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4585 returned, 10, fullname);
4586 if (returned[0] == 0)
4588 TRACE("Adding value %s to section %s in %s\n",
4589 debugstr_w(deformated_key), debugstr_w(deformated_section),
4590 debugstr_w(fullname));
4592 WritePrivateProfileStringW(deformated_section, deformated_key,
4593 deformated_value, fullname);
4596 else if (action == 3)
4597 FIXME("Append to existing section not yet implemented\n");
4599 uirow = MSI_CreateRecord(4);
4600 MSI_RecordSetStringW(uirow,1,identifier);
4601 MSI_RecordSetStringW(uirow,2,deformated_section);
4602 MSI_RecordSetStringW(uirow,3,deformated_key);
4603 MSI_RecordSetStringW(uirow,4,deformated_value);
4604 msi_ui_actiondata( package, szWriteIniValues, uirow );
4605 msiobj_release( &uirow->hdr );
4607 msi_free(fullname);
4608 msi_free(deformated_key);
4609 msi_free(deformated_value);
4610 msi_free(deformated_section);
4611 return ERROR_SUCCESS;
4614 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4616 static const WCHAR query[] = {
4617 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4618 '`','I','n','i','F','i','l','e','`',0};
4619 MSIQUERY *view;
4620 UINT rc;
4622 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4623 if (rc != ERROR_SUCCESS)
4624 return ERROR_SUCCESS;
4626 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4627 msiobj_release(&view->hdr);
4628 return rc;
4631 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4633 MSIPACKAGE *package = param;
4634 LPCWSTR component, section, key, value, identifier;
4635 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4636 MSICOMPONENT *comp;
4637 MSIRECORD *uirow;
4638 INT action;
4640 component = MSI_RecordGetString( row, 8 );
4641 comp = msi_get_loaded_component( package, component );
4642 if (!comp)
4643 return ERROR_SUCCESS;
4645 comp->Action = msi_get_component_action( package, comp );
4646 if (comp->Action != INSTALLSTATE_ABSENT)
4648 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4649 return ERROR_SUCCESS;
4652 identifier = MSI_RecordGetString( row, 1 );
4653 section = MSI_RecordGetString( row, 4 );
4654 key = MSI_RecordGetString( row, 5 );
4655 value = MSI_RecordGetString( row, 6 );
4656 action = MSI_RecordGetInteger( row, 7 );
4658 deformat_string( package, section, &deformated_section );
4659 deformat_string( package, key, &deformated_key );
4660 deformat_string( package, value, &deformated_value );
4662 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4664 filename = get_ini_file_name( package, row );
4666 TRACE("Removing key %s from section %s in %s\n",
4667 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4669 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4671 WARN("Unable to remove key %u\n", GetLastError());
4673 msi_free( filename );
4675 else
4676 FIXME("Unsupported action %d\n", action);
4679 uirow = MSI_CreateRecord( 4 );
4680 MSI_RecordSetStringW( uirow, 1, identifier );
4681 MSI_RecordSetStringW( uirow, 2, deformated_section );
4682 MSI_RecordSetStringW( uirow, 3, deformated_key );
4683 MSI_RecordSetStringW( uirow, 4, deformated_value );
4684 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4685 msiobj_release( &uirow->hdr );
4687 msi_free( deformated_key );
4688 msi_free( deformated_value );
4689 msi_free( deformated_section );
4690 return ERROR_SUCCESS;
4693 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4695 MSIPACKAGE *package = param;
4696 LPCWSTR component, section, key, value, identifier;
4697 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4698 MSICOMPONENT *comp;
4699 MSIRECORD *uirow;
4700 INT action;
4702 component = MSI_RecordGetString( row, 8 );
4703 comp = msi_get_loaded_component( package, component );
4704 if (!comp)
4705 return ERROR_SUCCESS;
4707 comp->Action = msi_get_component_action( package, comp );
4708 if (comp->Action != INSTALLSTATE_LOCAL)
4710 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4711 return ERROR_SUCCESS;
4714 identifier = MSI_RecordGetString( row, 1 );
4715 section = MSI_RecordGetString( row, 4 );
4716 key = MSI_RecordGetString( row, 5 );
4717 value = MSI_RecordGetString( row, 6 );
4718 action = MSI_RecordGetInteger( row, 7 );
4720 deformat_string( package, section, &deformated_section );
4721 deformat_string( package, key, &deformated_key );
4722 deformat_string( package, value, &deformated_value );
4724 if (action == msidbIniFileActionRemoveLine)
4726 filename = get_ini_file_name( package, row );
4728 TRACE("Removing key %s from section %s in %s\n",
4729 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4731 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4733 WARN("Unable to remove key %u\n", GetLastError());
4735 msi_free( filename );
4737 else
4738 FIXME("Unsupported action %d\n", action);
4740 uirow = MSI_CreateRecord( 4 );
4741 MSI_RecordSetStringW( uirow, 1, identifier );
4742 MSI_RecordSetStringW( uirow, 2, deformated_section );
4743 MSI_RecordSetStringW( uirow, 3, deformated_key );
4744 MSI_RecordSetStringW( uirow, 4, deformated_value );
4745 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4746 msiobj_release( &uirow->hdr );
4748 msi_free( deformated_key );
4749 msi_free( deformated_value );
4750 msi_free( deformated_section );
4751 return ERROR_SUCCESS;
4754 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4756 static const WCHAR query[] = {
4757 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4758 '`','I','n','i','F','i','l','e','`',0};
4759 static const WCHAR remove_query[] = {
4760 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4761 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4762 MSIQUERY *view;
4763 UINT rc;
4765 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4766 if (rc == ERROR_SUCCESS)
4768 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4769 msiobj_release( &view->hdr );
4770 if (rc != ERROR_SUCCESS)
4771 return rc;
4773 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4774 if (rc == ERROR_SUCCESS)
4776 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4777 msiobj_release( &view->hdr );
4778 if (rc != ERROR_SUCCESS)
4779 return rc;
4781 return ERROR_SUCCESS;
4784 static void register_dll( const WCHAR *dll, BOOL unregister )
4786 static const WCHAR regW[] =
4787 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4788 static const WCHAR unregW[] =
4789 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4790 PROCESS_INFORMATION pi;
4791 STARTUPINFOW si;
4792 WCHAR *cmd;
4794 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4796 if (unregister) sprintfW( cmd, unregW, dll );
4797 else sprintfW( cmd, regW, dll );
4799 memset( &si, 0, sizeof(STARTUPINFOW) );
4800 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4802 CloseHandle( pi.hThread );
4803 msi_dialog_check_messages( pi.hProcess );
4804 CloseHandle( pi.hProcess );
4806 msi_free( cmd );
4809 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4811 MSIPACKAGE *package = param;
4812 LPCWSTR filename;
4813 MSIFILE *file;
4814 MSIRECORD *uirow;
4816 filename = MSI_RecordGetString( row, 1 );
4817 file = msi_get_loaded_file( package, filename );
4818 if (!file)
4820 WARN("unable to find file %s\n", debugstr_w(filename));
4821 return ERROR_SUCCESS;
4823 file->Component->Action = msi_get_component_action( package, file->Component );
4824 if (file->Component->Action != INSTALLSTATE_LOCAL)
4826 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4827 return ERROR_SUCCESS;
4830 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4831 register_dll( file->TargetPath, FALSE );
4833 uirow = MSI_CreateRecord( 2 );
4834 MSI_RecordSetStringW( uirow, 1, file->File );
4835 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4836 msi_ui_actiondata( package, szSelfRegModules, uirow );
4837 msiobj_release( &uirow->hdr );
4839 return ERROR_SUCCESS;
4842 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4844 static const WCHAR query[] = {
4845 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4846 '`','S','e','l','f','R','e','g','`',0};
4847 MSIQUERY *view;
4848 UINT rc;
4850 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4851 if (rc != ERROR_SUCCESS)
4852 return ERROR_SUCCESS;
4854 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4855 msiobj_release(&view->hdr);
4856 return rc;
4859 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4861 MSIPACKAGE *package = param;
4862 LPCWSTR filename;
4863 MSIFILE *file;
4864 MSIRECORD *uirow;
4866 filename = MSI_RecordGetString( row, 1 );
4867 file = msi_get_loaded_file( package, filename );
4868 if (!file)
4870 WARN("unable to find file %s\n", debugstr_w(filename));
4871 return ERROR_SUCCESS;
4873 file->Component->Action = msi_get_component_action( package, file->Component );
4874 if (file->Component->Action != INSTALLSTATE_ABSENT)
4876 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4877 return ERROR_SUCCESS;
4880 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4881 register_dll( file->TargetPath, TRUE );
4883 uirow = MSI_CreateRecord( 2 );
4884 MSI_RecordSetStringW( uirow, 1, file->File );
4885 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4886 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4887 msiobj_release( &uirow->hdr );
4889 return ERROR_SUCCESS;
4892 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4894 static const WCHAR query[] = {
4895 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4896 '`','S','e','l','f','R','e','g','`',0};
4897 MSIQUERY *view;
4898 UINT rc;
4900 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4901 if (rc != ERROR_SUCCESS)
4902 return ERROR_SUCCESS;
4904 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4905 msiobj_release( &view->hdr );
4906 return rc;
4909 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4911 MSIFEATURE *feature;
4912 UINT rc;
4913 HKEY hkey = NULL, userdata = NULL;
4915 if (!msi_check_publish(package))
4916 return ERROR_SUCCESS;
4918 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4919 &hkey, TRUE);
4920 if (rc != ERROR_SUCCESS)
4921 goto end;
4923 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4924 &userdata, TRUE);
4925 if (rc != ERROR_SUCCESS)
4926 goto end;
4928 /* here the guids are base 85 encoded */
4929 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4931 ComponentList *cl;
4932 LPWSTR data = NULL;
4933 GUID clsid;
4934 INT size;
4935 BOOL absent = FALSE;
4936 MSIRECORD *uirow;
4938 if (feature->Level <= 0) continue;
4940 if (feature->Action != INSTALLSTATE_LOCAL &&
4941 feature->Action != INSTALLSTATE_SOURCE &&
4942 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4944 size = 1;
4945 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4947 size += 21;
4949 if (feature->Feature_Parent)
4950 size += strlenW( feature->Feature_Parent )+2;
4952 data = msi_alloc(size * sizeof(WCHAR));
4954 data[0] = 0;
4955 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4957 MSICOMPONENT* component = cl->component;
4958 WCHAR buf[21];
4960 buf[0] = 0;
4961 if (component->ComponentId)
4963 TRACE("From %s\n",debugstr_w(component->ComponentId));
4964 CLSIDFromString(component->ComponentId, &clsid);
4965 encode_base85_guid(&clsid,buf);
4966 TRACE("to %s\n",debugstr_w(buf));
4967 strcatW(data,buf);
4971 if (feature->Feature_Parent)
4973 static const WCHAR sep[] = {'\2',0};
4974 strcatW(data,sep);
4975 strcatW(data,feature->Feature_Parent);
4978 msi_reg_set_val_str( userdata, feature->Feature, data );
4979 msi_free(data);
4981 size = 0;
4982 if (feature->Feature_Parent)
4983 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4984 if (!absent)
4986 size += sizeof(WCHAR);
4987 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4988 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4990 else
4992 size += 2*sizeof(WCHAR);
4993 data = msi_alloc(size);
4994 data[0] = 0x6;
4995 data[1] = 0;
4996 if (feature->Feature_Parent)
4997 strcpyW( &data[1], feature->Feature_Parent );
4998 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4999 (LPBYTE)data,size);
5000 msi_free(data);
5003 /* the UI chunk */
5004 uirow = MSI_CreateRecord( 1 );
5005 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5006 msi_ui_actiondata( package, szPublishFeatures, uirow );
5007 msiobj_release( &uirow->hdr );
5008 /* FIXME: call msi_ui_progress? */
5011 end:
5012 RegCloseKey(hkey);
5013 RegCloseKey(userdata);
5014 return rc;
5017 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
5019 UINT r;
5020 HKEY hkey;
5021 MSIRECORD *uirow;
5023 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
5025 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
5026 &hkey, FALSE);
5027 if (r == ERROR_SUCCESS)
5029 RegDeleteValueW(hkey, feature->Feature);
5030 RegCloseKey(hkey);
5033 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
5034 &hkey, FALSE);
5035 if (r == ERROR_SUCCESS)
5037 RegDeleteValueW(hkey, feature->Feature);
5038 RegCloseKey(hkey);
5041 uirow = MSI_CreateRecord( 1 );
5042 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5043 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
5044 msiobj_release( &uirow->hdr );
5046 return ERROR_SUCCESS;
5049 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5051 MSIFEATURE *feature;
5053 if (!msi_check_unpublish(package))
5054 return ERROR_SUCCESS;
5056 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5058 msi_unpublish_feature(package, feature);
5061 return ERROR_SUCCESS;
5064 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5066 SYSTEMTIME systime;
5067 DWORD size, langid;
5068 WCHAR date[9], *val, *buffer;
5069 const WCHAR *prop, *key;
5071 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5072 static const WCHAR modpath_fmt[] =
5073 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5074 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5075 static const WCHAR szModifyPath[] =
5076 {'M','o','d','i','f','y','P','a','t','h',0};
5077 static const WCHAR szUninstallString[] =
5078 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5079 static const WCHAR szEstimatedSize[] =
5080 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5081 static const WCHAR szDisplayVersion[] =
5082 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5083 static const WCHAR szInstallSource[] =
5084 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5085 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5086 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5087 static const WCHAR szAuthorizedCDFPrefix[] =
5088 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5089 static const WCHAR szARPCONTACT[] =
5090 {'A','R','P','C','O','N','T','A','C','T',0};
5091 static const WCHAR szContact[] =
5092 {'C','o','n','t','a','c','t',0};
5093 static const WCHAR szARPCOMMENTS[] =
5094 {'A','R','P','C','O','M','M','E','N','T','S',0};
5095 static const WCHAR szComments[] =
5096 {'C','o','m','m','e','n','t','s',0};
5097 static const WCHAR szProductName[] =
5098 {'P','r','o','d','u','c','t','N','a','m','e',0};
5099 static const WCHAR szDisplayName[] =
5100 {'D','i','s','p','l','a','y','N','a','m','e',0};
5101 static const WCHAR szARPHELPLINK[] =
5102 {'A','R','P','H','E','L','P','L','I','N','K',0};
5103 static const WCHAR szHelpLink[] =
5104 {'H','e','l','p','L','i','n','k',0};
5105 static const WCHAR szARPHELPTELEPHONE[] =
5106 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5107 static const WCHAR szHelpTelephone[] =
5108 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5109 static const WCHAR szARPINSTALLLOCATION[] =
5110 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5111 static const WCHAR szManufacturer[] =
5112 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5113 static const WCHAR szPublisher[] =
5114 {'P','u','b','l','i','s','h','e','r',0};
5115 static const WCHAR szARPREADME[] =
5116 {'A','R','P','R','E','A','D','M','E',0};
5117 static const WCHAR szReadme[] =
5118 {'R','e','a','d','M','e',0};
5119 static const WCHAR szARPSIZE[] =
5120 {'A','R','P','S','I','Z','E',0};
5121 static const WCHAR szSize[] =
5122 {'S','i','z','e',0};
5123 static const WCHAR szARPURLINFOABOUT[] =
5124 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5125 static const WCHAR szURLInfoAbout[] =
5126 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5127 static const WCHAR szARPURLUPDATEINFO[] =
5128 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5129 static const WCHAR szURLUpdateInfo[] =
5130 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5131 static const WCHAR szARPSYSTEMCOMPONENT[] =
5132 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5133 static const WCHAR szSystemComponent[] =
5134 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5136 static const WCHAR *propval[] = {
5137 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5138 szARPCONTACT, szContact,
5139 szARPCOMMENTS, szComments,
5140 szProductName, szDisplayName,
5141 szARPHELPLINK, szHelpLink,
5142 szARPHELPTELEPHONE, szHelpTelephone,
5143 szARPINSTALLLOCATION, szInstallLocation,
5144 szSourceDir, szInstallSource,
5145 szManufacturer, szPublisher,
5146 szARPREADME, szReadme,
5147 szARPSIZE, szSize,
5148 szARPURLINFOABOUT, szURLInfoAbout,
5149 szARPURLUPDATEINFO, szURLUpdateInfo,
5150 NULL
5152 const WCHAR **p = propval;
5154 while (*p)
5156 prop = *p++;
5157 key = *p++;
5158 val = msi_dup_property(package->db, prop);
5159 msi_reg_set_val_str(hkey, key, val);
5160 msi_free(val);
5163 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5164 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5166 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5168 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5169 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5170 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5171 msi_free(buffer);
5173 /* FIXME: Write real Estimated Size when we have it */
5174 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5176 GetLocalTime(&systime);
5177 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5178 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5180 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5181 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5183 buffer = msi_dup_property(package->db, szProductVersion);
5184 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5185 if (buffer)
5187 DWORD verdword = msi_version_str_to_dword(buffer);
5189 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5190 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5191 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5192 msi_free(buffer);
5195 return ERROR_SUCCESS;
5198 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5200 WCHAR squashed_pc[SQUISH_GUID_SIZE];
5201 MSIRECORD *uirow;
5202 LPWSTR upgrade_code;
5203 HKEY hkey, props, upgrade_key;
5204 UINT rc;
5206 /* FIXME: also need to publish if the product is in advertise mode */
5207 if (!msi_check_publish(package))
5208 return ERROR_SUCCESS;
5210 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5211 if (rc != ERROR_SUCCESS)
5212 return rc;
5214 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5215 if (rc != ERROR_SUCCESS)
5216 goto done;
5218 rc = msi_publish_install_properties(package, hkey);
5219 if (rc != ERROR_SUCCESS)
5220 goto done;
5222 rc = msi_publish_install_properties(package, props);
5223 if (rc != ERROR_SUCCESS)
5224 goto done;
5226 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5227 if (upgrade_code)
5229 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5230 if (rc == ERROR_SUCCESS)
5232 squash_guid( package->ProductCode, squashed_pc );
5233 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5234 RegCloseKey( upgrade_key );
5236 msi_free( upgrade_code );
5238 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5239 package->delete_on_close = FALSE;
5241 done:
5242 uirow = MSI_CreateRecord( 1 );
5243 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5244 msi_ui_actiondata( package, szRegisterProduct, uirow );
5245 msiobj_release( &uirow->hdr );
5247 RegCloseKey(hkey);
5248 return ERROR_SUCCESS;
5251 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5253 return execute_script(package, SCRIPT_INSTALL);
5256 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5258 MSIPACKAGE *package = param;
5259 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5260 WCHAR *p, *icon_path;
5262 if (!icon) return ERROR_SUCCESS;
5263 if ((icon_path = msi_build_icon_path( package, icon )))
5265 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5266 DeleteFileW( icon_path );
5267 if ((p = strrchrW( icon_path, '\\' )))
5269 *p = 0;
5270 RemoveDirectoryW( icon_path );
5272 msi_free( icon_path );
5274 return ERROR_SUCCESS;
5277 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5279 static const WCHAR query[]= {
5280 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5281 MSIQUERY *view;
5282 UINT r;
5284 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5285 if (r == ERROR_SUCCESS)
5287 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5288 msiobj_release( &view->hdr );
5289 if (r != ERROR_SUCCESS)
5290 return r;
5292 return ERROR_SUCCESS;
5295 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
5297 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
5298 WCHAR *upgrade, **features;
5299 BOOL full_uninstall = TRUE;
5300 MSIFEATURE *feature;
5301 MSIPATCHINFO *patch;
5302 UINT i;
5304 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5306 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
5308 features = msi_split_string( remove, ',' );
5309 for (i = 0; features && features[i]; i++)
5311 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
5313 msi_free(features);
5315 if (!full_uninstall)
5316 return ERROR_SUCCESS;
5318 MSIREG_DeleteProductKey(package->ProductCode);
5319 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5320 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5322 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5323 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5324 MSIREG_DeleteUserProductKey(package->ProductCode);
5325 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5327 upgrade = msi_dup_property(package->db, szUpgradeCode);
5328 if (upgrade)
5330 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5331 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5332 msi_free(upgrade);
5335 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5337 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5338 if (!strcmpW( package->ProductCode, patch->products ))
5340 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5341 patch->delete_on_close = TRUE;
5343 /* FIXME: remove local patch package if this is the last product */
5345 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5346 package->delete_on_close = TRUE;
5348 msi_unpublish_icons( package );
5349 return ERROR_SUCCESS;
5352 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5354 UINT rc;
5355 WCHAR *remove;
5357 /* first do the same as an InstallExecute */
5358 rc = execute_script(package, SCRIPT_INSTALL);
5359 if (rc != ERROR_SUCCESS)
5360 return rc;
5362 /* then handle commit actions */
5363 rc = execute_script(package, SCRIPT_COMMIT);
5364 if (rc != ERROR_SUCCESS)
5365 return rc;
5367 remove = msi_dup_property(package->db, szRemove);
5368 rc = msi_unpublish_product(package, remove);
5369 msi_free(remove);
5370 return rc;
5373 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5375 static const WCHAR RunOnce[] = {
5376 'S','o','f','t','w','a','r','e','\\',
5377 'M','i','c','r','o','s','o','f','t','\\',
5378 'W','i','n','d','o','w','s','\\',
5379 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5380 'R','u','n','O','n','c','e',0};
5381 static const WCHAR InstallRunOnce[] = {
5382 'S','o','f','t','w','a','r','e','\\',
5383 'M','i','c','r','o','s','o','f','t','\\',
5384 'W','i','n','d','o','w','s','\\',
5385 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5386 'I','n','s','t','a','l','l','e','r','\\',
5387 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5389 static const WCHAR msiexec_fmt[] = {
5390 '%','s',
5391 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5392 '\"','%','s','\"',0};
5393 static const WCHAR install_fmt[] = {
5394 '/','I',' ','\"','%','s','\"',' ',
5395 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5396 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5397 WCHAR buffer[256], sysdir[MAX_PATH];
5398 HKEY hkey;
5399 WCHAR squished_pc[100];
5401 squash_guid(package->ProductCode,squished_pc);
5403 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5404 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5405 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5406 squished_pc);
5408 msi_reg_set_val_str( hkey, squished_pc, buffer );
5409 RegCloseKey(hkey);
5411 TRACE("Reboot command %s\n",debugstr_w(buffer));
5413 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5414 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5416 msi_reg_set_val_str( hkey, squished_pc, buffer );
5417 RegCloseKey(hkey);
5419 return ERROR_INSTALL_SUSPEND;
5422 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5424 static const WCHAR query[] =
5425 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5426 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5427 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5428 MSIRECORD *rec, *row;
5429 DWORD i, size = 0;
5430 va_list va;
5431 const WCHAR *str;
5432 WCHAR *data;
5434 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5436 rec = MSI_CreateRecord( count + 2 );
5437 str = MSI_RecordGetString( row, 1 );
5438 MSI_RecordSetStringW( rec, 0, str );
5439 msiobj_release( &row->hdr );
5440 MSI_RecordSetInteger( rec, 1, error );
5442 va_start( va, count );
5443 for (i = 0; i < count; i++)
5445 str = va_arg( va, const WCHAR *);
5446 MSI_RecordSetStringW( rec, i + 2, str );
5448 va_end( va );
5450 MSI_FormatRecordW( package, rec, NULL, &size );
5451 size++;
5452 data = msi_alloc( size * sizeof(WCHAR) );
5453 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5454 else data[0] = 0;
5455 msiobj_release( &rec->hdr );
5456 return data;
5459 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5461 DWORD attrib;
5462 UINT rc;
5465 * We are currently doing what should be done here in the top level Install
5466 * however for Administrative and uninstalls this step will be needed
5468 if (!package->PackagePath)
5469 return ERROR_SUCCESS;
5471 msi_set_sourcedir_props(package, TRUE);
5473 attrib = GetFileAttributesW(package->db->path);
5474 if (attrib == INVALID_FILE_ATTRIBUTES)
5476 LPWSTR prompt, msg;
5477 DWORD size = 0;
5479 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5480 package->Context, MSICODE_PRODUCT,
5481 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5482 if (rc == ERROR_MORE_DATA)
5484 prompt = msi_alloc(size * sizeof(WCHAR));
5485 MsiSourceListGetInfoW(package->ProductCode, NULL,
5486 package->Context, MSICODE_PRODUCT,
5487 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5489 else
5490 prompt = strdupW(package->db->path);
5492 msg = msi_build_error_string(package, 1302, 1, prompt);
5493 msi_free(prompt);
5494 while(attrib == INVALID_FILE_ATTRIBUTES)
5496 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5497 if (rc == IDCANCEL)
5499 msi_free(msg);
5500 return ERROR_INSTALL_USEREXIT;
5502 attrib = GetFileAttributesW(package->db->path);
5504 msi_free(msg);
5505 rc = ERROR_SUCCESS;
5507 else
5508 return ERROR_SUCCESS;
5510 return rc;
5513 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5515 HKEY hkey = 0;
5516 LPWSTR buffer, productid = NULL;
5517 UINT i, rc = ERROR_SUCCESS;
5518 MSIRECORD *uirow;
5520 static const WCHAR szPropKeys[][80] =
5522 {'P','r','o','d','u','c','t','I','D',0},
5523 {'U','S','E','R','N','A','M','E',0},
5524 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5525 {0},
5528 static const WCHAR szRegKeys[][80] =
5530 {'P','r','o','d','u','c','t','I','D',0},
5531 {'R','e','g','O','w','n','e','r',0},
5532 {'R','e','g','C','o','m','p','a','n','y',0},
5533 {0},
5536 if (msi_check_unpublish(package))
5538 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5539 goto end;
5542 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5543 if (!productid)
5544 goto end;
5546 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5547 NULL, &hkey, TRUE);
5548 if (rc != ERROR_SUCCESS)
5549 goto end;
5551 for( i = 0; szPropKeys[i][0]; i++ )
5553 buffer = msi_dup_property( package->db, szPropKeys[i] );
5554 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5555 msi_free( buffer );
5558 end:
5559 uirow = MSI_CreateRecord( 1 );
5560 MSI_RecordSetStringW( uirow, 1, productid );
5561 msi_ui_actiondata( package, szRegisterUser, uirow );
5562 msiobj_release( &uirow->hdr );
5564 msi_free(productid);
5565 RegCloseKey(hkey);
5566 return rc;
5570 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5572 UINT rc;
5574 package->script->InWhatSequence |= SEQUENCE_EXEC;
5575 rc = ACTION_ProcessExecSequence(package,FALSE);
5576 return rc;
5579 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5581 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5582 WCHAR productid_85[21], component_85[21], *ret;
5583 GUID clsid;
5584 DWORD sz;
5586 /* > is used if there is a component GUID and < if not. */
5588 productid_85[0] = 0;
5589 component_85[0] = 0;
5590 CLSIDFromString( package->ProductCode, &clsid );
5592 encode_base85_guid( &clsid, productid_85 );
5593 if (component)
5595 CLSIDFromString( component->ComponentId, &clsid );
5596 encode_base85_guid( &clsid, component_85 );
5599 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5600 debugstr_w(component_85));
5602 sz = 20 + strlenW( feature ) + 20 + 3;
5603 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5604 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5605 return ret;
5608 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5610 MSIPACKAGE *package = param;
5611 LPCWSTR compgroupid, component, feature, qualifier, text;
5612 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5613 HKEY hkey = NULL;
5614 UINT rc;
5615 MSICOMPONENT *comp;
5616 MSIFEATURE *feat;
5617 DWORD sz;
5618 MSIRECORD *uirow;
5619 int len;
5621 feature = MSI_RecordGetString(rec, 5);
5622 feat = msi_get_loaded_feature(package, feature);
5623 if (!feat)
5624 return ERROR_SUCCESS;
5626 feat->Action = msi_get_feature_action( package, feat );
5627 if (feat->Action != INSTALLSTATE_LOCAL &&
5628 feat->Action != INSTALLSTATE_SOURCE &&
5629 feat->Action != INSTALLSTATE_ADVERTISED)
5631 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5632 return ERROR_SUCCESS;
5635 component = MSI_RecordGetString(rec, 3);
5636 comp = msi_get_loaded_component(package, component);
5637 if (!comp)
5638 return ERROR_SUCCESS;
5640 compgroupid = MSI_RecordGetString(rec,1);
5641 qualifier = MSI_RecordGetString(rec,2);
5643 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5644 if (rc != ERROR_SUCCESS)
5645 goto end;
5647 advertise = msi_create_component_advertise_string( package, comp, feature );
5648 text = MSI_RecordGetString( rec, 4 );
5649 if (text)
5651 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5652 strcpyW( p, advertise );
5653 strcatW( p, text );
5654 msi_free( advertise );
5655 advertise = p;
5657 existing = msi_reg_get_val_str( hkey, qualifier );
5659 sz = strlenW( advertise ) + 1;
5660 if (existing)
5662 for (p = existing; *p; p += len)
5664 len = strlenW( p ) + 1;
5665 if (strcmpW( advertise, p )) sz += len;
5668 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5670 rc = ERROR_OUTOFMEMORY;
5671 goto end;
5673 q = output;
5674 if (existing)
5676 for (p = existing; *p; p += len)
5678 len = strlenW( p ) + 1;
5679 if (strcmpW( advertise, p ))
5681 memcpy( q, p, len * sizeof(WCHAR) );
5682 q += len;
5686 strcpyW( q, advertise );
5687 q[strlenW( q ) + 1] = 0;
5689 msi_reg_set_val_multi_str( hkey, qualifier, output );
5691 end:
5692 RegCloseKey(hkey);
5693 msi_free( output );
5694 msi_free( advertise );
5695 msi_free( existing );
5697 /* the UI chunk */
5698 uirow = MSI_CreateRecord( 2 );
5699 MSI_RecordSetStringW( uirow, 1, compgroupid );
5700 MSI_RecordSetStringW( uirow, 2, qualifier);
5701 msi_ui_actiondata( package, szPublishComponents, uirow );
5702 msiobj_release( &uirow->hdr );
5703 /* FIXME: call ui_progress? */
5705 return rc;
5709 * At present I am ignorning the advertised components part of this and only
5710 * focusing on the qualified component sets
5712 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5714 static const WCHAR query[] = {
5715 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5716 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5717 MSIQUERY *view;
5718 UINT rc;
5720 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5721 if (rc != ERROR_SUCCESS)
5722 return ERROR_SUCCESS;
5724 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5725 msiobj_release(&view->hdr);
5726 return rc;
5729 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5731 static const WCHAR szInstallerComponents[] = {
5732 'S','o','f','t','w','a','r','e','\\',
5733 'M','i','c','r','o','s','o','f','t','\\',
5734 'I','n','s','t','a','l','l','e','r','\\',
5735 'C','o','m','p','o','n','e','n','t','s','\\',0};
5737 MSIPACKAGE *package = param;
5738 LPCWSTR compgroupid, component, feature, qualifier;
5739 MSICOMPONENT *comp;
5740 MSIFEATURE *feat;
5741 MSIRECORD *uirow;
5742 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5743 LONG res;
5745 feature = MSI_RecordGetString( rec, 5 );
5746 feat = msi_get_loaded_feature( package, feature );
5747 if (!feat)
5748 return ERROR_SUCCESS;
5750 feat->Action = msi_get_feature_action( package, feat );
5751 if (feat->Action != INSTALLSTATE_ABSENT)
5753 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5754 return ERROR_SUCCESS;
5757 component = MSI_RecordGetString( rec, 3 );
5758 comp = msi_get_loaded_component( package, component );
5759 if (!comp)
5760 return ERROR_SUCCESS;
5762 compgroupid = MSI_RecordGetString( rec, 1 );
5763 qualifier = MSI_RecordGetString( rec, 2 );
5765 squash_guid( compgroupid, squashed );
5766 strcpyW( keypath, szInstallerComponents );
5767 strcatW( keypath, squashed );
5769 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5770 if (res != ERROR_SUCCESS)
5772 WARN("Unable to delete component key %d\n", res);
5775 uirow = MSI_CreateRecord( 2 );
5776 MSI_RecordSetStringW( uirow, 1, compgroupid );
5777 MSI_RecordSetStringW( uirow, 2, qualifier );
5778 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5779 msiobj_release( &uirow->hdr );
5781 return ERROR_SUCCESS;
5784 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5786 static const WCHAR query[] = {
5787 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5788 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5789 MSIQUERY *view;
5790 UINT rc;
5792 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5793 if (rc != ERROR_SUCCESS)
5794 return ERROR_SUCCESS;
5796 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5797 msiobj_release( &view->hdr );
5798 return rc;
5801 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5803 static const WCHAR query[] =
5804 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5805 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5806 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5807 MSIPACKAGE *package = param;
5808 MSICOMPONENT *component;
5809 MSIRECORD *row;
5810 MSIFILE *file;
5811 SC_HANDLE hscm = NULL, service = NULL;
5812 LPCWSTR comp, key;
5813 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5814 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5815 DWORD serv_type, start_type, err_control;
5816 SERVICE_DESCRIPTIONW sd = {NULL};
5817 UINT ret = ERROR_SUCCESS;
5819 comp = MSI_RecordGetString( rec, 12 );
5820 component = msi_get_loaded_component( package, comp );
5821 if (!component)
5823 WARN("service component not found\n");
5824 goto done;
5826 component->Action = msi_get_component_action( package, component );
5827 if (component->Action != INSTALLSTATE_LOCAL)
5829 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5830 goto done;
5832 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5833 if (!hscm)
5835 ERR("Failed to open the SC Manager!\n");
5836 goto done;
5839 start_type = MSI_RecordGetInteger(rec, 5);
5840 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5841 goto done;
5843 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5844 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5845 serv_type = MSI_RecordGetInteger(rec, 4);
5846 err_control = MSI_RecordGetInteger(rec, 6);
5847 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5848 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5849 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5850 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5851 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5852 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5854 /* fetch the service path */
5855 row = MSI_QueryGetRecord(package->db, query, comp);
5856 if (!row)
5858 ERR("Query failed\n");
5859 goto done;
5861 if (!(key = MSI_RecordGetString(row, 6)))
5863 msiobj_release(&row->hdr);
5864 goto done;
5866 file = msi_get_loaded_file(package, key);
5867 msiobj_release(&row->hdr);
5868 if (!file)
5870 ERR("Failed to load the service file\n");
5871 goto done;
5874 if (!args || !args[0]) image_path = file->TargetPath;
5875 else
5877 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5878 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5880 ret = ERROR_OUTOFMEMORY;
5881 goto done;
5884 strcpyW(image_path, file->TargetPath);
5885 strcatW(image_path, szSpace);
5886 strcatW(image_path, args);
5888 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5889 start_type, err_control, image_path, load_order,
5890 NULL, depends, serv_name, pass);
5892 if (!service)
5894 if (GetLastError() != ERROR_SERVICE_EXISTS)
5895 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5897 else if (sd.lpDescription)
5899 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5900 WARN("failed to set service description %u\n", GetLastError());
5903 if (image_path != file->TargetPath) msi_free(image_path);
5904 done:
5905 if (service) CloseServiceHandle(service);
5906 if (hscm) CloseServiceHandle(hscm);
5907 msi_free(name);
5908 msi_free(disp);
5909 msi_free(sd.lpDescription);
5910 msi_free(load_order);
5911 msi_free(serv_name);
5912 msi_free(pass);
5913 msi_free(depends);
5914 msi_free(args);
5916 return ret;
5919 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5921 static const WCHAR query[] = {
5922 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5923 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5924 MSIQUERY *view;
5925 UINT rc;
5927 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5928 if (rc != ERROR_SUCCESS)
5929 return ERROR_SUCCESS;
5931 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5932 msiobj_release(&view->hdr);
5933 return rc;
5936 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5937 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5939 LPCWSTR *vector, *temp_vector;
5940 LPWSTR p, q;
5941 DWORD sep_len;
5943 static const WCHAR separator[] = {'[','~',']',0};
5945 *numargs = 0;
5946 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5948 if (!args)
5949 return NULL;
5951 vector = msi_alloc(sizeof(LPWSTR));
5952 if (!vector)
5953 return NULL;
5955 p = args;
5958 (*numargs)++;
5959 vector[*numargs - 1] = p;
5961 if ((q = strstrW(p, separator)))
5963 *q = '\0';
5965 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5966 if (!temp_vector)
5968 msi_free(vector);
5969 return NULL;
5971 vector = temp_vector;
5973 p = q + sep_len;
5975 } while (q);
5977 return vector;
5980 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5982 MSIPACKAGE *package = param;
5983 MSICOMPONENT *comp;
5984 MSIRECORD *uirow;
5985 SC_HANDLE scm = NULL, service = NULL;
5986 LPCWSTR component, *vector = NULL;
5987 LPWSTR name, args, display_name = NULL;
5988 DWORD event, numargs, len, wait, dummy;
5989 UINT r = ERROR_FUNCTION_FAILED;
5990 SERVICE_STATUS_PROCESS status;
5991 ULONGLONG start_time;
5993 component = MSI_RecordGetString(rec, 6);
5994 comp = msi_get_loaded_component(package, component);
5995 if (!comp)
5996 return ERROR_SUCCESS;
5998 event = MSI_RecordGetInteger( rec, 3 );
5999 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6001 comp->Action = msi_get_component_action( package, comp );
6002 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
6003 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
6005 TRACE("not starting %s\n", debugstr_w(name));
6006 msi_free( name );
6007 return ERROR_SUCCESS;
6010 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
6011 wait = MSI_RecordGetInteger(rec, 5);
6013 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
6014 if (!scm)
6016 ERR("Failed to open the service control manager\n");
6017 goto done;
6020 len = 0;
6021 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6022 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6024 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6025 GetServiceDisplayNameW( scm, name, display_name, &len );
6028 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
6029 if (!service)
6031 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6032 goto done;
6035 vector = msi_service_args_to_vector(args, &numargs);
6037 if (!StartServiceW(service, numargs, vector) &&
6038 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6040 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6041 goto done;
6044 r = ERROR_SUCCESS;
6045 if (wait)
6047 /* wait for at most 30 seconds for the service to be up and running */
6048 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6049 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6051 TRACE("failed to query service status (%u)\n", GetLastError());
6052 goto done;
6054 start_time = GetTickCount64();
6055 while (status.dwCurrentState == SERVICE_START_PENDING)
6057 if (GetTickCount64() - start_time > 30000) break;
6058 Sleep(1000);
6059 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6060 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6062 TRACE("failed to query service status (%u)\n", GetLastError());
6063 goto done;
6066 if (status.dwCurrentState != SERVICE_RUNNING)
6068 WARN("service failed to start %u\n", status.dwCurrentState);
6069 r = ERROR_FUNCTION_FAILED;
6073 done:
6074 uirow = MSI_CreateRecord( 2 );
6075 MSI_RecordSetStringW( uirow, 1, display_name );
6076 MSI_RecordSetStringW( uirow, 2, name );
6077 msi_ui_actiondata( package, szStartServices, uirow );
6078 msiobj_release( &uirow->hdr );
6080 if (service) CloseServiceHandle(service);
6081 if (scm) CloseServiceHandle(scm);
6083 msi_free(name);
6084 msi_free(args);
6085 msi_free(vector);
6086 msi_free(display_name);
6087 return r;
6090 static UINT ACTION_StartServices( MSIPACKAGE *package )
6092 static const WCHAR query[] = {
6093 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6094 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6095 MSIQUERY *view;
6096 UINT rc;
6098 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6099 if (rc != ERROR_SUCCESS)
6100 return ERROR_SUCCESS;
6102 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6103 msiobj_release(&view->hdr);
6104 return rc;
6107 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6109 DWORD i, needed, count;
6110 ENUM_SERVICE_STATUSW *dependencies;
6111 SERVICE_STATUS ss;
6112 SC_HANDLE depserv;
6113 BOOL stopped, ret = FALSE;
6115 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6116 0, &needed, &count))
6117 return TRUE;
6119 if (GetLastError() != ERROR_MORE_DATA)
6120 return FALSE;
6122 dependencies = msi_alloc(needed);
6123 if (!dependencies)
6124 return FALSE;
6126 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6127 needed, &needed, &count))
6128 goto done;
6130 for (i = 0; i < count; i++)
6132 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6133 SERVICE_STOP | SERVICE_QUERY_STATUS);
6134 if (!depserv)
6135 goto done;
6137 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6138 CloseServiceHandle(depserv);
6139 if (!stopped)
6140 goto done;
6143 ret = TRUE;
6145 done:
6146 msi_free(dependencies);
6147 return ret;
6150 static UINT stop_service( LPCWSTR name )
6152 SC_HANDLE scm = NULL, service = NULL;
6153 SERVICE_STATUS status;
6154 SERVICE_STATUS_PROCESS ssp;
6155 DWORD needed;
6157 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6158 if (!scm)
6160 WARN("Failed to open the SCM: %d\n", GetLastError());
6161 goto done;
6164 service = OpenServiceW(scm, name,
6165 SERVICE_STOP |
6166 SERVICE_QUERY_STATUS |
6167 SERVICE_ENUMERATE_DEPENDENTS);
6168 if (!service)
6170 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6171 goto done;
6174 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6175 sizeof(SERVICE_STATUS_PROCESS), &needed))
6177 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6178 goto done;
6181 if (ssp.dwCurrentState == SERVICE_STOPPED)
6182 goto done;
6184 stop_service_dependents(scm, service);
6186 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6187 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6189 done:
6190 if (service) CloseServiceHandle(service);
6191 if (scm) CloseServiceHandle(scm);
6193 return ERROR_SUCCESS;
6196 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6198 MSIPACKAGE *package = param;
6199 MSICOMPONENT *comp;
6200 MSIRECORD *uirow;
6201 LPCWSTR component;
6202 WCHAR *name, *display_name = NULL;
6203 DWORD event, len;
6204 SC_HANDLE scm;
6206 component = MSI_RecordGetString( rec, 6 );
6207 comp = msi_get_loaded_component( package, component );
6208 if (!comp)
6209 return ERROR_SUCCESS;
6211 event = MSI_RecordGetInteger( rec, 3 );
6212 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6214 comp->Action = msi_get_component_action( package, comp );
6215 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6216 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6218 TRACE("not stopping %s\n", debugstr_w(name));
6219 msi_free( name );
6220 return ERROR_SUCCESS;
6223 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6224 if (!scm)
6226 ERR("Failed to open the service control manager\n");
6227 goto done;
6230 len = 0;
6231 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6232 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6234 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6235 GetServiceDisplayNameW( scm, name, display_name, &len );
6237 CloseServiceHandle( scm );
6239 stop_service( name );
6241 done:
6242 uirow = MSI_CreateRecord( 2 );
6243 MSI_RecordSetStringW( uirow, 1, display_name );
6244 MSI_RecordSetStringW( uirow, 2, name );
6245 msi_ui_actiondata( package, szStopServices, uirow );
6246 msiobj_release( &uirow->hdr );
6248 msi_free( name );
6249 msi_free( display_name );
6250 return ERROR_SUCCESS;
6253 static UINT ACTION_StopServices( MSIPACKAGE *package )
6255 static const WCHAR query[] = {
6256 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6257 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6258 MSIQUERY *view;
6259 UINT rc;
6261 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6262 if (rc != ERROR_SUCCESS)
6263 return ERROR_SUCCESS;
6265 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6266 msiobj_release(&view->hdr);
6267 return rc;
6270 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6272 MSIPACKAGE *package = param;
6273 MSICOMPONENT *comp;
6274 MSIRECORD *uirow;
6275 LPWSTR name = NULL, display_name = NULL;
6276 DWORD event, len;
6277 SC_HANDLE scm = NULL, service = NULL;
6279 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6280 if (!comp)
6281 return ERROR_SUCCESS;
6283 event = MSI_RecordGetInteger( rec, 3 );
6284 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6286 comp->Action = msi_get_component_action( package, comp );
6287 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6288 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6290 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6291 msi_free( name );
6292 return ERROR_SUCCESS;
6294 stop_service( name );
6296 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6297 if (!scm)
6299 WARN("Failed to open the SCM: %d\n", GetLastError());
6300 goto done;
6303 len = 0;
6304 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6305 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6307 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6308 GetServiceDisplayNameW( scm, name, display_name, &len );
6311 service = OpenServiceW( scm, name, DELETE );
6312 if (!service)
6314 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6315 goto done;
6318 if (!DeleteService( service ))
6319 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6321 done:
6322 uirow = MSI_CreateRecord( 2 );
6323 MSI_RecordSetStringW( uirow, 1, display_name );
6324 MSI_RecordSetStringW( uirow, 2, name );
6325 msi_ui_actiondata( package, szDeleteServices, uirow );
6326 msiobj_release( &uirow->hdr );
6328 if (service) CloseServiceHandle( service );
6329 if (scm) CloseServiceHandle( scm );
6330 msi_free( name );
6331 msi_free( display_name );
6333 return ERROR_SUCCESS;
6336 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6338 static const WCHAR query[] = {
6339 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6340 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6341 MSIQUERY *view;
6342 UINT rc;
6344 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6345 if (rc != ERROR_SUCCESS)
6346 return ERROR_SUCCESS;
6348 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6349 msiobj_release( &view->hdr );
6350 return rc;
6353 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6355 MSIPACKAGE *package = param;
6356 LPWSTR driver, driver_path, ptr;
6357 WCHAR outpath[MAX_PATH];
6358 MSIFILE *driver_file = NULL, *setup_file = NULL;
6359 MSICOMPONENT *comp;
6360 MSIRECORD *uirow;
6361 LPCWSTR desc, file_key, component;
6362 DWORD len, usage;
6363 UINT r = ERROR_SUCCESS;
6365 static const WCHAR driver_fmt[] = {
6366 'D','r','i','v','e','r','=','%','s',0};
6367 static const WCHAR setup_fmt[] = {
6368 'S','e','t','u','p','=','%','s',0};
6369 static const WCHAR usage_fmt[] = {
6370 'F','i','l','e','U','s','a','g','e','=','1',0};
6372 component = MSI_RecordGetString( rec, 2 );
6373 comp = msi_get_loaded_component( package, component );
6374 if (!comp)
6375 return ERROR_SUCCESS;
6377 comp->Action = msi_get_component_action( package, comp );
6378 if (comp->Action != INSTALLSTATE_LOCAL)
6380 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6381 return ERROR_SUCCESS;
6383 desc = MSI_RecordGetString(rec, 3);
6385 file_key = MSI_RecordGetString( rec, 4 );
6386 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6388 file_key = MSI_RecordGetString( rec, 5 );
6389 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6391 if (!driver_file)
6393 ERR("ODBC Driver entry not found!\n");
6394 return ERROR_FUNCTION_FAILED;
6397 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6398 if (setup_file)
6399 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6400 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6402 driver = msi_alloc(len * sizeof(WCHAR));
6403 if (!driver)
6404 return ERROR_OUTOFMEMORY;
6406 ptr = driver;
6407 lstrcpyW(ptr, desc);
6408 ptr += lstrlenW(ptr) + 1;
6410 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6411 ptr += len + 1;
6413 if (setup_file)
6415 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6416 ptr += len + 1;
6419 lstrcpyW(ptr, usage_fmt);
6420 ptr += lstrlenW(ptr) + 1;
6421 *ptr = '\0';
6423 if (!driver_file->TargetPath)
6425 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6426 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6428 driver_path = strdupW(driver_file->TargetPath);
6429 ptr = strrchrW(driver_path, '\\');
6430 if (ptr) *ptr = '\0';
6432 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6433 NULL, ODBC_INSTALL_COMPLETE, &usage))
6435 ERR("Failed to install SQL driver!\n");
6436 r = ERROR_FUNCTION_FAILED;
6439 uirow = MSI_CreateRecord( 5 );
6440 MSI_RecordSetStringW( uirow, 1, desc );
6441 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6442 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6443 msi_ui_actiondata( package, szInstallODBC, uirow );
6444 msiobj_release( &uirow->hdr );
6446 msi_free(driver);
6447 msi_free(driver_path);
6449 return r;
6452 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6454 MSIPACKAGE *package = param;
6455 LPWSTR translator, translator_path, ptr;
6456 WCHAR outpath[MAX_PATH];
6457 MSIFILE *translator_file = NULL, *setup_file = NULL;
6458 MSICOMPONENT *comp;
6459 MSIRECORD *uirow;
6460 LPCWSTR desc, file_key, component;
6461 DWORD len, usage;
6462 UINT r = ERROR_SUCCESS;
6464 static const WCHAR translator_fmt[] = {
6465 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6466 static const WCHAR setup_fmt[] = {
6467 'S','e','t','u','p','=','%','s',0};
6469 component = MSI_RecordGetString( rec, 2 );
6470 comp = msi_get_loaded_component( package, component );
6471 if (!comp)
6472 return ERROR_SUCCESS;
6474 comp->Action = msi_get_component_action( package, comp );
6475 if (comp->Action != INSTALLSTATE_LOCAL)
6477 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6478 return ERROR_SUCCESS;
6480 desc = MSI_RecordGetString(rec, 3);
6482 file_key = MSI_RecordGetString( rec, 4 );
6483 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6485 file_key = MSI_RecordGetString( rec, 5 );
6486 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6488 if (!translator_file)
6490 ERR("ODBC Translator entry not found!\n");
6491 return ERROR_FUNCTION_FAILED;
6494 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6495 if (setup_file)
6496 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6498 translator = msi_alloc(len * sizeof(WCHAR));
6499 if (!translator)
6500 return ERROR_OUTOFMEMORY;
6502 ptr = translator;
6503 lstrcpyW(ptr, desc);
6504 ptr += lstrlenW(ptr) + 1;
6506 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6507 ptr += len + 1;
6509 if (setup_file)
6511 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6512 ptr += len + 1;
6514 *ptr = '\0';
6516 translator_path = strdupW(translator_file->TargetPath);
6517 ptr = strrchrW(translator_path, '\\');
6518 if (ptr) *ptr = '\0';
6520 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6521 NULL, ODBC_INSTALL_COMPLETE, &usage))
6523 ERR("Failed to install SQL translator!\n");
6524 r = ERROR_FUNCTION_FAILED;
6527 uirow = MSI_CreateRecord( 5 );
6528 MSI_RecordSetStringW( uirow, 1, desc );
6529 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6530 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6531 msi_ui_actiondata( package, szInstallODBC, uirow );
6532 msiobj_release( &uirow->hdr );
6534 msi_free(translator);
6535 msi_free(translator_path);
6537 return r;
6540 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6542 MSIPACKAGE *package = param;
6543 MSICOMPONENT *comp;
6544 LPWSTR attrs;
6545 LPCWSTR desc, driver, component;
6546 WORD request = ODBC_ADD_SYS_DSN;
6547 INT registration;
6548 DWORD len;
6549 UINT r = ERROR_SUCCESS;
6550 MSIRECORD *uirow;
6552 static const WCHAR attrs_fmt[] = {
6553 'D','S','N','=','%','s',0 };
6555 component = MSI_RecordGetString( rec, 2 );
6556 comp = msi_get_loaded_component( package, component );
6557 if (!comp)
6558 return ERROR_SUCCESS;
6560 comp->Action = msi_get_component_action( package, comp );
6561 if (comp->Action != INSTALLSTATE_LOCAL)
6563 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6564 return ERROR_SUCCESS;
6567 desc = MSI_RecordGetString(rec, 3);
6568 driver = MSI_RecordGetString(rec, 4);
6569 registration = MSI_RecordGetInteger(rec, 5);
6571 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6572 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6574 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6575 attrs = msi_alloc(len * sizeof(WCHAR));
6576 if (!attrs)
6577 return ERROR_OUTOFMEMORY;
6579 len = sprintfW(attrs, attrs_fmt, desc);
6580 attrs[len + 1] = 0;
6582 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6584 ERR("Failed to install SQL data source!\n");
6585 r = ERROR_FUNCTION_FAILED;
6588 uirow = MSI_CreateRecord( 5 );
6589 MSI_RecordSetStringW( uirow, 1, desc );
6590 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6591 MSI_RecordSetInteger( uirow, 3, request );
6592 msi_ui_actiondata( package, szInstallODBC, uirow );
6593 msiobj_release( &uirow->hdr );
6595 msi_free(attrs);
6597 return r;
6600 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6602 static const WCHAR driver_query[] = {
6603 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6604 'O','D','B','C','D','r','i','v','e','r',0};
6605 static const WCHAR translator_query[] = {
6606 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6607 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6608 static const WCHAR source_query[] = {
6609 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6610 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6611 MSIQUERY *view;
6612 UINT rc;
6614 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6615 if (rc == ERROR_SUCCESS)
6617 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6618 msiobj_release(&view->hdr);
6619 if (rc != ERROR_SUCCESS)
6620 return rc;
6622 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6623 if (rc == ERROR_SUCCESS)
6625 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6626 msiobj_release(&view->hdr);
6627 if (rc != ERROR_SUCCESS)
6628 return rc;
6630 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6631 if (rc == ERROR_SUCCESS)
6633 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6634 msiobj_release(&view->hdr);
6635 if (rc != ERROR_SUCCESS)
6636 return rc;
6638 return ERROR_SUCCESS;
6641 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6643 MSIPACKAGE *package = param;
6644 MSICOMPONENT *comp;
6645 MSIRECORD *uirow;
6646 DWORD usage;
6647 LPCWSTR desc, component;
6649 component = MSI_RecordGetString( rec, 2 );
6650 comp = msi_get_loaded_component( package, component );
6651 if (!comp)
6652 return ERROR_SUCCESS;
6654 comp->Action = msi_get_component_action( package, comp );
6655 if (comp->Action != INSTALLSTATE_ABSENT)
6657 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6658 return ERROR_SUCCESS;
6661 desc = MSI_RecordGetString( rec, 3 );
6662 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6664 WARN("Failed to remove ODBC driver\n");
6666 else if (!usage)
6668 FIXME("Usage count reached 0\n");
6671 uirow = MSI_CreateRecord( 2 );
6672 MSI_RecordSetStringW( uirow, 1, desc );
6673 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6674 msi_ui_actiondata( package, szRemoveODBC, uirow );
6675 msiobj_release( &uirow->hdr );
6677 return ERROR_SUCCESS;
6680 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6682 MSIPACKAGE *package = param;
6683 MSICOMPONENT *comp;
6684 MSIRECORD *uirow;
6685 DWORD usage;
6686 LPCWSTR desc, component;
6688 component = MSI_RecordGetString( rec, 2 );
6689 comp = msi_get_loaded_component( package, component );
6690 if (!comp)
6691 return ERROR_SUCCESS;
6693 comp->Action = msi_get_component_action( package, comp );
6694 if (comp->Action != INSTALLSTATE_ABSENT)
6696 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6697 return ERROR_SUCCESS;
6700 desc = MSI_RecordGetString( rec, 3 );
6701 if (!SQLRemoveTranslatorW( desc, &usage ))
6703 WARN("Failed to remove ODBC translator\n");
6705 else if (!usage)
6707 FIXME("Usage count reached 0\n");
6710 uirow = MSI_CreateRecord( 2 );
6711 MSI_RecordSetStringW( uirow, 1, desc );
6712 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6713 msi_ui_actiondata( package, szRemoveODBC, uirow );
6714 msiobj_release( &uirow->hdr );
6716 return ERROR_SUCCESS;
6719 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6721 MSIPACKAGE *package = param;
6722 MSICOMPONENT *comp;
6723 MSIRECORD *uirow;
6724 LPWSTR attrs;
6725 LPCWSTR desc, driver, component;
6726 WORD request = ODBC_REMOVE_SYS_DSN;
6727 INT registration;
6728 DWORD len;
6730 static const WCHAR attrs_fmt[] = {
6731 'D','S','N','=','%','s',0 };
6733 component = MSI_RecordGetString( rec, 2 );
6734 comp = msi_get_loaded_component( package, component );
6735 if (!comp)
6736 return ERROR_SUCCESS;
6738 comp->Action = msi_get_component_action( package, comp );
6739 if (comp->Action != INSTALLSTATE_ABSENT)
6741 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6742 return ERROR_SUCCESS;
6745 desc = MSI_RecordGetString( rec, 3 );
6746 driver = MSI_RecordGetString( rec, 4 );
6747 registration = MSI_RecordGetInteger( rec, 5 );
6749 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6750 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6752 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6753 attrs = msi_alloc( len * sizeof(WCHAR) );
6754 if (!attrs)
6755 return ERROR_OUTOFMEMORY;
6757 FIXME("Use ODBCSourceAttribute table\n");
6759 len = sprintfW( attrs, attrs_fmt, desc );
6760 attrs[len + 1] = 0;
6762 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6764 WARN("Failed to remove ODBC data source\n");
6766 msi_free( attrs );
6768 uirow = MSI_CreateRecord( 3 );
6769 MSI_RecordSetStringW( uirow, 1, desc );
6770 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6771 MSI_RecordSetInteger( uirow, 3, request );
6772 msi_ui_actiondata( package, szRemoveODBC, uirow );
6773 msiobj_release( &uirow->hdr );
6775 return ERROR_SUCCESS;
6778 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6780 static const WCHAR driver_query[] = {
6781 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6782 'O','D','B','C','D','r','i','v','e','r',0};
6783 static const WCHAR translator_query[] = {
6784 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6785 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6786 static const WCHAR source_query[] = {
6787 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6788 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6789 MSIQUERY *view;
6790 UINT rc;
6792 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6793 if (rc == ERROR_SUCCESS)
6795 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6796 msiobj_release( &view->hdr );
6797 if (rc != ERROR_SUCCESS)
6798 return rc;
6800 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6801 if (rc == ERROR_SUCCESS)
6803 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6804 msiobj_release( &view->hdr );
6805 if (rc != ERROR_SUCCESS)
6806 return rc;
6808 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6809 if (rc == ERROR_SUCCESS)
6811 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6812 msiobj_release( &view->hdr );
6813 if (rc != ERROR_SUCCESS)
6814 return rc;
6816 return ERROR_SUCCESS;
6819 #define ENV_ACT_SETALWAYS 0x1
6820 #define ENV_ACT_SETABSENT 0x2
6821 #define ENV_ACT_REMOVE 0x4
6822 #define ENV_ACT_REMOVEMATCH 0x8
6824 #define ENV_MOD_MACHINE 0x20000000
6825 #define ENV_MOD_APPEND 0x40000000
6826 #define ENV_MOD_PREFIX 0x80000000
6827 #define ENV_MOD_MASK 0xC0000000
6829 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6831 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6833 LPCWSTR cptr = *name;
6835 static const WCHAR prefix[] = {'[','~',']',0};
6836 static const int prefix_len = 3;
6838 *flags = 0;
6839 while (*cptr)
6841 if (*cptr == '=')
6842 *flags |= ENV_ACT_SETALWAYS;
6843 else if (*cptr == '+')
6844 *flags |= ENV_ACT_SETABSENT;
6845 else if (*cptr == '-')
6846 *flags |= ENV_ACT_REMOVE;
6847 else if (*cptr == '!')
6848 *flags |= ENV_ACT_REMOVEMATCH;
6849 else if (*cptr == '*')
6850 *flags |= ENV_MOD_MACHINE;
6851 else
6852 break;
6854 cptr++;
6855 (*name)++;
6858 if (!*cptr)
6860 ERR("Missing environment variable\n");
6861 return ERROR_FUNCTION_FAILED;
6864 if (*value)
6866 LPCWSTR ptr = *value;
6867 if (!strncmpW(ptr, prefix, prefix_len))
6869 if (ptr[prefix_len] == szSemiColon[0])
6871 *flags |= ENV_MOD_APPEND;
6872 *value += lstrlenW(prefix);
6874 else
6876 *value = NULL;
6879 else if (lstrlenW(*value) >= prefix_len)
6881 ptr += lstrlenW(ptr) - prefix_len;
6882 if (!strcmpW( ptr, prefix ))
6884 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6886 *flags |= ENV_MOD_PREFIX;
6887 /* the "[~]" will be removed by deformat_string */;
6889 else
6891 *value = NULL;
6897 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6898 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6899 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6900 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6902 ERR("Invalid flags: %08x\n", *flags);
6903 return ERROR_FUNCTION_FAILED;
6906 if (!*flags)
6907 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6909 return ERROR_SUCCESS;
6912 static UINT open_env_key( DWORD flags, HKEY *key )
6914 static const WCHAR user_env[] =
6915 {'E','n','v','i','r','o','n','m','e','n','t',0};
6916 static const WCHAR machine_env[] =
6917 {'S','y','s','t','e','m','\\',
6918 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6919 'C','o','n','t','r','o','l','\\',
6920 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6921 'E','n','v','i','r','o','n','m','e','n','t',0};
6922 const WCHAR *env;
6923 HKEY root;
6924 LONG res;
6926 if (flags & ENV_MOD_MACHINE)
6928 env = machine_env;
6929 root = HKEY_LOCAL_MACHINE;
6931 else
6933 env = user_env;
6934 root = HKEY_CURRENT_USER;
6937 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6938 if (res != ERROR_SUCCESS)
6940 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6941 return ERROR_FUNCTION_FAILED;
6944 return ERROR_SUCCESS;
6947 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6949 MSIPACKAGE *package = param;
6950 LPCWSTR name, value, component;
6951 WCHAR *data = NULL, *newval = NULL, *deformatted = NULL, *p, *q;
6952 DWORD flags, type, size, len, len_value = 0;
6953 UINT res;
6954 HKEY env = NULL;
6955 MSICOMPONENT *comp;
6956 MSIRECORD *uirow;
6957 int action = 0, found = 0;
6959 component = MSI_RecordGetString(rec, 4);
6960 comp = msi_get_loaded_component(package, component);
6961 if (!comp)
6962 return ERROR_SUCCESS;
6964 comp->Action = msi_get_component_action( package, comp );
6965 if (comp->Action != INSTALLSTATE_LOCAL)
6967 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6968 return ERROR_SUCCESS;
6970 name = MSI_RecordGetString(rec, 2);
6971 value = MSI_RecordGetString(rec, 3);
6973 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6975 res = env_parse_flags(&name, &value, &flags);
6976 if (res != ERROR_SUCCESS || !value)
6977 goto done;
6979 if (value && !deformat_string(package, value, &deformatted))
6981 res = ERROR_OUTOFMEMORY;
6982 goto done;
6985 if ((value = deformatted))
6987 if (flags & ENV_MOD_PREFIX)
6989 p = strrchrW( value, ';' );
6990 len_value = p - value;
6992 else if (flags & ENV_MOD_APPEND)
6994 value = strchrW( value, ';' ) + 1;
6995 len_value = strlenW( value );
6997 else len_value = strlenW( value );
7000 res = open_env_key( flags, &env );
7001 if (res != ERROR_SUCCESS)
7002 goto done;
7004 if (flags & ENV_MOD_MACHINE)
7005 action |= 0x20000000;
7007 size = 0;
7008 type = REG_SZ;
7009 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
7010 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
7011 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
7012 goto done;
7014 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
7016 action = 0x2;
7018 /* Nothing to do. */
7019 if (!value)
7021 res = ERROR_SUCCESS;
7022 goto done;
7024 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
7025 newval = strdupW(value);
7026 if (!newval)
7028 res = ERROR_OUTOFMEMORY;
7029 goto done;
7032 else
7034 action = 0x1;
7036 /* Contrary to MSDN, +-variable to [~];path works */
7037 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7039 res = ERROR_SUCCESS;
7040 goto done;
7043 if (!(p = q = data = msi_alloc( size )))
7045 msi_free(deformatted);
7046 RegCloseKey(env);
7047 return ERROR_OUTOFMEMORY;
7050 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)data, &size );
7051 if (res != ERROR_SUCCESS)
7052 goto done;
7054 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7056 action = 0x4;
7057 res = RegDeleteValueW(env, name);
7058 if (res != ERROR_SUCCESS)
7059 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7060 goto done;
7063 for (;;)
7065 while (*q && *q != ';') q++;
7066 len = q - p;
7067 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ) &&
7068 (!p[len] || p[len] == ';'))
7070 found = 1;
7071 break;
7073 if (!*q) break;
7074 p = ++q;
7077 if (found)
7079 TRACE("string already set\n");
7080 goto done;
7083 size = (len_value + 1 + strlenW( data ) + 1) * sizeof(WCHAR);
7084 if (!(p = newval = msi_alloc( size )))
7086 res = ERROR_OUTOFMEMORY;
7087 goto done;
7090 if (flags & ENV_MOD_PREFIX)
7092 memcpy( newval, value, len_value * sizeof(WCHAR) );
7093 newval[len_value] = ';';
7094 p = newval + len_value + 1;
7095 action |= 0x80000000;
7098 strcpyW( p, data );
7100 if (flags & ENV_MOD_APPEND)
7102 p += strlenW( data );
7103 *p++ = ';';
7104 memcpy( p, value, (len_value + 1) * sizeof(WCHAR) );
7105 action |= 0x40000000;
7108 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7109 res = RegSetValueExW( env, name, 0, type, (BYTE *)newval, size );
7110 if (res)
7112 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7115 done:
7116 uirow = MSI_CreateRecord( 3 );
7117 MSI_RecordSetStringW( uirow, 1, name );
7118 MSI_RecordSetStringW( uirow, 2, newval );
7119 MSI_RecordSetInteger( uirow, 3, action );
7120 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7121 msiobj_release( &uirow->hdr );
7123 if (env) RegCloseKey(env);
7124 msi_free(deformatted);
7125 msi_free(data);
7126 msi_free(newval);
7127 return res;
7130 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7132 static const WCHAR query[] = {
7133 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7134 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7135 MSIQUERY *view;
7136 UINT rc;
7138 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7139 if (rc != ERROR_SUCCESS)
7140 return ERROR_SUCCESS;
7142 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7143 msiobj_release(&view->hdr);
7144 return rc;
7147 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7149 MSIPACKAGE *package = param;
7150 LPCWSTR name, value, component;
7151 WCHAR *p, *q, *deformatted = NULL, *new_value = NULL;
7152 DWORD flags, type, size, len, len_value = 0, len_new_value;
7153 HKEY env;
7154 MSICOMPONENT *comp;
7155 MSIRECORD *uirow;
7156 int action = 0;
7157 LONG res;
7158 UINT r;
7160 component = MSI_RecordGetString( rec, 4 );
7161 comp = msi_get_loaded_component( package, component );
7162 if (!comp)
7163 return ERROR_SUCCESS;
7165 comp->Action = msi_get_component_action( package, comp );
7166 if (comp->Action != INSTALLSTATE_ABSENT)
7168 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7169 return ERROR_SUCCESS;
7171 name = MSI_RecordGetString( rec, 2 );
7172 value = MSI_RecordGetString( rec, 3 );
7174 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7176 r = env_parse_flags( &name, &value, &flags );
7177 if (r != ERROR_SUCCESS)
7178 return r;
7180 if (!(flags & ENV_ACT_REMOVE))
7182 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7183 return ERROR_SUCCESS;
7186 if (value && !deformat_string( package, value, &deformatted ))
7187 return ERROR_OUTOFMEMORY;
7189 if ((value = deformatted))
7191 if (flags & ENV_MOD_PREFIX)
7193 p = strchrW( value, ';' );
7194 len_value = p - value;
7196 else if (flags & ENV_MOD_APPEND)
7198 value = strchrW( value, ';' ) + 1;
7199 len_value = strlenW( value );
7201 else len_value = strlenW( value );
7204 r = open_env_key( flags, &env );
7205 if (r != ERROR_SUCCESS)
7207 r = ERROR_SUCCESS;
7208 goto done;
7211 if (flags & ENV_MOD_MACHINE)
7212 action |= 0x20000000;
7214 size = 0;
7215 type = REG_SZ;
7216 res = RegQueryValueExW( env, name, NULL, &type, NULL, &size );
7217 if (res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ))
7218 goto done;
7220 if (!(new_value = msi_alloc( size ))) goto done;
7222 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)new_value, &size );
7223 if (res != ERROR_SUCCESS)
7224 goto done;
7226 len_new_value = size / sizeof(WCHAR) - 1;
7227 p = q = new_value;
7228 for (;;)
7230 while (*q && *q != ';') q++;
7231 len = q - p;
7232 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ))
7234 if (*q == ';') q++;
7235 memmove( p, q, (len_new_value - (q - new_value) + 1) * sizeof(WCHAR) );
7236 break;
7238 if (!*q) break;
7239 p = ++q;
7242 if (!new_value[0] || !value)
7244 TRACE("removing %s\n", debugstr_w(name));
7245 res = RegDeleteValueW( env, name );
7246 if (res != ERROR_SUCCESS)
7247 WARN("failed to delete value %s (%d)\n", debugstr_w(name), res);
7249 else
7251 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(new_value));
7252 size = (strlenW( new_value ) + 1) * sizeof(WCHAR);
7253 res = RegSetValueExW( env, name, 0, type, (BYTE *)new_value, size );
7254 if (res != ERROR_SUCCESS)
7255 WARN("failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(new_value), res);
7258 done:
7259 uirow = MSI_CreateRecord( 3 );
7260 MSI_RecordSetStringW( uirow, 1, name );
7261 MSI_RecordSetStringW( uirow, 2, value );
7262 MSI_RecordSetInteger( uirow, 3, action );
7263 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7264 msiobj_release( &uirow->hdr );
7266 if (env) RegCloseKey( env );
7267 msi_free( deformatted );
7268 msi_free( new_value );
7269 return r;
7272 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7274 static const WCHAR query[] = {
7275 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7276 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7277 MSIQUERY *view;
7278 UINT rc;
7280 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7281 if (rc != ERROR_SUCCESS)
7282 return ERROR_SUCCESS;
7284 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7285 msiobj_release( &view->hdr );
7286 return rc;
7289 UINT msi_validate_product_id( MSIPACKAGE *package )
7291 LPWSTR key, template, id;
7292 UINT r = ERROR_SUCCESS;
7294 id = msi_dup_property( package->db, szProductID );
7295 if (id)
7297 msi_free( id );
7298 return ERROR_SUCCESS;
7300 template = msi_dup_property( package->db, szPIDTemplate );
7301 key = msi_dup_property( package->db, szPIDKEY );
7302 if (key && template)
7304 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7305 r = msi_set_property( package->db, szProductID, key, -1 );
7307 msi_free( template );
7308 msi_free( key );
7309 return r;
7312 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7314 return msi_validate_product_id( package );
7317 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7319 TRACE("\n");
7320 package->need_reboot_at_end = 1;
7321 return ERROR_SUCCESS;
7324 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7326 static const WCHAR szAvailableFreeReg[] =
7327 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7328 MSIRECORD *uirow;
7329 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7331 TRACE("%p %d kilobytes\n", package, space);
7333 uirow = MSI_CreateRecord( 1 );
7334 MSI_RecordSetInteger( uirow, 1, space );
7335 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7336 msiobj_release( &uirow->hdr );
7338 return ERROR_SUCCESS;
7341 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7343 TRACE("%p\n", package);
7345 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7346 return ERROR_SUCCESS;
7349 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7351 FIXME("%p\n", package);
7352 return ERROR_SUCCESS;
7355 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7357 static const WCHAR driver_query[] = {
7358 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7359 'O','D','B','C','D','r','i','v','e','r',0};
7360 static const WCHAR translator_query[] = {
7361 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7362 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7363 MSIQUERY *view;
7364 UINT r, count;
7366 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7367 if (r == ERROR_SUCCESS)
7369 count = 0;
7370 r = MSI_IterateRecords( view, &count, NULL, package );
7371 msiobj_release( &view->hdr );
7372 if (r != ERROR_SUCCESS)
7373 return r;
7374 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7376 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7377 if (r == ERROR_SUCCESS)
7379 count = 0;
7380 r = MSI_IterateRecords( view, &count, NULL, package );
7381 msiobj_release( &view->hdr );
7382 if (r != ERROR_SUCCESS)
7383 return r;
7384 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7386 return ERROR_SUCCESS;
7389 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7391 static const WCHAR fmtW[] =
7392 {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7393 MSIPACKAGE *package = param;
7394 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7395 int attrs = MSI_RecordGetInteger( rec, 5 );
7396 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7397 WCHAR *product, *features, *cmd;
7398 STARTUPINFOW si;
7399 PROCESS_INFORMATION info;
7400 BOOL ret;
7402 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7403 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7405 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7407 len += strlenW( product );
7408 if (features)
7409 len += strlenW( features );
7410 else
7411 len += sizeof(szAll) / sizeof(szAll[0]);
7413 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7415 msi_free( product );
7416 msi_free( features );
7417 return ERROR_OUTOFMEMORY;
7419 sprintfW( cmd, fmtW, product, features ? features : szAll );
7420 msi_free( product );
7421 msi_free( features );
7423 memset( &si, 0, sizeof(STARTUPINFOW) );
7424 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7425 msi_free( cmd );
7426 if (!ret) return GetLastError();
7427 CloseHandle( info.hThread );
7429 WaitForSingleObject( info.hProcess, INFINITE );
7430 CloseHandle( info.hProcess );
7431 return ERROR_SUCCESS;
7434 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7436 static const WCHAR query[] = {
7437 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7438 MSIQUERY *view;
7439 UINT r;
7441 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7442 if (r == ERROR_SUCCESS)
7444 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7445 msiobj_release( &view->hdr );
7446 if (r != ERROR_SUCCESS)
7447 return r;
7449 return ERROR_SUCCESS;
7452 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7454 MSIPACKAGE *package = param;
7455 int attributes = MSI_RecordGetInteger( rec, 5 );
7457 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7459 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7460 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7461 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7462 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7463 HKEY hkey;
7464 UINT r;
7466 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7468 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7469 if (r != ERROR_SUCCESS)
7470 return ERROR_SUCCESS;
7472 else
7474 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7475 if (r != ERROR_SUCCESS)
7476 return ERROR_SUCCESS;
7478 RegCloseKey( hkey );
7480 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7481 debugstr_w(upgrade_code), debugstr_w(version_min),
7482 debugstr_w(version_max), debugstr_w(language));
7484 return ERROR_SUCCESS;
7487 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7489 static const WCHAR query[] = {
7490 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7491 'U','p','g','r','a','d','e',0};
7492 MSIQUERY *view;
7493 UINT r;
7495 if (msi_get_property_int( package->db, szInstalled, 0 ))
7497 TRACE("product is installed, skipping action\n");
7498 return ERROR_SUCCESS;
7500 if (msi_get_property_int( package->db, szPreselected, 0 ))
7502 TRACE("Preselected property is set, not migrating feature states\n");
7503 return ERROR_SUCCESS;
7505 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7506 if (r == ERROR_SUCCESS)
7508 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7509 msiobj_release( &view->hdr );
7510 if (r != ERROR_SUCCESS)
7511 return r;
7513 return ERROR_SUCCESS;
7516 static void bind_image( const char *filename, const char *path )
7518 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7520 WARN("failed to bind image %u\n", GetLastError());
7524 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7526 UINT i;
7527 MSIFILE *file;
7528 MSIPACKAGE *package = param;
7529 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7530 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7531 char *filenameA, *pathA;
7532 WCHAR *pathW, **path_list;
7534 if (!(file = msi_get_loaded_file( package, key )))
7536 WARN("file %s not found\n", debugstr_w(key));
7537 return ERROR_SUCCESS;
7539 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7540 path_list = msi_split_string( paths, ';' );
7541 if (!path_list) bind_image( filenameA, NULL );
7542 else
7544 for (i = 0; path_list[i] && path_list[i][0]; i++)
7546 deformat_string( package, path_list[i], &pathW );
7547 if ((pathA = strdupWtoA( pathW )))
7549 bind_image( filenameA, pathA );
7550 msi_free( pathA );
7552 msi_free( pathW );
7555 msi_free( path_list );
7556 msi_free( filenameA );
7557 return ERROR_SUCCESS;
7560 static UINT ACTION_BindImage( MSIPACKAGE *package )
7562 static const WCHAR query[] = {
7563 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7564 'B','i','n','d','I','m','a','g','e',0};
7565 MSIQUERY *view;
7566 UINT r;
7568 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7569 if (r == ERROR_SUCCESS)
7571 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7572 msiobj_release( &view->hdr );
7573 if (r != ERROR_SUCCESS)
7574 return r;
7576 return ERROR_SUCCESS;
7579 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7581 static const WCHAR query[] = {
7582 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7583 MSIQUERY *view;
7584 DWORD count = 0;
7585 UINT r;
7587 r = MSI_OpenQuery( package->db, &view, query, table );
7588 if (r == ERROR_SUCCESS)
7590 r = MSI_IterateRecords(view, &count, NULL, package);
7591 msiobj_release(&view->hdr);
7592 if (r != ERROR_SUCCESS)
7593 return r;
7595 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7596 return ERROR_SUCCESS;
7599 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7601 static const WCHAR table[] = {
7602 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7603 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7606 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7608 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7609 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7612 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7614 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7615 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7618 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7620 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7621 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7624 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7626 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7627 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7630 static const struct
7632 const WCHAR *action;
7633 UINT (*handler)(MSIPACKAGE *);
7634 const WCHAR *action_rollback;
7636 StandardActions[] =
7638 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7639 { szAppSearch, ACTION_AppSearch, NULL },
7640 { szBindImage, ACTION_BindImage, NULL },
7641 { szCCPSearch, ACTION_CCPSearch, NULL },
7642 { szCostFinalize, ACTION_CostFinalize, NULL },
7643 { szCostInitialize, ACTION_CostInitialize, NULL },
7644 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7645 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7646 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7647 { szDisableRollback, ACTION_DisableRollback, NULL },
7648 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7649 { szExecuteAction, ACTION_ExecuteAction, NULL },
7650 { szFileCost, ACTION_FileCost, NULL },
7651 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7652 { szForceReboot, ACTION_ForceReboot, NULL },
7653 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7654 { szInstallExecute, ACTION_InstallExecute, NULL },
7655 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7656 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7657 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7658 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7659 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7660 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7661 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7662 { szInstallValidate, ACTION_InstallValidate, NULL },
7663 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7664 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7665 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7666 { szMoveFiles, ACTION_MoveFiles, NULL },
7667 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7668 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7669 { szPatchFiles, ACTION_PatchFiles, NULL },
7670 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7671 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7672 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7673 { szPublishProduct, ACTION_PublishProduct, NULL },
7674 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7675 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7676 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7677 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7678 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7679 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7680 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7681 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7682 { szRegisterUser, ACTION_RegisterUser, NULL },
7683 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7684 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7685 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7686 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7687 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7688 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7689 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7690 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7691 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7692 { szResolveSource, ACTION_ResolveSource, NULL },
7693 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7694 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7695 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7696 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7697 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7698 { szStartServices, ACTION_StartServices, szStopServices },
7699 { szStopServices, ACTION_StopServices, szStartServices },
7700 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7701 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7702 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7703 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7704 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7705 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7706 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7707 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7708 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7709 { szValidateProductID, ACTION_ValidateProductID, NULL },
7710 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7711 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7712 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7713 { NULL, NULL, NULL }
7716 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7718 BOOL ret = FALSE;
7719 UINT i;
7721 i = 0;
7722 while (StandardActions[i].action != NULL)
7724 if (!strcmpW( StandardActions[i].action, action ))
7726 ui_actionstart( package, action );
7727 if (StandardActions[i].handler)
7729 ui_actioninfo( package, action, TRUE, 0 );
7730 *rc = StandardActions[i].handler( package );
7731 ui_actioninfo( package, action, FALSE, *rc );
7733 if (StandardActions[i].action_rollback && !package->need_rollback)
7735 TRACE("scheduling rollback action\n");
7736 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7739 else
7741 FIXME("unhandled standard action %s\n", debugstr_w(action));
7742 *rc = ERROR_SUCCESS;
7744 ret = TRUE;
7745 break;
7747 i++;
7749 return ret;
7752 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7754 UINT rc = ERROR_SUCCESS;
7755 BOOL handled;
7757 TRACE("Performing action (%s)\n", debugstr_w(action));
7759 handled = ACTION_HandleStandardAction(package, action, &rc);
7761 if (!handled)
7762 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7764 if (!handled)
7766 WARN("unhandled msi action %s\n", debugstr_w(action));
7767 rc = ERROR_FUNCTION_NOT_CALLED;
7770 return rc;
7773 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7775 UINT rc = ERROR_SUCCESS;
7776 BOOL handled = FALSE;
7778 TRACE("Performing action (%s)\n", debugstr_w(action));
7780 package->action_progress_increment = 0;
7781 handled = ACTION_HandleStandardAction(package, action, &rc);
7783 if (!handled)
7784 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7786 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7787 handled = TRUE;
7789 if (!handled)
7791 WARN("unhandled msi action %s\n", debugstr_w(action));
7792 rc = ERROR_FUNCTION_NOT_CALLED;
7795 return rc;
7798 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7800 UINT rc = ERROR_SUCCESS;
7801 MSIRECORD *row;
7803 static const WCHAR query[] =
7804 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7805 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7806 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7807 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7808 static const WCHAR ui_query[] =
7809 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7810 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7811 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7812 ' ', '=',' ','%','i',0};
7814 if (needs_ui_sequence(package))
7815 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7816 else
7817 row = MSI_QueryGetRecord(package->db, query, seq);
7819 if (row)
7821 LPCWSTR action, cond;
7823 TRACE("Running the actions\n");
7825 /* check conditions */
7826 cond = MSI_RecordGetString(row, 2);
7828 /* this is a hack to skip errors in the condition code */
7829 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7831 msiobj_release(&row->hdr);
7832 return ERROR_SUCCESS;
7835 action = MSI_RecordGetString(row, 1);
7836 if (!action)
7838 ERR("failed to fetch action\n");
7839 msiobj_release(&row->hdr);
7840 return ERROR_FUNCTION_FAILED;
7843 if (needs_ui_sequence(package))
7844 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7845 else
7846 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7848 msiobj_release(&row->hdr);
7851 return rc;
7854 /****************************************************
7855 * TOP level entry points
7856 *****************************************************/
7858 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7859 LPCWSTR szCommandLine )
7861 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7862 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7863 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7864 WCHAR *reinstall, *remove, *patch, *productcode;
7865 BOOL ui_exists;
7866 UINT rc;
7868 msi_set_property( package->db, szAction, szInstall, -1 );
7870 package->script->InWhatSequence = SEQUENCE_INSTALL;
7872 if (szPackagePath)
7874 LPWSTR p, dir;
7875 LPCWSTR file;
7877 dir = strdupW(szPackagePath);
7878 p = strrchrW(dir, '\\');
7879 if (p)
7881 *(++p) = 0;
7882 file = szPackagePath + (p - dir);
7884 else
7886 msi_free(dir);
7887 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7888 GetCurrentDirectoryW(MAX_PATH, dir);
7889 lstrcatW(dir, szBackSlash);
7890 file = szPackagePath;
7893 msi_free( package->PackagePath );
7894 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7895 if (!package->PackagePath)
7897 msi_free(dir);
7898 return ERROR_OUTOFMEMORY;
7901 lstrcpyW(package->PackagePath, dir);
7902 lstrcatW(package->PackagePath, file);
7903 msi_free(dir);
7905 msi_set_sourcedir_props(package, FALSE);
7908 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7909 if (rc != ERROR_SUCCESS)
7910 return rc;
7912 msi_apply_transforms( package );
7913 msi_apply_patches( package );
7915 patch = msi_dup_property( package->db, szPatch );
7916 remove = msi_dup_property( package->db, szRemove );
7917 reinstall = msi_dup_property( package->db, szReinstall );
7918 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7920 TRACE("setting REINSTALL property to ALL\n");
7921 msi_set_property( package->db, szReinstall, szAll, -1 );
7922 package->full_reinstall = 1;
7925 msi_set_original_database_property( package->db, szPackagePath );
7926 msi_parse_command_line( package, szCommandLine, FALSE );
7927 msi_adjust_privilege_properties( package );
7928 msi_set_context( package );
7930 productcode = msi_dup_property( package->db, szProductCode );
7931 if (strcmpiW( productcode, package->ProductCode ))
7933 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
7934 msi_free( package->ProductCode );
7935 package->ProductCode = productcode;
7937 else msi_free( productcode );
7939 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7941 TRACE("disabling rollback\n");
7942 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7945 if (needs_ui_sequence( package))
7947 package->script->InWhatSequence |= SEQUENCE_UI;
7948 rc = ACTION_ProcessUISequence(package);
7949 ui_exists = ui_sequence_exists(package);
7950 if (rc == ERROR_SUCCESS || !ui_exists)
7952 package->script->InWhatSequence |= SEQUENCE_EXEC;
7953 rc = ACTION_ProcessExecSequence(package, ui_exists);
7956 else
7957 rc = ACTION_ProcessExecSequence(package, FALSE);
7959 /* process the ending type action */
7960 if (rc == ERROR_SUCCESS)
7961 ACTION_PerformActionSequence(package, -1);
7962 else if (rc == ERROR_INSTALL_USEREXIT)
7963 ACTION_PerformActionSequence(package, -2);
7964 else if (rc == ERROR_INSTALL_SUSPEND)
7965 ACTION_PerformActionSequence(package, -4);
7966 else /* failed */
7968 ACTION_PerformActionSequence(package, -3);
7969 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7971 package->need_rollback = TRUE;
7975 /* finish up running custom actions */
7976 ACTION_FinishCustomActions(package);
7978 if (package->need_rollback && !reinstall)
7980 WARN("installation failed, running rollback script\n");
7981 execute_script( package, SCRIPT_ROLLBACK );
7983 msi_free( reinstall );
7984 msi_free( remove );
7985 msi_free( patch );
7987 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7988 return ERROR_SUCCESS_REBOOT_REQUIRED;
7990 return rc;