msi: Improve support for advertized shortcuts.
[wine/multimedia.git] / dlls / msi / action.c
blobe310ba3bad3521b0cfffeaf2a055f6578caa04c5
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) 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 (%s)\n",debugstr_w(file->File));
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] != '#') return ERROR_SUCCESS;
1317 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1318 return ERROR_SUCCESS;
1321 static UINT load_all_media( MSIPACKAGE *package )
1323 static const WCHAR query[] = {
1324 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1325 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1326 '`','D','i','s','k','I','d','`',0};
1327 MSIQUERY *view;
1328 UINT r;
1330 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1331 if (r != ERROR_SUCCESS)
1332 return ERROR_SUCCESS;
1334 r = MSI_IterateRecords( view, NULL, load_media, package );
1335 msiobj_release( &view->hdr );
1336 return r;
1339 static UINT load_patch(MSIRECORD *row, LPVOID param)
1341 MSIPACKAGE *package = param;
1342 MSIFILEPATCH *patch;
1343 LPWSTR file_key;
1345 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1346 if (!patch)
1347 return ERROR_NOT_ENOUGH_MEMORY;
1349 file_key = msi_dup_record_field( row, 1 );
1350 patch->File = msi_get_loaded_file( package, file_key );
1351 msi_free(file_key);
1353 if( !patch->File )
1355 ERR("Failed to find target for patch in File table\n");
1356 msi_free(patch);
1357 return ERROR_FUNCTION_FAILED;
1360 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1362 /* FIXME: The database should be properly transformed */
1363 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1365 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1366 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1367 patch->IsApplied = FALSE;
1369 /* FIXME:
1370 * Header field - for patch validation.
1371 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1374 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1376 list_add_tail( &package->filepatches, &patch->entry );
1378 return ERROR_SUCCESS;
1381 static UINT load_all_patches(MSIPACKAGE *package)
1383 static const WCHAR query[] = {
1384 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1385 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1386 '`','S','e','q','u','e','n','c','e','`',0};
1387 MSIQUERY *view;
1388 UINT rc;
1390 if (!list_empty(&package->filepatches))
1391 return ERROR_SUCCESS;
1393 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1394 if (rc != ERROR_SUCCESS)
1395 return ERROR_SUCCESS;
1397 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1398 msiobj_release(&view->hdr);
1399 return rc;
1402 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1404 static const WCHAR query[] = {
1405 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1406 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1407 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1408 MSIQUERY *view;
1410 folder->persistent = FALSE;
1411 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1413 if (!MSI_ViewExecute( view, NULL ))
1415 MSIRECORD *rec;
1416 if (!MSI_ViewFetch( view, &rec ))
1418 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1419 folder->persistent = TRUE;
1420 msiobj_release( &rec->hdr );
1423 msiobj_release( &view->hdr );
1425 return ERROR_SUCCESS;
1428 static UINT load_folder( MSIRECORD *row, LPVOID param )
1430 MSIPACKAGE *package = param;
1431 static WCHAR szEmpty[] = { 0 };
1432 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1433 MSIFOLDER *folder;
1435 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1436 list_init( &folder->children );
1437 folder->Directory = msi_dup_record_field( row, 1 );
1438 folder->Parent = msi_dup_record_field( row, 2 );
1439 p = msi_dup_record_field(row, 3);
1441 TRACE("%s\n", debugstr_w(folder->Directory));
1443 /* split src and target dir */
1444 tgt_short = p;
1445 src_short = folder_split_path( p, ':' );
1447 /* split the long and short paths */
1448 tgt_long = folder_split_path( tgt_short, '|' );
1449 src_long = folder_split_path( src_short, '|' );
1451 /* check for no-op dirs */
1452 if (tgt_short && !strcmpW( szDot, tgt_short ))
1453 tgt_short = szEmpty;
1454 if (src_short && !strcmpW( szDot, src_short ))
1455 src_short = szEmpty;
1457 if (!tgt_long)
1458 tgt_long = tgt_short;
1460 if (!src_short) {
1461 src_short = tgt_short;
1462 src_long = tgt_long;
1465 if (!src_long)
1466 src_long = src_short;
1468 /* FIXME: use the target short path too */
1469 folder->TargetDefault = strdupW(tgt_long);
1470 folder->SourceShortPath = strdupW(src_short);
1471 folder->SourceLongPath = strdupW(src_long);
1472 msi_free(p);
1474 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1475 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1476 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1478 load_folder_persistence( package, folder );
1480 list_add_tail( &package->folders, &folder->entry );
1481 return ERROR_SUCCESS;
1484 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1486 FolderList *fl;
1488 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1489 fl->folder = child;
1490 list_add_tail( &parent->children, &fl->entry );
1491 return ERROR_SUCCESS;
1494 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1496 MSIPACKAGE *package = param;
1497 MSIFOLDER *parent, *child;
1499 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1500 return ERROR_FUNCTION_FAILED;
1502 if (!child->Parent) return ERROR_SUCCESS;
1504 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1505 return ERROR_FUNCTION_FAILED;
1507 return add_folder_child( parent, child );
1510 static UINT load_all_folders( MSIPACKAGE *package )
1512 static const WCHAR query[] = {
1513 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1514 '`','D','i','r','e','c','t','o','r','y','`',0};
1515 MSIQUERY *view;
1516 UINT r;
1518 if (!list_empty(&package->folders))
1519 return ERROR_SUCCESS;
1521 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1522 if (r != ERROR_SUCCESS)
1523 return r;
1525 r = MSI_IterateRecords( view, NULL, load_folder, package );
1526 if (r != ERROR_SUCCESS)
1528 msiobj_release( &view->hdr );
1529 return r;
1531 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1532 msiobj_release( &view->hdr );
1533 return r;
1536 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1538 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1539 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1541 load_all_folders( package );
1542 msi_load_all_components( package );
1543 msi_load_all_features( package );
1544 load_all_files( package );
1545 load_all_patches( package );
1546 load_all_media( package );
1548 return ERROR_SUCCESS;
1551 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1553 const WCHAR *action = package->script->Actions[script][index];
1554 ui_actionstart( package, action );
1555 TRACE("executing %s\n", debugstr_w(action));
1556 return ACTION_PerformAction( package, action, script );
1559 static UINT execute_script( MSIPACKAGE *package, UINT script )
1561 UINT i, rc = ERROR_SUCCESS;
1563 TRACE("executing script %u\n", script);
1565 if (!package->script)
1567 ERR("no script!\n");
1568 return ERROR_FUNCTION_FAILED;
1570 if (script == SCRIPT_ROLLBACK)
1572 for (i = package->script->ActionCount[script]; i > 0; i--)
1574 rc = execute_script_action( package, script, i - 1 );
1575 if (rc != ERROR_SUCCESS) break;
1578 else
1580 for (i = 0; i < package->script->ActionCount[script]; i++)
1582 rc = execute_script_action( package, script, i );
1583 if (rc != ERROR_SUCCESS) break;
1586 msi_free_action_script(package, script);
1587 return rc;
1590 static UINT ACTION_FileCost(MSIPACKAGE *package)
1592 return ERROR_SUCCESS;
1595 static void get_client_counts( MSIPACKAGE *package )
1597 MSICOMPONENT *comp;
1598 HKEY hkey;
1600 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1602 if (!comp->ComponentId) continue;
1604 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1605 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1607 comp->num_clients = 0;
1608 continue;
1610 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1611 NULL, NULL, NULL, NULL );
1612 RegCloseKey( hkey );
1616 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1618 MSICOMPONENT *comp;
1619 UINT r;
1621 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1623 if (!comp->ComponentId) continue;
1625 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1626 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1627 &comp->Installed );
1628 if (r == ERROR_SUCCESS) continue;
1630 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1631 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1632 &comp->Installed );
1633 if (r == ERROR_SUCCESS) continue;
1635 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1636 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1637 &comp->Installed );
1638 if (r == ERROR_SUCCESS) continue;
1640 comp->Installed = INSTALLSTATE_ABSENT;
1644 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1646 MSIFEATURE *feature;
1648 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1650 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1652 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1653 feature->Installed = INSTALLSTATE_ABSENT;
1654 else
1655 feature->Installed = state;
1659 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1661 return (feature->Level > 0 && feature->Level <= level);
1664 static BOOL process_state_property(MSIPACKAGE* package, int level,
1665 LPCWSTR property, INSTALLSTATE state)
1667 LPWSTR override;
1668 MSIFEATURE *feature;
1669 BOOL remove = !strcmpW(property, szRemove);
1670 BOOL reinstall = !strcmpW(property, szReinstall);
1672 override = msi_dup_property( package->db, property );
1673 if (!override)
1674 return FALSE;
1676 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1678 if (feature->Level <= 0)
1679 continue;
1681 if (reinstall)
1682 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1683 else if (remove)
1684 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1686 if (!strcmpiW( override, szAll ))
1688 feature->Action = state;
1689 feature->ActionRequest = state;
1691 else
1693 LPWSTR ptr = override;
1694 LPWSTR ptr2 = strchrW(override,',');
1696 while (ptr)
1698 int len = ptr2 - ptr;
1700 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1701 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1703 feature->Action = state;
1704 feature->ActionRequest = state;
1705 break;
1707 if (ptr2)
1709 ptr=ptr2+1;
1710 ptr2 = strchrW(ptr,',');
1712 else
1713 break;
1717 msi_free(override);
1718 return TRUE;
1721 static BOOL process_overrides( MSIPACKAGE *package, int level )
1723 static const WCHAR szAddLocal[] =
1724 {'A','D','D','L','O','C','A','L',0};
1725 static const WCHAR szAddSource[] =
1726 {'A','D','D','S','O','U','R','C','E',0};
1727 static const WCHAR szAdvertise[] =
1728 {'A','D','V','E','R','T','I','S','E',0};
1729 BOOL ret = FALSE;
1731 /* all these activation/deactivation things happen in order and things
1732 * later on the list override things earlier on the list.
1734 * 0 INSTALLLEVEL processing
1735 * 1 ADDLOCAL
1736 * 2 REMOVE
1737 * 3 ADDSOURCE
1738 * 4 ADDDEFAULT
1739 * 5 REINSTALL
1740 * 6 ADVERTISE
1741 * 7 COMPADDLOCAL
1742 * 8 COMPADDSOURCE
1743 * 9 FILEADDLOCAL
1744 * 10 FILEADDSOURCE
1745 * 11 FILEADDDEFAULT
1747 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1748 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1749 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1750 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1751 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1753 if (ret && !package->full_reinstall)
1754 msi_set_property( package->db, szPreselected, szOne, -1 );
1756 return ret;
1759 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1761 int level;
1762 MSICOMPONENT* component;
1763 MSIFEATURE *feature;
1765 TRACE("Checking Install Level\n");
1767 level = msi_get_property_int(package->db, szInstallLevel, 1);
1769 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1771 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1773 if (!is_feature_selected( feature, level )) continue;
1775 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1777 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1779 feature->Action = INSTALLSTATE_SOURCE;
1780 feature->ActionRequest = INSTALLSTATE_SOURCE;
1782 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1784 feature->Action = INSTALLSTATE_ADVERTISED;
1785 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1787 else
1789 feature->Action = INSTALLSTATE_LOCAL;
1790 feature->ActionRequest = INSTALLSTATE_LOCAL;
1794 /* disable child features of unselected parent or follow parent */
1795 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1797 FeatureList *fl;
1799 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1801 if (!is_feature_selected( feature, level ))
1803 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1804 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1806 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1808 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1809 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1810 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1811 fl->feature->Action = feature->Action;
1812 fl->feature->ActionRequest = feature->ActionRequest;
1817 else /* preselected */
1819 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1821 if (!is_feature_selected( feature, level )) continue;
1823 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1825 if (feature->Installed == INSTALLSTATE_ABSENT)
1827 feature->Action = INSTALLSTATE_UNKNOWN;
1828 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1830 else
1832 feature->Action = feature->Installed;
1833 feature->ActionRequest = feature->Installed;
1837 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1839 FeatureList *fl;
1841 if (!is_feature_selected( feature, level )) continue;
1843 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1845 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1846 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1848 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1849 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1850 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1851 fl->feature->Action = feature->Action;
1852 fl->feature->ActionRequest = feature->ActionRequest;
1858 /* now we want to set component state based based on feature state */
1859 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1861 ComponentList *cl;
1863 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1864 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1865 feature->ActionRequest, feature->Action);
1867 /* features with components that have compressed files are made local */
1868 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1870 if (cl->component->ForceLocalState &&
1871 feature->ActionRequest == INSTALLSTATE_SOURCE)
1873 feature->Action = INSTALLSTATE_LOCAL;
1874 feature->ActionRequest = INSTALLSTATE_LOCAL;
1875 break;
1879 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1881 component = cl->component;
1883 switch (feature->ActionRequest)
1885 case INSTALLSTATE_ABSENT:
1886 component->anyAbsent = 1;
1887 break;
1888 case INSTALLSTATE_ADVERTISED:
1889 component->hasAdvertisedFeature = 1;
1890 break;
1891 case INSTALLSTATE_SOURCE:
1892 component->hasSourceFeature = 1;
1893 break;
1894 case INSTALLSTATE_LOCAL:
1895 component->hasLocalFeature = 1;
1896 break;
1897 case INSTALLSTATE_DEFAULT:
1898 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1899 component->hasAdvertisedFeature = 1;
1900 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1901 component->hasSourceFeature = 1;
1902 else
1903 component->hasLocalFeature = 1;
1904 break;
1905 default:
1906 break;
1911 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1913 /* check if it's local or source */
1914 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1915 (component->hasLocalFeature || component->hasSourceFeature))
1917 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1918 !component->ForceLocalState)
1920 component->Action = INSTALLSTATE_SOURCE;
1921 component->ActionRequest = INSTALLSTATE_SOURCE;
1923 else
1925 component->Action = INSTALLSTATE_LOCAL;
1926 component->ActionRequest = INSTALLSTATE_LOCAL;
1928 continue;
1931 /* if any feature is local, the component must be local too */
1932 if (component->hasLocalFeature)
1934 component->Action = INSTALLSTATE_LOCAL;
1935 component->ActionRequest = INSTALLSTATE_LOCAL;
1936 continue;
1938 if (component->hasSourceFeature)
1940 component->Action = INSTALLSTATE_SOURCE;
1941 component->ActionRequest = INSTALLSTATE_SOURCE;
1942 continue;
1944 if (component->hasAdvertisedFeature)
1946 component->Action = INSTALLSTATE_ADVERTISED;
1947 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1948 continue;
1950 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1951 if (component->anyAbsent && component->ComponentId)
1953 component->Action = INSTALLSTATE_ABSENT;
1954 component->ActionRequest = INSTALLSTATE_ABSENT;
1958 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1960 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1962 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1963 component->Action = INSTALLSTATE_LOCAL;
1964 component->ActionRequest = INSTALLSTATE_LOCAL;
1967 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1968 component->Installed == INSTALLSTATE_SOURCE &&
1969 component->hasSourceFeature)
1971 component->Action = INSTALLSTATE_UNKNOWN;
1972 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1975 TRACE("component %s (installed %d request %d action %d)\n",
1976 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1978 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1979 component->num_clients++;
1980 else if (component->Action == INSTALLSTATE_ABSENT)
1981 component->num_clients--;
1984 return ERROR_SUCCESS;
1987 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1989 MSIPACKAGE *package = param;
1990 LPCWSTR name;
1991 MSIFEATURE *feature;
1993 name = MSI_RecordGetString( row, 1 );
1995 feature = msi_get_loaded_feature( package, name );
1996 if (!feature)
1997 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1998 else
2000 LPCWSTR Condition;
2001 Condition = MSI_RecordGetString(row,3);
2003 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2005 int level = MSI_RecordGetInteger(row,2);
2006 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2007 feature->Level = level;
2010 return ERROR_SUCCESS;
2013 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2015 static const WCHAR name[] = {'\\',0};
2016 VS_FIXEDFILEINFO *ptr, *ret;
2017 LPVOID version;
2018 DWORD versize, handle;
2019 UINT sz;
2021 versize = GetFileVersionInfoSizeW( filename, &handle );
2022 if (!versize)
2023 return NULL;
2025 version = msi_alloc( versize );
2026 if (!version)
2027 return NULL;
2029 GetFileVersionInfoW( filename, 0, versize, version );
2031 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2033 msi_free( version );
2034 return NULL;
2037 ret = msi_alloc( sz );
2038 memcpy( ret, ptr, sz );
2040 msi_free( version );
2041 return ret;
2044 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2046 DWORD ms, ls;
2048 msi_parse_version_string( version, &ms, &ls );
2050 if (fi->dwFileVersionMS > ms) return 1;
2051 else if (fi->dwFileVersionMS < ms) return -1;
2052 else if (fi->dwFileVersionLS > ls) return 1;
2053 else if (fi->dwFileVersionLS < ls) return -1;
2054 return 0;
2057 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2059 DWORD ms1, ms2;
2061 msi_parse_version_string( ver1, &ms1, NULL );
2062 msi_parse_version_string( ver2, &ms2, NULL );
2064 if (ms1 > ms2) return 1;
2065 else if (ms1 < ms2) return -1;
2066 return 0;
2069 DWORD msi_get_disk_file_size( LPCWSTR filename )
2071 HANDLE file;
2072 DWORD size;
2074 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2075 if (file == INVALID_HANDLE_VALUE)
2076 return INVALID_FILE_SIZE;
2078 size = GetFileSize( file, NULL );
2079 TRACE("size is %u\n", size);
2080 CloseHandle( file );
2081 return size;
2084 BOOL msi_file_hash_matches( MSIFILE *file )
2086 UINT r;
2087 MSIFILEHASHINFO hash;
2089 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2090 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2091 if (r != ERROR_SUCCESS)
2092 return FALSE;
2094 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2097 static WCHAR *get_temp_dir( void )
2099 static UINT id;
2100 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2102 GetTempPathW( MAX_PATH, tmp );
2103 for (;;)
2105 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2106 if (CreateDirectoryW( dir, NULL )) break;
2108 return strdupW( dir );
2112 * msi_build_directory_name()
2114 * This function is to save messing round with directory names
2115 * It handles adding backslashes between path segments,
2116 * and can add \ at the end of the directory name if told to.
2118 * It takes a variable number of arguments.
2119 * It always allocates a new string for the result, so make sure
2120 * to free the return value when finished with it.
2122 * The first arg is the number of path segments that follow.
2123 * The arguments following count are a list of path segments.
2124 * A path segment may be NULL.
2126 * Path segments will be added with a \ separating them.
2127 * A \ will not be added after the last segment, however if the
2128 * last segment is NULL, then the last character will be a \
2130 WCHAR *msi_build_directory_name( DWORD count, ... )
2132 DWORD sz = 1, i;
2133 WCHAR *dir;
2134 va_list va;
2136 va_start( va, count );
2137 for (i = 0; i < count; i++)
2139 const WCHAR *str = va_arg( va, const WCHAR * );
2140 if (str) sz += strlenW( str ) + 1;
2142 va_end( va );
2144 dir = msi_alloc( sz * sizeof(WCHAR) );
2145 dir[0] = 0;
2147 va_start( va, count );
2148 for (i = 0; i < count; i++)
2150 const WCHAR *str = va_arg( va, const WCHAR * );
2151 if (!str) continue;
2152 strcatW( dir, str );
2153 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2155 va_end( va );
2156 return dir;
2159 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2161 MSIASSEMBLY *assembly = file->Component->assembly;
2163 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2165 msi_free( file->TargetPath );
2166 if (assembly && !assembly->application)
2168 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2169 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2170 msi_track_tempfile( package, file->TargetPath );
2172 else
2174 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2175 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2178 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2181 static UINT calculate_file_cost( MSIPACKAGE *package )
2183 VS_FIXEDFILEINFO *file_version;
2184 WCHAR *font_version;
2185 MSIFILE *file;
2187 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2189 MSICOMPONENT *comp = file->Component;
2190 DWORD file_size;
2192 if (!comp->Enabled) continue;
2194 if (file->IsCompressed)
2195 comp->ForceLocalState = TRUE;
2197 set_target_path( package, file );
2199 if ((comp->assembly && !comp->assembly->installed) ||
2200 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2202 comp->Cost += file->FileSize;
2203 continue;
2205 file_size = msi_get_disk_file_size( file->TargetPath );
2207 if (file->Version)
2209 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2211 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2213 comp->Cost += file->FileSize - file_size;
2215 msi_free( file_version );
2216 continue;
2218 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2220 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2222 comp->Cost += file->FileSize - file_size;
2224 msi_free( font_version );
2225 continue;
2228 if (file_size != file->FileSize)
2230 comp->Cost += file->FileSize - file_size;
2233 return ERROR_SUCCESS;
2236 WCHAR *msi_normalize_path( const WCHAR *in )
2238 const WCHAR *p = in;
2239 WCHAR *q, *ret;
2240 int n, len = strlenW( in ) + 2;
2242 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2244 len = 0;
2245 while (1)
2247 /* copy until the end of the string or a space */
2248 while (*p != ' ' && (*q = *p))
2250 p++, len++;
2251 /* reduce many backslashes to one */
2252 if (*p != '\\' || *q != '\\')
2253 q++;
2256 /* quit at the end of the string */
2257 if (!*p)
2258 break;
2260 /* count the number of spaces */
2261 n = 0;
2262 while (p[n] == ' ')
2263 n++;
2265 /* if it's leading or trailing space, skip it */
2266 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2267 p += n;
2268 else /* copy n spaces */
2269 while (n && (*q++ = *p++)) n--;
2271 while (q - ret > 0 && q[-1] == ' ') q--;
2272 if (q - ret > 0 && q[-1] != '\\')
2274 q[0] = '\\';
2275 q[1] = 0;
2277 return ret;
2280 static WCHAR *get_install_location( MSIPACKAGE *package )
2282 HKEY hkey;
2283 WCHAR *path;
2285 if (!package->ProductCode) return NULL;
2286 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2287 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2289 msi_free( path );
2290 path = NULL;
2292 RegCloseKey( hkey );
2293 return path;
2296 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2298 FolderList *fl;
2299 MSIFOLDER *folder, *parent, *child;
2300 WCHAR *path, *normalized_path;
2302 TRACE("resolving %s\n", debugstr_w(name));
2304 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2306 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2308 if (!(path = get_install_location( package )) &&
2309 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2311 path = msi_dup_property( package->db, szRootDrive );
2314 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2316 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2318 parent = msi_get_loaded_folder( package, folder->Parent );
2319 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2321 else
2322 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2324 normalized_path = msi_normalize_path( path );
2325 msi_free( path );
2326 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2328 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2329 msi_free( normalized_path );
2330 return;
2332 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2333 msi_free( folder->ResolvedTarget );
2334 folder->ResolvedTarget = normalized_path;
2336 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2338 child = fl->folder;
2339 msi_resolve_target_folder( package, child->Directory, load_prop );
2341 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2344 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2346 static const WCHAR query[] =
2347 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2348 '`','C','o','n','d','i','t','i','o','n','`',0};
2349 static const WCHAR szOutOfDiskSpace[] =
2350 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2351 static const WCHAR szPrimaryFolder[] =
2352 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2353 static const WCHAR szPrimaryVolumePath[] =
2354 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2355 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2356 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2357 'A','v','a','i','l','a','b','l','e',0};
2358 static const WCHAR szOutOfNoRbDiskSpace[] =
2359 {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0};
2360 MSICOMPONENT *comp;
2361 MSIQUERY *view;
2362 WCHAR *level, *primary_key, *primary_folder;
2363 UINT rc;
2365 TRACE("Building directory properties\n");
2366 msi_resolve_target_folder( package, szTargetDir, TRUE );
2368 TRACE("Evaluating component conditions\n");
2369 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2371 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2373 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2374 comp->Enabled = FALSE;
2376 else
2377 comp->Enabled = TRUE;
2379 get_client_counts( package );
2381 /* read components states from the registry */
2382 ACTION_GetComponentInstallStates(package);
2383 ACTION_GetFeatureInstallStates(package);
2385 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2387 TRACE("Evaluating feature conditions\n");
2389 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2390 if (rc == ERROR_SUCCESS)
2392 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2393 msiobj_release( &view->hdr );
2394 if (rc != ERROR_SUCCESS)
2395 return rc;
2399 TRACE("Calculating file cost\n");
2400 calculate_file_cost( package );
2402 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2403 /* set default run level if not set */
2404 level = msi_dup_property( package->db, szInstallLevel );
2405 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2406 msi_free(level);
2408 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2410 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2412 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2413 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2415 ULARGE_INTEGER free;
2417 primary_folder[2] = 0;
2418 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2420 static const WCHAR fmtW[] = {'%','l','u',0};
2421 WCHAR buf[21];
2423 sprintfW( buf, fmtW, free.QuadPart / 512 );
2424 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2426 toupperW( primary_folder[0] );
2427 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2429 msi_free( primary_folder );
2431 msi_free( primary_key );
2434 /* FIXME: check volume disk space */
2435 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2436 msi_set_property( package->db, szOutOfNoRbDiskSpace, szZero, -1 );
2438 return MSI_SetFeatureStates(package);
2441 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD *type, DWORD *size )
2443 BYTE *data = NULL;
2445 if (!value)
2447 *size = sizeof(WCHAR);
2448 *type = REG_SZ;
2449 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2450 return data;
2452 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2454 if (value[1]=='x')
2456 LPWSTR ptr;
2457 CHAR byte[5];
2458 LPWSTR deformated = NULL;
2459 int count;
2461 deformat_string(package, &value[2], &deformated);
2463 /* binary value type */
2464 ptr = deformated;
2465 *type = REG_BINARY;
2466 if (strlenW(ptr)%2)
2467 *size = (strlenW(ptr)/2)+1;
2468 else
2469 *size = strlenW(ptr)/2;
2471 data = msi_alloc(*size);
2473 byte[0] = '0';
2474 byte[1] = 'x';
2475 byte[4] = 0;
2476 count = 0;
2477 /* if uneven pad with a zero in front */
2478 if (strlenW(ptr)%2)
2480 byte[2]= '0';
2481 byte[3]= *ptr;
2482 ptr++;
2483 data[count] = (BYTE)strtol(byte,NULL,0);
2484 count ++;
2485 TRACE("Uneven byte count\n");
2487 while (*ptr)
2489 byte[2]= *ptr;
2490 ptr++;
2491 byte[3]= *ptr;
2492 ptr++;
2493 data[count] = (BYTE)strtol(byte,NULL,0);
2494 count ++;
2496 msi_free(deformated);
2498 TRACE("Data %i bytes(%i)\n",*size,count);
2500 else
2502 LPWSTR deformated;
2503 LPWSTR p;
2504 DWORD d = 0;
2505 deformat_string(package, &value[1], &deformated);
2507 *type=REG_DWORD;
2508 *size = sizeof(DWORD);
2509 data = msi_alloc(*size);
2510 p = deformated;
2511 if (*p == '-')
2512 p++;
2513 while (*p)
2515 if ( (*p < '0') || (*p > '9') )
2516 break;
2517 d *= 10;
2518 d += (*p - '0');
2519 p++;
2521 if (deformated[0] == '-')
2522 d = -d;
2523 *(LPDWORD)data = d;
2524 TRACE("DWORD %i\n",*(LPDWORD)data);
2526 msi_free(deformated);
2529 else
2531 const WCHAR *ptr = value;
2532 DWORD len;
2534 *type = REG_SZ;
2535 if (value[0] == '#')
2537 ptr++;
2538 if (value[1] == '%')
2540 ptr++;
2541 *type = REG_EXPAND_SZ;
2544 len = deformat_string( package, ptr, (WCHAR **)&data );
2545 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2546 *size = (len + 1) * sizeof(WCHAR);
2548 return data;
2551 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2553 const WCHAR *ret;
2555 switch (root)
2557 case -1:
2558 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2560 *root_key = HKEY_LOCAL_MACHINE;
2561 ret = szHLM;
2563 else
2565 *root_key = HKEY_CURRENT_USER;
2566 ret = szHCU;
2568 break;
2569 case 0:
2570 *root_key = HKEY_CLASSES_ROOT;
2571 ret = szHCR;
2572 break;
2573 case 1:
2574 *root_key = HKEY_CURRENT_USER;
2575 ret = szHCU;
2576 break;
2577 case 2:
2578 *root_key = HKEY_LOCAL_MACHINE;
2579 ret = szHLM;
2580 break;
2581 case 3:
2582 *root_key = HKEY_USERS;
2583 ret = szHU;
2584 break;
2585 default:
2586 ERR("Unknown root %i\n", root);
2587 return NULL;
2590 return ret;
2593 static WCHAR *get_keypath( MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2595 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2596 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2598 if ((is_64bit || is_wow64) &&
2599 !(comp->Attributes & msidbComponentAttributes64bit) &&
2600 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2602 UINT size;
2603 WCHAR *path_32node;
2605 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2606 if (!(path_32node = msi_alloc( size ))) return NULL;
2608 memcpy( path_32node, path, len * sizeof(WCHAR) );
2609 strcpyW( path_32node + len, szWow6432Node );
2610 strcatW( path_32node, szBackSlash );
2611 strcatW( path_32node, path + len );
2612 return path_32node;
2614 return strdupW( path );
2617 static HKEY open_key( HKEY root, const WCHAR *path, BOOL create )
2619 REGSAM access = KEY_ALL_ACCESS;
2620 WCHAR *subkey, *p, *q;
2621 HKEY hkey, ret = NULL;
2622 LONG res;
2624 if (is_wow64) access |= KEY_WOW64_64KEY;
2626 if (!(subkey = strdupW( path ))) return NULL;
2627 p = subkey;
2628 if ((q = strchrW( p, '\\' ))) *q = 0;
2629 if (create)
2630 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2631 else
2632 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2633 if (res)
2635 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2636 msi_free( subkey );
2637 return NULL;
2639 if (q && q[1])
2641 ret = open_key( hkey, q + 1, create );
2642 RegCloseKey( hkey );
2644 else ret = hkey;
2645 msi_free( subkey );
2646 return ret;
2649 static BOOL is_special_entry( const WCHAR *name )
2651 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2654 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2656 const WCHAR *p = str;
2657 WCHAR **ret;
2658 int i = 0;
2660 *count = 0;
2661 if (!str) return NULL;
2662 while ((p - str) < len)
2664 p += strlenW( p ) + 1;
2665 (*count)++;
2667 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2668 p = str;
2669 while ((p - str) < len)
2671 if (!(ret[i] = strdupW( p )))
2673 for (; i >= 0; i--) msi_free( ret[i] );
2674 msi_free( ret );
2675 return NULL;
2677 p += strlenW( p ) + 1;
2678 i++;
2680 return ret;
2683 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2684 WCHAR **right, DWORD right_count, DWORD *size )
2686 WCHAR *ret, *p;
2687 unsigned int i;
2689 *size = sizeof(WCHAR);
2690 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2691 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2693 if (!(ret = p = msi_alloc( *size ))) return NULL;
2695 for (i = 0; i < left_count; i++)
2697 strcpyW( p, left[i] );
2698 p += strlenW( p ) + 1;
2700 for (i = 0; i < right_count; i++)
2702 strcpyW( p, right[i] );
2703 p += strlenW( p ) + 1;
2705 *p = 0;
2706 return ret;
2709 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2710 WCHAR **new, DWORD new_count )
2712 DWORD ret = old_count;
2713 unsigned int i, j, k;
2715 for (i = 0; i < new_count; i++)
2717 for (j = 0; j < old_count; j++)
2719 if (old[j] && !strcmpW( new[i], old[j] ))
2721 msi_free( old[j] );
2722 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2723 old[k] = NULL;
2724 ret--;
2728 return ret;
2731 enum join_op
2733 JOIN_OP_APPEND,
2734 JOIN_OP_PREPEND,
2735 JOIN_OP_REPLACE
2738 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2739 WCHAR **new, DWORD new_count, DWORD *size )
2741 switch (op)
2743 case JOIN_OP_APPEND:
2744 old_count = remove_duplicate_values( old, old_count, new, new_count );
2745 return flatten_multi_string_values( old, old_count, new, new_count, size );
2747 case JOIN_OP_PREPEND:
2748 old_count = remove_duplicate_values( old, old_count, new, new_count );
2749 return flatten_multi_string_values( new, new_count, old, old_count, size );
2751 case JOIN_OP_REPLACE:
2752 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2754 default:
2755 ERR("unhandled join op %u\n", op);
2756 return NULL;
2760 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2761 BYTE *new_value, DWORD new_size, DWORD *size )
2763 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2764 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2765 enum join_op op = JOIN_OP_REPLACE;
2766 WCHAR **old = NULL, **new = NULL;
2767 BYTE *ret;
2769 if (new_size / sizeof(WCHAR) - 1 > 1)
2771 new_ptr = (const WCHAR *)new_value;
2772 new_len = new_size / sizeof(WCHAR) - 1;
2774 if (!new_ptr[0] && new_ptr[new_len - 1])
2776 op = JOIN_OP_APPEND;
2777 new_len--;
2778 new_ptr++;
2780 else if (new_ptr[0] && !new_ptr[new_len - 1])
2782 op = JOIN_OP_PREPEND;
2783 new_len--;
2785 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2787 op = JOIN_OP_REPLACE;
2788 new_len -= 2;
2789 new_ptr++;
2791 new = split_multi_string_values( new_ptr, new_len, &new_count );
2793 if (old_size / sizeof(WCHAR) - 1 > 1)
2795 old_ptr = (const WCHAR *)old_value;
2796 old_len = old_size / sizeof(WCHAR) - 1;
2797 old = split_multi_string_values( old_ptr, old_len, &old_count );
2799 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2800 for (i = 0; i < old_count; i++) msi_free( old[i] );
2801 for (i = 0; i < new_count; i++) msi_free( new[i] );
2802 msi_free( old );
2803 msi_free( new );
2804 return ret;
2807 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2809 BYTE *ret;
2810 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2811 if (!(ret = msi_alloc( *size ))) return NULL;
2812 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2813 return ret;
2816 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2818 MSIPACKAGE *package = param;
2819 BYTE *new_value, *old_value = NULL;
2820 HKEY root_key, hkey;
2821 DWORD type, old_type, new_size, old_size = 0;
2822 LPWSTR deformated, uikey, keypath;
2823 const WCHAR *szRoot, *component, *name, *key, *str;
2824 MSICOMPONENT *comp;
2825 MSIRECORD * uirow;
2826 INT root;
2827 BOOL check_first = FALSE;
2828 int len;
2830 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2832 component = MSI_RecordGetString(row, 6);
2833 comp = msi_get_loaded_component(package,component);
2834 if (!comp)
2835 return ERROR_SUCCESS;
2837 comp->Action = msi_get_component_action( package, comp );
2838 if (comp->Action != INSTALLSTATE_LOCAL)
2840 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2841 return ERROR_SUCCESS;
2844 name = MSI_RecordGetString(row, 4);
2845 if( MSI_RecordIsNull(row,5) && name )
2847 /* null values can have special meanings */
2848 if (name[0]=='-' && name[1] == 0)
2849 return ERROR_SUCCESS;
2850 if ((name[0] == '+' || name[0] == '*') && !name[1])
2851 check_first = TRUE;
2854 root = MSI_RecordGetInteger(row,2);
2855 key = MSI_RecordGetString(row, 3);
2857 szRoot = get_root_key( package, root, &root_key );
2858 if (!szRoot)
2859 return ERROR_SUCCESS;
2861 deformat_string(package, key , &deformated);
2862 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2863 strcpyW(uikey,szRoot);
2864 strcatW(uikey,deformated);
2866 keypath = get_keypath( comp, root_key, deformated );
2867 msi_free( deformated );
2868 if (!(hkey = open_key( root_key, keypath, TRUE )))
2870 ERR("Could not create key %s\n", debugstr_w(keypath));
2871 msi_free(uikey);
2872 msi_free(keypath);
2873 return ERROR_FUNCTION_FAILED;
2875 str = msi_record_get_string( row, 5, &len );
2876 if (str && len > strlenW( str ))
2878 type = REG_MULTI_SZ;
2879 new_size = (len + 1) * sizeof(WCHAR);
2880 new_value = (BYTE *)msi_strdupW( str, len );
2882 else new_value = parse_value( package, str, &type, &new_size );
2883 deformat_string(package, name, &deformated);
2885 if (!is_special_entry( name ))
2887 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2888 if (type == REG_MULTI_SZ)
2890 BYTE *new;
2891 if (old_value && old_type != REG_MULTI_SZ)
2893 msi_free( old_value );
2894 old_value = NULL;
2895 old_size = 0;
2897 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2898 msi_free( new_value );
2899 new_value = new;
2901 if (!check_first)
2903 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2904 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2906 else if (!old_value)
2908 if (deformated || new_size)
2910 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2911 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2914 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2916 RegCloseKey(hkey);
2918 uirow = MSI_CreateRecord(3);
2919 MSI_RecordSetStringW(uirow,2,deformated);
2920 MSI_RecordSetStringW(uirow,1,uikey);
2921 if (type == REG_SZ || type == REG_EXPAND_SZ)
2922 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2923 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2924 msiobj_release( &uirow->hdr );
2926 msi_free(new_value);
2927 msi_free(old_value);
2928 msi_free(deformated);
2929 msi_free(uikey);
2930 msi_free(keypath);
2932 return ERROR_SUCCESS;
2935 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2937 static const WCHAR query[] = {
2938 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2939 '`','R','e','g','i','s','t','r','y','`',0};
2940 MSIQUERY *view;
2941 UINT rc;
2943 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2944 if (rc != ERROR_SUCCESS)
2945 return ERROR_SUCCESS;
2947 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2948 msiobj_release(&view->hdr);
2949 return rc;
2952 static void delete_key( HKEY root, const WCHAR *path )
2954 REGSAM access = 0;
2955 WCHAR *subkey, *p;
2956 HKEY hkey;
2957 LONG res;
2959 if (is_wow64) access |= KEY_WOW64_64KEY;
2961 if (!(subkey = strdupW( path ))) return;
2962 for (;;)
2964 if ((p = strrchrW( subkey, '\\' ))) *p = 0;
2965 hkey = open_key( root, subkey, FALSE );
2966 if (!hkey) break;
2967 if (p && p[1])
2968 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2969 else
2970 res = RegDeleteKeyExW( root, subkey, access, 0 );
2971 if (res)
2973 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
2974 break;
2976 if (p && p[1]) RegCloseKey( hkey );
2977 else break;
2979 msi_free( subkey );
2982 static void delete_value( HKEY root, const WCHAR *path, const WCHAR *value )
2984 LONG res;
2985 HKEY hkey;
2986 DWORD num_subkeys, num_values;
2988 if ((hkey = open_key( root, path, FALSE )))
2990 if ((res = RegDeleteValueW( hkey, value )))
2991 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2993 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2994 NULL, NULL, NULL, NULL );
2995 RegCloseKey( hkey );
2996 if (!res && !num_subkeys && !num_values)
2998 TRACE("removing empty key %s\n", debugstr_w(path));
2999 delete_key( root, path );
3004 static void delete_tree( HKEY root, const WCHAR *path )
3006 LONG res;
3007 HKEY hkey;
3009 if (!(hkey = open_key( root, path, FALSE ))) return;
3010 res = RegDeleteTreeW( hkey, NULL );
3011 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3012 delete_key( root, path );
3013 RegCloseKey( hkey );
3016 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3018 MSIPACKAGE *package = param;
3019 LPCWSTR component, name, key_str, root_key_str;
3020 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3021 MSICOMPONENT *comp;
3022 MSIRECORD *uirow;
3023 BOOL delete_key = FALSE;
3024 HKEY hkey_root;
3025 UINT size;
3026 INT root;
3028 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3030 component = MSI_RecordGetString( row, 6 );
3031 comp = msi_get_loaded_component( package, component );
3032 if (!comp)
3033 return ERROR_SUCCESS;
3035 comp->Action = msi_get_component_action( package, comp );
3036 if (comp->Action != INSTALLSTATE_ABSENT)
3038 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3039 return ERROR_SUCCESS;
3042 name = MSI_RecordGetString( row, 4 );
3043 if (MSI_RecordIsNull( row, 5 ) && name )
3045 if (name[0] == '+' && !name[1])
3046 return ERROR_SUCCESS;
3047 if ((name[0] == '-' || name[0] == '*') && !name[1])
3049 delete_key = TRUE;
3050 name = NULL;
3054 root = MSI_RecordGetInteger( row, 2 );
3055 key_str = MSI_RecordGetString( row, 3 );
3057 root_key_str = get_root_key( package, root, &hkey_root );
3058 if (!root_key_str)
3059 return ERROR_SUCCESS;
3061 deformat_string( package, key_str, &deformated_key );
3062 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3063 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3064 strcpyW( ui_key_str, root_key_str );
3065 strcatW( ui_key_str, deformated_key );
3067 deformat_string( package, name, &deformated_name );
3069 keypath = get_keypath( comp, hkey_root, deformated_key );
3070 msi_free( deformated_key );
3071 if (delete_key) delete_tree( hkey_root, keypath );
3072 else delete_value( hkey_root, keypath, deformated_name );
3073 msi_free( keypath );
3075 uirow = MSI_CreateRecord( 2 );
3076 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3077 MSI_RecordSetStringW( uirow, 2, deformated_name );
3078 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3079 msiobj_release( &uirow->hdr );
3081 msi_free( ui_key_str );
3082 msi_free( deformated_name );
3083 return ERROR_SUCCESS;
3086 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3088 MSIPACKAGE *package = param;
3089 LPCWSTR component, name, key_str, root_key_str;
3090 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3091 MSICOMPONENT *comp;
3092 MSIRECORD *uirow;
3093 BOOL delete_key = FALSE;
3094 HKEY hkey_root;
3095 UINT size;
3096 INT root;
3098 component = MSI_RecordGetString( row, 5 );
3099 comp = msi_get_loaded_component( package, component );
3100 if (!comp)
3101 return ERROR_SUCCESS;
3103 comp->Action = msi_get_component_action( package, comp );
3104 if (comp->Action != INSTALLSTATE_LOCAL)
3106 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3107 return ERROR_SUCCESS;
3110 if ((name = MSI_RecordGetString( row, 4 )))
3112 if (name[0] == '-' && !name[1])
3114 delete_key = TRUE;
3115 name = NULL;
3119 root = MSI_RecordGetInteger( row, 2 );
3120 key_str = MSI_RecordGetString( row, 3 );
3122 root_key_str = get_root_key( package, root, &hkey_root );
3123 if (!root_key_str)
3124 return ERROR_SUCCESS;
3126 deformat_string( package, key_str, &deformated_key );
3127 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3128 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3129 strcpyW( ui_key_str, root_key_str );
3130 strcatW( ui_key_str, deformated_key );
3132 deformat_string( package, name, &deformated_name );
3134 keypath = get_keypath( comp, hkey_root, deformated_key );
3135 msi_free( deformated_key );
3136 if (delete_key) delete_tree( hkey_root, keypath );
3137 else delete_value( hkey_root, keypath, deformated_name );
3138 msi_free( keypath );
3140 uirow = MSI_CreateRecord( 2 );
3141 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3142 MSI_RecordSetStringW( uirow, 2, deformated_name );
3143 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3144 msiobj_release( &uirow->hdr );
3146 msi_free( ui_key_str );
3147 msi_free( deformated_name );
3148 return ERROR_SUCCESS;
3151 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3153 static const WCHAR registry_query[] = {
3154 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3155 '`','R','e','g','i','s','t','r','y','`',0};
3156 static const WCHAR remove_registry_query[] = {
3157 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3158 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3159 MSIQUERY *view;
3160 UINT rc;
3162 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3163 if (rc == ERROR_SUCCESS)
3165 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3166 msiobj_release( &view->hdr );
3167 if (rc != ERROR_SUCCESS)
3168 return rc;
3170 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3171 if (rc == ERROR_SUCCESS)
3173 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3174 msiobj_release( &view->hdr );
3175 if (rc != ERROR_SUCCESS)
3176 return rc;
3178 return ERROR_SUCCESS;
3181 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3183 return ERROR_SUCCESS;
3187 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3189 static const WCHAR query[]= {
3190 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3191 '`','R','e','g','i','s','t','r','y','`',0};
3192 MSICOMPONENT *comp;
3193 DWORD total = 0, count = 0;
3194 MSIQUERY *view;
3195 MSIFEATURE *feature;
3196 MSIFILE *file;
3197 UINT rc;
3199 TRACE("InstallValidate\n");
3201 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3202 if (rc == ERROR_SUCCESS)
3204 rc = MSI_IterateRecords( view, &count, NULL, package );
3205 msiobj_release( &view->hdr );
3206 if (rc != ERROR_SUCCESS)
3207 return rc;
3208 total += count * REG_PROGRESS_VALUE;
3210 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3211 total += COMPONENT_PROGRESS_VALUE;
3213 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3214 total += file->FileSize;
3216 msi_ui_progress( package, 0, total, 0, 0 );
3218 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3220 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3221 debugstr_w(feature->Feature), feature->Installed,
3222 feature->ActionRequest, feature->Action);
3224 return ERROR_SUCCESS;
3227 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3229 MSIPACKAGE* package = param;
3230 LPCWSTR cond = NULL;
3231 LPCWSTR message = NULL;
3232 UINT r;
3234 static const WCHAR title[]=
3235 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3237 cond = MSI_RecordGetString(row,1);
3239 r = MSI_EvaluateConditionW(package,cond);
3240 if (r == MSICONDITION_FALSE)
3242 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3244 LPWSTR deformated;
3245 message = MSI_RecordGetString(row,2);
3246 deformat_string(package,message,&deformated);
3247 MessageBoxW(NULL,deformated,title,MB_OK);
3248 msi_free(deformated);
3251 return ERROR_INSTALL_FAILURE;
3254 return ERROR_SUCCESS;
3257 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3259 static const WCHAR query[] = {
3260 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3261 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3262 MSIQUERY *view;
3263 UINT rc;
3265 TRACE("Checking launch conditions\n");
3267 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3268 if (rc != ERROR_SUCCESS)
3269 return ERROR_SUCCESS;
3271 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3272 msiobj_release(&view->hdr);
3273 return rc;
3276 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3279 if (!cmp->KeyPath)
3280 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3282 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3284 static const WCHAR query[] = {
3285 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3286 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3287 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3288 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3289 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3290 MSIRECORD *row;
3291 UINT root, len;
3292 LPWSTR deformated, buffer, deformated_name;
3293 LPCWSTR key, name;
3295 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3296 if (!row)
3297 return NULL;
3299 root = MSI_RecordGetInteger(row,2);
3300 key = MSI_RecordGetString(row, 3);
3301 name = MSI_RecordGetString(row, 4);
3302 deformat_string(package, key , &deformated);
3303 deformat_string(package, name, &deformated_name);
3305 len = strlenW(deformated) + 6;
3306 if (deformated_name)
3307 len+=strlenW(deformated_name);
3309 buffer = msi_alloc( len *sizeof(WCHAR));
3311 if (deformated_name)
3312 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3313 else
3314 sprintfW(buffer,fmt,root,deformated);
3316 msi_free(deformated);
3317 msi_free(deformated_name);
3318 msiobj_release(&row->hdr);
3320 return buffer;
3322 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3324 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3325 return NULL;
3327 else
3329 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3331 if (file)
3332 return strdupW( file->TargetPath );
3334 return NULL;
3337 static HKEY openSharedDLLsKey(void)
3339 HKEY hkey=0;
3340 static const WCHAR path[] =
3341 {'S','o','f','t','w','a','r','e','\\',
3342 'M','i','c','r','o','s','o','f','t','\\',
3343 'W','i','n','d','o','w','s','\\',
3344 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3345 'S','h','a','r','e','d','D','L','L','s',0};
3347 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3348 return hkey;
3351 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3353 HKEY hkey;
3354 DWORD count=0;
3355 DWORD type;
3356 DWORD sz = sizeof(count);
3357 DWORD rc;
3359 hkey = openSharedDLLsKey();
3360 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3361 if (rc != ERROR_SUCCESS)
3362 count = 0;
3363 RegCloseKey(hkey);
3364 return count;
3367 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3369 HKEY hkey;
3371 hkey = openSharedDLLsKey();
3372 if (count > 0)
3373 msi_reg_set_val_dword( hkey, path, count );
3374 else
3375 RegDeleteValueW(hkey,path);
3376 RegCloseKey(hkey);
3377 return count;
3380 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3382 MSIFEATURE *feature;
3383 INT count = 0;
3384 BOOL write = FALSE;
3386 /* only refcount DLLs */
3387 if (comp->KeyPath == NULL ||
3388 comp->assembly ||
3389 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3390 comp->Attributes & msidbComponentAttributesODBCDataSource)
3391 write = FALSE;
3392 else
3394 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3395 write = (count > 0);
3397 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3398 write = TRUE;
3401 /* increment counts */
3402 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3404 ComponentList *cl;
3406 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3407 continue;
3409 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3411 if ( cl->component == comp )
3412 count++;
3416 /* decrement counts */
3417 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3419 ComponentList *cl;
3421 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3422 continue;
3424 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3426 if ( cl->component == comp )
3427 count--;
3431 /* ref count all the files in the component */
3432 if (write)
3434 MSIFILE *file;
3436 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3438 if (file->Component == comp)
3439 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3443 /* add a count for permanent */
3444 if (comp->Attributes & msidbComponentAttributesPermanent)
3445 count ++;
3447 comp->RefCount = count;
3449 if (write)
3450 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3453 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3455 if (comp->assembly)
3457 const WCHAR prefixW[] = {'<','\\',0};
3458 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3459 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3461 if (keypath)
3463 strcpyW( keypath, prefixW );
3464 strcatW( keypath, comp->assembly->display_name );
3466 return keypath;
3468 return resolve_keypath( package, comp );
3471 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3473 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3474 UINT rc;
3475 MSICOMPONENT *comp;
3476 HKEY hkey;
3478 TRACE("\n");
3480 squash_guid(package->ProductCode,squished_pc);
3481 msi_set_sourcedir_props(package, FALSE);
3483 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3485 MSIRECORD *uirow;
3486 INSTALLSTATE action;
3488 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3489 if (!comp->ComponentId)
3490 continue;
3492 squash_guid( comp->ComponentId, squished_cc );
3493 msi_free( comp->FullKeypath );
3494 comp->FullKeypath = build_full_keypath( package, comp );
3496 ACTION_RefCountComponent( package, comp );
3498 if (package->need_rollback) action = comp->Installed;
3499 else action = comp->ActionRequest;
3501 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3502 debugstr_w(comp->Component), debugstr_w(squished_cc),
3503 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3505 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3507 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3508 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3509 else
3510 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3512 if (rc != ERROR_SUCCESS)
3513 continue;
3515 if (comp->Attributes & msidbComponentAttributesPermanent)
3517 static const WCHAR szPermKey[] =
3518 { '0','0','0','0','0','0','0','0','0','0','0','0',
3519 '0','0','0','0','0','0','0','0','0','0','0','0',
3520 '0','0','0','0','0','0','0','0',0 };
3522 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3524 if (action == INSTALLSTATE_LOCAL)
3525 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3526 else
3528 MSIFILE *file;
3529 MSIRECORD *row;
3530 LPWSTR ptr, ptr2;
3531 WCHAR source[MAX_PATH];
3532 WCHAR base[MAX_PATH];
3533 LPWSTR sourcepath;
3535 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3536 static const WCHAR query[] = {
3537 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3538 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3539 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3540 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3541 '`','D','i','s','k','I','d','`',0};
3543 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3544 continue;
3546 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3547 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3548 ptr2 = strrchrW(source, '\\') + 1;
3549 msiobj_release(&row->hdr);
3551 lstrcpyW(base, package->PackagePath);
3552 ptr = strrchrW(base, '\\');
3553 *(ptr + 1) = '\0';
3555 sourcepath = msi_resolve_file_source(package, file);
3556 ptr = sourcepath + lstrlenW(base);
3557 lstrcpyW(ptr2, ptr);
3558 msi_free(sourcepath);
3560 msi_reg_set_val_str(hkey, squished_pc, source);
3562 RegCloseKey(hkey);
3564 else if (action == INSTALLSTATE_ABSENT)
3566 if (comp->num_clients <= 0)
3568 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3569 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3570 else
3571 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3573 if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
3575 else
3577 LONG res;
3579 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3580 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE );
3581 else
3582 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );
3584 if (rc != ERROR_SUCCESS)
3586 WARN( "failed to open component key %u\n", rc );
3587 continue;
3589 res = RegDeleteValueW( hkey, squished_pc );
3590 RegCloseKey(hkey);
3591 if (res) WARN( "failed to delete component value %d\n", res );
3595 /* UI stuff */
3596 uirow = MSI_CreateRecord(3);
3597 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3598 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3599 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3600 msi_ui_actiondata( package, szProcessComponents, uirow );
3601 msiobj_release( &uirow->hdr );
3603 return ERROR_SUCCESS;
3606 typedef struct {
3607 CLSID clsid;
3608 LPWSTR source;
3610 LPWSTR path;
3611 ITypeLib *ptLib;
3612 } typelib_struct;
3614 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3615 LPWSTR lpszName, LONG_PTR lParam)
3617 TLIBATTR *attr;
3618 typelib_struct *tl_struct = (typelib_struct*) lParam;
3619 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3620 int sz;
3621 HRESULT res;
3623 if (!IS_INTRESOURCE(lpszName))
3625 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3626 return TRUE;
3629 sz = strlenW(tl_struct->source)+4;
3630 sz *= sizeof(WCHAR);
3632 if ((INT_PTR)lpszName == 1)
3633 tl_struct->path = strdupW(tl_struct->source);
3634 else
3636 tl_struct->path = msi_alloc(sz);
3637 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3640 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3641 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3642 if (FAILED(res))
3644 msi_free(tl_struct->path);
3645 tl_struct->path = NULL;
3647 return TRUE;
3650 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3651 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3653 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3654 return FALSE;
3657 msi_free(tl_struct->path);
3658 tl_struct->path = NULL;
3660 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3661 ITypeLib_Release(tl_struct->ptLib);
3663 return TRUE;
3666 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3668 MSIPACKAGE* package = param;
3669 LPCWSTR component;
3670 MSICOMPONENT *comp;
3671 MSIFILE *file;
3672 typelib_struct tl_struct;
3673 ITypeLib *tlib;
3674 HMODULE module;
3675 HRESULT hr;
3677 component = MSI_RecordGetString(row,3);
3678 comp = msi_get_loaded_component(package,component);
3679 if (!comp)
3680 return ERROR_SUCCESS;
3682 comp->Action = msi_get_component_action( package, comp );
3683 if (comp->Action != INSTALLSTATE_LOCAL)
3685 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3686 return ERROR_SUCCESS;
3689 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3691 TRACE("component has no key path\n");
3692 return ERROR_SUCCESS;
3694 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3696 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3697 if (module)
3699 LPCWSTR guid;
3700 guid = MSI_RecordGetString(row,1);
3701 CLSIDFromString( guid, &tl_struct.clsid);
3702 tl_struct.source = strdupW( file->TargetPath );
3703 tl_struct.path = NULL;
3705 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3706 (LONG_PTR)&tl_struct);
3708 if (tl_struct.path)
3710 LPCWSTR helpid, help_path = NULL;
3711 HRESULT res;
3713 helpid = MSI_RecordGetString(row,6);
3715 if (helpid) help_path = msi_get_target_folder( package, helpid );
3716 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3718 if (FAILED(res))
3719 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3720 else
3721 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3723 ITypeLib_Release(tl_struct.ptLib);
3724 msi_free(tl_struct.path);
3726 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3728 FreeLibrary(module);
3729 msi_free(tl_struct.source);
3731 else
3733 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3734 if (FAILED(hr))
3736 ERR("Failed to load type library: %08x\n", hr);
3737 return ERROR_INSTALL_FAILURE;
3740 ITypeLib_Release(tlib);
3743 return ERROR_SUCCESS;
3746 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3748 static const WCHAR query[] = {
3749 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3750 '`','T','y','p','e','L','i','b','`',0};
3751 MSIQUERY *view;
3752 UINT rc;
3754 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3755 if (rc != ERROR_SUCCESS)
3756 return ERROR_SUCCESS;
3758 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3759 msiobj_release(&view->hdr);
3760 return rc;
3763 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3765 MSIPACKAGE *package = param;
3766 LPCWSTR component, guid;
3767 MSICOMPONENT *comp;
3768 GUID libid;
3769 UINT version;
3770 LCID language;
3771 SYSKIND syskind;
3772 HRESULT hr;
3774 component = MSI_RecordGetString( row, 3 );
3775 comp = msi_get_loaded_component( package, component );
3776 if (!comp)
3777 return ERROR_SUCCESS;
3779 comp->Action = msi_get_component_action( package, comp );
3780 if (comp->Action != INSTALLSTATE_ABSENT)
3782 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3783 return ERROR_SUCCESS;
3785 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3787 guid = MSI_RecordGetString( row, 1 );
3788 CLSIDFromString( guid, &libid );
3789 version = MSI_RecordGetInteger( row, 4 );
3790 language = MSI_RecordGetInteger( row, 2 );
3792 #ifdef _WIN64
3793 syskind = SYS_WIN64;
3794 #else
3795 syskind = SYS_WIN32;
3796 #endif
3798 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3799 if (FAILED(hr))
3801 WARN("Failed to unregister typelib: %08x\n", hr);
3804 return ERROR_SUCCESS;
3807 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3809 static const WCHAR query[] = {
3810 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3811 '`','T','y','p','e','L','i','b','`',0};
3812 MSIQUERY *view;
3813 UINT rc;
3815 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3816 if (rc != ERROR_SUCCESS)
3817 return ERROR_SUCCESS;
3819 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3820 msiobj_release( &view->hdr );
3821 return rc;
3824 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3826 static const WCHAR szlnk[] = {'.','l','n','k',0};
3827 LPCWSTR directory, extension, link_folder;
3828 LPWSTR link_file, filename;
3830 directory = MSI_RecordGetString( row, 2 );
3831 link_folder = msi_get_target_folder( package, directory );
3832 if (!link_folder)
3834 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3835 return NULL;
3837 /* may be needed because of a bug somewhere else */
3838 msi_create_full_path( link_folder );
3840 filename = msi_dup_record_field( row, 3 );
3841 msi_reduce_to_long_filename( filename );
3843 extension = strrchrW( filename, '.' );
3844 if (!extension || strcmpiW( extension, szlnk ))
3846 int len = strlenW( filename );
3847 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3848 memcpy( filename + len, szlnk, sizeof(szlnk) );
3850 link_file = msi_build_directory_name( 2, link_folder, filename );
3851 msi_free( filename );
3853 return link_file;
3856 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3858 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3859 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3860 WCHAR *folder, *dest, *path;
3862 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3863 folder = msi_dup_property( package->db, szWindowsFolder );
3864 else
3866 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3867 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3868 msi_free( appdata );
3870 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3871 msi_create_full_path( dest );
3872 path = msi_build_directory_name( 2, dest, icon_name );
3873 msi_free( folder );
3874 msi_free( dest );
3875 return path;
3878 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3880 MSIPACKAGE *package = param;
3881 LPWSTR link_file, deformated, path;
3882 LPCWSTR component, target;
3883 MSICOMPONENT *comp;
3884 IShellLinkW *sl = NULL;
3885 IPersistFile *pf = NULL;
3886 HRESULT res;
3888 component = MSI_RecordGetString(row, 4);
3889 comp = msi_get_loaded_component(package, component);
3890 if (!comp)
3891 return ERROR_SUCCESS;
3893 comp->Action = msi_get_component_action( package, comp );
3894 if (comp->Action != INSTALLSTATE_LOCAL)
3896 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3897 return ERROR_SUCCESS;
3899 msi_ui_actiondata( package, szCreateShortcuts, row );
3901 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3902 &IID_IShellLinkW, (LPVOID *) &sl );
3904 if (FAILED( res ))
3906 ERR("CLSID_ShellLink not available\n");
3907 goto err;
3910 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3911 if (FAILED( res ))
3913 ERR("QueryInterface(IID_IPersistFile) failed\n");
3914 goto err;
3917 target = MSI_RecordGetString(row, 5);
3918 if (strchrW(target, '['))
3920 deformat_string( package, target, &path );
3921 TRACE("target path is %s\n", debugstr_w(path));
3922 IShellLinkW_SetPath( sl, path );
3923 msi_free( path );
3925 else
3927 FIXME("poorly handled shortcut format, advertised shortcut\n");
3928 path = resolve_keypath( package, comp );
3929 IShellLinkW_SetPath( sl, path );
3930 msi_free( path );
3933 if (!MSI_RecordIsNull(row,6))
3935 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3936 deformat_string(package, arguments, &deformated);
3937 IShellLinkW_SetArguments(sl,deformated);
3938 msi_free(deformated);
3941 if (!MSI_RecordIsNull(row,7))
3943 LPCWSTR description = MSI_RecordGetString(row, 7);
3944 IShellLinkW_SetDescription(sl, description);
3947 if (!MSI_RecordIsNull(row,8))
3948 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3950 if (!MSI_RecordIsNull(row,9))
3952 INT index;
3953 LPCWSTR icon = MSI_RecordGetString(row, 9);
3955 path = msi_build_icon_path(package, icon);
3956 index = MSI_RecordGetInteger(row,10);
3958 /* no value means 0 */
3959 if (index == MSI_NULL_INTEGER)
3960 index = 0;
3962 IShellLinkW_SetIconLocation(sl, path, index);
3963 msi_free(path);
3966 if (!MSI_RecordIsNull(row,11))
3967 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3969 if (!MSI_RecordIsNull(row,12))
3971 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3972 full_path = msi_get_target_folder( package, wkdir );
3973 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3975 link_file = get_link_file(package, row);
3977 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3978 IPersistFile_Save(pf, link_file, FALSE);
3979 msi_free(link_file);
3981 err:
3982 if (pf)
3983 IPersistFile_Release( pf );
3984 if (sl)
3985 IShellLinkW_Release( sl );
3987 return ERROR_SUCCESS;
3990 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3992 static const WCHAR query[] = {
3993 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3994 '`','S','h','o','r','t','c','u','t','`',0};
3995 MSIQUERY *view;
3996 HRESULT res;
3997 UINT rc;
3999 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4000 if (rc != ERROR_SUCCESS)
4001 return ERROR_SUCCESS;
4003 res = CoInitialize( NULL );
4005 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4006 msiobj_release(&view->hdr);
4008 if (SUCCEEDED(res)) CoUninitialize();
4009 return rc;
4012 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4014 MSIPACKAGE *package = param;
4015 LPWSTR link_file;
4016 LPCWSTR component;
4017 MSICOMPONENT *comp;
4019 component = MSI_RecordGetString( row, 4 );
4020 comp = msi_get_loaded_component( package, component );
4021 if (!comp)
4022 return ERROR_SUCCESS;
4024 comp->Action = msi_get_component_action( package, comp );
4025 if (comp->Action != INSTALLSTATE_ABSENT)
4027 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4028 return ERROR_SUCCESS;
4030 msi_ui_actiondata( package, szRemoveShortcuts, row );
4032 link_file = get_link_file( package, row );
4034 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4035 if (!DeleteFileW( link_file ))
4037 WARN("Failed to remove shortcut file %u\n", GetLastError());
4039 msi_free( link_file );
4041 return ERROR_SUCCESS;
4044 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4046 static const WCHAR query[] = {
4047 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4048 '`','S','h','o','r','t','c','u','t','`',0};
4049 MSIQUERY *view;
4050 UINT rc;
4052 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4053 if (rc != ERROR_SUCCESS)
4054 return ERROR_SUCCESS;
4056 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4057 msiobj_release( &view->hdr );
4058 return rc;
4061 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4063 MSIPACKAGE* package = param;
4064 HANDLE the_file;
4065 LPWSTR FilePath;
4066 LPCWSTR FileName;
4067 CHAR buffer[1024];
4068 DWORD sz;
4069 UINT rc;
4071 FileName = MSI_RecordGetString(row,1);
4072 if (!FileName)
4074 ERR("Unable to get FileName\n");
4075 return ERROR_SUCCESS;
4078 FilePath = msi_build_icon_path(package, FileName);
4080 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4082 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4083 FILE_ATTRIBUTE_NORMAL, NULL);
4085 if (the_file == INVALID_HANDLE_VALUE)
4087 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4088 msi_free(FilePath);
4089 return ERROR_SUCCESS;
4094 DWORD write;
4095 sz = 1024;
4096 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4097 if (rc != ERROR_SUCCESS)
4099 ERR("Failed to get stream\n");
4100 CloseHandle(the_file);
4101 DeleteFileW(FilePath);
4102 break;
4104 WriteFile(the_file,buffer,sz,&write,NULL);
4105 } while (sz == 1024);
4107 msi_free(FilePath);
4108 CloseHandle(the_file);
4110 return ERROR_SUCCESS;
4113 static UINT msi_publish_icons(MSIPACKAGE *package)
4115 static const WCHAR query[]= {
4116 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4117 '`','I','c','o','n','`',0};
4118 MSIQUERY *view;
4119 UINT r;
4121 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4122 if (r == ERROR_SUCCESS)
4124 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4125 msiobj_release(&view->hdr);
4126 if (r != ERROR_SUCCESS)
4127 return r;
4129 return ERROR_SUCCESS;
4132 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4134 UINT r;
4135 HKEY source;
4136 LPWSTR buffer;
4137 MSIMEDIADISK *disk;
4138 MSISOURCELISTINFO *info;
4140 r = RegCreateKeyW(hkey, szSourceList, &source);
4141 if (r != ERROR_SUCCESS)
4142 return r;
4144 RegCloseKey(source);
4146 buffer = strrchrW(package->PackagePath, '\\') + 1;
4147 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4148 package->Context, MSICODE_PRODUCT,
4149 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4150 if (r != ERROR_SUCCESS)
4151 return r;
4153 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4154 package->Context, MSICODE_PRODUCT,
4155 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4156 if (r != ERROR_SUCCESS)
4157 return r;
4159 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4160 package->Context, MSICODE_PRODUCT,
4161 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4162 if (r != ERROR_SUCCESS)
4163 return r;
4165 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4167 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4168 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4169 info->options, info->value);
4170 else
4171 MsiSourceListSetInfoW(package->ProductCode, NULL,
4172 info->context, info->options,
4173 info->property, info->value);
4176 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4178 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4179 disk->context, disk->options,
4180 disk->disk_id, disk->volume_label, disk->disk_prompt);
4183 return ERROR_SUCCESS;
4186 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4188 MSIHANDLE hdb, suminfo;
4189 WCHAR guids[MAX_PATH];
4190 WCHAR packcode[SQUISH_GUID_SIZE];
4191 LPWSTR buffer;
4192 LPWSTR ptr;
4193 DWORD langid;
4194 DWORD size;
4195 UINT r;
4197 static const WCHAR szARPProductIcon[] =
4198 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4199 static const WCHAR szAssignment[] =
4200 {'A','s','s','i','g','n','m','e','n','t',0};
4201 static const WCHAR szAdvertiseFlags[] =
4202 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4203 static const WCHAR szClients[] =
4204 {'C','l','i','e','n','t','s',0};
4205 static const WCHAR szColon[] = {':',0};
4207 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4208 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4209 msi_free(buffer);
4211 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4212 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4214 /* FIXME */
4215 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4217 buffer = msi_dup_property(package->db, szARPProductIcon);
4218 if (buffer)
4220 LPWSTR path = msi_build_icon_path(package, buffer);
4221 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4222 msi_free(path);
4223 msi_free(buffer);
4226 buffer = msi_dup_property(package->db, szProductVersion);
4227 if (buffer)
4229 DWORD verdword = msi_version_str_to_dword(buffer);
4230 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4231 msi_free(buffer);
4234 msi_reg_set_val_dword(hkey, szAssignment, 0);
4235 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4236 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4237 msi_reg_set_val_str(hkey, szClients, szColon);
4239 hdb = alloc_msihandle(&package->db->hdr);
4240 if (!hdb)
4241 return ERROR_NOT_ENOUGH_MEMORY;
4243 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4244 MsiCloseHandle(hdb);
4245 if (r != ERROR_SUCCESS)
4246 goto done;
4248 size = MAX_PATH;
4249 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4250 NULL, guids, &size);
4251 if (r != ERROR_SUCCESS)
4252 goto done;
4254 ptr = strchrW(guids, ';');
4255 if (ptr) *ptr = 0;
4256 squash_guid(guids, packcode);
4257 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4259 done:
4260 MsiCloseHandle(suminfo);
4261 return ERROR_SUCCESS;
4264 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4266 UINT r;
4267 HKEY hkey;
4268 LPWSTR upgrade;
4269 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4271 upgrade = msi_dup_property(package->db, szUpgradeCode);
4272 if (!upgrade)
4273 return ERROR_SUCCESS;
4275 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4276 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4277 else
4278 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4280 if (r != ERROR_SUCCESS)
4282 WARN("failed to open upgrade code key\n");
4283 msi_free(upgrade);
4284 return ERROR_SUCCESS;
4286 squash_guid(package->ProductCode, squashed_pc);
4287 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4288 RegCloseKey(hkey);
4289 msi_free(upgrade);
4290 return ERROR_SUCCESS;
4293 static BOOL msi_check_publish(MSIPACKAGE *package)
4295 MSIFEATURE *feature;
4297 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4299 feature->Action = msi_get_feature_action( package, feature );
4300 if (feature->Action == INSTALLSTATE_LOCAL)
4301 return TRUE;
4304 return FALSE;
4307 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4309 MSIFEATURE *feature;
4311 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4313 feature->Action = msi_get_feature_action( package, feature );
4314 if (feature->Action != INSTALLSTATE_ABSENT)
4315 return FALSE;
4318 return TRUE;
4321 static UINT msi_publish_patches( MSIPACKAGE *package )
4323 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4324 WCHAR patch_squashed[GUID_SIZE];
4325 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4326 LONG res;
4327 MSIPATCHINFO *patch;
4328 UINT r;
4329 WCHAR *p, *all_patches = NULL;
4330 DWORD len = 0;
4332 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4333 if (r != ERROR_SUCCESS)
4334 return ERROR_FUNCTION_FAILED;
4336 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4337 if (res != ERROR_SUCCESS)
4339 r = ERROR_FUNCTION_FAILED;
4340 goto done;
4343 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4344 if (r != ERROR_SUCCESS)
4345 goto done;
4347 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4349 squash_guid( patch->patchcode, patch_squashed );
4350 len += strlenW( patch_squashed ) + 1;
4353 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4354 if (!all_patches)
4355 goto done;
4357 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4359 HKEY patch_key;
4361 squash_guid( patch->patchcode, p );
4362 p += strlenW( p ) + 1;
4364 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4365 (const BYTE *)patch->transforms,
4366 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4367 if (res != ERROR_SUCCESS)
4368 goto done;
4370 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4371 if (r != ERROR_SUCCESS)
4372 goto done;
4374 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4375 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4376 RegCloseKey( patch_key );
4377 if (res != ERROR_SUCCESS)
4378 goto done;
4380 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4382 res = GetLastError();
4383 ERR("Unable to copy patch package %d\n", res);
4384 goto done;
4386 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4387 if (res != ERROR_SUCCESS)
4388 goto done;
4390 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4391 RegCloseKey( patch_key );
4392 if (res != ERROR_SUCCESS)
4393 goto done;
4396 all_patches[len] = 0;
4397 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4398 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4399 if (res != ERROR_SUCCESS)
4400 goto done;
4402 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4403 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4404 if (res != ERROR_SUCCESS)
4405 r = ERROR_FUNCTION_FAILED;
4407 done:
4408 RegCloseKey( product_patches_key );
4409 RegCloseKey( patches_key );
4410 RegCloseKey( product_key );
4411 msi_free( all_patches );
4412 return r;
4415 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4417 UINT rc;
4418 HKEY hukey = NULL, hudkey = NULL;
4419 MSIRECORD *uirow;
4421 if (!list_empty(&package->patches))
4423 rc = msi_publish_patches(package);
4424 if (rc != ERROR_SUCCESS)
4425 goto end;
4428 /* FIXME: also need to publish if the product is in advertise mode */
4429 if (!msi_check_publish(package))
4430 return ERROR_SUCCESS;
4432 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4433 &hukey, TRUE);
4434 if (rc != ERROR_SUCCESS)
4435 goto end;
4437 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4438 NULL, &hudkey, TRUE);
4439 if (rc != ERROR_SUCCESS)
4440 goto end;
4442 rc = msi_publish_upgrade_code(package);
4443 if (rc != ERROR_SUCCESS)
4444 goto end;
4446 rc = msi_publish_product_properties(package, hukey);
4447 if (rc != ERROR_SUCCESS)
4448 goto end;
4450 rc = msi_publish_sourcelist(package, hukey);
4451 if (rc != ERROR_SUCCESS)
4452 goto end;
4454 rc = msi_publish_icons(package);
4456 end:
4457 uirow = MSI_CreateRecord( 1 );
4458 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4459 msi_ui_actiondata( package, szPublishProduct, uirow );
4460 msiobj_release( &uirow->hdr );
4462 RegCloseKey(hukey);
4463 RegCloseKey(hudkey);
4464 return rc;
4467 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4469 WCHAR *filename, *ptr, *folder, *ret;
4470 const WCHAR *dirprop;
4472 filename = msi_dup_record_field( row, 2 );
4473 if (filename && (ptr = strchrW( filename, '|' )))
4474 ptr++;
4475 else
4476 ptr = filename;
4478 dirprop = MSI_RecordGetString( row, 3 );
4479 if (dirprop)
4481 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4482 if (!folder) folder = msi_dup_property( package->db, dirprop );
4484 else
4485 folder = msi_dup_property( package->db, szWindowsFolder );
4487 if (!folder)
4489 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4490 msi_free( filename );
4491 return NULL;
4494 ret = msi_build_directory_name( 2, folder, ptr );
4496 msi_free( filename );
4497 msi_free( folder );
4498 return ret;
4501 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4503 MSIPACKAGE *package = param;
4504 LPCWSTR component, section, key, value, identifier;
4505 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4506 MSIRECORD * uirow;
4507 INT action;
4508 MSICOMPONENT *comp;
4510 component = MSI_RecordGetString(row, 8);
4511 comp = msi_get_loaded_component(package,component);
4512 if (!comp)
4513 return ERROR_SUCCESS;
4515 comp->Action = msi_get_component_action( package, comp );
4516 if (comp->Action != INSTALLSTATE_LOCAL)
4518 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4519 return ERROR_SUCCESS;
4522 identifier = MSI_RecordGetString(row,1);
4523 section = MSI_RecordGetString(row,4);
4524 key = MSI_RecordGetString(row,5);
4525 value = MSI_RecordGetString(row,6);
4526 action = MSI_RecordGetInteger(row,7);
4528 deformat_string(package,section,&deformated_section);
4529 deformat_string(package,key,&deformated_key);
4530 deformat_string(package,value,&deformated_value);
4532 fullname = get_ini_file_name(package, row);
4534 if (action == 0)
4536 TRACE("Adding value %s to section %s in %s\n",
4537 debugstr_w(deformated_key), debugstr_w(deformated_section),
4538 debugstr_w(fullname));
4539 WritePrivateProfileStringW(deformated_section, deformated_key,
4540 deformated_value, fullname);
4542 else if (action == 1)
4544 WCHAR returned[10];
4545 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4546 returned, 10, fullname);
4547 if (returned[0] == 0)
4549 TRACE("Adding value %s to section %s in %s\n",
4550 debugstr_w(deformated_key), debugstr_w(deformated_section),
4551 debugstr_w(fullname));
4553 WritePrivateProfileStringW(deformated_section, deformated_key,
4554 deformated_value, fullname);
4557 else if (action == 3)
4558 FIXME("Append to existing section not yet implemented\n");
4560 uirow = MSI_CreateRecord(4);
4561 MSI_RecordSetStringW(uirow,1,identifier);
4562 MSI_RecordSetStringW(uirow,2,deformated_section);
4563 MSI_RecordSetStringW(uirow,3,deformated_key);
4564 MSI_RecordSetStringW(uirow,4,deformated_value);
4565 msi_ui_actiondata( package, szWriteIniValues, uirow );
4566 msiobj_release( &uirow->hdr );
4568 msi_free(fullname);
4569 msi_free(deformated_key);
4570 msi_free(deformated_value);
4571 msi_free(deformated_section);
4572 return ERROR_SUCCESS;
4575 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4577 static const WCHAR query[] = {
4578 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4579 '`','I','n','i','F','i','l','e','`',0};
4580 MSIQUERY *view;
4581 UINT rc;
4583 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4584 if (rc != ERROR_SUCCESS)
4585 return ERROR_SUCCESS;
4587 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4588 msiobj_release(&view->hdr);
4589 return rc;
4592 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4594 MSIPACKAGE *package = param;
4595 LPCWSTR component, section, key, value, identifier;
4596 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4597 MSICOMPONENT *comp;
4598 MSIRECORD *uirow;
4599 INT action;
4601 component = MSI_RecordGetString( row, 8 );
4602 comp = msi_get_loaded_component( package, component );
4603 if (!comp)
4604 return ERROR_SUCCESS;
4606 comp->Action = msi_get_component_action( package, comp );
4607 if (comp->Action != INSTALLSTATE_ABSENT)
4609 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4610 return ERROR_SUCCESS;
4613 identifier = MSI_RecordGetString( row, 1 );
4614 section = MSI_RecordGetString( row, 4 );
4615 key = MSI_RecordGetString( row, 5 );
4616 value = MSI_RecordGetString( row, 6 );
4617 action = MSI_RecordGetInteger( row, 7 );
4619 deformat_string( package, section, &deformated_section );
4620 deformat_string( package, key, &deformated_key );
4621 deformat_string( package, value, &deformated_value );
4623 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4625 filename = get_ini_file_name( package, row );
4627 TRACE("Removing key %s from section %s in %s\n",
4628 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4630 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4632 WARN("Unable to remove key %u\n", GetLastError());
4634 msi_free( filename );
4636 else
4637 FIXME("Unsupported action %d\n", action);
4640 uirow = MSI_CreateRecord( 4 );
4641 MSI_RecordSetStringW( uirow, 1, identifier );
4642 MSI_RecordSetStringW( uirow, 2, deformated_section );
4643 MSI_RecordSetStringW( uirow, 3, deformated_key );
4644 MSI_RecordSetStringW( uirow, 4, deformated_value );
4645 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4646 msiobj_release( &uirow->hdr );
4648 msi_free( deformated_key );
4649 msi_free( deformated_value );
4650 msi_free( deformated_section );
4651 return ERROR_SUCCESS;
4654 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4656 MSIPACKAGE *package = param;
4657 LPCWSTR component, section, key, value, identifier;
4658 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4659 MSICOMPONENT *comp;
4660 MSIRECORD *uirow;
4661 INT action;
4663 component = MSI_RecordGetString( row, 8 );
4664 comp = msi_get_loaded_component( package, component );
4665 if (!comp)
4666 return ERROR_SUCCESS;
4668 comp->Action = msi_get_component_action( package, comp );
4669 if (comp->Action != INSTALLSTATE_LOCAL)
4671 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4672 return ERROR_SUCCESS;
4675 identifier = MSI_RecordGetString( row, 1 );
4676 section = MSI_RecordGetString( row, 4 );
4677 key = MSI_RecordGetString( row, 5 );
4678 value = MSI_RecordGetString( row, 6 );
4679 action = MSI_RecordGetInteger( row, 7 );
4681 deformat_string( package, section, &deformated_section );
4682 deformat_string( package, key, &deformated_key );
4683 deformat_string( package, value, &deformated_value );
4685 if (action == msidbIniFileActionRemoveLine)
4687 filename = get_ini_file_name( package, row );
4689 TRACE("Removing key %s from section %s in %s\n",
4690 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4692 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4694 WARN("Unable to remove key %u\n", GetLastError());
4696 msi_free( filename );
4698 else
4699 FIXME("Unsupported action %d\n", action);
4701 uirow = MSI_CreateRecord( 4 );
4702 MSI_RecordSetStringW( uirow, 1, identifier );
4703 MSI_RecordSetStringW( uirow, 2, deformated_section );
4704 MSI_RecordSetStringW( uirow, 3, deformated_key );
4705 MSI_RecordSetStringW( uirow, 4, deformated_value );
4706 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4707 msiobj_release( &uirow->hdr );
4709 msi_free( deformated_key );
4710 msi_free( deformated_value );
4711 msi_free( deformated_section );
4712 return ERROR_SUCCESS;
4715 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4717 static const WCHAR query[] = {
4718 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4719 '`','I','n','i','F','i','l','e','`',0};
4720 static const WCHAR remove_query[] = {
4721 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4722 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4723 MSIQUERY *view;
4724 UINT rc;
4726 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4727 if (rc == ERROR_SUCCESS)
4729 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4730 msiobj_release( &view->hdr );
4731 if (rc != ERROR_SUCCESS)
4732 return rc;
4734 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4735 if (rc == ERROR_SUCCESS)
4737 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4738 msiobj_release( &view->hdr );
4739 if (rc != ERROR_SUCCESS)
4740 return rc;
4742 return ERROR_SUCCESS;
4745 static void register_dll( const WCHAR *dll, BOOL unregister )
4747 static const WCHAR regW[] =
4748 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4749 static const WCHAR unregW[] =
4750 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4751 PROCESS_INFORMATION pi;
4752 STARTUPINFOW si;
4753 WCHAR *cmd;
4755 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4757 if (unregister) sprintfW( cmd, unregW, dll );
4758 else sprintfW( cmd, regW, dll );
4760 memset( &si, 0, sizeof(STARTUPINFOW) );
4761 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4763 CloseHandle( pi.hThread );
4764 msi_dialog_check_messages( pi.hProcess );
4765 CloseHandle( pi.hProcess );
4767 msi_free( cmd );
4770 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4772 MSIPACKAGE *package = param;
4773 LPCWSTR filename;
4774 MSIFILE *file;
4775 MSIRECORD *uirow;
4777 filename = MSI_RecordGetString( row, 1 );
4778 file = msi_get_loaded_file( package, filename );
4779 if (!file)
4781 WARN("unable to find file %s\n", debugstr_w(filename));
4782 return ERROR_SUCCESS;
4784 file->Component->Action = msi_get_component_action( package, file->Component );
4785 if (file->Component->Action != INSTALLSTATE_LOCAL)
4787 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4788 return ERROR_SUCCESS;
4791 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4792 register_dll( file->TargetPath, FALSE );
4794 uirow = MSI_CreateRecord( 2 );
4795 MSI_RecordSetStringW( uirow, 1, file->File );
4796 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4797 msi_ui_actiondata( package, szSelfRegModules, uirow );
4798 msiobj_release( &uirow->hdr );
4800 return ERROR_SUCCESS;
4803 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4805 static const WCHAR query[] = {
4806 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4807 '`','S','e','l','f','R','e','g','`',0};
4808 MSIQUERY *view;
4809 UINT rc;
4811 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4812 if (rc != ERROR_SUCCESS)
4813 return ERROR_SUCCESS;
4815 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4816 msiobj_release(&view->hdr);
4817 return rc;
4820 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4822 MSIPACKAGE *package = param;
4823 LPCWSTR filename;
4824 MSIFILE *file;
4825 MSIRECORD *uirow;
4827 filename = MSI_RecordGetString( row, 1 );
4828 file = msi_get_loaded_file( package, filename );
4829 if (!file)
4831 WARN("unable to find file %s\n", debugstr_w(filename));
4832 return ERROR_SUCCESS;
4834 file->Component->Action = msi_get_component_action( package, file->Component );
4835 if (file->Component->Action != INSTALLSTATE_ABSENT)
4837 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4838 return ERROR_SUCCESS;
4841 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4842 register_dll( file->TargetPath, TRUE );
4844 uirow = MSI_CreateRecord( 2 );
4845 MSI_RecordSetStringW( uirow, 1, file->File );
4846 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4847 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4848 msiobj_release( &uirow->hdr );
4850 return ERROR_SUCCESS;
4853 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4855 static const WCHAR query[] = {
4856 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4857 '`','S','e','l','f','R','e','g','`',0};
4858 MSIQUERY *view;
4859 UINT rc;
4861 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4862 if (rc != ERROR_SUCCESS)
4863 return ERROR_SUCCESS;
4865 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4866 msiobj_release( &view->hdr );
4867 return rc;
4870 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4872 MSIFEATURE *feature;
4873 UINT rc;
4874 HKEY hkey = NULL, userdata = NULL;
4876 if (!msi_check_publish(package))
4877 return ERROR_SUCCESS;
4879 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4880 &hkey, TRUE);
4881 if (rc != ERROR_SUCCESS)
4882 goto end;
4884 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4885 &userdata, TRUE);
4886 if (rc != ERROR_SUCCESS)
4887 goto end;
4889 /* here the guids are base 85 encoded */
4890 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4892 ComponentList *cl;
4893 LPWSTR data = NULL;
4894 GUID clsid;
4895 INT size;
4896 BOOL absent = FALSE;
4897 MSIRECORD *uirow;
4899 if (feature->Level <= 0) continue;
4901 if (feature->Action != INSTALLSTATE_LOCAL &&
4902 feature->Action != INSTALLSTATE_SOURCE &&
4903 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4905 size = 1;
4906 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4908 size += 21;
4910 if (feature->Feature_Parent)
4911 size += strlenW( feature->Feature_Parent )+2;
4913 data = msi_alloc(size * sizeof(WCHAR));
4915 data[0] = 0;
4916 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4918 MSICOMPONENT* component = cl->component;
4919 WCHAR buf[21];
4921 buf[0] = 0;
4922 if (component->ComponentId)
4924 TRACE("From %s\n",debugstr_w(component->ComponentId));
4925 CLSIDFromString(component->ComponentId, &clsid);
4926 encode_base85_guid(&clsid,buf);
4927 TRACE("to %s\n",debugstr_w(buf));
4928 strcatW(data,buf);
4932 if (feature->Feature_Parent)
4934 static const WCHAR sep[] = {'\2',0};
4935 strcatW(data,sep);
4936 strcatW(data,feature->Feature_Parent);
4939 msi_reg_set_val_str( userdata, feature->Feature, data );
4940 msi_free(data);
4942 size = 0;
4943 if (feature->Feature_Parent)
4944 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4945 if (!absent)
4947 size += sizeof(WCHAR);
4948 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4949 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4951 else
4953 size += 2*sizeof(WCHAR);
4954 data = msi_alloc(size);
4955 data[0] = 0x6;
4956 data[1] = 0;
4957 if (feature->Feature_Parent)
4958 strcpyW( &data[1], feature->Feature_Parent );
4959 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4960 (LPBYTE)data,size);
4961 msi_free(data);
4964 /* the UI chunk */
4965 uirow = MSI_CreateRecord( 1 );
4966 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4967 msi_ui_actiondata( package, szPublishFeatures, uirow );
4968 msiobj_release( &uirow->hdr );
4969 /* FIXME: call msi_ui_progress? */
4972 end:
4973 RegCloseKey(hkey);
4974 RegCloseKey(userdata);
4975 return rc;
4978 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4980 UINT r;
4981 HKEY hkey;
4982 MSIRECORD *uirow;
4984 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4986 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4987 &hkey, FALSE);
4988 if (r == ERROR_SUCCESS)
4990 RegDeleteValueW(hkey, feature->Feature);
4991 RegCloseKey(hkey);
4994 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4995 &hkey, FALSE);
4996 if (r == ERROR_SUCCESS)
4998 RegDeleteValueW(hkey, feature->Feature);
4999 RegCloseKey(hkey);
5002 uirow = MSI_CreateRecord( 1 );
5003 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5004 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
5005 msiobj_release( &uirow->hdr );
5007 return ERROR_SUCCESS;
5010 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5012 MSIFEATURE *feature;
5014 if (!msi_check_unpublish(package))
5015 return ERROR_SUCCESS;
5017 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5019 msi_unpublish_feature(package, feature);
5022 return ERROR_SUCCESS;
5025 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5027 SYSTEMTIME systime;
5028 DWORD size, langid;
5029 WCHAR date[9], *val, *buffer;
5030 const WCHAR *prop, *key;
5032 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5033 static const WCHAR modpath_fmt[] =
5034 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5035 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5036 static const WCHAR szModifyPath[] =
5037 {'M','o','d','i','f','y','P','a','t','h',0};
5038 static const WCHAR szUninstallString[] =
5039 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5040 static const WCHAR szEstimatedSize[] =
5041 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5042 static const WCHAR szDisplayVersion[] =
5043 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5044 static const WCHAR szInstallSource[] =
5045 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5046 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5047 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5048 static const WCHAR szAuthorizedCDFPrefix[] =
5049 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5050 static const WCHAR szARPCONTACT[] =
5051 {'A','R','P','C','O','N','T','A','C','T',0};
5052 static const WCHAR szContact[] =
5053 {'C','o','n','t','a','c','t',0};
5054 static const WCHAR szARPCOMMENTS[] =
5055 {'A','R','P','C','O','M','M','E','N','T','S',0};
5056 static const WCHAR szComments[] =
5057 {'C','o','m','m','e','n','t','s',0};
5058 static const WCHAR szProductName[] =
5059 {'P','r','o','d','u','c','t','N','a','m','e',0};
5060 static const WCHAR szDisplayName[] =
5061 {'D','i','s','p','l','a','y','N','a','m','e',0};
5062 static const WCHAR szARPHELPLINK[] =
5063 {'A','R','P','H','E','L','P','L','I','N','K',0};
5064 static const WCHAR szHelpLink[] =
5065 {'H','e','l','p','L','i','n','k',0};
5066 static const WCHAR szARPHELPTELEPHONE[] =
5067 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5068 static const WCHAR szHelpTelephone[] =
5069 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5070 static const WCHAR szARPINSTALLLOCATION[] =
5071 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5072 static const WCHAR szManufacturer[] =
5073 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5074 static const WCHAR szPublisher[] =
5075 {'P','u','b','l','i','s','h','e','r',0};
5076 static const WCHAR szARPREADME[] =
5077 {'A','R','P','R','E','A','D','M','E',0};
5078 static const WCHAR szReadme[] =
5079 {'R','e','a','d','M','e',0};
5080 static const WCHAR szARPSIZE[] =
5081 {'A','R','P','S','I','Z','E',0};
5082 static const WCHAR szSize[] =
5083 {'S','i','z','e',0};
5084 static const WCHAR szARPURLINFOABOUT[] =
5085 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5086 static const WCHAR szURLInfoAbout[] =
5087 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5088 static const WCHAR szARPURLUPDATEINFO[] =
5089 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5090 static const WCHAR szURLUpdateInfo[] =
5091 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5092 static const WCHAR szARPSYSTEMCOMPONENT[] =
5093 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5094 static const WCHAR szSystemComponent[] =
5095 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5097 static const WCHAR *propval[] = {
5098 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5099 szARPCONTACT, szContact,
5100 szARPCOMMENTS, szComments,
5101 szProductName, szDisplayName,
5102 szARPHELPLINK, szHelpLink,
5103 szARPHELPTELEPHONE, szHelpTelephone,
5104 szARPINSTALLLOCATION, szInstallLocation,
5105 szSourceDir, szInstallSource,
5106 szManufacturer, szPublisher,
5107 szARPREADME, szReadme,
5108 szARPSIZE, szSize,
5109 szARPURLINFOABOUT, szURLInfoAbout,
5110 szARPURLUPDATEINFO, szURLUpdateInfo,
5111 NULL
5113 const WCHAR **p = propval;
5115 while (*p)
5117 prop = *p++;
5118 key = *p++;
5119 val = msi_dup_property(package->db, prop);
5120 msi_reg_set_val_str(hkey, key, val);
5121 msi_free(val);
5124 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5125 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5127 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5129 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5130 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5131 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5132 msi_free(buffer);
5134 /* FIXME: Write real Estimated Size when we have it */
5135 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5137 GetLocalTime(&systime);
5138 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5139 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5141 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5142 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5144 buffer = msi_dup_property(package->db, szProductVersion);
5145 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5146 if (buffer)
5148 DWORD verdword = msi_version_str_to_dword(buffer);
5150 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5151 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5152 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5153 msi_free(buffer);
5156 return ERROR_SUCCESS;
5159 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5161 WCHAR squashed_pc[SQUISH_GUID_SIZE];
5162 MSIRECORD *uirow;
5163 LPWSTR upgrade_code;
5164 HKEY hkey, props, upgrade_key;
5165 UINT rc;
5167 /* FIXME: also need to publish if the product is in advertise mode */
5168 if (!msi_check_publish(package))
5169 return ERROR_SUCCESS;
5171 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5172 if (rc != ERROR_SUCCESS)
5173 return rc;
5175 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5176 if (rc != ERROR_SUCCESS)
5177 goto done;
5179 rc = msi_publish_install_properties(package, hkey);
5180 if (rc != ERROR_SUCCESS)
5181 goto done;
5183 rc = msi_publish_install_properties(package, props);
5184 if (rc != ERROR_SUCCESS)
5185 goto done;
5187 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5188 if (upgrade_code)
5190 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5191 if (rc == ERROR_SUCCESS)
5193 squash_guid( package->ProductCode, squashed_pc );
5194 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5195 RegCloseKey( upgrade_key );
5197 msi_free( upgrade_code );
5199 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5200 package->delete_on_close = FALSE;
5202 done:
5203 uirow = MSI_CreateRecord( 1 );
5204 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5205 msi_ui_actiondata( package, szRegisterProduct, uirow );
5206 msiobj_release( &uirow->hdr );
5208 RegCloseKey(hkey);
5209 return ERROR_SUCCESS;
5212 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5214 return execute_script(package, SCRIPT_INSTALL);
5217 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5219 MSIPACKAGE *package = param;
5220 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5221 WCHAR *p, *icon_path;
5223 if (!icon) return ERROR_SUCCESS;
5224 if ((icon_path = msi_build_icon_path( package, icon )))
5226 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5227 DeleteFileW( icon_path );
5228 if ((p = strrchrW( icon_path, '\\' )))
5230 *p = 0;
5231 RemoveDirectoryW( icon_path );
5233 msi_free( icon_path );
5235 return ERROR_SUCCESS;
5238 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5240 static const WCHAR query[]= {
5241 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5242 MSIQUERY *view;
5243 UINT r;
5245 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5246 if (r == ERROR_SUCCESS)
5248 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5249 msiobj_release( &view->hdr );
5250 if (r != ERROR_SUCCESS)
5251 return r;
5253 return ERROR_SUCCESS;
5256 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
5258 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
5259 WCHAR *upgrade, **features;
5260 BOOL full_uninstall = TRUE;
5261 MSIFEATURE *feature;
5262 MSIPATCHINFO *patch;
5263 UINT i;
5265 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5267 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
5269 features = msi_split_string( remove, ',' );
5270 for (i = 0; features && features[i]; i++)
5272 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
5274 msi_free(features);
5276 if (!full_uninstall)
5277 return ERROR_SUCCESS;
5279 MSIREG_DeleteProductKey(package->ProductCode);
5280 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5281 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5283 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5284 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5285 MSIREG_DeleteUserProductKey(package->ProductCode);
5286 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5288 upgrade = msi_dup_property(package->db, szUpgradeCode);
5289 if (upgrade)
5291 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5292 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5293 msi_free(upgrade);
5296 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5298 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5299 if (!strcmpW( package->ProductCode, patch->products ))
5301 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5302 patch->delete_on_close = TRUE;
5304 /* FIXME: remove local patch package if this is the last product */
5306 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5307 package->delete_on_close = TRUE;
5309 msi_unpublish_icons( package );
5310 return ERROR_SUCCESS;
5313 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5315 UINT rc;
5316 WCHAR *remove;
5318 /* first do the same as an InstallExecute */
5319 rc = ACTION_InstallExecute(package);
5320 if (rc != ERROR_SUCCESS)
5321 return rc;
5323 /* then handle commit actions */
5324 rc = execute_script(package, SCRIPT_COMMIT);
5325 if (rc != ERROR_SUCCESS)
5326 return rc;
5328 remove = msi_dup_property(package->db, szRemove);
5329 rc = msi_unpublish_product(package, remove);
5330 msi_free(remove);
5331 return rc;
5334 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5336 static const WCHAR RunOnce[] = {
5337 'S','o','f','t','w','a','r','e','\\',
5338 'M','i','c','r','o','s','o','f','t','\\',
5339 'W','i','n','d','o','w','s','\\',
5340 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5341 'R','u','n','O','n','c','e',0};
5342 static const WCHAR InstallRunOnce[] = {
5343 'S','o','f','t','w','a','r','e','\\',
5344 'M','i','c','r','o','s','o','f','t','\\',
5345 'W','i','n','d','o','w','s','\\',
5346 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5347 'I','n','s','t','a','l','l','e','r','\\',
5348 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5350 static const WCHAR msiexec_fmt[] = {
5351 '%','s',
5352 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5353 '\"','%','s','\"',0};
5354 static const WCHAR install_fmt[] = {
5355 '/','I',' ','\"','%','s','\"',' ',
5356 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5357 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5358 WCHAR buffer[256], sysdir[MAX_PATH];
5359 HKEY hkey;
5360 WCHAR squished_pc[100];
5362 squash_guid(package->ProductCode,squished_pc);
5364 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5365 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5366 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5367 squished_pc);
5369 msi_reg_set_val_str( hkey, squished_pc, buffer );
5370 RegCloseKey(hkey);
5372 TRACE("Reboot command %s\n",debugstr_w(buffer));
5374 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5375 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5377 msi_reg_set_val_str( hkey, squished_pc, buffer );
5378 RegCloseKey(hkey);
5380 return ERROR_INSTALL_SUSPEND;
5383 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5385 static const WCHAR query[] =
5386 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5387 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5388 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5389 MSIRECORD *rec, *row;
5390 DWORD i, size = 0;
5391 va_list va;
5392 const WCHAR *str;
5393 WCHAR *data;
5395 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5397 rec = MSI_CreateRecord( count + 2 );
5398 str = MSI_RecordGetString( row, 1 );
5399 MSI_RecordSetStringW( rec, 0, str );
5400 msiobj_release( &row->hdr );
5401 MSI_RecordSetInteger( rec, 1, error );
5403 va_start( va, count );
5404 for (i = 0; i < count; i++)
5406 str = va_arg( va, const WCHAR *);
5407 MSI_RecordSetStringW( rec, i + 2, str );
5409 va_end( va );
5411 MSI_FormatRecordW( package, rec, NULL, &size );
5412 size++;
5413 data = msi_alloc( size * sizeof(WCHAR) );
5414 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5415 else data[0] = 0;
5416 msiobj_release( &rec->hdr );
5417 return data;
5420 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5422 DWORD attrib;
5423 UINT rc;
5426 * We are currently doing what should be done here in the top level Install
5427 * however for Administrative and uninstalls this step will be needed
5429 if (!package->PackagePath)
5430 return ERROR_SUCCESS;
5432 msi_set_sourcedir_props(package, TRUE);
5434 attrib = GetFileAttributesW(package->db->path);
5435 if (attrib == INVALID_FILE_ATTRIBUTES)
5437 LPWSTR prompt, msg;
5438 DWORD size = 0;
5440 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5441 package->Context, MSICODE_PRODUCT,
5442 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5443 if (rc == ERROR_MORE_DATA)
5445 prompt = msi_alloc(size * sizeof(WCHAR));
5446 MsiSourceListGetInfoW(package->ProductCode, NULL,
5447 package->Context, MSICODE_PRODUCT,
5448 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5450 else
5451 prompt = strdupW(package->db->path);
5453 msg = msi_build_error_string(package, 1302, 1, prompt);
5454 msi_free(prompt);
5455 while(attrib == INVALID_FILE_ATTRIBUTES)
5457 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5458 if (rc == IDCANCEL)
5460 msi_free(msg);
5461 return ERROR_INSTALL_USEREXIT;
5463 attrib = GetFileAttributesW(package->db->path);
5465 msi_free(msg);
5466 rc = ERROR_SUCCESS;
5468 else
5469 return ERROR_SUCCESS;
5471 return rc;
5474 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5476 HKEY hkey = 0;
5477 LPWSTR buffer, productid = NULL;
5478 UINT i, rc = ERROR_SUCCESS;
5479 MSIRECORD *uirow;
5481 static const WCHAR szPropKeys[][80] =
5483 {'P','r','o','d','u','c','t','I','D',0},
5484 {'U','S','E','R','N','A','M','E',0},
5485 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5486 {0},
5489 static const WCHAR szRegKeys[][80] =
5491 {'P','r','o','d','u','c','t','I','D',0},
5492 {'R','e','g','O','w','n','e','r',0},
5493 {'R','e','g','C','o','m','p','a','n','y',0},
5494 {0},
5497 if (msi_check_unpublish(package))
5499 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5500 goto end;
5503 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5504 if (!productid)
5505 goto end;
5507 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5508 NULL, &hkey, TRUE);
5509 if (rc != ERROR_SUCCESS)
5510 goto end;
5512 for( i = 0; szPropKeys[i][0]; i++ )
5514 buffer = msi_dup_property( package->db, szPropKeys[i] );
5515 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5516 msi_free( buffer );
5519 end:
5520 uirow = MSI_CreateRecord( 1 );
5521 MSI_RecordSetStringW( uirow, 1, productid );
5522 msi_ui_actiondata( package, szRegisterUser, uirow );
5523 msiobj_release( &uirow->hdr );
5525 msi_free(productid);
5526 RegCloseKey(hkey);
5527 return rc;
5531 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5533 UINT rc;
5535 package->script->InWhatSequence |= SEQUENCE_EXEC;
5536 rc = ACTION_ProcessExecSequence(package,FALSE);
5537 return rc;
5540 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5542 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5543 WCHAR productid_85[21], component_85[21], *ret;
5544 GUID clsid;
5545 DWORD sz;
5547 /* > is used if there is a component GUID and < if not. */
5549 productid_85[0] = 0;
5550 component_85[0] = 0;
5551 CLSIDFromString( package->ProductCode, &clsid );
5553 encode_base85_guid( &clsid, productid_85 );
5554 if (component)
5556 CLSIDFromString( component->ComponentId, &clsid );
5557 encode_base85_guid( &clsid, component_85 );
5560 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5561 debugstr_w(component_85));
5563 sz = 20 + strlenW( feature ) + 20 + 3;
5564 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5565 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5566 return ret;
5569 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5571 MSIPACKAGE *package = param;
5572 LPCWSTR compgroupid, component, feature, qualifier, text;
5573 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5574 HKEY hkey = NULL;
5575 UINT rc;
5576 MSICOMPONENT *comp;
5577 MSIFEATURE *feat;
5578 DWORD sz;
5579 MSIRECORD *uirow;
5580 int len;
5582 feature = MSI_RecordGetString(rec, 5);
5583 feat = msi_get_loaded_feature(package, feature);
5584 if (!feat)
5585 return ERROR_SUCCESS;
5587 feat->Action = msi_get_feature_action( package, feat );
5588 if (feat->Action != INSTALLSTATE_LOCAL &&
5589 feat->Action != INSTALLSTATE_SOURCE &&
5590 feat->Action != INSTALLSTATE_ADVERTISED)
5592 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5593 return ERROR_SUCCESS;
5596 component = MSI_RecordGetString(rec, 3);
5597 comp = msi_get_loaded_component(package, component);
5598 if (!comp)
5599 return ERROR_SUCCESS;
5601 compgroupid = MSI_RecordGetString(rec,1);
5602 qualifier = MSI_RecordGetString(rec,2);
5604 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5605 if (rc != ERROR_SUCCESS)
5606 goto end;
5608 advertise = msi_create_component_advertise_string( package, comp, feature );
5609 text = MSI_RecordGetString( rec, 4 );
5610 if (text)
5612 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5613 strcpyW( p, advertise );
5614 strcatW( p, text );
5615 msi_free( advertise );
5616 advertise = p;
5618 existing = msi_reg_get_val_str( hkey, qualifier );
5620 sz = strlenW( advertise ) + 1;
5621 if (existing)
5623 for (p = existing; *p; p += len)
5625 len = strlenW( p ) + 1;
5626 if (strcmpW( advertise, p )) sz += len;
5629 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5631 rc = ERROR_OUTOFMEMORY;
5632 goto end;
5634 q = output;
5635 if (existing)
5637 for (p = existing; *p; p += len)
5639 len = strlenW( p ) + 1;
5640 if (strcmpW( advertise, p ))
5642 memcpy( q, p, len * sizeof(WCHAR) );
5643 q += len;
5647 strcpyW( q, advertise );
5648 q[strlenW( q ) + 1] = 0;
5650 msi_reg_set_val_multi_str( hkey, qualifier, output );
5652 end:
5653 RegCloseKey(hkey);
5654 msi_free( output );
5655 msi_free( advertise );
5656 msi_free( existing );
5658 /* the UI chunk */
5659 uirow = MSI_CreateRecord( 2 );
5660 MSI_RecordSetStringW( uirow, 1, compgroupid );
5661 MSI_RecordSetStringW( uirow, 2, qualifier);
5662 msi_ui_actiondata( package, szPublishComponents, uirow );
5663 msiobj_release( &uirow->hdr );
5664 /* FIXME: call ui_progress? */
5666 return rc;
5670 * At present I am ignorning the advertised components part of this and only
5671 * focusing on the qualified component sets
5673 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5675 static const WCHAR query[] = {
5676 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5677 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5678 MSIQUERY *view;
5679 UINT rc;
5681 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5682 if (rc != ERROR_SUCCESS)
5683 return ERROR_SUCCESS;
5685 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5686 msiobj_release(&view->hdr);
5687 return rc;
5690 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5692 static const WCHAR szInstallerComponents[] = {
5693 'S','o','f','t','w','a','r','e','\\',
5694 'M','i','c','r','o','s','o','f','t','\\',
5695 'I','n','s','t','a','l','l','e','r','\\',
5696 'C','o','m','p','o','n','e','n','t','s','\\',0};
5698 MSIPACKAGE *package = param;
5699 LPCWSTR compgroupid, component, feature, qualifier;
5700 MSICOMPONENT *comp;
5701 MSIFEATURE *feat;
5702 MSIRECORD *uirow;
5703 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5704 LONG res;
5706 feature = MSI_RecordGetString( rec, 5 );
5707 feat = msi_get_loaded_feature( package, feature );
5708 if (!feat)
5709 return ERROR_SUCCESS;
5711 feat->Action = msi_get_feature_action( package, feat );
5712 if (feat->Action != INSTALLSTATE_ABSENT)
5714 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5715 return ERROR_SUCCESS;
5718 component = MSI_RecordGetString( rec, 3 );
5719 comp = msi_get_loaded_component( package, component );
5720 if (!comp)
5721 return ERROR_SUCCESS;
5723 compgroupid = MSI_RecordGetString( rec, 1 );
5724 qualifier = MSI_RecordGetString( rec, 2 );
5726 squash_guid( compgroupid, squashed );
5727 strcpyW( keypath, szInstallerComponents );
5728 strcatW( keypath, squashed );
5730 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5731 if (res != ERROR_SUCCESS)
5733 WARN("Unable to delete component key %d\n", res);
5736 uirow = MSI_CreateRecord( 2 );
5737 MSI_RecordSetStringW( uirow, 1, compgroupid );
5738 MSI_RecordSetStringW( uirow, 2, qualifier );
5739 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5740 msiobj_release( &uirow->hdr );
5742 return ERROR_SUCCESS;
5745 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5747 static const WCHAR query[] = {
5748 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5749 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5750 MSIQUERY *view;
5751 UINT rc;
5753 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5754 if (rc != ERROR_SUCCESS)
5755 return ERROR_SUCCESS;
5757 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5758 msiobj_release( &view->hdr );
5759 return rc;
5762 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5764 static const WCHAR query[] =
5765 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5766 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5767 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5768 MSIPACKAGE *package = param;
5769 MSICOMPONENT *component;
5770 MSIRECORD *row;
5771 MSIFILE *file;
5772 SC_HANDLE hscm = NULL, service = NULL;
5773 LPCWSTR comp, key;
5774 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5775 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5776 DWORD serv_type, start_type, err_control;
5777 SERVICE_DESCRIPTIONW sd = {NULL};
5778 UINT ret = ERROR_SUCCESS;
5780 comp = MSI_RecordGetString( rec, 12 );
5781 component = msi_get_loaded_component( package, comp );
5782 if (!component)
5784 WARN("service component not found\n");
5785 goto done;
5787 component->Action = msi_get_component_action( package, component );
5788 if (component->Action != INSTALLSTATE_LOCAL)
5790 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5791 goto done;
5793 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5794 if (!hscm)
5796 ERR("Failed to open the SC Manager!\n");
5797 goto done;
5800 start_type = MSI_RecordGetInteger(rec, 5);
5801 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5802 goto done;
5804 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5805 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5806 serv_type = MSI_RecordGetInteger(rec, 4);
5807 err_control = MSI_RecordGetInteger(rec, 6);
5808 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5809 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5810 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5811 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5812 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5813 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5815 /* fetch the service path */
5816 row = MSI_QueryGetRecord(package->db, query, comp);
5817 if (!row)
5819 ERR("Query failed\n");
5820 goto done;
5822 if (!(key = MSI_RecordGetString(row, 6)))
5824 msiobj_release(&row->hdr);
5825 goto done;
5827 file = msi_get_loaded_file(package, key);
5828 msiobj_release(&row->hdr);
5829 if (!file)
5831 ERR("Failed to load the service file\n");
5832 goto done;
5835 if (!args || !args[0]) image_path = file->TargetPath;
5836 else
5838 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5839 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5841 ret = ERROR_OUTOFMEMORY;
5842 goto done;
5845 strcpyW(image_path, file->TargetPath);
5846 strcatW(image_path, szSpace);
5847 strcatW(image_path, args);
5849 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5850 start_type, err_control, image_path, load_order,
5851 NULL, depends, serv_name, pass);
5853 if (!service)
5855 if (GetLastError() != ERROR_SERVICE_EXISTS)
5856 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5858 else if (sd.lpDescription)
5860 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5861 WARN("failed to set service description %u\n", GetLastError());
5864 if (image_path != file->TargetPath) msi_free(image_path);
5865 done:
5866 CloseServiceHandle(service);
5867 CloseServiceHandle(hscm);
5868 msi_free(name);
5869 msi_free(disp);
5870 msi_free(sd.lpDescription);
5871 msi_free(load_order);
5872 msi_free(serv_name);
5873 msi_free(pass);
5874 msi_free(depends);
5875 msi_free(args);
5877 return ret;
5880 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5882 static const WCHAR query[] = {
5883 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5884 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5885 MSIQUERY *view;
5886 UINT rc;
5888 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5889 if (rc != ERROR_SUCCESS)
5890 return ERROR_SUCCESS;
5892 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5893 msiobj_release(&view->hdr);
5894 return rc;
5897 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5898 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5900 LPCWSTR *vector, *temp_vector;
5901 LPWSTR p, q;
5902 DWORD sep_len;
5904 static const WCHAR separator[] = {'[','~',']',0};
5906 *numargs = 0;
5907 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5909 if (!args)
5910 return NULL;
5912 vector = msi_alloc(sizeof(LPWSTR));
5913 if (!vector)
5914 return NULL;
5916 p = args;
5919 (*numargs)++;
5920 vector[*numargs - 1] = p;
5922 if ((q = strstrW(p, separator)))
5924 *q = '\0';
5926 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5927 if (!temp_vector)
5929 msi_free(vector);
5930 return NULL;
5932 vector = temp_vector;
5934 p = q + sep_len;
5936 } while (q);
5938 return vector;
5941 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5943 MSIPACKAGE *package = param;
5944 MSICOMPONENT *comp;
5945 MSIRECORD *uirow;
5946 SC_HANDLE scm = NULL, service = NULL;
5947 LPCWSTR component, *vector = NULL;
5948 LPWSTR name, args, display_name = NULL;
5949 DWORD event, numargs, len, wait, dummy;
5950 UINT r = ERROR_FUNCTION_FAILED;
5951 SERVICE_STATUS_PROCESS status;
5952 ULONGLONG start_time;
5954 component = MSI_RecordGetString(rec, 6);
5955 comp = msi_get_loaded_component(package, component);
5956 if (!comp)
5957 return ERROR_SUCCESS;
5959 comp->Action = msi_get_component_action( package, comp );
5960 if (comp->Action != INSTALLSTATE_LOCAL)
5962 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5963 return ERROR_SUCCESS;
5966 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5967 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5968 event = MSI_RecordGetInteger(rec, 3);
5969 wait = MSI_RecordGetInteger(rec, 5);
5971 if (!(event & msidbServiceControlEventStart))
5973 r = ERROR_SUCCESS;
5974 goto done;
5977 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5978 if (!scm)
5980 ERR("Failed to open the service control manager\n");
5981 goto done;
5984 len = 0;
5985 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5986 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5988 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5989 GetServiceDisplayNameW( scm, name, display_name, &len );
5992 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5993 if (!service)
5995 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5996 goto done;
5999 vector = msi_service_args_to_vector(args, &numargs);
6001 if (!StartServiceW(service, numargs, vector) &&
6002 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6004 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6005 goto done;
6008 r = ERROR_SUCCESS;
6009 if (wait)
6011 /* wait for at most 30 seconds for the service to be up and running */
6012 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6013 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6015 TRACE("failed to query service status (%u)\n", GetLastError());
6016 goto done;
6018 start_time = GetTickCount64();
6019 while (status.dwCurrentState == SERVICE_START_PENDING)
6021 if (GetTickCount64() - start_time > 30000) break;
6022 Sleep(1000);
6023 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6024 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6026 TRACE("failed to query service status (%u)\n", GetLastError());
6027 goto done;
6030 if (status.dwCurrentState != SERVICE_RUNNING)
6032 WARN("service failed to start %u\n", status.dwCurrentState);
6033 r = ERROR_FUNCTION_FAILED;
6037 done:
6038 uirow = MSI_CreateRecord( 2 );
6039 MSI_RecordSetStringW( uirow, 1, display_name );
6040 MSI_RecordSetStringW( uirow, 2, name );
6041 msi_ui_actiondata( package, szStartServices, uirow );
6042 msiobj_release( &uirow->hdr );
6044 CloseServiceHandle(service);
6045 CloseServiceHandle(scm);
6047 msi_free(name);
6048 msi_free(args);
6049 msi_free(vector);
6050 msi_free(display_name);
6051 return r;
6054 static UINT ACTION_StartServices( MSIPACKAGE *package )
6056 static const WCHAR query[] = {
6057 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6058 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6059 MSIQUERY *view;
6060 UINT rc;
6062 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6063 if (rc != ERROR_SUCCESS)
6064 return ERROR_SUCCESS;
6066 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6067 msiobj_release(&view->hdr);
6068 return rc;
6071 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6073 DWORD i, needed, count;
6074 ENUM_SERVICE_STATUSW *dependencies;
6075 SERVICE_STATUS ss;
6076 SC_HANDLE depserv;
6077 BOOL stopped, ret = FALSE;
6079 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6080 0, &needed, &count))
6081 return TRUE;
6083 if (GetLastError() != ERROR_MORE_DATA)
6084 return FALSE;
6086 dependencies = msi_alloc(needed);
6087 if (!dependencies)
6088 return FALSE;
6090 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6091 needed, &needed, &count))
6092 goto done;
6094 for (i = 0; i < count; i++)
6096 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6097 SERVICE_STOP | SERVICE_QUERY_STATUS);
6098 if (!depserv)
6099 goto done;
6101 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6102 CloseServiceHandle(depserv);
6103 if (!stopped)
6104 goto done;
6107 ret = TRUE;
6109 done:
6110 msi_free(dependencies);
6111 return ret;
6114 static UINT stop_service( LPCWSTR name )
6116 SC_HANDLE scm = NULL, service = NULL;
6117 SERVICE_STATUS status;
6118 SERVICE_STATUS_PROCESS ssp;
6119 DWORD needed;
6121 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6122 if (!scm)
6124 WARN("Failed to open the SCM: %d\n", GetLastError());
6125 goto done;
6128 service = OpenServiceW(scm, name,
6129 SERVICE_STOP |
6130 SERVICE_QUERY_STATUS |
6131 SERVICE_ENUMERATE_DEPENDENTS);
6132 if (!service)
6134 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6135 goto done;
6138 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6139 sizeof(SERVICE_STATUS_PROCESS), &needed))
6141 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6142 goto done;
6145 if (ssp.dwCurrentState == SERVICE_STOPPED)
6146 goto done;
6148 stop_service_dependents(scm, service);
6150 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6151 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6153 done:
6154 CloseServiceHandle(service);
6155 CloseServiceHandle(scm);
6157 return ERROR_SUCCESS;
6160 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6162 MSIPACKAGE *package = param;
6163 MSICOMPONENT *comp;
6164 MSIRECORD *uirow;
6165 LPCWSTR component;
6166 LPWSTR name = NULL, display_name = NULL;
6167 DWORD event, len;
6168 SC_HANDLE scm;
6170 event = MSI_RecordGetInteger( rec, 3 );
6171 if (!(event & msidbServiceControlEventStop))
6172 return ERROR_SUCCESS;
6174 component = MSI_RecordGetString( rec, 6 );
6175 comp = msi_get_loaded_component( package, component );
6176 if (!comp)
6177 return ERROR_SUCCESS;
6179 comp->Action = msi_get_component_action( package, comp );
6180 if (comp->Action != INSTALLSTATE_ABSENT)
6182 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6183 return ERROR_SUCCESS;
6186 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6187 if (!scm)
6189 ERR("Failed to open the service control manager\n");
6190 goto done;
6193 len = 0;
6194 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6195 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6197 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6198 GetServiceDisplayNameW( scm, name, display_name, &len );
6200 CloseServiceHandle( scm );
6202 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6203 stop_service( name );
6205 done:
6206 uirow = MSI_CreateRecord( 2 );
6207 MSI_RecordSetStringW( uirow, 1, display_name );
6208 MSI_RecordSetStringW( uirow, 2, name );
6209 msi_ui_actiondata( package, szStopServices, uirow );
6210 msiobj_release( &uirow->hdr );
6212 msi_free( name );
6213 msi_free( display_name );
6214 return ERROR_SUCCESS;
6217 static UINT ACTION_StopServices( MSIPACKAGE *package )
6219 static const WCHAR query[] = {
6220 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6221 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6222 MSIQUERY *view;
6223 UINT rc;
6225 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6226 if (rc != ERROR_SUCCESS)
6227 return ERROR_SUCCESS;
6229 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6230 msiobj_release(&view->hdr);
6231 return rc;
6234 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6236 MSIPACKAGE *package = param;
6237 MSICOMPONENT *comp;
6238 MSIRECORD *uirow;
6239 LPWSTR name = NULL, display_name = NULL;
6240 DWORD event, len;
6241 SC_HANDLE scm = NULL, service = NULL;
6243 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6244 if (!comp)
6245 return ERROR_SUCCESS;
6247 event = MSI_RecordGetInteger( rec, 3 );
6248 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6250 comp->Action = msi_get_component_action( package, comp );
6251 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6252 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6254 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6255 msi_free( name );
6256 return ERROR_SUCCESS;
6258 stop_service( name );
6260 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6261 if (!scm)
6263 WARN("Failed to open the SCM: %d\n", GetLastError());
6264 goto done;
6267 len = 0;
6268 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6269 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6271 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6272 GetServiceDisplayNameW( scm, name, display_name, &len );
6275 service = OpenServiceW( scm, name, DELETE );
6276 if (!service)
6278 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6279 goto done;
6282 if (!DeleteService( service ))
6283 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6285 done:
6286 uirow = MSI_CreateRecord( 2 );
6287 MSI_RecordSetStringW( uirow, 1, display_name );
6288 MSI_RecordSetStringW( uirow, 2, name );
6289 msi_ui_actiondata( package, szDeleteServices, uirow );
6290 msiobj_release( &uirow->hdr );
6292 CloseServiceHandle( service );
6293 CloseServiceHandle( scm );
6294 msi_free( name );
6295 msi_free( display_name );
6297 return ERROR_SUCCESS;
6300 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6302 static const WCHAR query[] = {
6303 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6304 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6305 MSIQUERY *view;
6306 UINT rc;
6308 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6309 if (rc != ERROR_SUCCESS)
6310 return ERROR_SUCCESS;
6312 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6313 msiobj_release( &view->hdr );
6314 return rc;
6317 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6319 MSIPACKAGE *package = param;
6320 LPWSTR driver, driver_path, ptr;
6321 WCHAR outpath[MAX_PATH];
6322 MSIFILE *driver_file = NULL, *setup_file = NULL;
6323 MSICOMPONENT *comp;
6324 MSIRECORD *uirow;
6325 LPCWSTR desc, file_key, component;
6326 DWORD len, usage;
6327 UINT r = ERROR_SUCCESS;
6329 static const WCHAR driver_fmt[] = {
6330 'D','r','i','v','e','r','=','%','s',0};
6331 static const WCHAR setup_fmt[] = {
6332 'S','e','t','u','p','=','%','s',0};
6333 static const WCHAR usage_fmt[] = {
6334 'F','i','l','e','U','s','a','g','e','=','1',0};
6336 component = MSI_RecordGetString( rec, 2 );
6337 comp = msi_get_loaded_component( package, component );
6338 if (!comp)
6339 return ERROR_SUCCESS;
6341 comp->Action = msi_get_component_action( package, comp );
6342 if (comp->Action != INSTALLSTATE_LOCAL)
6344 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6345 return ERROR_SUCCESS;
6347 desc = MSI_RecordGetString(rec, 3);
6349 file_key = MSI_RecordGetString( rec, 4 );
6350 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6352 file_key = MSI_RecordGetString( rec, 5 );
6353 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6355 if (!driver_file)
6357 ERR("ODBC Driver entry not found!\n");
6358 return ERROR_FUNCTION_FAILED;
6361 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6362 if (setup_file)
6363 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6364 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6366 driver = msi_alloc(len * sizeof(WCHAR));
6367 if (!driver)
6368 return ERROR_OUTOFMEMORY;
6370 ptr = driver;
6371 lstrcpyW(ptr, desc);
6372 ptr += lstrlenW(ptr) + 1;
6374 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6375 ptr += len + 1;
6377 if (setup_file)
6379 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6380 ptr += len + 1;
6383 lstrcpyW(ptr, usage_fmt);
6384 ptr += lstrlenW(ptr) + 1;
6385 *ptr = '\0';
6387 if (!driver_file->TargetPath)
6389 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6390 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6392 driver_path = strdupW(driver_file->TargetPath);
6393 ptr = strrchrW(driver_path, '\\');
6394 if (ptr) *ptr = '\0';
6396 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6397 NULL, ODBC_INSTALL_COMPLETE, &usage))
6399 ERR("Failed to install SQL driver!\n");
6400 r = ERROR_FUNCTION_FAILED;
6403 uirow = MSI_CreateRecord( 5 );
6404 MSI_RecordSetStringW( uirow, 1, desc );
6405 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6406 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6407 msi_ui_actiondata( package, szInstallODBC, uirow );
6408 msiobj_release( &uirow->hdr );
6410 msi_free(driver);
6411 msi_free(driver_path);
6413 return r;
6416 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6418 MSIPACKAGE *package = param;
6419 LPWSTR translator, translator_path, ptr;
6420 WCHAR outpath[MAX_PATH];
6421 MSIFILE *translator_file = NULL, *setup_file = NULL;
6422 MSICOMPONENT *comp;
6423 MSIRECORD *uirow;
6424 LPCWSTR desc, file_key, component;
6425 DWORD len, usage;
6426 UINT r = ERROR_SUCCESS;
6428 static const WCHAR translator_fmt[] = {
6429 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6430 static const WCHAR setup_fmt[] = {
6431 'S','e','t','u','p','=','%','s',0};
6433 component = MSI_RecordGetString( rec, 2 );
6434 comp = msi_get_loaded_component( package, component );
6435 if (!comp)
6436 return ERROR_SUCCESS;
6438 comp->Action = msi_get_component_action( package, comp );
6439 if (comp->Action != INSTALLSTATE_LOCAL)
6441 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6442 return ERROR_SUCCESS;
6444 desc = MSI_RecordGetString(rec, 3);
6446 file_key = MSI_RecordGetString( rec, 4 );
6447 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6449 file_key = MSI_RecordGetString( rec, 5 );
6450 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6452 if (!translator_file)
6454 ERR("ODBC Translator entry not found!\n");
6455 return ERROR_FUNCTION_FAILED;
6458 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6459 if (setup_file)
6460 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6462 translator = msi_alloc(len * sizeof(WCHAR));
6463 if (!translator)
6464 return ERROR_OUTOFMEMORY;
6466 ptr = translator;
6467 lstrcpyW(ptr, desc);
6468 ptr += lstrlenW(ptr) + 1;
6470 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6471 ptr += len + 1;
6473 if (setup_file)
6475 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6476 ptr += len + 1;
6478 *ptr = '\0';
6480 translator_path = strdupW(translator_file->TargetPath);
6481 ptr = strrchrW(translator_path, '\\');
6482 if (ptr) *ptr = '\0';
6484 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6485 NULL, ODBC_INSTALL_COMPLETE, &usage))
6487 ERR("Failed to install SQL translator!\n");
6488 r = ERROR_FUNCTION_FAILED;
6491 uirow = MSI_CreateRecord( 5 );
6492 MSI_RecordSetStringW( uirow, 1, desc );
6493 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6494 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6495 msi_ui_actiondata( package, szInstallODBC, uirow );
6496 msiobj_release( &uirow->hdr );
6498 msi_free(translator);
6499 msi_free(translator_path);
6501 return r;
6504 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6506 MSIPACKAGE *package = param;
6507 MSICOMPONENT *comp;
6508 LPWSTR attrs;
6509 LPCWSTR desc, driver, component;
6510 WORD request = ODBC_ADD_SYS_DSN;
6511 INT registration;
6512 DWORD len;
6513 UINT r = ERROR_SUCCESS;
6514 MSIRECORD *uirow;
6516 static const WCHAR attrs_fmt[] = {
6517 'D','S','N','=','%','s',0 };
6519 component = MSI_RecordGetString( rec, 2 );
6520 comp = msi_get_loaded_component( package, component );
6521 if (!comp)
6522 return ERROR_SUCCESS;
6524 comp->Action = msi_get_component_action( package, comp );
6525 if (comp->Action != INSTALLSTATE_LOCAL)
6527 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6528 return ERROR_SUCCESS;
6531 desc = MSI_RecordGetString(rec, 3);
6532 driver = MSI_RecordGetString(rec, 4);
6533 registration = MSI_RecordGetInteger(rec, 5);
6535 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6536 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6538 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6539 attrs = msi_alloc(len * sizeof(WCHAR));
6540 if (!attrs)
6541 return ERROR_OUTOFMEMORY;
6543 len = sprintfW(attrs, attrs_fmt, desc);
6544 attrs[len + 1] = 0;
6546 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6548 ERR("Failed to install SQL data source!\n");
6549 r = ERROR_FUNCTION_FAILED;
6552 uirow = MSI_CreateRecord( 5 );
6553 MSI_RecordSetStringW( uirow, 1, desc );
6554 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6555 MSI_RecordSetInteger( uirow, 3, request );
6556 msi_ui_actiondata( package, szInstallODBC, uirow );
6557 msiobj_release( &uirow->hdr );
6559 msi_free(attrs);
6561 return r;
6564 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6566 static const WCHAR driver_query[] = {
6567 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6568 'O','D','B','C','D','r','i','v','e','r',0};
6569 static const WCHAR translator_query[] = {
6570 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6571 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6572 static const WCHAR source_query[] = {
6573 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6574 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6575 MSIQUERY *view;
6576 UINT rc;
6578 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6579 if (rc == ERROR_SUCCESS)
6581 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6582 msiobj_release(&view->hdr);
6583 if (rc != ERROR_SUCCESS)
6584 return rc;
6586 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6587 if (rc == ERROR_SUCCESS)
6589 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6590 msiobj_release(&view->hdr);
6591 if (rc != ERROR_SUCCESS)
6592 return rc;
6594 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6595 if (rc == ERROR_SUCCESS)
6597 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6598 msiobj_release(&view->hdr);
6599 if (rc != ERROR_SUCCESS)
6600 return rc;
6602 return ERROR_SUCCESS;
6605 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6607 MSIPACKAGE *package = param;
6608 MSICOMPONENT *comp;
6609 MSIRECORD *uirow;
6610 DWORD usage;
6611 LPCWSTR desc, component;
6613 component = MSI_RecordGetString( rec, 2 );
6614 comp = msi_get_loaded_component( package, component );
6615 if (!comp)
6616 return ERROR_SUCCESS;
6618 comp->Action = msi_get_component_action( package, comp );
6619 if (comp->Action != INSTALLSTATE_ABSENT)
6621 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6622 return ERROR_SUCCESS;
6625 desc = MSI_RecordGetString( rec, 3 );
6626 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6628 WARN("Failed to remove ODBC driver\n");
6630 else if (!usage)
6632 FIXME("Usage count reached 0\n");
6635 uirow = MSI_CreateRecord( 2 );
6636 MSI_RecordSetStringW( uirow, 1, desc );
6637 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6638 msi_ui_actiondata( package, szRemoveODBC, uirow );
6639 msiobj_release( &uirow->hdr );
6641 return ERROR_SUCCESS;
6644 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6646 MSIPACKAGE *package = param;
6647 MSICOMPONENT *comp;
6648 MSIRECORD *uirow;
6649 DWORD usage;
6650 LPCWSTR desc, component;
6652 component = MSI_RecordGetString( rec, 2 );
6653 comp = msi_get_loaded_component( package, component );
6654 if (!comp)
6655 return ERROR_SUCCESS;
6657 comp->Action = msi_get_component_action( package, comp );
6658 if (comp->Action != INSTALLSTATE_ABSENT)
6660 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6661 return ERROR_SUCCESS;
6664 desc = MSI_RecordGetString( rec, 3 );
6665 if (!SQLRemoveTranslatorW( desc, &usage ))
6667 WARN("Failed to remove ODBC translator\n");
6669 else if (!usage)
6671 FIXME("Usage count reached 0\n");
6674 uirow = MSI_CreateRecord( 2 );
6675 MSI_RecordSetStringW( uirow, 1, desc );
6676 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6677 msi_ui_actiondata( package, szRemoveODBC, uirow );
6678 msiobj_release( &uirow->hdr );
6680 return ERROR_SUCCESS;
6683 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6685 MSIPACKAGE *package = param;
6686 MSICOMPONENT *comp;
6687 MSIRECORD *uirow;
6688 LPWSTR attrs;
6689 LPCWSTR desc, driver, component;
6690 WORD request = ODBC_REMOVE_SYS_DSN;
6691 INT registration;
6692 DWORD len;
6694 static const WCHAR attrs_fmt[] = {
6695 'D','S','N','=','%','s',0 };
6697 component = MSI_RecordGetString( rec, 2 );
6698 comp = msi_get_loaded_component( package, component );
6699 if (!comp)
6700 return ERROR_SUCCESS;
6702 comp->Action = msi_get_component_action( package, comp );
6703 if (comp->Action != INSTALLSTATE_ABSENT)
6705 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6706 return ERROR_SUCCESS;
6709 desc = MSI_RecordGetString( rec, 3 );
6710 driver = MSI_RecordGetString( rec, 4 );
6711 registration = MSI_RecordGetInteger( rec, 5 );
6713 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6714 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6716 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6717 attrs = msi_alloc( len * sizeof(WCHAR) );
6718 if (!attrs)
6719 return ERROR_OUTOFMEMORY;
6721 FIXME("Use ODBCSourceAttribute table\n");
6723 len = sprintfW( attrs, attrs_fmt, desc );
6724 attrs[len + 1] = 0;
6726 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6728 WARN("Failed to remove ODBC data source\n");
6730 msi_free( attrs );
6732 uirow = MSI_CreateRecord( 3 );
6733 MSI_RecordSetStringW( uirow, 1, desc );
6734 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6735 MSI_RecordSetInteger( uirow, 3, request );
6736 msi_ui_actiondata( package, szRemoveODBC, uirow );
6737 msiobj_release( &uirow->hdr );
6739 return ERROR_SUCCESS;
6742 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6744 static const WCHAR driver_query[] = {
6745 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6746 'O','D','B','C','D','r','i','v','e','r',0};
6747 static const WCHAR translator_query[] = {
6748 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6749 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6750 static const WCHAR source_query[] = {
6751 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6752 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6753 MSIQUERY *view;
6754 UINT rc;
6756 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6757 if (rc == ERROR_SUCCESS)
6759 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6760 msiobj_release( &view->hdr );
6761 if (rc != ERROR_SUCCESS)
6762 return rc;
6764 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6765 if (rc == ERROR_SUCCESS)
6767 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6768 msiobj_release( &view->hdr );
6769 if (rc != ERROR_SUCCESS)
6770 return rc;
6772 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6773 if (rc == ERROR_SUCCESS)
6775 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6776 msiobj_release( &view->hdr );
6777 if (rc != ERROR_SUCCESS)
6778 return rc;
6780 return ERROR_SUCCESS;
6783 #define ENV_ACT_SETALWAYS 0x1
6784 #define ENV_ACT_SETABSENT 0x2
6785 #define ENV_ACT_REMOVE 0x4
6786 #define ENV_ACT_REMOVEMATCH 0x8
6788 #define ENV_MOD_MACHINE 0x20000000
6789 #define ENV_MOD_APPEND 0x40000000
6790 #define ENV_MOD_PREFIX 0x80000000
6791 #define ENV_MOD_MASK 0xC0000000
6793 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6795 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6797 LPCWSTR cptr = *name;
6799 static const WCHAR prefix[] = {'[','~',']',0};
6800 static const int prefix_len = 3;
6802 *flags = 0;
6803 while (*cptr)
6805 if (*cptr == '=')
6806 *flags |= ENV_ACT_SETALWAYS;
6807 else if (*cptr == '+')
6808 *flags |= ENV_ACT_SETABSENT;
6809 else if (*cptr == '-')
6810 *flags |= ENV_ACT_REMOVE;
6811 else if (*cptr == '!')
6812 *flags |= ENV_ACT_REMOVEMATCH;
6813 else if (*cptr == '*')
6814 *flags |= ENV_MOD_MACHINE;
6815 else
6816 break;
6818 cptr++;
6819 (*name)++;
6822 if (!*cptr)
6824 ERR("Missing environment variable\n");
6825 return ERROR_FUNCTION_FAILED;
6828 if (*value)
6830 LPCWSTR ptr = *value;
6831 if (!strncmpW(ptr, prefix, prefix_len))
6833 if (ptr[prefix_len] == szSemiColon[0])
6835 *flags |= ENV_MOD_APPEND;
6836 *value += lstrlenW(prefix);
6838 else
6840 *value = NULL;
6843 else if (lstrlenW(*value) >= prefix_len)
6845 ptr += lstrlenW(ptr) - prefix_len;
6846 if (!strcmpW( ptr, prefix ))
6848 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6850 *flags |= ENV_MOD_PREFIX;
6851 /* the "[~]" will be removed by deformat_string */;
6853 else
6855 *value = NULL;
6861 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6862 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6863 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6864 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6866 ERR("Invalid flags: %08x\n", *flags);
6867 return ERROR_FUNCTION_FAILED;
6870 if (!*flags)
6871 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6873 return ERROR_SUCCESS;
6876 static UINT open_env_key( DWORD flags, HKEY *key )
6878 static const WCHAR user_env[] =
6879 {'E','n','v','i','r','o','n','m','e','n','t',0};
6880 static const WCHAR machine_env[] =
6881 {'S','y','s','t','e','m','\\',
6882 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6883 'C','o','n','t','r','o','l','\\',
6884 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6885 'E','n','v','i','r','o','n','m','e','n','t',0};
6886 const WCHAR *env;
6887 HKEY root;
6888 LONG res;
6890 if (flags & ENV_MOD_MACHINE)
6892 env = machine_env;
6893 root = HKEY_LOCAL_MACHINE;
6895 else
6897 env = user_env;
6898 root = HKEY_CURRENT_USER;
6901 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6902 if (res != ERROR_SUCCESS)
6904 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6905 return ERROR_FUNCTION_FAILED;
6908 return ERROR_SUCCESS;
6911 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6913 MSIPACKAGE *package = param;
6914 LPCWSTR name, value, component;
6915 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6916 DWORD flags, type, size;
6917 UINT res;
6918 HKEY env = NULL;
6919 MSICOMPONENT *comp;
6920 MSIRECORD *uirow;
6921 int action = 0;
6923 component = MSI_RecordGetString(rec, 4);
6924 comp = msi_get_loaded_component(package, component);
6925 if (!comp)
6926 return ERROR_SUCCESS;
6928 comp->Action = msi_get_component_action( package, comp );
6929 if (comp->Action != INSTALLSTATE_LOCAL)
6931 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6932 return ERROR_SUCCESS;
6934 name = MSI_RecordGetString(rec, 2);
6935 value = MSI_RecordGetString(rec, 3);
6937 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6939 res = env_parse_flags(&name, &value, &flags);
6940 if (res != ERROR_SUCCESS || !value)
6941 goto done;
6943 if (value && !deformat_string(package, value, &deformatted))
6945 res = ERROR_OUTOFMEMORY;
6946 goto done;
6949 value = deformatted;
6951 res = open_env_key( flags, &env );
6952 if (res != ERROR_SUCCESS)
6953 goto done;
6955 if (flags & ENV_MOD_MACHINE)
6956 action |= 0x20000000;
6958 size = 0;
6959 type = REG_SZ;
6960 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6961 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6962 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6963 goto done;
6965 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6967 action = 0x2;
6969 /* Nothing to do. */
6970 if (!value)
6972 res = ERROR_SUCCESS;
6973 goto done;
6976 /* If we are appending but the string was empty, strip ; */
6977 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6979 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6980 newval = strdupW(value);
6981 if (!newval)
6983 res = ERROR_OUTOFMEMORY;
6984 goto done;
6987 else
6989 action = 0x1;
6991 /* Contrary to MSDN, +-variable to [~];path works */
6992 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6994 res = ERROR_SUCCESS;
6995 goto done;
6998 data = msi_alloc(size);
6999 if (!data)
7001 RegCloseKey(env);
7002 return ERROR_OUTOFMEMORY;
7005 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
7006 if (res != ERROR_SUCCESS)
7007 goto done;
7009 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7011 action = 0x4;
7012 res = RegDeleteValueW(env, name);
7013 if (res != ERROR_SUCCESS)
7014 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7015 goto done;
7018 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
7019 if (flags & ENV_MOD_MASK)
7021 DWORD mod_size;
7022 int multiplier = 0;
7023 if (flags & ENV_MOD_APPEND) multiplier++;
7024 if (flags & ENV_MOD_PREFIX) multiplier++;
7025 mod_size = lstrlenW(value) * multiplier;
7026 size += mod_size * sizeof(WCHAR);
7029 newval = msi_alloc(size);
7030 ptr = newval;
7031 if (!newval)
7033 res = ERROR_OUTOFMEMORY;
7034 goto done;
7037 if (flags & ENV_MOD_PREFIX)
7039 lstrcpyW(newval, value);
7040 ptr = newval + lstrlenW(value);
7041 action |= 0x80000000;
7044 lstrcpyW(ptr, data);
7046 if (flags & ENV_MOD_APPEND)
7048 lstrcatW(newval, value);
7049 action |= 0x40000000;
7052 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7053 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
7054 if (res)
7056 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7059 done:
7060 uirow = MSI_CreateRecord( 3 );
7061 MSI_RecordSetStringW( uirow, 1, name );
7062 MSI_RecordSetStringW( uirow, 2, newval );
7063 MSI_RecordSetInteger( uirow, 3, action );
7064 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7065 msiobj_release( &uirow->hdr );
7067 if (env) RegCloseKey(env);
7068 msi_free(deformatted);
7069 msi_free(data);
7070 msi_free(newval);
7071 return res;
7074 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7076 static const WCHAR query[] = {
7077 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7078 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7079 MSIQUERY *view;
7080 UINT rc;
7082 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7083 if (rc != ERROR_SUCCESS)
7084 return ERROR_SUCCESS;
7086 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7087 msiobj_release(&view->hdr);
7088 return rc;
7091 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7093 MSIPACKAGE *package = param;
7094 LPCWSTR name, value, component;
7095 LPWSTR deformatted = NULL;
7096 DWORD flags;
7097 HKEY env;
7098 MSICOMPONENT *comp;
7099 MSIRECORD *uirow;
7100 int action = 0;
7101 LONG res;
7102 UINT r;
7104 component = MSI_RecordGetString( rec, 4 );
7105 comp = msi_get_loaded_component( package, component );
7106 if (!comp)
7107 return ERROR_SUCCESS;
7109 comp->Action = msi_get_component_action( package, comp );
7110 if (comp->Action != INSTALLSTATE_ABSENT)
7112 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7113 return ERROR_SUCCESS;
7115 name = MSI_RecordGetString( rec, 2 );
7116 value = MSI_RecordGetString( rec, 3 );
7118 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7120 r = env_parse_flags( &name, &value, &flags );
7121 if (r != ERROR_SUCCESS)
7122 return r;
7124 if (!(flags & ENV_ACT_REMOVE))
7126 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7127 return ERROR_SUCCESS;
7130 if (value && !deformat_string( package, value, &deformatted ))
7131 return ERROR_OUTOFMEMORY;
7133 value = deformatted;
7135 r = open_env_key( flags, &env );
7136 if (r != ERROR_SUCCESS)
7138 r = ERROR_SUCCESS;
7139 goto done;
7142 if (flags & ENV_MOD_MACHINE)
7143 action |= 0x20000000;
7145 TRACE("Removing %s\n", debugstr_w(name));
7147 res = RegDeleteValueW( env, name );
7148 if (res != ERROR_SUCCESS)
7150 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
7151 r = ERROR_SUCCESS;
7154 done:
7155 uirow = MSI_CreateRecord( 3 );
7156 MSI_RecordSetStringW( uirow, 1, name );
7157 MSI_RecordSetStringW( uirow, 2, value );
7158 MSI_RecordSetInteger( uirow, 3, action );
7159 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7160 msiobj_release( &uirow->hdr );
7162 if (env) RegCloseKey( env );
7163 msi_free( deformatted );
7164 return r;
7167 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7169 static const WCHAR query[] = {
7170 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7171 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7172 MSIQUERY *view;
7173 UINT rc;
7175 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7176 if (rc != ERROR_SUCCESS)
7177 return ERROR_SUCCESS;
7179 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7180 msiobj_release( &view->hdr );
7181 return rc;
7184 UINT msi_validate_product_id( MSIPACKAGE *package )
7186 LPWSTR key, template, id;
7187 UINT r = ERROR_SUCCESS;
7189 id = msi_dup_property( package->db, szProductID );
7190 if (id)
7192 msi_free( id );
7193 return ERROR_SUCCESS;
7195 template = msi_dup_property( package->db, szPIDTemplate );
7196 key = msi_dup_property( package->db, szPIDKEY );
7197 if (key && template)
7199 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7200 r = msi_set_property( package->db, szProductID, key, -1 );
7202 msi_free( template );
7203 msi_free( key );
7204 return r;
7207 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7209 return msi_validate_product_id( package );
7212 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7214 TRACE("\n");
7215 package->need_reboot_at_end = 1;
7216 return ERROR_SUCCESS;
7219 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7221 static const WCHAR szAvailableFreeReg[] =
7222 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7223 MSIRECORD *uirow;
7224 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7226 TRACE("%p %d kilobytes\n", package, space);
7228 uirow = MSI_CreateRecord( 1 );
7229 MSI_RecordSetInteger( uirow, 1, space );
7230 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7231 msiobj_release( &uirow->hdr );
7233 return ERROR_SUCCESS;
7236 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7238 TRACE("%p\n", package);
7240 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7241 return ERROR_SUCCESS;
7244 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7246 FIXME("%p\n", package);
7247 return ERROR_SUCCESS;
7250 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7252 static const WCHAR driver_query[] = {
7253 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7254 'O','D','B','C','D','r','i','v','e','r',0};
7255 static const WCHAR translator_query[] = {
7256 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7257 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7258 MSIQUERY *view;
7259 UINT r, count;
7261 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7262 if (r == ERROR_SUCCESS)
7264 count = 0;
7265 r = MSI_IterateRecords( view, &count, NULL, package );
7266 msiobj_release( &view->hdr );
7267 if (r != ERROR_SUCCESS)
7268 return r;
7269 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7271 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7272 if (r == ERROR_SUCCESS)
7274 count = 0;
7275 r = MSI_IterateRecords( view, &count, NULL, package );
7276 msiobj_release( &view->hdr );
7277 if (r != ERROR_SUCCESS)
7278 return r;
7279 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7281 return ERROR_SUCCESS;
7284 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7286 static const WCHAR fmtW[] =
7287 {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7288 MSIPACKAGE *package = param;
7289 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7290 int attrs = MSI_RecordGetInteger( rec, 5 );
7291 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7292 WCHAR *product, *features, *cmd;
7293 STARTUPINFOW si;
7294 PROCESS_INFORMATION info;
7295 BOOL ret;
7297 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7298 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7300 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7302 len += strlenW( product );
7303 if (features)
7304 len += strlenW( features );
7305 else
7306 len += sizeof(szAll) / sizeof(szAll[0]);
7308 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7310 msi_free( product );
7311 msi_free( features );
7312 return ERROR_OUTOFMEMORY;
7314 sprintfW( cmd, fmtW, product, features ? features : szAll );
7315 msi_free( product );
7316 msi_free( features );
7318 memset( &si, 0, sizeof(STARTUPINFOW) );
7319 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7320 msi_free( cmd );
7321 if (!ret) return GetLastError();
7322 CloseHandle( info.hThread );
7324 WaitForSingleObject( info.hProcess, INFINITE );
7325 CloseHandle( info.hProcess );
7326 return ERROR_SUCCESS;
7329 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7331 static const WCHAR query[] = {
7332 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7333 MSIQUERY *view;
7334 UINT r;
7336 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7337 if (r == ERROR_SUCCESS)
7339 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7340 msiobj_release( &view->hdr );
7341 if (r != ERROR_SUCCESS)
7342 return r;
7344 return ERROR_SUCCESS;
7347 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7349 MSIPACKAGE *package = param;
7350 int attributes = MSI_RecordGetInteger( rec, 5 );
7352 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7354 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7355 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7356 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7357 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7358 HKEY hkey;
7359 UINT r;
7361 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7363 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7364 if (r != ERROR_SUCCESS)
7365 return ERROR_SUCCESS;
7367 else
7369 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7370 if (r != ERROR_SUCCESS)
7371 return ERROR_SUCCESS;
7373 RegCloseKey( hkey );
7375 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7376 debugstr_w(upgrade_code), debugstr_w(version_min),
7377 debugstr_w(version_max), debugstr_w(language));
7379 return ERROR_SUCCESS;
7382 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7384 static const WCHAR query[] = {
7385 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7386 'U','p','g','r','a','d','e',0};
7387 MSIQUERY *view;
7388 UINT r;
7390 if (msi_get_property_int( package->db, szInstalled, 0 ))
7392 TRACE("product is installed, skipping action\n");
7393 return ERROR_SUCCESS;
7395 if (msi_get_property_int( package->db, szPreselected, 0 ))
7397 TRACE("Preselected property is set, not migrating feature states\n");
7398 return ERROR_SUCCESS;
7400 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7401 if (r == ERROR_SUCCESS)
7403 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7404 msiobj_release( &view->hdr );
7405 if (r != ERROR_SUCCESS)
7406 return r;
7408 return ERROR_SUCCESS;
7411 static void bind_image( const char *filename, const char *path )
7413 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7415 WARN("failed to bind image %u\n", GetLastError());
7419 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7421 UINT i;
7422 MSIFILE *file;
7423 MSIPACKAGE *package = param;
7424 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7425 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7426 char *filenameA, *pathA;
7427 WCHAR *pathW, **path_list;
7429 if (!(file = msi_get_loaded_file( package, key )))
7431 WARN("file %s not found\n", debugstr_w(key));
7432 return ERROR_SUCCESS;
7434 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7435 path_list = msi_split_string( paths, ';' );
7436 if (!path_list) bind_image( filenameA, NULL );
7437 else
7439 for (i = 0; path_list[i] && path_list[i][0]; i++)
7441 deformat_string( package, path_list[i], &pathW );
7442 if ((pathA = strdupWtoA( pathW )))
7444 bind_image( filenameA, pathA );
7445 msi_free( pathA );
7447 msi_free( pathW );
7450 msi_free( path_list );
7451 msi_free( filenameA );
7452 return ERROR_SUCCESS;
7455 static UINT ACTION_BindImage( MSIPACKAGE *package )
7457 static const WCHAR query[] = {
7458 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7459 'B','i','n','d','I','m','a','g','e',0};
7460 MSIQUERY *view;
7461 UINT r;
7463 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7464 if (r == ERROR_SUCCESS)
7466 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7467 msiobj_release( &view->hdr );
7468 if (r != ERROR_SUCCESS)
7469 return r;
7471 return ERROR_SUCCESS;
7474 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7476 static const WCHAR query[] = {
7477 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7478 MSIQUERY *view;
7479 DWORD count = 0;
7480 UINT r;
7482 r = MSI_OpenQuery( package->db, &view, query, table );
7483 if (r == ERROR_SUCCESS)
7485 r = MSI_IterateRecords(view, &count, NULL, package);
7486 msiobj_release(&view->hdr);
7487 if (r != ERROR_SUCCESS)
7488 return r;
7490 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7491 return ERROR_SUCCESS;
7494 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7496 static const WCHAR table[] = {
7497 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7498 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7501 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7503 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7504 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7507 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7509 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7510 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7513 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7515 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7516 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7519 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7521 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7522 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7525 static const struct
7527 const WCHAR *action;
7528 UINT (*handler)(MSIPACKAGE *);
7529 const WCHAR *action_rollback;
7531 StandardActions[] =
7533 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7534 { szAppSearch, ACTION_AppSearch, NULL },
7535 { szBindImage, ACTION_BindImage, NULL },
7536 { szCCPSearch, ACTION_CCPSearch, NULL },
7537 { szCostFinalize, ACTION_CostFinalize, NULL },
7538 { szCostInitialize, ACTION_CostInitialize, NULL },
7539 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7540 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7541 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7542 { szDisableRollback, ACTION_DisableRollback, NULL },
7543 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7544 { szExecuteAction, ACTION_ExecuteAction, NULL },
7545 { szFileCost, ACTION_FileCost, NULL },
7546 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7547 { szForceReboot, ACTION_ForceReboot, NULL },
7548 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7549 { szInstallExecute, ACTION_InstallExecute, NULL },
7550 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7551 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7552 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7553 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7554 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7555 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7556 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7557 { szInstallValidate, ACTION_InstallValidate, NULL },
7558 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7559 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7560 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7561 { szMoveFiles, ACTION_MoveFiles, NULL },
7562 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7563 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7564 { szPatchFiles, ACTION_PatchFiles, NULL },
7565 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7566 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7567 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7568 { szPublishProduct, ACTION_PublishProduct, NULL },
7569 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7570 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7571 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7572 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7573 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7574 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7575 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7576 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7577 { szRegisterUser, ACTION_RegisterUser, NULL },
7578 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7579 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7580 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7581 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7582 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7583 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7584 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7585 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7586 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7587 { szResolveSource, ACTION_ResolveSource, NULL },
7588 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7589 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7590 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7591 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7592 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7593 { szStartServices, ACTION_StartServices, szStopServices },
7594 { szStopServices, ACTION_StopServices, szStartServices },
7595 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7596 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7597 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7598 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7599 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7600 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7601 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7602 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7603 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7604 { szValidateProductID, ACTION_ValidateProductID, NULL },
7605 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7606 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7607 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7608 { NULL, NULL, NULL }
7611 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7613 BOOL ret = FALSE;
7614 UINT i;
7616 i = 0;
7617 while (StandardActions[i].action != NULL)
7619 if (!strcmpW( StandardActions[i].action, action ))
7621 ui_actionstart( package, action );
7622 if (StandardActions[i].handler)
7624 ui_actioninfo( package, action, TRUE, 0 );
7625 *rc = StandardActions[i].handler( package );
7626 ui_actioninfo( package, action, FALSE, *rc );
7628 if (StandardActions[i].action_rollback && !package->need_rollback)
7630 TRACE("scheduling rollback action\n");
7631 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7634 else
7636 FIXME("unhandled standard action %s\n", debugstr_w(action));
7637 *rc = ERROR_SUCCESS;
7639 ret = TRUE;
7640 break;
7642 i++;
7644 return ret;
7647 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7649 UINT rc = ERROR_SUCCESS;
7650 BOOL handled;
7652 TRACE("Performing action (%s)\n", debugstr_w(action));
7654 handled = ACTION_HandleStandardAction(package, action, &rc);
7656 if (!handled)
7657 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7659 if (!handled)
7661 WARN("unhandled msi action %s\n", debugstr_w(action));
7662 rc = ERROR_FUNCTION_NOT_CALLED;
7665 return rc;
7668 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7670 UINT rc = ERROR_SUCCESS;
7671 BOOL handled = FALSE;
7673 TRACE("Performing action (%s)\n", debugstr_w(action));
7675 package->action_progress_increment = 0;
7676 handled = ACTION_HandleStandardAction(package, action, &rc);
7678 if (!handled)
7679 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7681 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7682 handled = TRUE;
7684 if (!handled)
7686 WARN("unhandled msi action %s\n", debugstr_w(action));
7687 rc = ERROR_FUNCTION_NOT_CALLED;
7690 return rc;
7693 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7695 UINT rc = ERROR_SUCCESS;
7696 MSIRECORD *row;
7698 static const WCHAR query[] =
7699 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7700 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7701 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7702 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7703 static const WCHAR ui_query[] =
7704 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7705 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7706 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7707 ' ', '=',' ','%','i',0};
7709 if (needs_ui_sequence(package))
7710 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7711 else
7712 row = MSI_QueryGetRecord(package->db, query, seq);
7714 if (row)
7716 LPCWSTR action, cond;
7718 TRACE("Running the actions\n");
7720 /* check conditions */
7721 cond = MSI_RecordGetString(row, 2);
7723 /* this is a hack to skip errors in the condition code */
7724 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7726 msiobj_release(&row->hdr);
7727 return ERROR_SUCCESS;
7730 action = MSI_RecordGetString(row, 1);
7731 if (!action)
7733 ERR("failed to fetch action\n");
7734 msiobj_release(&row->hdr);
7735 return ERROR_FUNCTION_FAILED;
7738 if (needs_ui_sequence(package))
7739 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7740 else
7741 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7743 msiobj_release(&row->hdr);
7746 return rc;
7749 /****************************************************
7750 * TOP level entry points
7751 *****************************************************/
7753 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7754 LPCWSTR szCommandLine )
7756 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7757 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7758 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7759 WCHAR *reinstall, *remove, *patch, *productcode;
7760 BOOL ui_exists;
7761 UINT rc;
7763 msi_set_property( package->db, szAction, szInstall, -1 );
7765 package->script->InWhatSequence = SEQUENCE_INSTALL;
7767 if (szPackagePath)
7769 LPWSTR p, dir;
7770 LPCWSTR file;
7772 dir = strdupW(szPackagePath);
7773 p = strrchrW(dir, '\\');
7774 if (p)
7776 *(++p) = 0;
7777 file = szPackagePath + (p - dir);
7779 else
7781 msi_free(dir);
7782 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7783 GetCurrentDirectoryW(MAX_PATH, dir);
7784 lstrcatW(dir, szBackSlash);
7785 file = szPackagePath;
7788 msi_free( package->PackagePath );
7789 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7790 if (!package->PackagePath)
7792 msi_free(dir);
7793 return ERROR_OUTOFMEMORY;
7796 lstrcpyW(package->PackagePath, dir);
7797 lstrcatW(package->PackagePath, file);
7798 msi_free(dir);
7800 msi_set_sourcedir_props(package, FALSE);
7803 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7804 if (rc != ERROR_SUCCESS)
7805 return rc;
7807 msi_apply_transforms( package );
7808 msi_apply_patches( package );
7810 patch = msi_dup_property( package->db, szPatch );
7811 remove = msi_dup_property( package->db, szRemove );
7812 reinstall = msi_dup_property( package->db, szReinstall );
7813 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7815 TRACE("setting REINSTALL property to ALL\n");
7816 msi_set_property( package->db, szReinstall, szAll, -1 );
7817 package->full_reinstall = 1;
7820 /* properties may have been added by a transform */
7821 msi_clone_properties( package );
7822 msi_set_original_database_property( package->db, szPackagePath );
7824 msi_parse_command_line( package, szCommandLine, FALSE );
7825 msi_adjust_privilege_properties( package );
7826 msi_set_context( package );
7828 productcode = msi_dup_property( package->db, szProductCode );
7829 if (strcmpiW( productcode, package->ProductCode ))
7831 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
7832 msi_free( package->ProductCode );
7833 package->ProductCode = productcode;
7835 else msi_free( productcode );
7837 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7839 TRACE("disabling rollback\n");
7840 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7843 if (needs_ui_sequence( package))
7845 package->script->InWhatSequence |= SEQUENCE_UI;
7846 rc = ACTION_ProcessUISequence(package);
7847 ui_exists = ui_sequence_exists(package);
7848 if (rc == ERROR_SUCCESS || !ui_exists)
7850 package->script->InWhatSequence |= SEQUENCE_EXEC;
7851 rc = ACTION_ProcessExecSequence(package, ui_exists);
7854 else
7855 rc = ACTION_ProcessExecSequence(package, FALSE);
7857 /* process the ending type action */
7858 if (rc == ERROR_SUCCESS)
7859 ACTION_PerformActionSequence(package, -1);
7860 else if (rc == ERROR_INSTALL_USEREXIT)
7861 ACTION_PerformActionSequence(package, -2);
7862 else if (rc == ERROR_INSTALL_SUSPEND)
7863 ACTION_PerformActionSequence(package, -4);
7864 else /* failed */
7866 ACTION_PerformActionSequence(package, -3);
7867 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7869 package->need_rollback = TRUE;
7873 /* finish up running custom actions */
7874 ACTION_FinishCustomActions(package);
7876 if (package->need_rollback && !reinstall)
7878 WARN("installation failed, running rollback script\n");
7879 execute_script( package, SCRIPT_ROLLBACK );
7881 msi_free( reinstall );
7882 msi_free( remove );
7883 msi_free( patch );
7885 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7886 return ERROR_SUCCESS_REBOOT_REQUIRED;
7888 return rc;