comctl32: Close previously selected treeview item at common ancestor.
[wine.git] / dlls / msi / action.c
blobe87dd7f410281b5952b59761cbe719c45880e2fe
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 (file %s sequence %u)\n", debugstr_w(file->File), file->Sequence);
1283 list_add_tail( &package->files, &file->entry );
1285 return ERROR_SUCCESS;
1288 static UINT load_all_files(MSIPACKAGE *package)
1290 static const WCHAR query[] = {
1291 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1292 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1293 '`','S','e','q','u','e','n','c','e','`', 0};
1294 MSIQUERY *view;
1295 UINT rc;
1297 if (!list_empty(&package->files))
1298 return ERROR_SUCCESS;
1300 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1301 if (rc != ERROR_SUCCESS)
1302 return ERROR_SUCCESS;
1304 rc = MSI_IterateRecords(view, NULL, load_file, package);
1305 msiobj_release(&view->hdr);
1306 return rc;
1309 static UINT load_media( MSIRECORD *row, LPVOID param )
1311 MSIPACKAGE *package = param;
1312 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1313 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1315 /* FIXME: load external cabinets and directory sources too */
1316 if (!cabinet || cabinet[0] != '#') 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_disk_id( MSIPACKAGE *package, MSIFILEPATCH *patch )
1341 static const WCHAR query[] =
1342 {'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1343 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1344 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','u',0};
1345 MSIRECORD *rec;
1347 if (!(rec = MSI_QueryGetRecord( package->db, query, patch->Sequence )))
1349 WARN("query failed\n");
1350 return ERROR_FUNCTION_FAILED;
1353 patch->disk_id = MSI_RecordGetInteger( rec, 1 );
1354 msiobj_release( &rec->hdr );
1355 return ERROR_SUCCESS;
1358 static UINT load_patch(MSIRECORD *row, LPVOID param)
1360 MSIPACKAGE *package = param;
1361 MSIFILEPATCH *patch;
1362 const WCHAR *file_key;
1364 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1365 if (!patch)
1366 return ERROR_NOT_ENOUGH_MEMORY;
1368 file_key = MSI_RecordGetString( row, 1 );
1369 patch->File = msi_get_loaded_file( package, file_key );
1370 if (!patch->File)
1372 ERR("Failed to find target for patch in File table\n");
1373 msi_free(patch);
1374 return ERROR_FUNCTION_FAILED;
1377 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1378 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1379 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1381 /* FIXME:
1382 * Header field - for patch validation.
1383 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1386 load_patch_disk_id( package, patch );
1388 TRACE("Patch loaded (file %s sequence %u)\n", debugstr_w(patch->File->File), patch->Sequence);
1390 list_add_tail( &package->filepatches, &patch->entry );
1392 return ERROR_SUCCESS;
1395 static UINT load_all_patches(MSIPACKAGE *package)
1397 static const WCHAR query[] = {
1398 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1399 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1400 '`','S','e','q','u','e','n','c','e','`',0};
1401 MSIQUERY *view;
1402 UINT rc;
1404 if (!list_empty(&package->filepatches))
1405 return ERROR_SUCCESS;
1407 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1408 if (rc != ERROR_SUCCESS)
1409 return ERROR_SUCCESS;
1411 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1412 msiobj_release(&view->hdr);
1413 return rc;
1416 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1418 static const WCHAR query[] = {
1419 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1420 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1421 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1422 MSIQUERY *view;
1424 folder->persistent = FALSE;
1425 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1427 if (!MSI_ViewExecute( view, NULL ))
1429 MSIRECORD *rec;
1430 if (!MSI_ViewFetch( view, &rec ))
1432 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1433 folder->persistent = TRUE;
1434 msiobj_release( &rec->hdr );
1437 msiobj_release( &view->hdr );
1439 return ERROR_SUCCESS;
1442 static UINT load_folder( MSIRECORD *row, LPVOID param )
1444 MSIPACKAGE *package = param;
1445 static WCHAR szEmpty[] = { 0 };
1446 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1447 MSIFOLDER *folder;
1449 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1450 list_init( &folder->children );
1451 folder->Directory = msi_dup_record_field( row, 1 );
1452 folder->Parent = msi_dup_record_field( row, 2 );
1453 p = msi_dup_record_field(row, 3);
1455 TRACE("%s\n", debugstr_w(folder->Directory));
1457 /* split src and target dir */
1458 tgt_short = p;
1459 src_short = folder_split_path( p, ':' );
1461 /* split the long and short paths */
1462 tgt_long = folder_split_path( tgt_short, '|' );
1463 src_long = folder_split_path( src_short, '|' );
1465 /* check for no-op dirs */
1466 if (tgt_short && !strcmpW( szDot, tgt_short ))
1467 tgt_short = szEmpty;
1468 if (src_short && !strcmpW( szDot, src_short ))
1469 src_short = szEmpty;
1471 if (!tgt_long)
1472 tgt_long = tgt_short;
1474 if (!src_short) {
1475 src_short = tgt_short;
1476 src_long = tgt_long;
1479 if (!src_long)
1480 src_long = src_short;
1482 /* FIXME: use the target short path too */
1483 folder->TargetDefault = strdupW(tgt_long);
1484 folder->SourceShortPath = strdupW(src_short);
1485 folder->SourceLongPath = strdupW(src_long);
1486 msi_free(p);
1488 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1489 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1490 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1492 load_folder_persistence( package, folder );
1494 list_add_tail( &package->folders, &folder->entry );
1495 return ERROR_SUCCESS;
1498 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1500 FolderList *fl;
1502 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1503 fl->folder = child;
1504 list_add_tail( &parent->children, &fl->entry );
1505 return ERROR_SUCCESS;
1508 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1510 MSIPACKAGE *package = param;
1511 MSIFOLDER *parent, *child;
1513 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1514 return ERROR_FUNCTION_FAILED;
1516 if (!child->Parent) return ERROR_SUCCESS;
1518 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1519 return ERROR_FUNCTION_FAILED;
1521 return add_folder_child( parent, child );
1524 static UINT load_all_folders( MSIPACKAGE *package )
1526 static const WCHAR query[] = {
1527 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1528 '`','D','i','r','e','c','t','o','r','y','`',0};
1529 MSIQUERY *view;
1530 UINT r;
1532 if (!list_empty(&package->folders))
1533 return ERROR_SUCCESS;
1535 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1536 if (r != ERROR_SUCCESS)
1537 return r;
1539 r = MSI_IterateRecords( view, NULL, load_folder, package );
1540 if (r != ERROR_SUCCESS)
1542 msiobj_release( &view->hdr );
1543 return r;
1545 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1546 msiobj_release( &view->hdr );
1547 return r;
1550 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1552 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1553 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1555 load_all_folders( package );
1556 msi_load_all_components( package );
1557 msi_load_all_features( package );
1558 load_all_files( package );
1559 load_all_patches( package );
1560 load_all_media( package );
1562 return ERROR_SUCCESS;
1565 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1567 const WCHAR *action = package->script->Actions[script][index];
1568 ui_actionstart( package, action );
1569 TRACE("executing %s\n", debugstr_w(action));
1570 return ACTION_PerformAction( package, action, script );
1573 static UINT execute_script( MSIPACKAGE *package, UINT script )
1575 UINT i, rc = ERROR_SUCCESS;
1577 TRACE("executing script %u\n", script);
1579 if (!package->script)
1581 ERR("no script!\n");
1582 return ERROR_FUNCTION_FAILED;
1584 if (script == SCRIPT_ROLLBACK)
1586 for (i = package->script->ActionCount[script]; i > 0; i--)
1588 rc = execute_script_action( package, script, i - 1 );
1589 if (rc != ERROR_SUCCESS) break;
1592 else
1594 for (i = 0; i < package->script->ActionCount[script]; i++)
1596 rc = execute_script_action( package, script, i );
1597 if (rc != ERROR_SUCCESS) break;
1600 msi_free_action_script(package, script);
1601 return rc;
1604 static UINT ACTION_FileCost(MSIPACKAGE *package)
1606 return ERROR_SUCCESS;
1609 static void get_client_counts( MSIPACKAGE *package )
1611 MSICOMPONENT *comp;
1612 HKEY hkey;
1614 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1616 if (!comp->ComponentId) continue;
1618 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1619 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1621 comp->num_clients = 0;
1622 continue;
1624 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1625 NULL, NULL, NULL, NULL );
1626 RegCloseKey( hkey );
1630 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1632 MSICOMPONENT *comp;
1633 UINT r;
1635 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1637 if (!comp->ComponentId) continue;
1639 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1640 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1641 &comp->Installed );
1642 if (r == ERROR_SUCCESS) continue;
1644 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1645 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1646 &comp->Installed );
1647 if (r == ERROR_SUCCESS) continue;
1649 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1650 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1651 &comp->Installed );
1652 if (r == ERROR_SUCCESS) continue;
1654 comp->Installed = INSTALLSTATE_ABSENT;
1658 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1660 MSIFEATURE *feature;
1662 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1664 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1666 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1667 feature->Installed = INSTALLSTATE_ABSENT;
1668 else
1669 feature->Installed = state;
1673 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1675 return (feature->Level > 0 && feature->Level <= level);
1678 static BOOL process_state_property(MSIPACKAGE* package, int level,
1679 LPCWSTR property, INSTALLSTATE state)
1681 LPWSTR override;
1682 MSIFEATURE *feature;
1683 BOOL remove = !strcmpW(property, szRemove);
1684 BOOL reinstall = !strcmpW(property, szReinstall);
1686 override = msi_dup_property( package->db, property );
1687 if (!override)
1688 return FALSE;
1690 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1692 if (feature->Level <= 0)
1693 continue;
1695 if (reinstall)
1696 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1697 else if (remove)
1698 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1700 if (!strcmpiW( override, szAll ))
1702 feature->Action = state;
1703 feature->ActionRequest = state;
1705 else
1707 LPWSTR ptr = override;
1708 LPWSTR ptr2 = strchrW(override,',');
1710 while (ptr)
1712 int len = ptr2 - ptr;
1714 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1715 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1717 feature->Action = state;
1718 feature->ActionRequest = state;
1719 break;
1721 if (ptr2)
1723 ptr=ptr2+1;
1724 ptr2 = strchrW(ptr,',');
1726 else
1727 break;
1731 msi_free(override);
1732 return TRUE;
1735 static BOOL process_overrides( MSIPACKAGE *package, int level )
1737 static const WCHAR szAddLocal[] =
1738 {'A','D','D','L','O','C','A','L',0};
1739 static const WCHAR szAddSource[] =
1740 {'A','D','D','S','O','U','R','C','E',0};
1741 static const WCHAR szAdvertise[] =
1742 {'A','D','V','E','R','T','I','S','E',0};
1743 BOOL ret = FALSE;
1745 /* all these activation/deactivation things happen in order and things
1746 * later on the list override things earlier on the list.
1748 * 0 INSTALLLEVEL processing
1749 * 1 ADDLOCAL
1750 * 2 REMOVE
1751 * 3 ADDSOURCE
1752 * 4 ADDDEFAULT
1753 * 5 REINSTALL
1754 * 6 ADVERTISE
1755 * 7 COMPADDLOCAL
1756 * 8 COMPADDSOURCE
1757 * 9 FILEADDLOCAL
1758 * 10 FILEADDSOURCE
1759 * 11 FILEADDDEFAULT
1761 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1762 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1763 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1764 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1765 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1767 if (ret && !package->full_reinstall)
1768 msi_set_property( package->db, szPreselected, szOne, -1 );
1770 return ret;
1773 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1775 int level;
1776 MSICOMPONENT* component;
1777 MSIFEATURE *feature;
1779 TRACE("Checking Install Level\n");
1781 level = msi_get_property_int(package->db, szInstallLevel, 1);
1783 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1785 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1787 if (!is_feature_selected( feature, level )) continue;
1789 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1791 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1793 feature->Action = INSTALLSTATE_SOURCE;
1794 feature->ActionRequest = INSTALLSTATE_SOURCE;
1796 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1798 feature->Action = INSTALLSTATE_ADVERTISED;
1799 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1801 else
1803 feature->Action = INSTALLSTATE_LOCAL;
1804 feature->ActionRequest = INSTALLSTATE_LOCAL;
1808 /* disable child features of unselected parent or follow parent */
1809 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1811 FeatureList *fl;
1813 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1815 if (!is_feature_selected( feature, level ))
1817 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1818 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1820 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1822 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1823 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1824 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1825 fl->feature->Action = feature->Action;
1826 fl->feature->ActionRequest = feature->ActionRequest;
1831 else /* preselected */
1833 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1835 if (!is_feature_selected( feature, level )) continue;
1837 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1839 if (feature->Installed == INSTALLSTATE_ABSENT)
1841 feature->Action = INSTALLSTATE_UNKNOWN;
1842 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1844 else
1846 feature->Action = feature->Installed;
1847 feature->ActionRequest = feature->Installed;
1851 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1853 FeatureList *fl;
1855 if (!is_feature_selected( feature, level )) continue;
1857 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1859 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1860 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1862 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1863 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1864 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1865 fl->feature->Action = feature->Action;
1866 fl->feature->ActionRequest = feature->ActionRequest;
1872 /* now we want to set component state based based on feature state */
1873 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1875 ComponentList *cl;
1877 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1878 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1879 feature->ActionRequest, feature->Action);
1881 /* features with components that have compressed files are made local */
1882 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1884 if (cl->component->ForceLocalState &&
1885 feature->ActionRequest == INSTALLSTATE_SOURCE)
1887 feature->Action = INSTALLSTATE_LOCAL;
1888 feature->ActionRequest = INSTALLSTATE_LOCAL;
1889 break;
1893 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1895 component = cl->component;
1897 switch (feature->ActionRequest)
1899 case INSTALLSTATE_ABSENT:
1900 component->anyAbsent = 1;
1901 break;
1902 case INSTALLSTATE_ADVERTISED:
1903 component->hasAdvertisedFeature = 1;
1904 break;
1905 case INSTALLSTATE_SOURCE:
1906 component->hasSourceFeature = 1;
1907 break;
1908 case INSTALLSTATE_LOCAL:
1909 component->hasLocalFeature = 1;
1910 break;
1911 case INSTALLSTATE_DEFAULT:
1912 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1913 component->hasAdvertisedFeature = 1;
1914 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1915 component->hasSourceFeature = 1;
1916 else
1917 component->hasLocalFeature = 1;
1918 break;
1919 default:
1920 break;
1925 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1927 /* check if it's local or source */
1928 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1929 (component->hasLocalFeature || component->hasSourceFeature))
1931 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1932 !component->ForceLocalState)
1934 component->Action = INSTALLSTATE_SOURCE;
1935 component->ActionRequest = INSTALLSTATE_SOURCE;
1937 else
1939 component->Action = INSTALLSTATE_LOCAL;
1940 component->ActionRequest = INSTALLSTATE_LOCAL;
1942 continue;
1945 /* if any feature is local, the component must be local too */
1946 if (component->hasLocalFeature)
1948 component->Action = INSTALLSTATE_LOCAL;
1949 component->ActionRequest = INSTALLSTATE_LOCAL;
1950 continue;
1952 if (component->hasSourceFeature)
1954 component->Action = INSTALLSTATE_SOURCE;
1955 component->ActionRequest = INSTALLSTATE_SOURCE;
1956 continue;
1958 if (component->hasAdvertisedFeature)
1960 component->Action = INSTALLSTATE_ADVERTISED;
1961 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1962 continue;
1964 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1965 if (component->anyAbsent && component->ComponentId)
1967 component->Action = INSTALLSTATE_ABSENT;
1968 component->ActionRequest = INSTALLSTATE_ABSENT;
1972 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1974 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1976 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1977 component->Action = INSTALLSTATE_LOCAL;
1978 component->ActionRequest = INSTALLSTATE_LOCAL;
1981 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1982 component->Installed == INSTALLSTATE_SOURCE &&
1983 component->hasSourceFeature)
1985 component->Action = INSTALLSTATE_UNKNOWN;
1986 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1989 TRACE("component %s (installed %d request %d action %d)\n",
1990 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1992 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1993 component->num_clients++;
1994 else if (component->Action == INSTALLSTATE_ABSENT)
1995 component->num_clients--;
1998 return ERROR_SUCCESS;
2001 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2003 MSIPACKAGE *package = param;
2004 LPCWSTR name;
2005 MSIFEATURE *feature;
2007 name = MSI_RecordGetString( row, 1 );
2009 feature = msi_get_loaded_feature( package, name );
2010 if (!feature)
2011 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2012 else
2014 LPCWSTR Condition;
2015 Condition = MSI_RecordGetString(row,3);
2017 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2019 int level = MSI_RecordGetInteger(row,2);
2020 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2021 feature->Level = level;
2024 return ERROR_SUCCESS;
2027 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2029 static const WCHAR name[] = {'\\',0};
2030 VS_FIXEDFILEINFO *ptr, *ret;
2031 LPVOID version;
2032 DWORD versize, handle;
2033 UINT sz;
2035 versize = GetFileVersionInfoSizeW( filename, &handle );
2036 if (!versize)
2037 return NULL;
2039 version = msi_alloc( versize );
2040 if (!version)
2041 return NULL;
2043 GetFileVersionInfoW( filename, 0, versize, version );
2045 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2047 msi_free( version );
2048 return NULL;
2051 ret = msi_alloc( sz );
2052 memcpy( ret, ptr, sz );
2054 msi_free( version );
2055 return ret;
2058 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2060 DWORD ms, ls;
2062 msi_parse_version_string( version, &ms, &ls );
2064 if (fi->dwFileVersionMS > ms) return 1;
2065 else if (fi->dwFileVersionMS < ms) return -1;
2066 else if (fi->dwFileVersionLS > ls) return 1;
2067 else if (fi->dwFileVersionLS < ls) return -1;
2068 return 0;
2071 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2073 DWORD ms1, ms2;
2075 msi_parse_version_string( ver1, &ms1, NULL );
2076 msi_parse_version_string( ver2, &ms2, NULL );
2078 if (ms1 > ms2) return 1;
2079 else if (ms1 < ms2) return -1;
2080 return 0;
2083 DWORD msi_get_disk_file_size( LPCWSTR filename )
2085 HANDLE file;
2086 DWORD size;
2088 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2089 if (file == INVALID_HANDLE_VALUE)
2090 return INVALID_FILE_SIZE;
2092 size = GetFileSize( file, NULL );
2093 CloseHandle( file );
2094 return size;
2097 BOOL msi_file_hash_matches( MSIFILE *file )
2099 UINT r;
2100 MSIFILEHASHINFO hash;
2102 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2103 r = msi_get_filehash( file->TargetPath, &hash );
2104 if (r != ERROR_SUCCESS)
2105 return FALSE;
2107 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2110 static WCHAR *create_temp_dir( MSIDATABASE *db )
2112 static UINT id;
2113 WCHAR *ret;
2115 if (!db->tempfolder)
2117 WCHAR tmp[MAX_PATH];
2118 UINT len = sizeof(tmp)/sizeof(tmp[0]);
2120 if (msi_get_property( db, szTempFolder, tmp, &len ) ||
2121 GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY)
2123 GetTempPathW( MAX_PATH, tmp );
2125 if (!(db->tempfolder = strdupW( tmp ))) return NULL;
2128 if ((ret = msi_alloc( (strlenW( db->tempfolder ) + 20) * sizeof(WCHAR) )))
2130 for (;;)
2132 if (!GetTempFileNameW( db->tempfolder, szMsi, ++id, ret ))
2134 msi_free( ret );
2135 return NULL;
2137 if (CreateDirectoryW( ret, NULL )) break;
2141 return ret;
2145 * msi_build_directory_name()
2147 * This function is to save messing round with directory names
2148 * It handles adding backslashes between path segments,
2149 * and can add \ at the end of the directory name if told to.
2151 * It takes a variable number of arguments.
2152 * It always allocates a new string for the result, so make sure
2153 * to free the return value when finished with it.
2155 * The first arg is the number of path segments that follow.
2156 * The arguments following count are a list of path segments.
2157 * A path segment may be NULL.
2159 * Path segments will be added with a \ separating them.
2160 * A \ will not be added after the last segment, however if the
2161 * last segment is NULL, then the last character will be a \
2163 WCHAR *msi_build_directory_name( DWORD count, ... )
2165 DWORD sz = 1, i;
2166 WCHAR *dir;
2167 va_list va;
2169 va_start( va, count );
2170 for (i = 0; i < count; i++)
2172 const WCHAR *str = va_arg( va, const WCHAR * );
2173 if (str) sz += strlenW( str ) + 1;
2175 va_end( va );
2177 dir = msi_alloc( sz * sizeof(WCHAR) );
2178 dir[0] = 0;
2180 va_start( va, count );
2181 for (i = 0; i < count; i++)
2183 const WCHAR *str = va_arg( va, const WCHAR * );
2184 if (!str) continue;
2185 strcatW( dir, str );
2186 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2188 va_end( va );
2189 return dir;
2192 BOOL msi_is_global_assembly( MSICOMPONENT *comp )
2194 return comp->assembly && !comp->assembly->application;
2197 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2199 msi_free( file->TargetPath );
2200 if (msi_is_global_assembly( file->Component ))
2202 MSIASSEMBLY *assembly = file->Component->assembly;
2204 if (!assembly->tempdir) assembly->tempdir = create_temp_dir( package->db );
2205 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2207 else
2209 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2210 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2213 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2216 static UINT calculate_file_cost( MSIPACKAGE *package )
2218 VS_FIXEDFILEINFO *file_version;
2219 WCHAR *font_version;
2220 MSIFILE *file;
2222 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2224 MSICOMPONENT *comp = file->Component;
2225 DWORD file_size;
2227 if (!comp->Enabled) continue;
2229 if (file->IsCompressed)
2230 comp->ForceLocalState = TRUE;
2232 set_target_path( package, file );
2234 if ((comp->assembly && !comp->assembly->installed) ||
2235 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2237 comp->Cost += file->FileSize;
2238 continue;
2240 file_size = msi_get_disk_file_size( file->TargetPath );
2241 TRACE("%s (size %u)\n", debugstr_w(file->TargetPath), file_size);
2243 if (file->Version)
2245 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2247 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2249 comp->Cost += file->FileSize - file_size;
2251 msi_free( file_version );
2252 continue;
2254 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2256 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2258 comp->Cost += file->FileSize - file_size;
2260 msi_free( font_version );
2261 continue;
2264 if (file_size != file->FileSize)
2266 comp->Cost += file->FileSize - file_size;
2269 return ERROR_SUCCESS;
2272 WCHAR *msi_normalize_path( const WCHAR *in )
2274 const WCHAR *p = in;
2275 WCHAR *q, *ret;
2276 int n, len = strlenW( in ) + 2;
2278 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2280 len = 0;
2281 while (1)
2283 /* copy until the end of the string or a space */
2284 while (*p != ' ' && (*q = *p))
2286 p++, len++;
2287 /* reduce many backslashes to one */
2288 if (*p != '\\' || *q != '\\')
2289 q++;
2292 /* quit at the end of the string */
2293 if (!*p)
2294 break;
2296 /* count the number of spaces */
2297 n = 0;
2298 while (p[n] == ' ')
2299 n++;
2301 /* if it's leading or trailing space, skip it */
2302 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2303 p += n;
2304 else /* copy n spaces */
2305 while (n && (*q++ = *p++)) n--;
2307 while (q - ret > 0 && q[-1] == ' ') q--;
2308 if (q - ret > 0 && q[-1] != '\\')
2310 q[0] = '\\';
2311 q[1] = 0;
2313 return ret;
2316 static WCHAR *get_install_location( MSIPACKAGE *package )
2318 HKEY hkey;
2319 WCHAR *path;
2321 if (!package->ProductCode) return NULL;
2322 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2323 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2325 msi_free( path );
2326 path = NULL;
2328 RegCloseKey( hkey );
2329 return path;
2332 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2334 FolderList *fl;
2335 MSIFOLDER *folder, *parent, *child;
2336 WCHAR *path, *normalized_path;
2338 TRACE("resolving %s\n", debugstr_w(name));
2340 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2342 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2344 if (!(path = get_install_location( package )) &&
2345 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2347 path = msi_dup_property( package->db, szRootDrive );
2350 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2352 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2354 parent = msi_get_loaded_folder( package, folder->Parent );
2355 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2357 else
2358 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2360 normalized_path = msi_normalize_path( path );
2361 msi_free( path );
2362 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2364 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2365 msi_free( normalized_path );
2366 return;
2368 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2369 msi_free( folder->ResolvedTarget );
2370 folder->ResolvedTarget = normalized_path;
2372 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2374 child = fl->folder;
2375 msi_resolve_target_folder( package, child->Directory, load_prop );
2377 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2380 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2382 static const WCHAR query[] =
2383 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2384 '`','C','o','n','d','i','t','i','o','n','`',0};
2385 static const WCHAR szOutOfDiskSpace[] =
2386 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2387 static const WCHAR szPrimaryFolder[] =
2388 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2389 static const WCHAR szPrimaryVolumePath[] =
2390 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2391 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2392 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2393 'A','v','a','i','l','a','b','l','e',0};
2394 static const WCHAR szOutOfNoRbDiskSpace[] =
2395 {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0};
2396 MSICOMPONENT *comp;
2397 MSIQUERY *view;
2398 WCHAR *level, *primary_key, *primary_folder;
2399 UINT rc;
2401 TRACE("Building directory properties\n");
2402 msi_resolve_target_folder( package, szTargetDir, TRUE );
2404 TRACE("Evaluating component conditions\n");
2405 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2407 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2409 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2410 comp->Enabled = FALSE;
2412 else
2413 comp->Enabled = TRUE;
2415 get_client_counts( package );
2417 /* read components states from the registry */
2418 ACTION_GetComponentInstallStates(package);
2419 ACTION_GetFeatureInstallStates(package);
2421 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2423 TRACE("Evaluating feature conditions\n");
2425 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2426 if (rc == ERROR_SUCCESS)
2428 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2429 msiobj_release( &view->hdr );
2430 if (rc != ERROR_SUCCESS)
2431 return rc;
2435 TRACE("Calculating file cost\n");
2436 calculate_file_cost( package );
2438 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2439 /* set default run level if not set */
2440 level = msi_dup_property( package->db, szInstallLevel );
2441 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2442 msi_free(level);
2444 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2446 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2448 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2449 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2451 ULARGE_INTEGER free;
2453 primary_folder[2] = 0;
2454 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2456 static const WCHAR fmtW[] = {'%','l','u',0};
2457 WCHAR buf[21];
2459 sprintfW( buf, fmtW, free.QuadPart / 512 );
2460 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2462 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2464 msi_free( primary_folder );
2466 msi_free( primary_key );
2469 /* FIXME: check volume disk space */
2470 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2471 msi_set_property( package->db, szOutOfNoRbDiskSpace, szZero, -1 );
2473 return MSI_SetFeatureStates(package);
2476 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size )
2478 BYTE *data;
2480 if (!value)
2482 *size = sizeof(WCHAR);
2483 *type = REG_SZ;
2484 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2485 return data;
2487 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2489 if (value[1]=='x')
2491 LPWSTR ptr;
2492 CHAR byte[5];
2493 LPWSTR deformated = NULL;
2494 int count;
2496 deformat_string(package, &value[2], &deformated);
2498 /* binary value type */
2499 ptr = deformated;
2500 *type = REG_BINARY;
2501 if (strlenW(ptr)%2)
2502 *size = (strlenW(ptr)/2)+1;
2503 else
2504 *size = strlenW(ptr)/2;
2506 data = msi_alloc(*size);
2508 byte[0] = '0';
2509 byte[1] = 'x';
2510 byte[4] = 0;
2511 count = 0;
2512 /* if uneven pad with a zero in front */
2513 if (strlenW(ptr)%2)
2515 byte[2]= '0';
2516 byte[3]= *ptr;
2517 ptr++;
2518 data[count] = (BYTE)strtol(byte,NULL,0);
2519 count ++;
2520 TRACE("Uneven byte count\n");
2522 while (*ptr)
2524 byte[2]= *ptr;
2525 ptr++;
2526 byte[3]= *ptr;
2527 ptr++;
2528 data[count] = (BYTE)strtol(byte,NULL,0);
2529 count ++;
2531 msi_free(deformated);
2533 TRACE("Data %i bytes(%i)\n",*size,count);
2535 else
2537 LPWSTR deformated;
2538 LPWSTR p;
2539 DWORD d = 0;
2540 deformat_string(package, &value[1], &deformated);
2542 *type=REG_DWORD;
2543 *size = sizeof(DWORD);
2544 data = msi_alloc(*size);
2545 p = deformated;
2546 if (*p == '-')
2547 p++;
2548 while (*p)
2550 if ( (*p < '0') || (*p > '9') )
2551 break;
2552 d *= 10;
2553 d += (*p - '0');
2554 p++;
2556 if (deformated[0] == '-')
2557 d = -d;
2558 *(LPDWORD)data = d;
2559 TRACE("DWORD %i\n",*(LPDWORD)data);
2561 msi_free(deformated);
2564 else
2566 const WCHAR *ptr = value;
2568 *type = REG_SZ;
2569 if (value[0] == '#')
2571 ptr++; len--;
2572 if (value[1] == '%')
2574 ptr++; len--;
2575 *type = REG_EXPAND_SZ;
2578 data = (BYTE *)msi_strdupW( ptr, len );
2579 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2580 *size = (len + 1) * sizeof(WCHAR);
2582 return data;
2585 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2587 const WCHAR *ret;
2589 switch (root)
2591 case -1:
2592 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2594 *root_key = HKEY_LOCAL_MACHINE;
2595 ret = szHLM;
2597 else
2599 *root_key = HKEY_CURRENT_USER;
2600 ret = szHCU;
2602 break;
2603 case 0:
2604 *root_key = HKEY_CLASSES_ROOT;
2605 ret = szHCR;
2606 break;
2607 case 1:
2608 *root_key = HKEY_CURRENT_USER;
2609 ret = szHCU;
2610 break;
2611 case 2:
2612 *root_key = HKEY_LOCAL_MACHINE;
2613 ret = szHLM;
2614 break;
2615 case 3:
2616 *root_key = HKEY_USERS;
2617 ret = szHU;
2618 break;
2619 default:
2620 ERR("Unknown root %i\n", root);
2621 return NULL;
2624 return ret;
2627 static inline REGSAM get_registry_view( const MSICOMPONENT *comp )
2629 REGSAM view = 0;
2630 if (is_wow64 || is_64bit)
2631 view |= (comp->Attributes & msidbComponentAttributes64bit) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
2632 return view;
2635 static HKEY open_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, BOOL create )
2637 REGSAM access = KEY_ALL_ACCESS;
2638 WCHAR *subkey, *p, *q;
2639 HKEY hkey, ret = NULL;
2640 LONG res;
2642 access |= get_registry_view( comp );
2644 if (!(subkey = strdupW( path ))) return NULL;
2645 p = subkey;
2646 if ((q = strchrW( p, '\\' ))) *q = 0;
2647 if (create)
2648 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2649 else
2650 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2651 if (res)
2653 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2654 msi_free( subkey );
2655 return NULL;
2657 if (q && q[1])
2659 ret = open_key( comp, hkey, q + 1, create );
2660 RegCloseKey( hkey );
2662 else ret = hkey;
2663 msi_free( subkey );
2664 return ret;
2667 static BOOL is_special_entry( const WCHAR *name )
2669 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2672 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2674 const WCHAR *p = str;
2675 WCHAR **ret;
2676 int i = 0;
2678 *count = 0;
2679 if (!str) return NULL;
2680 while ((p - str) < len)
2682 p += strlenW( p ) + 1;
2683 (*count)++;
2685 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2686 p = str;
2687 while ((p - str) < len)
2689 if (!(ret[i] = strdupW( p )))
2691 for (; i >= 0; i--) msi_free( ret[i] );
2692 msi_free( ret );
2693 return NULL;
2695 p += strlenW( p ) + 1;
2696 i++;
2698 return ret;
2701 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2702 WCHAR **right, DWORD right_count, DWORD *size )
2704 WCHAR *ret, *p;
2705 unsigned int i;
2707 *size = sizeof(WCHAR);
2708 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2709 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2711 if (!(ret = p = msi_alloc( *size ))) return NULL;
2713 for (i = 0; i < left_count; i++)
2715 strcpyW( p, left[i] );
2716 p += strlenW( p ) + 1;
2718 for (i = 0; i < right_count; i++)
2720 strcpyW( p, right[i] );
2721 p += strlenW( p ) + 1;
2723 *p = 0;
2724 return ret;
2727 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2728 WCHAR **new, DWORD new_count )
2730 DWORD ret = old_count;
2731 unsigned int i, j, k;
2733 for (i = 0; i < new_count; i++)
2735 for (j = 0; j < old_count; j++)
2737 if (old[j] && !strcmpW( new[i], old[j] ))
2739 msi_free( old[j] );
2740 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2741 old[k] = NULL;
2742 ret--;
2746 return ret;
2749 enum join_op
2751 JOIN_OP_APPEND,
2752 JOIN_OP_PREPEND,
2753 JOIN_OP_REPLACE
2756 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2757 WCHAR **new, DWORD new_count, DWORD *size )
2759 switch (op)
2761 case JOIN_OP_APPEND:
2762 old_count = remove_duplicate_values( old, old_count, new, new_count );
2763 return flatten_multi_string_values( old, old_count, new, new_count, size );
2765 case JOIN_OP_PREPEND:
2766 old_count = remove_duplicate_values( old, old_count, new, new_count );
2767 return flatten_multi_string_values( new, new_count, old, old_count, size );
2769 case JOIN_OP_REPLACE:
2770 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2772 default:
2773 ERR("unhandled join op %u\n", op);
2774 return NULL;
2778 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2779 BYTE *new_value, DWORD new_size, DWORD *size )
2781 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2782 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2783 enum join_op op = JOIN_OP_REPLACE;
2784 WCHAR **old = NULL, **new = NULL;
2785 BYTE *ret;
2787 if (new_size / sizeof(WCHAR) - 1 > 1)
2789 new_ptr = (const WCHAR *)new_value;
2790 new_len = new_size / sizeof(WCHAR) - 1;
2792 if (!new_ptr[0] && new_ptr[new_len - 1])
2794 op = JOIN_OP_APPEND;
2795 new_len--;
2796 new_ptr++;
2798 else if (new_ptr[0] && !new_ptr[new_len - 1])
2800 op = JOIN_OP_PREPEND;
2801 new_len--;
2803 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2805 op = JOIN_OP_REPLACE;
2806 new_len -= 2;
2807 new_ptr++;
2809 new = split_multi_string_values( new_ptr, new_len, &new_count );
2811 if (old_size / sizeof(WCHAR) - 1 > 1)
2813 old_ptr = (const WCHAR *)old_value;
2814 old_len = old_size / sizeof(WCHAR) - 1;
2815 old = split_multi_string_values( old_ptr, old_len, &old_count );
2817 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2818 for (i = 0; i < old_count; i++) msi_free( old[i] );
2819 for (i = 0; i < new_count; i++) msi_free( new[i] );
2820 msi_free( old );
2821 msi_free( new );
2822 return ret;
2825 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2827 BYTE *ret;
2828 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2829 if (!(ret = msi_alloc( *size ))) return NULL;
2830 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2831 return ret;
2834 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2836 MSIPACKAGE *package = param;
2837 BYTE *new_value, *old_value = NULL;
2838 HKEY root_key, hkey;
2839 DWORD type, old_type, new_size, old_size = 0;
2840 LPWSTR deformated, uikey;
2841 const WCHAR *szRoot, *component, *name, *key, *str;
2842 MSICOMPONENT *comp;
2843 MSIRECORD * uirow;
2844 INT root;
2845 BOOL check_first = FALSE;
2846 int len;
2848 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2850 component = MSI_RecordGetString(row, 6);
2851 comp = msi_get_loaded_component(package,component);
2852 if (!comp)
2853 return ERROR_SUCCESS;
2855 comp->Action = msi_get_component_action( package, comp );
2856 if (comp->Action != INSTALLSTATE_LOCAL)
2858 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2859 return ERROR_SUCCESS;
2862 name = MSI_RecordGetString(row, 4);
2863 if( MSI_RecordIsNull(row,5) && name )
2865 /* null values can have special meanings */
2866 if (name[0]=='-' && name[1] == 0)
2867 return ERROR_SUCCESS;
2868 if ((name[0] == '+' || name[0] == '*') && !name[1])
2869 check_first = TRUE;
2872 root = MSI_RecordGetInteger(row,2);
2873 key = MSI_RecordGetString(row, 3);
2875 szRoot = get_root_key( package, root, &root_key );
2876 if (!szRoot)
2877 return ERROR_SUCCESS;
2879 deformat_string(package, key , &deformated);
2880 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2881 strcpyW(uikey,szRoot);
2882 strcatW(uikey,deformated);
2884 if (!(hkey = open_key( comp, root_key, deformated, TRUE )))
2886 ERR("Could not create key %s\n", debugstr_w(deformated));
2887 msi_free(uikey);
2888 msi_free(deformated);
2889 return ERROR_FUNCTION_FAILED;
2891 msi_free( deformated );
2892 str = msi_record_get_string( row, 5, NULL );
2893 len = deformat_string( package, str, &deformated );
2894 new_value = parse_value( package, deformated, len, &type, &new_size );
2896 msi_free( deformated );
2897 deformat_string(package, name, &deformated);
2899 if (!is_special_entry( name ))
2901 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2902 if (type == REG_MULTI_SZ)
2904 BYTE *new;
2905 if (old_value && old_type != REG_MULTI_SZ)
2907 msi_free( old_value );
2908 old_value = NULL;
2909 old_size = 0;
2911 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2912 msi_free( new_value );
2913 new_value = new;
2915 if (!check_first)
2917 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2918 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2920 else if (!old_value)
2922 if (deformated || new_size)
2924 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2925 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2928 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2930 RegCloseKey(hkey);
2932 uirow = MSI_CreateRecord(3);
2933 MSI_RecordSetStringW(uirow,2,deformated);
2934 MSI_RecordSetStringW(uirow,1,uikey);
2935 if (type == REG_SZ || type == REG_EXPAND_SZ)
2936 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2937 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2938 msiobj_release( &uirow->hdr );
2940 msi_free(new_value);
2941 msi_free(old_value);
2942 msi_free(deformated);
2943 msi_free(uikey);
2945 return ERROR_SUCCESS;
2948 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2950 static const WCHAR query[] = {
2951 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2952 '`','R','e','g','i','s','t','r','y','`',0};
2953 MSIQUERY *view;
2954 UINT rc;
2956 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2957 if (rc != ERROR_SUCCESS)
2958 return ERROR_SUCCESS;
2960 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2961 msiobj_release(&view->hdr);
2962 return rc;
2965 static void delete_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2967 REGSAM access = 0;
2968 WCHAR *subkey, *p;
2969 HKEY hkey;
2970 LONG res;
2972 access |= get_registry_view( comp );
2974 if (!(subkey = strdupW( path ))) return;
2975 for (;;)
2977 if ((p = strrchrW( subkey, '\\' ))) *p = 0;
2978 hkey = open_key( comp, root, subkey, FALSE );
2979 if (!hkey) break;
2980 if (p && p[1])
2981 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2982 else
2983 res = RegDeleteKeyExW( root, subkey, access, 0 );
2984 if (res)
2986 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
2987 break;
2989 if (p && p[1]) RegCloseKey( hkey );
2990 else break;
2992 msi_free( subkey );
2995 static void delete_value( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, const WCHAR *value )
2997 LONG res;
2998 HKEY hkey;
2999 DWORD num_subkeys, num_values;
3001 if ((hkey = open_key( comp, root, path, FALSE )))
3003 if ((res = RegDeleteValueW( hkey, value )))
3004 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
3006 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
3007 NULL, NULL, NULL, NULL );
3008 RegCloseKey( hkey );
3009 if (!res && !num_subkeys && !num_values)
3011 TRACE("removing empty key %s\n", debugstr_w(path));
3012 delete_key( comp, root, path );
3017 static void delete_tree( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
3019 LONG res;
3020 HKEY hkey;
3022 if (!(hkey = open_key( comp, root, path, FALSE ))) return;
3023 res = RegDeleteTreeW( hkey, NULL );
3024 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3025 delete_key( comp, root, path );
3026 RegCloseKey( hkey );
3029 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3031 MSIPACKAGE *package = param;
3032 LPCWSTR component, name, key_str, root_key_str;
3033 LPWSTR deformated_key, deformated_name, ui_key_str;
3034 MSICOMPONENT *comp;
3035 MSIRECORD *uirow;
3036 BOOL delete_key = FALSE;
3037 HKEY hkey_root;
3038 UINT size;
3039 INT root;
3041 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3043 component = MSI_RecordGetString( row, 6 );
3044 comp = msi_get_loaded_component( package, component );
3045 if (!comp)
3046 return ERROR_SUCCESS;
3048 comp->Action = msi_get_component_action( package, comp );
3049 if (comp->Action != INSTALLSTATE_ABSENT)
3051 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3052 return ERROR_SUCCESS;
3055 name = MSI_RecordGetString( row, 4 );
3056 if (MSI_RecordIsNull( row, 5 ) && name )
3058 if (name[0] == '+' && !name[1])
3059 return ERROR_SUCCESS;
3060 if ((name[0] == '-' || name[0] == '*') && !name[1])
3062 delete_key = TRUE;
3063 name = NULL;
3067 root = MSI_RecordGetInteger( row, 2 );
3068 key_str = MSI_RecordGetString( row, 3 );
3070 root_key_str = get_root_key( package, root, &hkey_root );
3071 if (!root_key_str)
3072 return ERROR_SUCCESS;
3074 deformat_string( package, key_str, &deformated_key );
3075 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3076 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3077 strcpyW( ui_key_str, root_key_str );
3078 strcatW( ui_key_str, deformated_key );
3080 deformat_string( package, name, &deformated_name );
3082 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3083 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3084 msi_free( deformated_key );
3086 uirow = MSI_CreateRecord( 2 );
3087 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3088 MSI_RecordSetStringW( uirow, 2, deformated_name );
3089 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3090 msiobj_release( &uirow->hdr );
3092 msi_free( ui_key_str );
3093 msi_free( deformated_name );
3094 return ERROR_SUCCESS;
3097 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3099 MSIPACKAGE *package = param;
3100 LPCWSTR component, name, key_str, root_key_str;
3101 LPWSTR deformated_key, deformated_name, ui_key_str;
3102 MSICOMPONENT *comp;
3103 MSIRECORD *uirow;
3104 BOOL delete_key = FALSE;
3105 HKEY hkey_root;
3106 UINT size;
3107 INT root;
3109 component = MSI_RecordGetString( row, 5 );
3110 comp = msi_get_loaded_component( package, component );
3111 if (!comp)
3112 return ERROR_SUCCESS;
3114 comp->Action = msi_get_component_action( package, comp );
3115 if (comp->Action != INSTALLSTATE_LOCAL)
3117 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3118 return ERROR_SUCCESS;
3121 if ((name = MSI_RecordGetString( row, 4 )))
3123 if (name[0] == '-' && !name[1])
3125 delete_key = TRUE;
3126 name = NULL;
3130 root = MSI_RecordGetInteger( row, 2 );
3131 key_str = MSI_RecordGetString( row, 3 );
3133 root_key_str = get_root_key( package, root, &hkey_root );
3134 if (!root_key_str)
3135 return ERROR_SUCCESS;
3137 deformat_string( package, key_str, &deformated_key );
3138 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3139 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3140 strcpyW( ui_key_str, root_key_str );
3141 strcatW( ui_key_str, deformated_key );
3143 deformat_string( package, name, &deformated_name );
3145 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3146 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3147 msi_free( deformated_key );
3149 uirow = MSI_CreateRecord( 2 );
3150 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3151 MSI_RecordSetStringW( uirow, 2, deformated_name );
3152 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3153 msiobj_release( &uirow->hdr );
3155 msi_free( ui_key_str );
3156 msi_free( deformated_name );
3157 return ERROR_SUCCESS;
3160 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3162 static const WCHAR registry_query[] = {
3163 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3164 '`','R','e','g','i','s','t','r','y','`',0};
3165 static const WCHAR remove_registry_query[] = {
3166 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3167 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3168 MSIQUERY *view;
3169 UINT rc;
3171 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3172 if (rc == ERROR_SUCCESS)
3174 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3175 msiobj_release( &view->hdr );
3176 if (rc != ERROR_SUCCESS)
3177 return rc;
3179 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3180 if (rc == ERROR_SUCCESS)
3182 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3183 msiobj_release( &view->hdr );
3184 if (rc != ERROR_SUCCESS)
3185 return rc;
3187 return ERROR_SUCCESS;
3190 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3192 return ERROR_SUCCESS;
3196 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3198 static const WCHAR query[]= {
3199 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3200 '`','R','e','g','i','s','t','r','y','`',0};
3201 MSICOMPONENT *comp;
3202 DWORD total = 0, count = 0;
3203 MSIQUERY *view;
3204 MSIFEATURE *feature;
3205 MSIFILE *file;
3206 UINT rc;
3208 TRACE("InstallValidate\n");
3210 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3211 if (rc == ERROR_SUCCESS)
3213 rc = MSI_IterateRecords( view, &count, NULL, package );
3214 msiobj_release( &view->hdr );
3215 if (rc != ERROR_SUCCESS)
3216 return rc;
3217 total += count * REG_PROGRESS_VALUE;
3219 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3220 total += COMPONENT_PROGRESS_VALUE;
3222 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3223 total += file->FileSize;
3225 msi_ui_progress( package, 0, total, 0, 0 );
3227 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3229 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3230 debugstr_w(feature->Feature), feature->Installed,
3231 feature->ActionRequest, feature->Action);
3233 return ERROR_SUCCESS;
3236 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3238 MSIPACKAGE* package = param;
3239 LPCWSTR cond = NULL;
3240 LPCWSTR message = NULL;
3241 UINT r;
3243 static const WCHAR title[]=
3244 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3246 cond = MSI_RecordGetString(row,1);
3248 r = MSI_EvaluateConditionW(package,cond);
3249 if (r == MSICONDITION_FALSE)
3251 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3253 LPWSTR deformated;
3254 message = MSI_RecordGetString(row,2);
3255 deformat_string(package,message,&deformated);
3256 MessageBoxW(NULL,deformated,title,MB_OK);
3257 msi_free(deformated);
3260 return ERROR_INSTALL_FAILURE;
3263 return ERROR_SUCCESS;
3266 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3268 static const WCHAR query[] = {
3269 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3270 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3271 MSIQUERY *view;
3272 UINT rc;
3274 TRACE("Checking launch conditions\n");
3276 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3277 if (rc != ERROR_SUCCESS)
3278 return ERROR_SUCCESS;
3280 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3281 msiobj_release(&view->hdr);
3282 return rc;
3285 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3288 if (!cmp->KeyPath)
3289 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3291 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3293 static const WCHAR query[] = {
3294 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3295 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3296 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3297 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3298 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3299 MSIRECORD *row;
3300 UINT root, len;
3301 LPWSTR deformated, buffer, deformated_name;
3302 LPCWSTR key, name;
3304 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3305 if (!row)
3306 return NULL;
3308 root = MSI_RecordGetInteger(row,2);
3309 key = MSI_RecordGetString(row, 3);
3310 name = MSI_RecordGetString(row, 4);
3311 deformat_string(package, key , &deformated);
3312 deformat_string(package, name, &deformated_name);
3314 len = strlenW(deformated) + 6;
3315 if (deformated_name)
3316 len+=strlenW(deformated_name);
3318 buffer = msi_alloc( len *sizeof(WCHAR));
3320 if (deformated_name)
3321 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3322 else
3323 sprintfW(buffer,fmt,root,deformated);
3325 msi_free(deformated);
3326 msi_free(deformated_name);
3327 msiobj_release(&row->hdr);
3329 return buffer;
3331 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3333 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3334 return NULL;
3336 else
3338 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3340 if (file)
3341 return strdupW( file->TargetPath );
3343 return NULL;
3346 static HKEY openSharedDLLsKey(void)
3348 HKEY hkey=0;
3349 static const WCHAR path[] =
3350 {'S','o','f','t','w','a','r','e','\\',
3351 'M','i','c','r','o','s','o','f','t','\\',
3352 'W','i','n','d','o','w','s','\\',
3353 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3354 'S','h','a','r','e','d','D','L','L','s',0};
3356 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3357 return hkey;
3360 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3362 HKEY hkey;
3363 DWORD count=0;
3364 DWORD type;
3365 DWORD sz = sizeof(count);
3366 DWORD rc;
3368 hkey = openSharedDLLsKey();
3369 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3370 if (rc != ERROR_SUCCESS)
3371 count = 0;
3372 RegCloseKey(hkey);
3373 return count;
3376 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3378 HKEY hkey;
3380 hkey = openSharedDLLsKey();
3381 if (count > 0)
3382 msi_reg_set_val_dword( hkey, path, count );
3383 else
3384 RegDeleteValueW(hkey,path);
3385 RegCloseKey(hkey);
3386 return count;
3389 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3391 MSIFEATURE *feature;
3392 INT count = 0;
3393 BOOL write = FALSE;
3395 /* only refcount DLLs */
3396 if (comp->KeyPath == NULL ||
3397 comp->assembly ||
3398 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3399 comp->Attributes & msidbComponentAttributesODBCDataSource)
3400 write = FALSE;
3401 else
3403 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3404 write = (count > 0);
3406 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3407 write = TRUE;
3410 /* increment counts */
3411 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3413 ComponentList *cl;
3415 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3416 continue;
3418 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3420 if ( cl->component == comp )
3421 count++;
3425 /* decrement counts */
3426 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3428 ComponentList *cl;
3430 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3431 continue;
3433 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3435 if ( cl->component == comp )
3436 count--;
3440 /* ref count all the files in the component */
3441 if (write)
3443 MSIFILE *file;
3445 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3447 if (file->Component == comp)
3448 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3452 /* add a count for permanent */
3453 if (comp->Attributes & msidbComponentAttributesPermanent)
3454 count ++;
3456 comp->RefCount = count;
3458 if (write)
3459 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3462 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3464 if (comp->assembly)
3466 const WCHAR prefixW[] = {'<','\\',0};
3467 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3468 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3470 if (keypath)
3472 strcpyW( keypath, prefixW );
3473 strcatW( keypath, comp->assembly->display_name );
3475 return keypath;
3477 return resolve_keypath( package, comp );
3480 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3482 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3483 UINT rc;
3484 MSICOMPONENT *comp;
3485 HKEY hkey;
3487 TRACE("\n");
3489 squash_guid(package->ProductCode,squished_pc);
3490 msi_set_sourcedir_props(package, FALSE);
3492 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3494 MSIRECORD *uirow;
3495 INSTALLSTATE action;
3497 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3498 if (!comp->ComponentId)
3499 continue;
3501 squash_guid( comp->ComponentId, squished_cc );
3502 msi_free( comp->FullKeypath );
3503 comp->FullKeypath = build_full_keypath( package, comp );
3505 ACTION_RefCountComponent( package, comp );
3507 if (package->need_rollback) action = comp->Installed;
3508 else action = comp->ActionRequest;
3510 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3511 debugstr_w(comp->Component), debugstr_w(squished_cc),
3512 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3514 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3516 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3517 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3518 else
3519 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3521 if (rc != ERROR_SUCCESS)
3522 continue;
3524 if (comp->Attributes & msidbComponentAttributesPermanent)
3526 static const WCHAR szPermKey[] =
3527 { '0','0','0','0','0','0','0','0','0','0','0','0',
3528 '0','0','0','0','0','0','0','0','0','0','0','0',
3529 '0','0','0','0','0','0','0','0',0 };
3531 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3533 if (action == INSTALLSTATE_LOCAL)
3534 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3535 else
3537 MSIFILE *file;
3538 MSIRECORD *row;
3539 LPWSTR ptr, ptr2;
3540 WCHAR source[MAX_PATH];
3541 WCHAR base[MAX_PATH];
3542 LPWSTR sourcepath;
3544 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3545 static const WCHAR query[] = {
3546 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3547 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3548 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3549 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3550 '`','D','i','s','k','I','d','`',0};
3552 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3553 continue;
3555 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3556 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3557 ptr2 = strrchrW(source, '\\') + 1;
3558 msiobj_release(&row->hdr);
3560 lstrcpyW(base, package->PackagePath);
3561 ptr = strrchrW(base, '\\');
3562 *(ptr + 1) = '\0';
3564 sourcepath = msi_resolve_file_source(package, file);
3565 ptr = sourcepath + lstrlenW(base);
3566 lstrcpyW(ptr2, ptr);
3567 msi_free(sourcepath);
3569 msi_reg_set_val_str(hkey, squished_pc, source);
3571 RegCloseKey(hkey);
3573 else if (action == INSTALLSTATE_ABSENT)
3575 if (comp->num_clients <= 0)
3577 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3578 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3579 else
3580 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3582 if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
3584 else
3586 LONG res;
3588 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3589 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE );
3590 else
3591 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );
3593 if (rc != ERROR_SUCCESS)
3595 WARN( "failed to open component key %u\n", rc );
3596 continue;
3598 res = RegDeleteValueW( hkey, squished_pc );
3599 RegCloseKey(hkey);
3600 if (res) WARN( "failed to delete component value %d\n", res );
3604 /* UI stuff */
3605 uirow = MSI_CreateRecord(3);
3606 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3607 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3608 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3609 msi_ui_actiondata( package, szProcessComponents, uirow );
3610 msiobj_release( &uirow->hdr );
3612 return ERROR_SUCCESS;
3615 typedef struct {
3616 CLSID clsid;
3617 LPWSTR source;
3619 LPWSTR path;
3620 ITypeLib *ptLib;
3621 } typelib_struct;
3623 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3624 LPWSTR lpszName, LONG_PTR lParam)
3626 TLIBATTR *attr;
3627 typelib_struct *tl_struct = (typelib_struct*) lParam;
3628 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3629 int sz;
3630 HRESULT res;
3632 if (!IS_INTRESOURCE(lpszName))
3634 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3635 return TRUE;
3638 sz = strlenW(tl_struct->source)+4;
3639 sz *= sizeof(WCHAR);
3641 if ((INT_PTR)lpszName == 1)
3642 tl_struct->path = strdupW(tl_struct->source);
3643 else
3645 tl_struct->path = msi_alloc(sz);
3646 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3649 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3650 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3651 if (FAILED(res))
3653 msi_free(tl_struct->path);
3654 tl_struct->path = NULL;
3656 return TRUE;
3659 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3660 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3662 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3663 return FALSE;
3666 msi_free(tl_struct->path);
3667 tl_struct->path = NULL;
3669 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3670 ITypeLib_Release(tl_struct->ptLib);
3672 return TRUE;
3675 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3677 MSIPACKAGE* package = param;
3678 LPCWSTR component;
3679 MSICOMPONENT *comp;
3680 MSIFILE *file;
3681 typelib_struct tl_struct;
3682 ITypeLib *tlib;
3683 HMODULE module;
3684 HRESULT hr;
3686 component = MSI_RecordGetString(row,3);
3687 comp = msi_get_loaded_component(package,component);
3688 if (!comp)
3689 return ERROR_SUCCESS;
3691 comp->Action = msi_get_component_action( package, comp );
3692 if (comp->Action != INSTALLSTATE_LOCAL)
3694 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3695 return ERROR_SUCCESS;
3698 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3700 TRACE("component has no key path\n");
3701 return ERROR_SUCCESS;
3703 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3705 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3706 if (module)
3708 LPCWSTR guid;
3709 guid = MSI_RecordGetString(row,1);
3710 CLSIDFromString( guid, &tl_struct.clsid);
3711 tl_struct.source = strdupW( file->TargetPath );
3712 tl_struct.path = NULL;
3714 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3715 (LONG_PTR)&tl_struct);
3717 if (tl_struct.path)
3719 LPCWSTR helpid, help_path = NULL;
3720 HRESULT res;
3722 helpid = MSI_RecordGetString(row,6);
3724 if (helpid) help_path = msi_get_target_folder( package, helpid );
3725 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3727 if (FAILED(res))
3728 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3729 else
3730 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3732 ITypeLib_Release(tl_struct.ptLib);
3733 msi_free(tl_struct.path);
3735 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3737 FreeLibrary(module);
3738 msi_free(tl_struct.source);
3740 else
3742 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3743 if (FAILED(hr))
3745 ERR("Failed to load type library: %08x\n", hr);
3746 return ERROR_INSTALL_FAILURE;
3749 ITypeLib_Release(tlib);
3752 return ERROR_SUCCESS;
3755 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3757 static const WCHAR query[] = {
3758 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3759 '`','T','y','p','e','L','i','b','`',0};
3760 MSIQUERY *view;
3761 UINT rc;
3763 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3764 if (rc != ERROR_SUCCESS)
3765 return ERROR_SUCCESS;
3767 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3768 msiobj_release(&view->hdr);
3769 return rc;
3772 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3774 MSIPACKAGE *package = param;
3775 LPCWSTR component, guid;
3776 MSICOMPONENT *comp;
3777 GUID libid;
3778 UINT version;
3779 LCID language;
3780 SYSKIND syskind;
3781 HRESULT hr;
3783 component = MSI_RecordGetString( row, 3 );
3784 comp = msi_get_loaded_component( package, component );
3785 if (!comp)
3786 return ERROR_SUCCESS;
3788 comp->Action = msi_get_component_action( package, comp );
3789 if (comp->Action != INSTALLSTATE_ABSENT)
3791 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3792 return ERROR_SUCCESS;
3794 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3796 guid = MSI_RecordGetString( row, 1 );
3797 CLSIDFromString( guid, &libid );
3798 version = MSI_RecordGetInteger( row, 4 );
3799 language = MSI_RecordGetInteger( row, 2 );
3801 #ifdef _WIN64
3802 syskind = SYS_WIN64;
3803 #else
3804 syskind = SYS_WIN32;
3805 #endif
3807 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3808 if (FAILED(hr))
3810 WARN("Failed to unregister typelib: %08x\n", hr);
3813 return ERROR_SUCCESS;
3816 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3818 static const WCHAR query[] = {
3819 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3820 '`','T','y','p','e','L','i','b','`',0};
3821 MSIQUERY *view;
3822 UINT rc;
3824 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3825 if (rc != ERROR_SUCCESS)
3826 return ERROR_SUCCESS;
3828 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3829 msiobj_release( &view->hdr );
3830 return rc;
3833 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3835 static const WCHAR szlnk[] = {'.','l','n','k',0};
3836 LPCWSTR directory, extension, link_folder;
3837 LPWSTR link_file, filename;
3839 directory = MSI_RecordGetString( row, 2 );
3840 link_folder = msi_get_target_folder( package, directory );
3841 if (!link_folder)
3843 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3844 return NULL;
3846 /* may be needed because of a bug somewhere else */
3847 msi_create_full_path( link_folder );
3849 filename = msi_dup_record_field( row, 3 );
3850 msi_reduce_to_long_filename( filename );
3852 extension = strrchrW( filename, '.' );
3853 if (!extension || strcmpiW( extension, szlnk ))
3855 int len = strlenW( filename );
3856 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3857 memcpy( filename + len, szlnk, sizeof(szlnk) );
3859 link_file = msi_build_directory_name( 2, link_folder, filename );
3860 msi_free( filename );
3862 return link_file;
3865 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3867 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3868 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3869 WCHAR *folder, *dest, *path;
3871 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3872 folder = msi_dup_property( package->db, szWindowsFolder );
3873 else
3875 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3876 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3877 msi_free( appdata );
3879 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3880 msi_create_full_path( dest );
3881 path = msi_build_directory_name( 2, dest, icon_name );
3882 msi_free( folder );
3883 msi_free( dest );
3884 return path;
3887 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3889 MSIPACKAGE *package = param;
3890 LPWSTR link_file, deformated, path;
3891 LPCWSTR component, target;
3892 MSICOMPONENT *comp;
3893 IShellLinkW *sl = NULL;
3894 IPersistFile *pf = NULL;
3895 HRESULT res;
3897 component = MSI_RecordGetString(row, 4);
3898 comp = msi_get_loaded_component(package, component);
3899 if (!comp)
3900 return ERROR_SUCCESS;
3902 comp->Action = msi_get_component_action( package, comp );
3903 if (comp->Action != INSTALLSTATE_LOCAL)
3905 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3906 return ERROR_SUCCESS;
3908 msi_ui_actiondata( package, szCreateShortcuts, row );
3910 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3911 &IID_IShellLinkW, (LPVOID *) &sl );
3913 if (FAILED( res ))
3915 ERR("CLSID_ShellLink not available\n");
3916 goto err;
3919 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3920 if (FAILED( res ))
3922 ERR("QueryInterface(IID_IPersistFile) failed\n");
3923 goto err;
3926 target = MSI_RecordGetString(row, 5);
3927 if (strchrW(target, '['))
3929 deformat_string( package, target, &path );
3930 TRACE("target path is %s\n", debugstr_w(path));
3931 IShellLinkW_SetPath( sl, path );
3932 msi_free( path );
3934 else
3936 FIXME("poorly handled shortcut format, advertised shortcut\n");
3937 path = resolve_keypath( package, comp );
3938 IShellLinkW_SetPath( sl, path );
3939 msi_free( path );
3942 if (!MSI_RecordIsNull(row,6))
3944 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3945 deformat_string(package, arguments, &deformated);
3946 IShellLinkW_SetArguments(sl,deformated);
3947 msi_free(deformated);
3950 if (!MSI_RecordIsNull(row,7))
3952 LPCWSTR description = MSI_RecordGetString(row, 7);
3953 IShellLinkW_SetDescription(sl, description);
3956 if (!MSI_RecordIsNull(row,8))
3957 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3959 if (!MSI_RecordIsNull(row,9))
3961 INT index;
3962 LPCWSTR icon = MSI_RecordGetString(row, 9);
3964 path = msi_build_icon_path(package, icon);
3965 index = MSI_RecordGetInteger(row,10);
3967 /* no value means 0 */
3968 if (index == MSI_NULL_INTEGER)
3969 index = 0;
3971 IShellLinkW_SetIconLocation(sl, path, index);
3972 msi_free(path);
3975 if (!MSI_RecordIsNull(row,11))
3976 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3978 if (!MSI_RecordIsNull(row,12))
3980 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3981 full_path = msi_get_target_folder( package, wkdir );
3982 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3984 link_file = get_link_file(package, row);
3986 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3987 IPersistFile_Save(pf, link_file, FALSE);
3988 msi_free(link_file);
3990 err:
3991 if (pf)
3992 IPersistFile_Release( pf );
3993 if (sl)
3994 IShellLinkW_Release( sl );
3996 return ERROR_SUCCESS;
3999 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
4001 static const WCHAR query[] = {
4002 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4003 '`','S','h','o','r','t','c','u','t','`',0};
4004 MSIQUERY *view;
4005 HRESULT res;
4006 UINT rc;
4008 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4009 if (rc != ERROR_SUCCESS)
4010 return ERROR_SUCCESS;
4012 res = CoInitialize( NULL );
4014 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4015 msiobj_release(&view->hdr);
4017 if (SUCCEEDED(res)) CoUninitialize();
4018 return rc;
4021 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4023 MSIPACKAGE *package = param;
4024 LPWSTR link_file;
4025 LPCWSTR component;
4026 MSICOMPONENT *comp;
4028 component = MSI_RecordGetString( row, 4 );
4029 comp = msi_get_loaded_component( package, component );
4030 if (!comp)
4031 return ERROR_SUCCESS;
4033 comp->Action = msi_get_component_action( package, comp );
4034 if (comp->Action != INSTALLSTATE_ABSENT)
4036 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4037 return ERROR_SUCCESS;
4039 msi_ui_actiondata( package, szRemoveShortcuts, row );
4041 link_file = get_link_file( package, row );
4043 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4044 if (!DeleteFileW( link_file ))
4046 WARN("Failed to remove shortcut file %u\n", GetLastError());
4048 msi_free( link_file );
4050 return ERROR_SUCCESS;
4053 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4055 static const WCHAR query[] = {
4056 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4057 '`','S','h','o','r','t','c','u','t','`',0};
4058 MSIQUERY *view;
4059 UINT rc;
4061 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4062 if (rc != ERROR_SUCCESS)
4063 return ERROR_SUCCESS;
4065 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4066 msiobj_release( &view->hdr );
4067 return rc;
4070 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4072 MSIPACKAGE* package = param;
4073 HANDLE the_file;
4074 LPWSTR FilePath;
4075 LPCWSTR FileName;
4076 CHAR buffer[1024];
4077 DWORD sz;
4078 UINT rc;
4080 FileName = MSI_RecordGetString(row,1);
4081 if (!FileName)
4083 ERR("Unable to get FileName\n");
4084 return ERROR_SUCCESS;
4087 FilePath = msi_build_icon_path(package, FileName);
4089 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4091 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4092 FILE_ATTRIBUTE_NORMAL, NULL);
4094 if (the_file == INVALID_HANDLE_VALUE)
4096 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4097 msi_free(FilePath);
4098 return ERROR_SUCCESS;
4103 DWORD write;
4104 sz = 1024;
4105 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4106 if (rc != ERROR_SUCCESS)
4108 ERR("Failed to get stream\n");
4109 CloseHandle(the_file);
4110 DeleteFileW(FilePath);
4111 break;
4113 WriteFile(the_file,buffer,sz,&write,NULL);
4114 } while (sz == 1024);
4116 msi_free(FilePath);
4117 CloseHandle(the_file);
4119 return ERROR_SUCCESS;
4122 static UINT msi_publish_icons(MSIPACKAGE *package)
4124 static const WCHAR query[]= {
4125 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4126 '`','I','c','o','n','`',0};
4127 MSIQUERY *view;
4128 UINT r;
4130 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4131 if (r == ERROR_SUCCESS)
4133 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4134 msiobj_release(&view->hdr);
4135 if (r != ERROR_SUCCESS)
4136 return r;
4138 return ERROR_SUCCESS;
4141 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4143 UINT r;
4144 HKEY source;
4145 LPWSTR buffer;
4146 MSIMEDIADISK *disk;
4147 MSISOURCELISTINFO *info;
4149 r = RegCreateKeyW(hkey, szSourceList, &source);
4150 if (r != ERROR_SUCCESS)
4151 return r;
4153 RegCloseKey(source);
4155 buffer = strrchrW(package->PackagePath, '\\') + 1;
4156 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4157 package->Context, MSICODE_PRODUCT,
4158 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4159 if (r != ERROR_SUCCESS)
4160 return r;
4162 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4163 package->Context, MSICODE_PRODUCT,
4164 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4165 if (r != ERROR_SUCCESS)
4166 return r;
4168 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4169 package->Context, MSICODE_PRODUCT,
4170 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4171 if (r != ERROR_SUCCESS)
4172 return r;
4174 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4176 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4177 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4178 info->options, info->value);
4179 else
4180 MsiSourceListSetInfoW(package->ProductCode, NULL,
4181 info->context, info->options,
4182 info->property, info->value);
4185 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4187 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4188 disk->context, disk->options,
4189 disk->disk_id, disk->volume_label, disk->disk_prompt);
4192 return ERROR_SUCCESS;
4195 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4197 MSIHANDLE hdb, suminfo;
4198 WCHAR guids[MAX_PATH];
4199 WCHAR packcode[SQUISH_GUID_SIZE];
4200 LPWSTR buffer;
4201 LPWSTR ptr;
4202 DWORD langid;
4203 DWORD size;
4204 UINT r;
4206 static const WCHAR szARPProductIcon[] =
4207 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4208 static const WCHAR szAssignment[] =
4209 {'A','s','s','i','g','n','m','e','n','t',0};
4210 static const WCHAR szAdvertiseFlags[] =
4211 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4212 static const WCHAR szClients[] =
4213 {'C','l','i','e','n','t','s',0};
4214 static const WCHAR szColon[] = {':',0};
4216 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4217 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4218 msi_free(buffer);
4220 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4221 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4223 /* FIXME */
4224 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4226 buffer = msi_dup_property(package->db, szARPProductIcon);
4227 if (buffer)
4229 LPWSTR path = msi_build_icon_path(package, buffer);
4230 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4231 msi_free(path);
4232 msi_free(buffer);
4235 buffer = msi_dup_property(package->db, szProductVersion);
4236 if (buffer)
4238 DWORD verdword = msi_version_str_to_dword(buffer);
4239 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4240 msi_free(buffer);
4243 msi_reg_set_val_dword(hkey, szAssignment, 0);
4244 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4245 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4246 msi_reg_set_val_str(hkey, szClients, szColon);
4248 hdb = alloc_msihandle(&package->db->hdr);
4249 if (!hdb)
4250 return ERROR_NOT_ENOUGH_MEMORY;
4252 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4253 MsiCloseHandle(hdb);
4254 if (r != ERROR_SUCCESS)
4255 goto done;
4257 size = MAX_PATH;
4258 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4259 NULL, guids, &size);
4260 if (r != ERROR_SUCCESS)
4261 goto done;
4263 ptr = strchrW(guids, ';');
4264 if (ptr) *ptr = 0;
4265 squash_guid(guids, packcode);
4266 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4268 done:
4269 MsiCloseHandle(suminfo);
4270 return ERROR_SUCCESS;
4273 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4275 UINT r;
4276 HKEY hkey;
4277 LPWSTR upgrade;
4278 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4280 upgrade = msi_dup_property(package->db, szUpgradeCode);
4281 if (!upgrade)
4282 return ERROR_SUCCESS;
4284 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4285 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4286 else
4287 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4289 if (r != ERROR_SUCCESS)
4291 WARN("failed to open upgrade code key\n");
4292 msi_free(upgrade);
4293 return ERROR_SUCCESS;
4295 squash_guid(package->ProductCode, squashed_pc);
4296 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4297 RegCloseKey(hkey);
4298 msi_free(upgrade);
4299 return ERROR_SUCCESS;
4302 static BOOL msi_check_publish(MSIPACKAGE *package)
4304 MSIFEATURE *feature;
4306 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4308 feature->Action = msi_get_feature_action( package, feature );
4309 if (feature->Action == INSTALLSTATE_LOCAL)
4310 return TRUE;
4313 return FALSE;
4316 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4318 MSIFEATURE *feature;
4320 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4322 feature->Action = msi_get_feature_action( package, feature );
4323 if (feature->Action != INSTALLSTATE_ABSENT)
4324 return FALSE;
4327 return TRUE;
4330 static UINT msi_publish_patches( MSIPACKAGE *package )
4332 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4333 WCHAR patch_squashed[GUID_SIZE];
4334 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4335 LONG res;
4336 MSIPATCHINFO *patch;
4337 UINT r;
4338 WCHAR *p, *all_patches = NULL;
4339 DWORD len = 0;
4341 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4342 if (r != ERROR_SUCCESS)
4343 return ERROR_FUNCTION_FAILED;
4345 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4346 if (res != ERROR_SUCCESS)
4348 r = ERROR_FUNCTION_FAILED;
4349 goto done;
4352 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4353 if (r != ERROR_SUCCESS)
4354 goto done;
4356 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4358 squash_guid( patch->patchcode, patch_squashed );
4359 len += strlenW( patch_squashed ) + 1;
4362 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4363 if (!all_patches)
4364 goto done;
4366 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4368 HKEY patch_key;
4370 squash_guid( patch->patchcode, p );
4371 p += strlenW( p ) + 1;
4373 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4374 (const BYTE *)patch->transforms,
4375 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4376 if (res != ERROR_SUCCESS)
4377 goto done;
4379 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4380 if (r != ERROR_SUCCESS)
4381 goto done;
4383 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4384 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4385 RegCloseKey( patch_key );
4386 if (res != ERROR_SUCCESS)
4387 goto done;
4389 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4391 res = GetLastError();
4392 ERR("Unable to copy patch package %d\n", res);
4393 goto done;
4395 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4396 if (res != ERROR_SUCCESS)
4397 goto done;
4399 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4400 RegCloseKey( patch_key );
4401 if (res != ERROR_SUCCESS)
4402 goto done;
4405 all_patches[len] = 0;
4406 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4407 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4408 if (res != ERROR_SUCCESS)
4409 goto done;
4411 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4412 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4413 if (res != ERROR_SUCCESS)
4414 r = ERROR_FUNCTION_FAILED;
4416 done:
4417 RegCloseKey( product_patches_key );
4418 RegCloseKey( patches_key );
4419 RegCloseKey( product_key );
4420 msi_free( all_patches );
4421 return r;
4424 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4426 UINT rc;
4427 HKEY hukey = NULL, hudkey = NULL;
4428 MSIRECORD *uirow;
4430 if (!list_empty(&package->patches))
4432 rc = msi_publish_patches(package);
4433 if (rc != ERROR_SUCCESS)
4434 goto end;
4437 /* FIXME: also need to publish if the product is in advertise mode */
4438 if (!msi_check_publish(package))
4439 return ERROR_SUCCESS;
4441 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4442 &hukey, TRUE);
4443 if (rc != ERROR_SUCCESS)
4444 goto end;
4446 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4447 NULL, &hudkey, TRUE);
4448 if (rc != ERROR_SUCCESS)
4449 goto end;
4451 rc = msi_publish_upgrade_code(package);
4452 if (rc != ERROR_SUCCESS)
4453 goto end;
4455 rc = msi_publish_product_properties(package, hukey);
4456 if (rc != ERROR_SUCCESS)
4457 goto end;
4459 rc = msi_publish_sourcelist(package, hukey);
4460 if (rc != ERROR_SUCCESS)
4461 goto end;
4463 rc = msi_publish_icons(package);
4465 end:
4466 uirow = MSI_CreateRecord( 1 );
4467 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4468 msi_ui_actiondata( package, szPublishProduct, uirow );
4469 msiobj_release( &uirow->hdr );
4471 RegCloseKey(hukey);
4472 RegCloseKey(hudkey);
4473 return rc;
4476 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4478 WCHAR *filename, *ptr, *folder, *ret;
4479 const WCHAR *dirprop;
4481 filename = msi_dup_record_field( row, 2 );
4482 if (filename && (ptr = strchrW( filename, '|' )))
4483 ptr++;
4484 else
4485 ptr = filename;
4487 dirprop = MSI_RecordGetString( row, 3 );
4488 if (dirprop)
4490 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4491 if (!folder) folder = msi_dup_property( package->db, dirprop );
4493 else
4494 folder = msi_dup_property( package->db, szWindowsFolder );
4496 if (!folder)
4498 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4499 msi_free( filename );
4500 return NULL;
4503 ret = msi_build_directory_name( 2, folder, ptr );
4505 msi_free( filename );
4506 msi_free( folder );
4507 return ret;
4510 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4512 MSIPACKAGE *package = param;
4513 LPCWSTR component, section, key, value, identifier;
4514 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4515 MSIRECORD * uirow;
4516 INT action;
4517 MSICOMPONENT *comp;
4519 component = MSI_RecordGetString(row, 8);
4520 comp = msi_get_loaded_component(package,component);
4521 if (!comp)
4522 return ERROR_SUCCESS;
4524 comp->Action = msi_get_component_action( package, comp );
4525 if (comp->Action != INSTALLSTATE_LOCAL)
4527 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4528 return ERROR_SUCCESS;
4531 identifier = MSI_RecordGetString(row,1);
4532 section = MSI_RecordGetString(row,4);
4533 key = MSI_RecordGetString(row,5);
4534 value = MSI_RecordGetString(row,6);
4535 action = MSI_RecordGetInteger(row,7);
4537 deformat_string(package,section,&deformated_section);
4538 deformat_string(package,key,&deformated_key);
4539 deformat_string(package,value,&deformated_value);
4541 fullname = get_ini_file_name(package, row);
4543 if (action == 0)
4545 TRACE("Adding value %s to section %s in %s\n",
4546 debugstr_w(deformated_key), debugstr_w(deformated_section),
4547 debugstr_w(fullname));
4548 WritePrivateProfileStringW(deformated_section, deformated_key,
4549 deformated_value, fullname);
4551 else if (action == 1)
4553 WCHAR returned[10];
4554 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4555 returned, 10, fullname);
4556 if (returned[0] == 0)
4558 TRACE("Adding value %s to section %s in %s\n",
4559 debugstr_w(deformated_key), debugstr_w(deformated_section),
4560 debugstr_w(fullname));
4562 WritePrivateProfileStringW(deformated_section, deformated_key,
4563 deformated_value, fullname);
4566 else if (action == 3)
4567 FIXME("Append to existing section not yet implemented\n");
4569 uirow = MSI_CreateRecord(4);
4570 MSI_RecordSetStringW(uirow,1,identifier);
4571 MSI_RecordSetStringW(uirow,2,deformated_section);
4572 MSI_RecordSetStringW(uirow,3,deformated_key);
4573 MSI_RecordSetStringW(uirow,4,deformated_value);
4574 msi_ui_actiondata( package, szWriteIniValues, uirow );
4575 msiobj_release( &uirow->hdr );
4577 msi_free(fullname);
4578 msi_free(deformated_key);
4579 msi_free(deformated_value);
4580 msi_free(deformated_section);
4581 return ERROR_SUCCESS;
4584 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4586 static const WCHAR query[] = {
4587 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4588 '`','I','n','i','F','i','l','e','`',0};
4589 MSIQUERY *view;
4590 UINT rc;
4592 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4593 if (rc != ERROR_SUCCESS)
4594 return ERROR_SUCCESS;
4596 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4597 msiobj_release(&view->hdr);
4598 return rc;
4601 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4603 MSIPACKAGE *package = param;
4604 LPCWSTR component, section, key, value, identifier;
4605 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4606 MSICOMPONENT *comp;
4607 MSIRECORD *uirow;
4608 INT action;
4610 component = MSI_RecordGetString( row, 8 );
4611 comp = msi_get_loaded_component( package, component );
4612 if (!comp)
4613 return ERROR_SUCCESS;
4615 comp->Action = msi_get_component_action( package, comp );
4616 if (comp->Action != INSTALLSTATE_ABSENT)
4618 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4619 return ERROR_SUCCESS;
4622 identifier = MSI_RecordGetString( row, 1 );
4623 section = MSI_RecordGetString( row, 4 );
4624 key = MSI_RecordGetString( row, 5 );
4625 value = MSI_RecordGetString( row, 6 );
4626 action = MSI_RecordGetInteger( row, 7 );
4628 deformat_string( package, section, &deformated_section );
4629 deformat_string( package, key, &deformated_key );
4630 deformat_string( package, value, &deformated_value );
4632 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4634 filename = get_ini_file_name( package, row );
4636 TRACE("Removing key %s from section %s in %s\n",
4637 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4639 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4641 WARN("Unable to remove key %u\n", GetLastError());
4643 msi_free( filename );
4645 else
4646 FIXME("Unsupported action %d\n", action);
4649 uirow = MSI_CreateRecord( 4 );
4650 MSI_RecordSetStringW( uirow, 1, identifier );
4651 MSI_RecordSetStringW( uirow, 2, deformated_section );
4652 MSI_RecordSetStringW( uirow, 3, deformated_key );
4653 MSI_RecordSetStringW( uirow, 4, deformated_value );
4654 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4655 msiobj_release( &uirow->hdr );
4657 msi_free( deformated_key );
4658 msi_free( deformated_value );
4659 msi_free( deformated_section );
4660 return ERROR_SUCCESS;
4663 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4665 MSIPACKAGE *package = param;
4666 LPCWSTR component, section, key, value, identifier;
4667 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4668 MSICOMPONENT *comp;
4669 MSIRECORD *uirow;
4670 INT action;
4672 component = MSI_RecordGetString( row, 8 );
4673 comp = msi_get_loaded_component( package, component );
4674 if (!comp)
4675 return ERROR_SUCCESS;
4677 comp->Action = msi_get_component_action( package, comp );
4678 if (comp->Action != INSTALLSTATE_LOCAL)
4680 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4681 return ERROR_SUCCESS;
4684 identifier = MSI_RecordGetString( row, 1 );
4685 section = MSI_RecordGetString( row, 4 );
4686 key = MSI_RecordGetString( row, 5 );
4687 value = MSI_RecordGetString( row, 6 );
4688 action = MSI_RecordGetInteger( row, 7 );
4690 deformat_string( package, section, &deformated_section );
4691 deformat_string( package, key, &deformated_key );
4692 deformat_string( package, value, &deformated_value );
4694 if (action == msidbIniFileActionRemoveLine)
4696 filename = get_ini_file_name( package, row );
4698 TRACE("Removing key %s from section %s in %s\n",
4699 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4701 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4703 WARN("Unable to remove key %u\n", GetLastError());
4705 msi_free( filename );
4707 else
4708 FIXME("Unsupported action %d\n", action);
4710 uirow = MSI_CreateRecord( 4 );
4711 MSI_RecordSetStringW( uirow, 1, identifier );
4712 MSI_RecordSetStringW( uirow, 2, deformated_section );
4713 MSI_RecordSetStringW( uirow, 3, deformated_key );
4714 MSI_RecordSetStringW( uirow, 4, deformated_value );
4715 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4716 msiobj_release( &uirow->hdr );
4718 msi_free( deformated_key );
4719 msi_free( deformated_value );
4720 msi_free( deformated_section );
4721 return ERROR_SUCCESS;
4724 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4726 static const WCHAR query[] = {
4727 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4728 '`','I','n','i','F','i','l','e','`',0};
4729 static const WCHAR remove_query[] = {
4730 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4731 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4732 MSIQUERY *view;
4733 UINT rc;
4735 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4736 if (rc == ERROR_SUCCESS)
4738 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4739 msiobj_release( &view->hdr );
4740 if (rc != ERROR_SUCCESS)
4741 return rc;
4743 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4744 if (rc == ERROR_SUCCESS)
4746 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4747 msiobj_release( &view->hdr );
4748 if (rc != ERROR_SUCCESS)
4749 return rc;
4751 return ERROR_SUCCESS;
4754 static void register_dll( const WCHAR *dll, BOOL unregister )
4756 static const WCHAR regW[] =
4757 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4758 static const WCHAR unregW[] =
4759 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4760 PROCESS_INFORMATION pi;
4761 STARTUPINFOW si;
4762 WCHAR *cmd;
4764 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4766 if (unregister) sprintfW( cmd, unregW, dll );
4767 else sprintfW( cmd, regW, dll );
4769 memset( &si, 0, sizeof(STARTUPINFOW) );
4770 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4772 CloseHandle( pi.hThread );
4773 msi_dialog_check_messages( pi.hProcess );
4774 CloseHandle( pi.hProcess );
4776 msi_free( cmd );
4779 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4781 MSIPACKAGE *package = param;
4782 LPCWSTR filename;
4783 MSIFILE *file;
4784 MSIRECORD *uirow;
4786 filename = MSI_RecordGetString( row, 1 );
4787 file = msi_get_loaded_file( package, filename );
4788 if (!file)
4790 WARN("unable to find file %s\n", debugstr_w(filename));
4791 return ERROR_SUCCESS;
4793 file->Component->Action = msi_get_component_action( package, file->Component );
4794 if (file->Component->Action != INSTALLSTATE_LOCAL)
4796 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4797 return ERROR_SUCCESS;
4800 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4801 register_dll( file->TargetPath, FALSE );
4803 uirow = MSI_CreateRecord( 2 );
4804 MSI_RecordSetStringW( uirow, 1, file->File );
4805 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4806 msi_ui_actiondata( package, szSelfRegModules, uirow );
4807 msiobj_release( &uirow->hdr );
4809 return ERROR_SUCCESS;
4812 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4814 static const WCHAR query[] = {
4815 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4816 '`','S','e','l','f','R','e','g','`',0};
4817 MSIQUERY *view;
4818 UINT rc;
4820 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4821 if (rc != ERROR_SUCCESS)
4822 return ERROR_SUCCESS;
4824 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4825 msiobj_release(&view->hdr);
4826 return rc;
4829 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4831 MSIPACKAGE *package = param;
4832 LPCWSTR filename;
4833 MSIFILE *file;
4834 MSIRECORD *uirow;
4836 filename = MSI_RecordGetString( row, 1 );
4837 file = msi_get_loaded_file( package, filename );
4838 if (!file)
4840 WARN("unable to find file %s\n", debugstr_w(filename));
4841 return ERROR_SUCCESS;
4843 file->Component->Action = msi_get_component_action( package, file->Component );
4844 if (file->Component->Action != INSTALLSTATE_ABSENT)
4846 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4847 return ERROR_SUCCESS;
4850 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4851 register_dll( file->TargetPath, TRUE );
4853 uirow = MSI_CreateRecord( 2 );
4854 MSI_RecordSetStringW( uirow, 1, file->File );
4855 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4856 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4857 msiobj_release( &uirow->hdr );
4859 return ERROR_SUCCESS;
4862 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4864 static const WCHAR query[] = {
4865 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4866 '`','S','e','l','f','R','e','g','`',0};
4867 MSIQUERY *view;
4868 UINT rc;
4870 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4871 if (rc != ERROR_SUCCESS)
4872 return ERROR_SUCCESS;
4874 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4875 msiobj_release( &view->hdr );
4876 return rc;
4879 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4881 MSIFEATURE *feature;
4882 UINT rc;
4883 HKEY hkey = NULL, userdata = NULL;
4885 if (!msi_check_publish(package))
4886 return ERROR_SUCCESS;
4888 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4889 &hkey, TRUE);
4890 if (rc != ERROR_SUCCESS)
4891 goto end;
4893 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4894 &userdata, TRUE);
4895 if (rc != ERROR_SUCCESS)
4896 goto end;
4898 /* here the guids are base 85 encoded */
4899 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4901 ComponentList *cl;
4902 LPWSTR data = NULL;
4903 GUID clsid;
4904 INT size;
4905 BOOL absent = FALSE;
4906 MSIRECORD *uirow;
4908 if (feature->Level <= 0) continue;
4910 if (feature->Action != INSTALLSTATE_LOCAL &&
4911 feature->Action != INSTALLSTATE_SOURCE &&
4912 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4914 size = 1;
4915 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4917 size += 21;
4919 if (feature->Feature_Parent)
4920 size += strlenW( feature->Feature_Parent )+2;
4922 data = msi_alloc(size * sizeof(WCHAR));
4924 data[0] = 0;
4925 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4927 MSICOMPONENT* component = cl->component;
4928 WCHAR buf[21];
4930 buf[0] = 0;
4931 if (component->ComponentId)
4933 TRACE("From %s\n",debugstr_w(component->ComponentId));
4934 CLSIDFromString(component->ComponentId, &clsid);
4935 encode_base85_guid(&clsid,buf);
4936 TRACE("to %s\n",debugstr_w(buf));
4937 strcatW(data,buf);
4941 if (feature->Feature_Parent)
4943 static const WCHAR sep[] = {'\2',0};
4944 strcatW(data,sep);
4945 strcatW(data,feature->Feature_Parent);
4948 msi_reg_set_val_str( userdata, feature->Feature, data );
4949 msi_free(data);
4951 size = 0;
4952 if (feature->Feature_Parent)
4953 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4954 if (!absent)
4956 size += sizeof(WCHAR);
4957 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4958 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4960 else
4962 size += 2*sizeof(WCHAR);
4963 data = msi_alloc(size);
4964 data[0] = 0x6;
4965 data[1] = 0;
4966 if (feature->Feature_Parent)
4967 strcpyW( &data[1], feature->Feature_Parent );
4968 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4969 (LPBYTE)data,size);
4970 msi_free(data);
4973 /* the UI chunk */
4974 uirow = MSI_CreateRecord( 1 );
4975 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4976 msi_ui_actiondata( package, szPublishFeatures, uirow );
4977 msiobj_release( &uirow->hdr );
4978 /* FIXME: call msi_ui_progress? */
4981 end:
4982 RegCloseKey(hkey);
4983 RegCloseKey(userdata);
4984 return rc;
4987 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4989 UINT r;
4990 HKEY hkey;
4991 MSIRECORD *uirow;
4993 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4995 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4996 &hkey, FALSE);
4997 if (r == ERROR_SUCCESS)
4999 RegDeleteValueW(hkey, feature->Feature);
5000 RegCloseKey(hkey);
5003 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
5004 &hkey, FALSE);
5005 if (r == ERROR_SUCCESS)
5007 RegDeleteValueW(hkey, feature->Feature);
5008 RegCloseKey(hkey);
5011 uirow = MSI_CreateRecord( 1 );
5012 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5013 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
5014 msiobj_release( &uirow->hdr );
5016 return ERROR_SUCCESS;
5019 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5021 MSIFEATURE *feature;
5023 if (!msi_check_unpublish(package))
5024 return ERROR_SUCCESS;
5026 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5028 msi_unpublish_feature(package, feature);
5031 return ERROR_SUCCESS;
5034 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5036 SYSTEMTIME systime;
5037 DWORD size, langid;
5038 WCHAR date[9], *val, *buffer;
5039 const WCHAR *prop, *key;
5041 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5042 static const WCHAR modpath_fmt[] =
5043 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5044 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5045 static const WCHAR szModifyPath[] =
5046 {'M','o','d','i','f','y','P','a','t','h',0};
5047 static const WCHAR szUninstallString[] =
5048 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5049 static const WCHAR szEstimatedSize[] =
5050 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5051 static const WCHAR szDisplayVersion[] =
5052 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5053 static const WCHAR szInstallSource[] =
5054 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5055 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5056 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5057 static const WCHAR szAuthorizedCDFPrefix[] =
5058 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5059 static const WCHAR szARPCONTACT[] =
5060 {'A','R','P','C','O','N','T','A','C','T',0};
5061 static const WCHAR szContact[] =
5062 {'C','o','n','t','a','c','t',0};
5063 static const WCHAR szARPCOMMENTS[] =
5064 {'A','R','P','C','O','M','M','E','N','T','S',0};
5065 static const WCHAR szComments[] =
5066 {'C','o','m','m','e','n','t','s',0};
5067 static const WCHAR szProductName[] =
5068 {'P','r','o','d','u','c','t','N','a','m','e',0};
5069 static const WCHAR szDisplayName[] =
5070 {'D','i','s','p','l','a','y','N','a','m','e',0};
5071 static const WCHAR szARPHELPLINK[] =
5072 {'A','R','P','H','E','L','P','L','I','N','K',0};
5073 static const WCHAR szHelpLink[] =
5074 {'H','e','l','p','L','i','n','k',0};
5075 static const WCHAR szARPHELPTELEPHONE[] =
5076 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5077 static const WCHAR szHelpTelephone[] =
5078 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5079 static const WCHAR szARPINSTALLLOCATION[] =
5080 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5081 static const WCHAR szManufacturer[] =
5082 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5083 static const WCHAR szPublisher[] =
5084 {'P','u','b','l','i','s','h','e','r',0};
5085 static const WCHAR szARPREADME[] =
5086 {'A','R','P','R','E','A','D','M','E',0};
5087 static const WCHAR szReadme[] =
5088 {'R','e','a','d','M','e',0};
5089 static const WCHAR szARPSIZE[] =
5090 {'A','R','P','S','I','Z','E',0};
5091 static const WCHAR szSize[] =
5092 {'S','i','z','e',0};
5093 static const WCHAR szARPURLINFOABOUT[] =
5094 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5095 static const WCHAR szURLInfoAbout[] =
5096 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5097 static const WCHAR szARPURLUPDATEINFO[] =
5098 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5099 static const WCHAR szURLUpdateInfo[] =
5100 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5101 static const WCHAR szARPSYSTEMCOMPONENT[] =
5102 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5103 static const WCHAR szSystemComponent[] =
5104 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5106 static const WCHAR *propval[] = {
5107 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5108 szARPCONTACT, szContact,
5109 szARPCOMMENTS, szComments,
5110 szProductName, szDisplayName,
5111 szARPHELPLINK, szHelpLink,
5112 szARPHELPTELEPHONE, szHelpTelephone,
5113 szARPINSTALLLOCATION, szInstallLocation,
5114 szSourceDir, szInstallSource,
5115 szManufacturer, szPublisher,
5116 szARPREADME, szReadme,
5117 szARPSIZE, szSize,
5118 szARPURLINFOABOUT, szURLInfoAbout,
5119 szARPURLUPDATEINFO, szURLUpdateInfo,
5120 NULL
5122 const WCHAR **p = propval;
5124 while (*p)
5126 prop = *p++;
5127 key = *p++;
5128 val = msi_dup_property(package->db, prop);
5129 msi_reg_set_val_str(hkey, key, val);
5130 msi_free(val);
5133 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5134 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5136 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5138 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5139 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5140 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5141 msi_free(buffer);
5143 /* FIXME: Write real Estimated Size when we have it */
5144 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5146 GetLocalTime(&systime);
5147 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5148 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5150 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5151 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5153 buffer = msi_dup_property(package->db, szProductVersion);
5154 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5155 if (buffer)
5157 DWORD verdword = msi_version_str_to_dword(buffer);
5159 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5160 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5161 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5162 msi_free(buffer);
5165 return ERROR_SUCCESS;
5168 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5170 WCHAR squashed_pc[SQUISH_GUID_SIZE];
5171 MSIRECORD *uirow;
5172 LPWSTR upgrade_code;
5173 HKEY hkey, props, upgrade_key;
5174 UINT rc;
5176 /* FIXME: also need to publish if the product is in advertise mode */
5177 if (!msi_check_publish(package))
5178 return ERROR_SUCCESS;
5180 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5181 if (rc != ERROR_SUCCESS)
5182 return rc;
5184 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5185 if (rc != ERROR_SUCCESS)
5186 goto done;
5188 rc = msi_publish_install_properties(package, hkey);
5189 if (rc != ERROR_SUCCESS)
5190 goto done;
5192 rc = msi_publish_install_properties(package, props);
5193 if (rc != ERROR_SUCCESS)
5194 goto done;
5196 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5197 if (upgrade_code)
5199 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5200 if (rc == ERROR_SUCCESS)
5202 squash_guid( package->ProductCode, squashed_pc );
5203 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5204 RegCloseKey( upgrade_key );
5206 msi_free( upgrade_code );
5208 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5209 package->delete_on_close = FALSE;
5211 done:
5212 uirow = MSI_CreateRecord( 1 );
5213 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5214 msi_ui_actiondata( package, szRegisterProduct, uirow );
5215 msiobj_release( &uirow->hdr );
5217 RegCloseKey(hkey);
5218 return ERROR_SUCCESS;
5221 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5223 return execute_script(package, SCRIPT_INSTALL);
5226 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5228 MSIPACKAGE *package = param;
5229 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5230 WCHAR *p, *icon_path;
5232 if (!icon) return ERROR_SUCCESS;
5233 if ((icon_path = msi_build_icon_path( package, icon )))
5235 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5236 DeleteFileW( icon_path );
5237 if ((p = strrchrW( icon_path, '\\' )))
5239 *p = 0;
5240 RemoveDirectoryW( icon_path );
5242 msi_free( icon_path );
5244 return ERROR_SUCCESS;
5247 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5249 static const WCHAR query[]= {
5250 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5251 MSIQUERY *view;
5252 UINT r;
5254 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5255 if (r == ERROR_SUCCESS)
5257 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5258 msiobj_release( &view->hdr );
5259 if (r != ERROR_SUCCESS)
5260 return r;
5262 return ERROR_SUCCESS;
5265 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
5267 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
5268 WCHAR *upgrade, **features;
5269 BOOL full_uninstall = TRUE;
5270 MSIFEATURE *feature;
5271 MSIPATCHINFO *patch;
5272 UINT i;
5274 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5276 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
5278 features = msi_split_string( remove, ',' );
5279 for (i = 0; features && features[i]; i++)
5281 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
5283 msi_free(features);
5285 if (!full_uninstall)
5286 return ERROR_SUCCESS;
5288 MSIREG_DeleteProductKey(package->ProductCode);
5289 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5290 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5292 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5293 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5294 MSIREG_DeleteUserProductKey(package->ProductCode);
5295 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5297 upgrade = msi_dup_property(package->db, szUpgradeCode);
5298 if (upgrade)
5300 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5301 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5302 msi_free(upgrade);
5305 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5307 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5308 if (!strcmpW( package->ProductCode, patch->products ))
5310 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5311 patch->delete_on_close = TRUE;
5313 /* FIXME: remove local patch package if this is the last product */
5315 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5316 package->delete_on_close = TRUE;
5318 msi_unpublish_icons( package );
5319 return ERROR_SUCCESS;
5322 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5324 UINT rc;
5325 WCHAR *remove;
5327 /* first do the same as an InstallExecute */
5328 rc = execute_script(package, SCRIPT_INSTALL);
5329 if (rc != ERROR_SUCCESS)
5330 return rc;
5332 /* then handle commit actions */
5333 rc = execute_script(package, SCRIPT_COMMIT);
5334 if (rc != ERROR_SUCCESS)
5335 return rc;
5337 remove = msi_dup_property(package->db, szRemove);
5338 rc = msi_unpublish_product(package, remove);
5339 msi_free(remove);
5340 return rc;
5343 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5345 static const WCHAR RunOnce[] = {
5346 'S','o','f','t','w','a','r','e','\\',
5347 'M','i','c','r','o','s','o','f','t','\\',
5348 'W','i','n','d','o','w','s','\\',
5349 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5350 'R','u','n','O','n','c','e',0};
5351 static const WCHAR InstallRunOnce[] = {
5352 'S','o','f','t','w','a','r','e','\\',
5353 'M','i','c','r','o','s','o','f','t','\\',
5354 'W','i','n','d','o','w','s','\\',
5355 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5356 'I','n','s','t','a','l','l','e','r','\\',
5357 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5359 static const WCHAR msiexec_fmt[] = {
5360 '%','s',
5361 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5362 '\"','%','s','\"',0};
5363 static const WCHAR install_fmt[] = {
5364 '/','I',' ','\"','%','s','\"',' ',
5365 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5366 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5367 WCHAR buffer[256], sysdir[MAX_PATH];
5368 HKEY hkey;
5369 WCHAR squished_pc[100];
5371 squash_guid(package->ProductCode,squished_pc);
5373 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5374 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5375 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5376 squished_pc);
5378 msi_reg_set_val_str( hkey, squished_pc, buffer );
5379 RegCloseKey(hkey);
5381 TRACE("Reboot command %s\n",debugstr_w(buffer));
5383 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5384 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5386 msi_reg_set_val_str( hkey, squished_pc, buffer );
5387 RegCloseKey(hkey);
5389 return ERROR_INSTALL_SUSPEND;
5392 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5394 static const WCHAR query[] =
5395 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5396 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5397 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5398 MSIRECORD *rec, *row;
5399 DWORD i, size = 0;
5400 va_list va;
5401 const WCHAR *str;
5402 WCHAR *data;
5404 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5406 rec = MSI_CreateRecord( count + 2 );
5407 str = MSI_RecordGetString( row, 1 );
5408 MSI_RecordSetStringW( rec, 0, str );
5409 msiobj_release( &row->hdr );
5410 MSI_RecordSetInteger( rec, 1, error );
5412 va_start( va, count );
5413 for (i = 0; i < count; i++)
5415 str = va_arg( va, const WCHAR *);
5416 MSI_RecordSetStringW( rec, i + 2, str );
5418 va_end( va );
5420 MSI_FormatRecordW( package, rec, NULL, &size );
5421 size++;
5422 data = msi_alloc( size * sizeof(WCHAR) );
5423 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5424 else data[0] = 0;
5425 msiobj_release( &rec->hdr );
5426 return data;
5429 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5431 DWORD attrib;
5432 UINT rc;
5435 * We are currently doing what should be done here in the top level Install
5436 * however for Administrative and uninstalls this step will be needed
5438 if (!package->PackagePath)
5439 return ERROR_SUCCESS;
5441 msi_set_sourcedir_props(package, TRUE);
5443 attrib = GetFileAttributesW(package->db->path);
5444 if (attrib == INVALID_FILE_ATTRIBUTES)
5446 LPWSTR prompt, msg;
5447 DWORD size = 0;
5449 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5450 package->Context, MSICODE_PRODUCT,
5451 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5452 if (rc == ERROR_MORE_DATA)
5454 prompt = msi_alloc(size * sizeof(WCHAR));
5455 MsiSourceListGetInfoW(package->ProductCode, NULL,
5456 package->Context, MSICODE_PRODUCT,
5457 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5459 else
5460 prompt = strdupW(package->db->path);
5462 msg = msi_build_error_string(package, 1302, 1, prompt);
5463 msi_free(prompt);
5464 while(attrib == INVALID_FILE_ATTRIBUTES)
5466 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5467 if (rc == IDCANCEL)
5469 msi_free(msg);
5470 return ERROR_INSTALL_USEREXIT;
5472 attrib = GetFileAttributesW(package->db->path);
5474 msi_free(msg);
5475 rc = ERROR_SUCCESS;
5477 else
5478 return ERROR_SUCCESS;
5480 return rc;
5483 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5485 HKEY hkey = 0;
5486 LPWSTR buffer, productid = NULL;
5487 UINT i, rc = ERROR_SUCCESS;
5488 MSIRECORD *uirow;
5490 static const WCHAR szPropKeys[][80] =
5492 {'P','r','o','d','u','c','t','I','D',0},
5493 {'U','S','E','R','N','A','M','E',0},
5494 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5495 {0},
5498 static const WCHAR szRegKeys[][80] =
5500 {'P','r','o','d','u','c','t','I','D',0},
5501 {'R','e','g','O','w','n','e','r',0},
5502 {'R','e','g','C','o','m','p','a','n','y',0},
5503 {0},
5506 if (msi_check_unpublish(package))
5508 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5509 goto end;
5512 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5513 if (!productid)
5514 goto end;
5516 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5517 NULL, &hkey, TRUE);
5518 if (rc != ERROR_SUCCESS)
5519 goto end;
5521 for( i = 0; szPropKeys[i][0]; i++ )
5523 buffer = msi_dup_property( package->db, szPropKeys[i] );
5524 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5525 msi_free( buffer );
5528 end:
5529 uirow = MSI_CreateRecord( 1 );
5530 MSI_RecordSetStringW( uirow, 1, productid );
5531 msi_ui_actiondata( package, szRegisterUser, uirow );
5532 msiobj_release( &uirow->hdr );
5534 msi_free(productid);
5535 RegCloseKey(hkey);
5536 return rc;
5540 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5542 UINT rc;
5544 package->script->InWhatSequence |= SEQUENCE_EXEC;
5545 rc = ACTION_ProcessExecSequence(package,FALSE);
5546 return rc;
5549 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5551 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5552 WCHAR productid_85[21], component_85[21], *ret;
5553 GUID clsid;
5554 DWORD sz;
5556 /* > is used if there is a component GUID and < if not. */
5558 productid_85[0] = 0;
5559 component_85[0] = 0;
5560 CLSIDFromString( package->ProductCode, &clsid );
5562 encode_base85_guid( &clsid, productid_85 );
5563 if (component)
5565 CLSIDFromString( component->ComponentId, &clsid );
5566 encode_base85_guid( &clsid, component_85 );
5569 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5570 debugstr_w(component_85));
5572 sz = 20 + strlenW( feature ) + 20 + 3;
5573 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5574 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5575 return ret;
5578 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5580 MSIPACKAGE *package = param;
5581 LPCWSTR compgroupid, component, feature, qualifier, text;
5582 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5583 HKEY hkey = NULL;
5584 UINT rc;
5585 MSICOMPONENT *comp;
5586 MSIFEATURE *feat;
5587 DWORD sz;
5588 MSIRECORD *uirow;
5589 int len;
5591 feature = MSI_RecordGetString(rec, 5);
5592 feat = msi_get_loaded_feature(package, feature);
5593 if (!feat)
5594 return ERROR_SUCCESS;
5596 feat->Action = msi_get_feature_action( package, feat );
5597 if (feat->Action != INSTALLSTATE_LOCAL &&
5598 feat->Action != INSTALLSTATE_SOURCE &&
5599 feat->Action != INSTALLSTATE_ADVERTISED)
5601 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5602 return ERROR_SUCCESS;
5605 component = MSI_RecordGetString(rec, 3);
5606 comp = msi_get_loaded_component(package, component);
5607 if (!comp)
5608 return ERROR_SUCCESS;
5610 compgroupid = MSI_RecordGetString(rec,1);
5611 qualifier = MSI_RecordGetString(rec,2);
5613 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5614 if (rc != ERROR_SUCCESS)
5615 goto end;
5617 advertise = msi_create_component_advertise_string( package, comp, feature );
5618 text = MSI_RecordGetString( rec, 4 );
5619 if (text)
5621 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5622 strcpyW( p, advertise );
5623 strcatW( p, text );
5624 msi_free( advertise );
5625 advertise = p;
5627 existing = msi_reg_get_val_str( hkey, qualifier );
5629 sz = strlenW( advertise ) + 1;
5630 if (existing)
5632 for (p = existing; *p; p += len)
5634 len = strlenW( p ) + 1;
5635 if (strcmpW( advertise, p )) sz += len;
5638 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5640 rc = ERROR_OUTOFMEMORY;
5641 goto end;
5643 q = output;
5644 if (existing)
5646 for (p = existing; *p; p += len)
5648 len = strlenW( p ) + 1;
5649 if (strcmpW( advertise, p ))
5651 memcpy( q, p, len * sizeof(WCHAR) );
5652 q += len;
5656 strcpyW( q, advertise );
5657 q[strlenW( q ) + 1] = 0;
5659 msi_reg_set_val_multi_str( hkey, qualifier, output );
5661 end:
5662 RegCloseKey(hkey);
5663 msi_free( output );
5664 msi_free( advertise );
5665 msi_free( existing );
5667 /* the UI chunk */
5668 uirow = MSI_CreateRecord( 2 );
5669 MSI_RecordSetStringW( uirow, 1, compgroupid );
5670 MSI_RecordSetStringW( uirow, 2, qualifier);
5671 msi_ui_actiondata( package, szPublishComponents, uirow );
5672 msiobj_release( &uirow->hdr );
5673 /* FIXME: call ui_progress? */
5675 return rc;
5679 * At present I am ignorning the advertised components part of this and only
5680 * focusing on the qualified component sets
5682 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5684 static const WCHAR query[] = {
5685 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5686 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5687 MSIQUERY *view;
5688 UINT rc;
5690 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5691 if (rc != ERROR_SUCCESS)
5692 return ERROR_SUCCESS;
5694 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5695 msiobj_release(&view->hdr);
5696 return rc;
5699 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5701 static const WCHAR szInstallerComponents[] = {
5702 'S','o','f','t','w','a','r','e','\\',
5703 'M','i','c','r','o','s','o','f','t','\\',
5704 'I','n','s','t','a','l','l','e','r','\\',
5705 'C','o','m','p','o','n','e','n','t','s','\\',0};
5707 MSIPACKAGE *package = param;
5708 LPCWSTR compgroupid, component, feature, qualifier;
5709 MSICOMPONENT *comp;
5710 MSIFEATURE *feat;
5711 MSIRECORD *uirow;
5712 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5713 LONG res;
5715 feature = MSI_RecordGetString( rec, 5 );
5716 feat = msi_get_loaded_feature( package, feature );
5717 if (!feat)
5718 return ERROR_SUCCESS;
5720 feat->Action = msi_get_feature_action( package, feat );
5721 if (feat->Action != INSTALLSTATE_ABSENT)
5723 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5724 return ERROR_SUCCESS;
5727 component = MSI_RecordGetString( rec, 3 );
5728 comp = msi_get_loaded_component( package, component );
5729 if (!comp)
5730 return ERROR_SUCCESS;
5732 compgroupid = MSI_RecordGetString( rec, 1 );
5733 qualifier = MSI_RecordGetString( rec, 2 );
5735 squash_guid( compgroupid, squashed );
5736 strcpyW( keypath, szInstallerComponents );
5737 strcatW( keypath, squashed );
5739 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5740 if (res != ERROR_SUCCESS)
5742 WARN("Unable to delete component key %d\n", res);
5745 uirow = MSI_CreateRecord( 2 );
5746 MSI_RecordSetStringW( uirow, 1, compgroupid );
5747 MSI_RecordSetStringW( uirow, 2, qualifier );
5748 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5749 msiobj_release( &uirow->hdr );
5751 return ERROR_SUCCESS;
5754 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5756 static const WCHAR query[] = {
5757 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5758 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5759 MSIQUERY *view;
5760 UINT rc;
5762 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5763 if (rc != ERROR_SUCCESS)
5764 return ERROR_SUCCESS;
5766 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5767 msiobj_release( &view->hdr );
5768 return rc;
5771 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5773 static const WCHAR query[] =
5774 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5775 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5776 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5777 MSIPACKAGE *package = param;
5778 MSICOMPONENT *component;
5779 MSIRECORD *row;
5780 MSIFILE *file;
5781 SC_HANDLE hscm = NULL, service = NULL;
5782 LPCWSTR comp, key;
5783 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5784 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5785 DWORD serv_type, start_type, err_control;
5786 SERVICE_DESCRIPTIONW sd = {NULL};
5787 UINT ret = ERROR_SUCCESS;
5789 comp = MSI_RecordGetString( rec, 12 );
5790 component = msi_get_loaded_component( package, comp );
5791 if (!component)
5793 WARN("service component not found\n");
5794 goto done;
5796 component->Action = msi_get_component_action( package, component );
5797 if (component->Action != INSTALLSTATE_LOCAL)
5799 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5800 goto done;
5802 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5803 if (!hscm)
5805 ERR("Failed to open the SC Manager!\n");
5806 goto done;
5809 start_type = MSI_RecordGetInteger(rec, 5);
5810 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5811 goto done;
5813 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5814 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5815 serv_type = MSI_RecordGetInteger(rec, 4);
5816 err_control = MSI_RecordGetInteger(rec, 6);
5817 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5818 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5819 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5820 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5821 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5822 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5824 /* fetch the service path */
5825 row = MSI_QueryGetRecord(package->db, query, comp);
5826 if (!row)
5828 ERR("Query failed\n");
5829 goto done;
5831 if (!(key = MSI_RecordGetString(row, 6)))
5833 msiobj_release(&row->hdr);
5834 goto done;
5836 file = msi_get_loaded_file(package, key);
5837 msiobj_release(&row->hdr);
5838 if (!file)
5840 ERR("Failed to load the service file\n");
5841 goto done;
5844 if (!args || !args[0]) image_path = file->TargetPath;
5845 else
5847 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5848 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5850 ret = ERROR_OUTOFMEMORY;
5851 goto done;
5854 strcpyW(image_path, file->TargetPath);
5855 strcatW(image_path, szSpace);
5856 strcatW(image_path, args);
5858 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5859 start_type, err_control, image_path, load_order,
5860 NULL, depends, serv_name, pass);
5862 if (!service)
5864 if (GetLastError() != ERROR_SERVICE_EXISTS)
5865 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5867 else if (sd.lpDescription)
5869 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5870 WARN("failed to set service description %u\n", GetLastError());
5873 if (image_path != file->TargetPath) msi_free(image_path);
5874 done:
5875 CloseServiceHandle(service);
5876 CloseServiceHandle(hscm);
5877 msi_free(name);
5878 msi_free(disp);
5879 msi_free(sd.lpDescription);
5880 msi_free(load_order);
5881 msi_free(serv_name);
5882 msi_free(pass);
5883 msi_free(depends);
5884 msi_free(args);
5886 return ret;
5889 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5891 static const WCHAR query[] = {
5892 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5893 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5894 MSIQUERY *view;
5895 UINT rc;
5897 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5898 if (rc != ERROR_SUCCESS)
5899 return ERROR_SUCCESS;
5901 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5902 msiobj_release(&view->hdr);
5903 return rc;
5906 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5907 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5909 LPCWSTR *vector, *temp_vector;
5910 LPWSTR p, q;
5911 DWORD sep_len;
5913 static const WCHAR separator[] = {'[','~',']',0};
5915 *numargs = 0;
5916 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5918 if (!args)
5919 return NULL;
5921 vector = msi_alloc(sizeof(LPWSTR));
5922 if (!vector)
5923 return NULL;
5925 p = args;
5928 (*numargs)++;
5929 vector[*numargs - 1] = p;
5931 if ((q = strstrW(p, separator)))
5933 *q = '\0';
5935 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5936 if (!temp_vector)
5938 msi_free(vector);
5939 return NULL;
5941 vector = temp_vector;
5943 p = q + sep_len;
5945 } while (q);
5947 return vector;
5950 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5952 MSIPACKAGE *package = param;
5953 MSICOMPONENT *comp;
5954 MSIRECORD *uirow;
5955 SC_HANDLE scm = NULL, service = NULL;
5956 LPCWSTR component, *vector = NULL;
5957 LPWSTR name, args, display_name = NULL;
5958 DWORD event, numargs, len, wait, dummy;
5959 UINT r = ERROR_FUNCTION_FAILED;
5960 SERVICE_STATUS_PROCESS status;
5961 ULONGLONG start_time;
5963 component = MSI_RecordGetString(rec, 6);
5964 comp = msi_get_loaded_component(package, component);
5965 if (!comp)
5966 return ERROR_SUCCESS;
5968 event = MSI_RecordGetInteger( rec, 3 );
5969 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5971 comp->Action = msi_get_component_action( package, comp );
5972 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
5973 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
5975 TRACE("not starting %s\n", debugstr_w(name));
5976 msi_free( name );
5977 return ERROR_SUCCESS;
5980 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5981 wait = MSI_RecordGetInteger(rec, 5);
5983 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5984 if (!scm)
5986 ERR("Failed to open the service control manager\n");
5987 goto done;
5990 len = 0;
5991 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5992 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5994 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5995 GetServiceDisplayNameW( scm, name, display_name, &len );
5998 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5999 if (!service)
6001 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6002 goto done;
6005 vector = msi_service_args_to_vector(args, &numargs);
6007 if (!StartServiceW(service, numargs, vector) &&
6008 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6010 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6011 goto done;
6014 r = ERROR_SUCCESS;
6015 if (wait)
6017 /* wait for at most 30 seconds for the service to be up and running */
6018 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6019 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6021 TRACE("failed to query service status (%u)\n", GetLastError());
6022 goto done;
6024 start_time = GetTickCount64();
6025 while (status.dwCurrentState == SERVICE_START_PENDING)
6027 if (GetTickCount64() - start_time > 30000) break;
6028 Sleep(1000);
6029 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6030 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6032 TRACE("failed to query service status (%u)\n", GetLastError());
6033 goto done;
6036 if (status.dwCurrentState != SERVICE_RUNNING)
6038 WARN("service failed to start %u\n", status.dwCurrentState);
6039 r = ERROR_FUNCTION_FAILED;
6043 done:
6044 uirow = MSI_CreateRecord( 2 );
6045 MSI_RecordSetStringW( uirow, 1, display_name );
6046 MSI_RecordSetStringW( uirow, 2, name );
6047 msi_ui_actiondata( package, szStartServices, uirow );
6048 msiobj_release( &uirow->hdr );
6050 CloseServiceHandle(service);
6051 CloseServiceHandle(scm);
6053 msi_free(name);
6054 msi_free(args);
6055 msi_free(vector);
6056 msi_free(display_name);
6057 return r;
6060 static UINT ACTION_StartServices( MSIPACKAGE *package )
6062 static const WCHAR query[] = {
6063 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6064 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6065 MSIQUERY *view;
6066 UINT rc;
6068 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6069 if (rc != ERROR_SUCCESS)
6070 return ERROR_SUCCESS;
6072 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6073 msiobj_release(&view->hdr);
6074 return rc;
6077 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6079 DWORD i, needed, count;
6080 ENUM_SERVICE_STATUSW *dependencies;
6081 SERVICE_STATUS ss;
6082 SC_HANDLE depserv;
6083 BOOL stopped, ret = FALSE;
6085 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6086 0, &needed, &count))
6087 return TRUE;
6089 if (GetLastError() != ERROR_MORE_DATA)
6090 return FALSE;
6092 dependencies = msi_alloc(needed);
6093 if (!dependencies)
6094 return FALSE;
6096 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6097 needed, &needed, &count))
6098 goto done;
6100 for (i = 0; i < count; i++)
6102 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6103 SERVICE_STOP | SERVICE_QUERY_STATUS);
6104 if (!depserv)
6105 goto done;
6107 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6108 CloseServiceHandle(depserv);
6109 if (!stopped)
6110 goto done;
6113 ret = TRUE;
6115 done:
6116 msi_free(dependencies);
6117 return ret;
6120 static UINT stop_service( LPCWSTR name )
6122 SC_HANDLE scm = NULL, service = NULL;
6123 SERVICE_STATUS status;
6124 SERVICE_STATUS_PROCESS ssp;
6125 DWORD needed;
6127 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6128 if (!scm)
6130 WARN("Failed to open the SCM: %d\n", GetLastError());
6131 goto done;
6134 service = OpenServiceW(scm, name,
6135 SERVICE_STOP |
6136 SERVICE_QUERY_STATUS |
6137 SERVICE_ENUMERATE_DEPENDENTS);
6138 if (!service)
6140 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6141 goto done;
6144 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6145 sizeof(SERVICE_STATUS_PROCESS), &needed))
6147 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6148 goto done;
6151 if (ssp.dwCurrentState == SERVICE_STOPPED)
6152 goto done;
6154 stop_service_dependents(scm, service);
6156 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6157 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6159 done:
6160 CloseServiceHandle(service);
6161 CloseServiceHandle(scm);
6163 return ERROR_SUCCESS;
6166 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6168 MSIPACKAGE *package = param;
6169 MSICOMPONENT *comp;
6170 MSIRECORD *uirow;
6171 LPCWSTR component;
6172 WCHAR *name, *display_name = NULL;
6173 DWORD event, len;
6174 SC_HANDLE scm;
6176 component = MSI_RecordGetString( rec, 6 );
6177 comp = msi_get_loaded_component( package, component );
6178 if (!comp)
6179 return ERROR_SUCCESS;
6181 event = MSI_RecordGetInteger( rec, 3 );
6182 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6184 comp->Action = msi_get_component_action( package, comp );
6185 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6186 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6188 TRACE("not stopping %s\n", debugstr_w(name));
6189 msi_free( name );
6190 return ERROR_SUCCESS;
6193 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6194 if (!scm)
6196 ERR("Failed to open the service control manager\n");
6197 goto done;
6200 len = 0;
6201 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6202 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6204 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6205 GetServiceDisplayNameW( scm, name, display_name, &len );
6207 CloseServiceHandle( scm );
6209 stop_service( name );
6211 done:
6212 uirow = MSI_CreateRecord( 2 );
6213 MSI_RecordSetStringW( uirow, 1, display_name );
6214 MSI_RecordSetStringW( uirow, 2, name );
6215 msi_ui_actiondata( package, szStopServices, uirow );
6216 msiobj_release( &uirow->hdr );
6218 msi_free( name );
6219 msi_free( display_name );
6220 return ERROR_SUCCESS;
6223 static UINT ACTION_StopServices( MSIPACKAGE *package )
6225 static const WCHAR query[] = {
6226 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6227 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6228 MSIQUERY *view;
6229 UINT rc;
6231 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6232 if (rc != ERROR_SUCCESS)
6233 return ERROR_SUCCESS;
6235 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6236 msiobj_release(&view->hdr);
6237 return rc;
6240 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6242 MSIPACKAGE *package = param;
6243 MSICOMPONENT *comp;
6244 MSIRECORD *uirow;
6245 LPWSTR name = NULL, display_name = NULL;
6246 DWORD event, len;
6247 SC_HANDLE scm = NULL, service = NULL;
6249 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6250 if (!comp)
6251 return ERROR_SUCCESS;
6253 event = MSI_RecordGetInteger( rec, 3 );
6254 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6256 comp->Action = msi_get_component_action( package, comp );
6257 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6258 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6260 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6261 msi_free( name );
6262 return ERROR_SUCCESS;
6264 stop_service( name );
6266 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6267 if (!scm)
6269 WARN("Failed to open the SCM: %d\n", GetLastError());
6270 goto done;
6273 len = 0;
6274 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6275 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6277 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6278 GetServiceDisplayNameW( scm, name, display_name, &len );
6281 service = OpenServiceW( scm, name, DELETE );
6282 if (!service)
6284 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6285 goto done;
6288 if (!DeleteService( service ))
6289 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6291 done:
6292 uirow = MSI_CreateRecord( 2 );
6293 MSI_RecordSetStringW( uirow, 1, display_name );
6294 MSI_RecordSetStringW( uirow, 2, name );
6295 msi_ui_actiondata( package, szDeleteServices, uirow );
6296 msiobj_release( &uirow->hdr );
6298 CloseServiceHandle( service );
6299 CloseServiceHandle( scm );
6300 msi_free( name );
6301 msi_free( display_name );
6303 return ERROR_SUCCESS;
6306 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6308 static const WCHAR query[] = {
6309 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6310 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6311 MSIQUERY *view;
6312 UINT rc;
6314 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6315 if (rc != ERROR_SUCCESS)
6316 return ERROR_SUCCESS;
6318 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6319 msiobj_release( &view->hdr );
6320 return rc;
6323 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6325 MSIPACKAGE *package = param;
6326 LPWSTR driver, driver_path, ptr;
6327 WCHAR outpath[MAX_PATH];
6328 MSIFILE *driver_file = NULL, *setup_file = NULL;
6329 MSICOMPONENT *comp;
6330 MSIRECORD *uirow;
6331 LPCWSTR desc, file_key, component;
6332 DWORD len, usage;
6333 UINT r = ERROR_SUCCESS;
6335 static const WCHAR driver_fmt[] = {
6336 'D','r','i','v','e','r','=','%','s',0};
6337 static const WCHAR setup_fmt[] = {
6338 'S','e','t','u','p','=','%','s',0};
6339 static const WCHAR usage_fmt[] = {
6340 'F','i','l','e','U','s','a','g','e','=','1',0};
6342 component = MSI_RecordGetString( rec, 2 );
6343 comp = msi_get_loaded_component( package, component );
6344 if (!comp)
6345 return ERROR_SUCCESS;
6347 comp->Action = msi_get_component_action( package, comp );
6348 if (comp->Action != INSTALLSTATE_LOCAL)
6350 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6351 return ERROR_SUCCESS;
6353 desc = MSI_RecordGetString(rec, 3);
6355 file_key = MSI_RecordGetString( rec, 4 );
6356 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6358 file_key = MSI_RecordGetString( rec, 5 );
6359 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6361 if (!driver_file)
6363 ERR("ODBC Driver entry not found!\n");
6364 return ERROR_FUNCTION_FAILED;
6367 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6368 if (setup_file)
6369 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6370 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6372 driver = msi_alloc(len * sizeof(WCHAR));
6373 if (!driver)
6374 return ERROR_OUTOFMEMORY;
6376 ptr = driver;
6377 lstrcpyW(ptr, desc);
6378 ptr += lstrlenW(ptr) + 1;
6380 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6381 ptr += len + 1;
6383 if (setup_file)
6385 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6386 ptr += len + 1;
6389 lstrcpyW(ptr, usage_fmt);
6390 ptr += lstrlenW(ptr) + 1;
6391 *ptr = '\0';
6393 if (!driver_file->TargetPath)
6395 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6396 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6398 driver_path = strdupW(driver_file->TargetPath);
6399 ptr = strrchrW(driver_path, '\\');
6400 if (ptr) *ptr = '\0';
6402 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6403 NULL, ODBC_INSTALL_COMPLETE, &usage))
6405 ERR("Failed to install SQL driver!\n");
6406 r = ERROR_FUNCTION_FAILED;
6409 uirow = MSI_CreateRecord( 5 );
6410 MSI_RecordSetStringW( uirow, 1, desc );
6411 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6412 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6413 msi_ui_actiondata( package, szInstallODBC, uirow );
6414 msiobj_release( &uirow->hdr );
6416 msi_free(driver);
6417 msi_free(driver_path);
6419 return r;
6422 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6424 MSIPACKAGE *package = param;
6425 LPWSTR translator, translator_path, ptr;
6426 WCHAR outpath[MAX_PATH];
6427 MSIFILE *translator_file = NULL, *setup_file = NULL;
6428 MSICOMPONENT *comp;
6429 MSIRECORD *uirow;
6430 LPCWSTR desc, file_key, component;
6431 DWORD len, usage;
6432 UINT r = ERROR_SUCCESS;
6434 static const WCHAR translator_fmt[] = {
6435 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6436 static const WCHAR setup_fmt[] = {
6437 'S','e','t','u','p','=','%','s',0};
6439 component = MSI_RecordGetString( rec, 2 );
6440 comp = msi_get_loaded_component( package, component );
6441 if (!comp)
6442 return ERROR_SUCCESS;
6444 comp->Action = msi_get_component_action( package, comp );
6445 if (comp->Action != INSTALLSTATE_LOCAL)
6447 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6448 return ERROR_SUCCESS;
6450 desc = MSI_RecordGetString(rec, 3);
6452 file_key = MSI_RecordGetString( rec, 4 );
6453 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6455 file_key = MSI_RecordGetString( rec, 5 );
6456 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6458 if (!translator_file)
6460 ERR("ODBC Translator entry not found!\n");
6461 return ERROR_FUNCTION_FAILED;
6464 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6465 if (setup_file)
6466 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6468 translator = msi_alloc(len * sizeof(WCHAR));
6469 if (!translator)
6470 return ERROR_OUTOFMEMORY;
6472 ptr = translator;
6473 lstrcpyW(ptr, desc);
6474 ptr += lstrlenW(ptr) + 1;
6476 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6477 ptr += len + 1;
6479 if (setup_file)
6481 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6482 ptr += len + 1;
6484 *ptr = '\0';
6486 translator_path = strdupW(translator_file->TargetPath);
6487 ptr = strrchrW(translator_path, '\\');
6488 if (ptr) *ptr = '\0';
6490 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6491 NULL, ODBC_INSTALL_COMPLETE, &usage))
6493 ERR("Failed to install SQL translator!\n");
6494 r = ERROR_FUNCTION_FAILED;
6497 uirow = MSI_CreateRecord( 5 );
6498 MSI_RecordSetStringW( uirow, 1, desc );
6499 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6500 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6501 msi_ui_actiondata( package, szInstallODBC, uirow );
6502 msiobj_release( &uirow->hdr );
6504 msi_free(translator);
6505 msi_free(translator_path);
6507 return r;
6510 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6512 MSIPACKAGE *package = param;
6513 MSICOMPONENT *comp;
6514 LPWSTR attrs;
6515 LPCWSTR desc, driver, component;
6516 WORD request = ODBC_ADD_SYS_DSN;
6517 INT registration;
6518 DWORD len;
6519 UINT r = ERROR_SUCCESS;
6520 MSIRECORD *uirow;
6522 static const WCHAR attrs_fmt[] = {
6523 'D','S','N','=','%','s',0 };
6525 component = MSI_RecordGetString( rec, 2 );
6526 comp = msi_get_loaded_component( package, component );
6527 if (!comp)
6528 return ERROR_SUCCESS;
6530 comp->Action = msi_get_component_action( package, comp );
6531 if (comp->Action != INSTALLSTATE_LOCAL)
6533 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6534 return ERROR_SUCCESS;
6537 desc = MSI_RecordGetString(rec, 3);
6538 driver = MSI_RecordGetString(rec, 4);
6539 registration = MSI_RecordGetInteger(rec, 5);
6541 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6542 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6544 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6545 attrs = msi_alloc(len * sizeof(WCHAR));
6546 if (!attrs)
6547 return ERROR_OUTOFMEMORY;
6549 len = sprintfW(attrs, attrs_fmt, desc);
6550 attrs[len + 1] = 0;
6552 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6554 ERR("Failed to install SQL data source!\n");
6555 r = ERROR_FUNCTION_FAILED;
6558 uirow = MSI_CreateRecord( 5 );
6559 MSI_RecordSetStringW( uirow, 1, desc );
6560 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6561 MSI_RecordSetInteger( uirow, 3, request );
6562 msi_ui_actiondata( package, szInstallODBC, uirow );
6563 msiobj_release( &uirow->hdr );
6565 msi_free(attrs);
6567 return r;
6570 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6572 static const WCHAR driver_query[] = {
6573 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6574 'O','D','B','C','D','r','i','v','e','r',0};
6575 static const WCHAR translator_query[] = {
6576 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6577 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6578 static const WCHAR source_query[] = {
6579 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6580 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6581 MSIQUERY *view;
6582 UINT rc;
6584 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6585 if (rc == ERROR_SUCCESS)
6587 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6588 msiobj_release(&view->hdr);
6589 if (rc != ERROR_SUCCESS)
6590 return rc;
6592 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6593 if (rc == ERROR_SUCCESS)
6595 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6596 msiobj_release(&view->hdr);
6597 if (rc != ERROR_SUCCESS)
6598 return rc;
6600 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6601 if (rc == ERROR_SUCCESS)
6603 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6604 msiobj_release(&view->hdr);
6605 if (rc != ERROR_SUCCESS)
6606 return rc;
6608 return ERROR_SUCCESS;
6611 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6613 MSIPACKAGE *package = param;
6614 MSICOMPONENT *comp;
6615 MSIRECORD *uirow;
6616 DWORD usage;
6617 LPCWSTR desc, component;
6619 component = MSI_RecordGetString( rec, 2 );
6620 comp = msi_get_loaded_component( package, component );
6621 if (!comp)
6622 return ERROR_SUCCESS;
6624 comp->Action = msi_get_component_action( package, comp );
6625 if (comp->Action != INSTALLSTATE_ABSENT)
6627 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6628 return ERROR_SUCCESS;
6631 desc = MSI_RecordGetString( rec, 3 );
6632 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6634 WARN("Failed to remove ODBC driver\n");
6636 else if (!usage)
6638 FIXME("Usage count reached 0\n");
6641 uirow = MSI_CreateRecord( 2 );
6642 MSI_RecordSetStringW( uirow, 1, desc );
6643 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6644 msi_ui_actiondata( package, szRemoveODBC, uirow );
6645 msiobj_release( &uirow->hdr );
6647 return ERROR_SUCCESS;
6650 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6652 MSIPACKAGE *package = param;
6653 MSICOMPONENT *comp;
6654 MSIRECORD *uirow;
6655 DWORD usage;
6656 LPCWSTR desc, component;
6658 component = MSI_RecordGetString( rec, 2 );
6659 comp = msi_get_loaded_component( package, component );
6660 if (!comp)
6661 return ERROR_SUCCESS;
6663 comp->Action = msi_get_component_action( package, comp );
6664 if (comp->Action != INSTALLSTATE_ABSENT)
6666 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6667 return ERROR_SUCCESS;
6670 desc = MSI_RecordGetString( rec, 3 );
6671 if (!SQLRemoveTranslatorW( desc, &usage ))
6673 WARN("Failed to remove ODBC translator\n");
6675 else if (!usage)
6677 FIXME("Usage count reached 0\n");
6680 uirow = MSI_CreateRecord( 2 );
6681 MSI_RecordSetStringW( uirow, 1, desc );
6682 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6683 msi_ui_actiondata( package, szRemoveODBC, uirow );
6684 msiobj_release( &uirow->hdr );
6686 return ERROR_SUCCESS;
6689 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6691 MSIPACKAGE *package = param;
6692 MSICOMPONENT *comp;
6693 MSIRECORD *uirow;
6694 LPWSTR attrs;
6695 LPCWSTR desc, driver, component;
6696 WORD request = ODBC_REMOVE_SYS_DSN;
6697 INT registration;
6698 DWORD len;
6700 static const WCHAR attrs_fmt[] = {
6701 'D','S','N','=','%','s',0 };
6703 component = MSI_RecordGetString( rec, 2 );
6704 comp = msi_get_loaded_component( package, component );
6705 if (!comp)
6706 return ERROR_SUCCESS;
6708 comp->Action = msi_get_component_action( package, comp );
6709 if (comp->Action != INSTALLSTATE_ABSENT)
6711 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6712 return ERROR_SUCCESS;
6715 desc = MSI_RecordGetString( rec, 3 );
6716 driver = MSI_RecordGetString( rec, 4 );
6717 registration = MSI_RecordGetInteger( rec, 5 );
6719 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6720 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6722 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6723 attrs = msi_alloc( len * sizeof(WCHAR) );
6724 if (!attrs)
6725 return ERROR_OUTOFMEMORY;
6727 FIXME("Use ODBCSourceAttribute table\n");
6729 len = sprintfW( attrs, attrs_fmt, desc );
6730 attrs[len + 1] = 0;
6732 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6734 WARN("Failed to remove ODBC data source\n");
6736 msi_free( attrs );
6738 uirow = MSI_CreateRecord( 3 );
6739 MSI_RecordSetStringW( uirow, 1, desc );
6740 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6741 MSI_RecordSetInteger( uirow, 3, request );
6742 msi_ui_actiondata( package, szRemoveODBC, uirow );
6743 msiobj_release( &uirow->hdr );
6745 return ERROR_SUCCESS;
6748 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6750 static const WCHAR driver_query[] = {
6751 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6752 'O','D','B','C','D','r','i','v','e','r',0};
6753 static const WCHAR translator_query[] = {
6754 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6755 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6756 static const WCHAR source_query[] = {
6757 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6758 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6759 MSIQUERY *view;
6760 UINT rc;
6762 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6763 if (rc == ERROR_SUCCESS)
6765 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6766 msiobj_release( &view->hdr );
6767 if (rc != ERROR_SUCCESS)
6768 return rc;
6770 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6771 if (rc == ERROR_SUCCESS)
6773 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6774 msiobj_release( &view->hdr );
6775 if (rc != ERROR_SUCCESS)
6776 return rc;
6778 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6779 if (rc == ERROR_SUCCESS)
6781 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6782 msiobj_release( &view->hdr );
6783 if (rc != ERROR_SUCCESS)
6784 return rc;
6786 return ERROR_SUCCESS;
6789 #define ENV_ACT_SETALWAYS 0x1
6790 #define ENV_ACT_SETABSENT 0x2
6791 #define ENV_ACT_REMOVE 0x4
6792 #define ENV_ACT_REMOVEMATCH 0x8
6794 #define ENV_MOD_MACHINE 0x20000000
6795 #define ENV_MOD_APPEND 0x40000000
6796 #define ENV_MOD_PREFIX 0x80000000
6797 #define ENV_MOD_MASK 0xC0000000
6799 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6801 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6803 LPCWSTR cptr = *name;
6805 static const WCHAR prefix[] = {'[','~',']',0};
6806 static const int prefix_len = 3;
6808 *flags = 0;
6809 while (*cptr)
6811 if (*cptr == '=')
6812 *flags |= ENV_ACT_SETALWAYS;
6813 else if (*cptr == '+')
6814 *flags |= ENV_ACT_SETABSENT;
6815 else if (*cptr == '-')
6816 *flags |= ENV_ACT_REMOVE;
6817 else if (*cptr == '!')
6818 *flags |= ENV_ACT_REMOVEMATCH;
6819 else if (*cptr == '*')
6820 *flags |= ENV_MOD_MACHINE;
6821 else
6822 break;
6824 cptr++;
6825 (*name)++;
6828 if (!*cptr)
6830 ERR("Missing environment variable\n");
6831 return ERROR_FUNCTION_FAILED;
6834 if (*value)
6836 LPCWSTR ptr = *value;
6837 if (!strncmpW(ptr, prefix, prefix_len))
6839 if (ptr[prefix_len] == szSemiColon[0])
6841 *flags |= ENV_MOD_APPEND;
6842 *value += lstrlenW(prefix);
6844 else
6846 *value = NULL;
6849 else if (lstrlenW(*value) >= prefix_len)
6851 ptr += lstrlenW(ptr) - prefix_len;
6852 if (!strcmpW( ptr, prefix ))
6854 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6856 *flags |= ENV_MOD_PREFIX;
6857 /* the "[~]" will be removed by deformat_string */;
6859 else
6861 *value = NULL;
6867 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6868 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6869 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6870 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6872 ERR("Invalid flags: %08x\n", *flags);
6873 return ERROR_FUNCTION_FAILED;
6876 if (!*flags)
6877 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6879 return ERROR_SUCCESS;
6882 static UINT open_env_key( DWORD flags, HKEY *key )
6884 static const WCHAR user_env[] =
6885 {'E','n','v','i','r','o','n','m','e','n','t',0};
6886 static const WCHAR machine_env[] =
6887 {'S','y','s','t','e','m','\\',
6888 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6889 'C','o','n','t','r','o','l','\\',
6890 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6891 'E','n','v','i','r','o','n','m','e','n','t',0};
6892 const WCHAR *env;
6893 HKEY root;
6894 LONG res;
6896 if (flags & ENV_MOD_MACHINE)
6898 env = machine_env;
6899 root = HKEY_LOCAL_MACHINE;
6901 else
6903 env = user_env;
6904 root = HKEY_CURRENT_USER;
6907 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6908 if (res != ERROR_SUCCESS)
6910 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6911 return ERROR_FUNCTION_FAILED;
6914 return ERROR_SUCCESS;
6917 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6919 MSIPACKAGE *package = param;
6920 LPCWSTR name, value, component;
6921 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6922 DWORD flags, type, size;
6923 UINT res;
6924 HKEY env = NULL;
6925 MSICOMPONENT *comp;
6926 MSIRECORD *uirow;
6927 int action = 0;
6929 component = MSI_RecordGetString(rec, 4);
6930 comp = msi_get_loaded_component(package, component);
6931 if (!comp)
6932 return ERROR_SUCCESS;
6934 comp->Action = msi_get_component_action( package, comp );
6935 if (comp->Action != INSTALLSTATE_LOCAL)
6937 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6938 return ERROR_SUCCESS;
6940 name = MSI_RecordGetString(rec, 2);
6941 value = MSI_RecordGetString(rec, 3);
6943 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6945 res = env_parse_flags(&name, &value, &flags);
6946 if (res != ERROR_SUCCESS || !value)
6947 goto done;
6949 if (value && !deformat_string(package, value, &deformatted))
6951 res = ERROR_OUTOFMEMORY;
6952 goto done;
6955 value = deformatted;
6957 res = open_env_key( flags, &env );
6958 if (res != ERROR_SUCCESS)
6959 goto done;
6961 if (flags & ENV_MOD_MACHINE)
6962 action |= 0x20000000;
6964 size = 0;
6965 type = REG_SZ;
6966 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6967 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6968 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6969 goto done;
6971 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6973 action = 0x2;
6975 /* Nothing to do. */
6976 if (!value)
6978 res = ERROR_SUCCESS;
6979 goto done;
6982 /* If we are appending but the string was empty, strip ; */
6983 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6985 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6986 newval = strdupW(value);
6987 if (!newval)
6989 res = ERROR_OUTOFMEMORY;
6990 goto done;
6993 else
6995 action = 0x1;
6997 /* Contrary to MSDN, +-variable to [~];path works */
6998 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7000 res = ERROR_SUCCESS;
7001 goto done;
7004 data = msi_alloc(size);
7005 if (!data)
7007 msi_free(deformatted);
7008 RegCloseKey(env);
7009 return ERROR_OUTOFMEMORY;
7012 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
7013 if (res != ERROR_SUCCESS)
7014 goto done;
7016 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7018 action = 0x4;
7019 res = RegDeleteValueW(env, name);
7020 if (res != ERROR_SUCCESS)
7021 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7022 goto done;
7025 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
7026 if (flags & ENV_MOD_MASK)
7028 DWORD mod_size;
7029 int multiplier = 0;
7030 if (flags & ENV_MOD_APPEND) multiplier++;
7031 if (flags & ENV_MOD_PREFIX) multiplier++;
7032 mod_size = lstrlenW(value) * multiplier;
7033 size += mod_size * sizeof(WCHAR);
7036 newval = msi_alloc(size);
7037 ptr = newval;
7038 if (!newval)
7040 res = ERROR_OUTOFMEMORY;
7041 goto done;
7044 if (flags & ENV_MOD_PREFIX)
7046 lstrcpyW(newval, value);
7047 ptr = newval + lstrlenW(value);
7048 action |= 0x80000000;
7051 lstrcpyW(ptr, data);
7053 if (flags & ENV_MOD_APPEND)
7055 lstrcatW(newval, value);
7056 action |= 0x40000000;
7059 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7060 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
7061 if (res)
7063 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7066 done:
7067 uirow = MSI_CreateRecord( 3 );
7068 MSI_RecordSetStringW( uirow, 1, name );
7069 MSI_RecordSetStringW( uirow, 2, newval );
7070 MSI_RecordSetInteger( uirow, 3, action );
7071 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7072 msiobj_release( &uirow->hdr );
7074 if (env) RegCloseKey(env);
7075 msi_free(deformatted);
7076 msi_free(data);
7077 msi_free(newval);
7078 return res;
7081 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7083 static const WCHAR query[] = {
7084 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7085 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7086 MSIQUERY *view;
7087 UINT rc;
7089 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7090 if (rc != ERROR_SUCCESS)
7091 return ERROR_SUCCESS;
7093 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7094 msiobj_release(&view->hdr);
7095 return rc;
7098 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7100 MSIPACKAGE *package = param;
7101 LPCWSTR name, value, component;
7102 LPWSTR deformatted = NULL;
7103 DWORD flags;
7104 HKEY env;
7105 MSICOMPONENT *comp;
7106 MSIRECORD *uirow;
7107 int action = 0;
7108 LONG res;
7109 UINT r;
7111 component = MSI_RecordGetString( rec, 4 );
7112 comp = msi_get_loaded_component( package, component );
7113 if (!comp)
7114 return ERROR_SUCCESS;
7116 comp->Action = msi_get_component_action( package, comp );
7117 if (comp->Action != INSTALLSTATE_ABSENT)
7119 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7120 return ERROR_SUCCESS;
7122 name = MSI_RecordGetString( rec, 2 );
7123 value = MSI_RecordGetString( rec, 3 );
7125 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7127 r = env_parse_flags( &name, &value, &flags );
7128 if (r != ERROR_SUCCESS)
7129 return r;
7131 if (!(flags & ENV_ACT_REMOVE))
7133 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7134 return ERROR_SUCCESS;
7137 if (value && !deformat_string( package, value, &deformatted ))
7138 return ERROR_OUTOFMEMORY;
7140 value = deformatted;
7142 r = open_env_key( flags, &env );
7143 if (r != ERROR_SUCCESS)
7145 r = ERROR_SUCCESS;
7146 goto done;
7149 if (flags & ENV_MOD_MACHINE)
7150 action |= 0x20000000;
7152 TRACE("Removing %s\n", debugstr_w(name));
7154 res = RegDeleteValueW( env, name );
7155 if (res != ERROR_SUCCESS)
7157 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
7158 r = ERROR_SUCCESS;
7161 done:
7162 uirow = MSI_CreateRecord( 3 );
7163 MSI_RecordSetStringW( uirow, 1, name );
7164 MSI_RecordSetStringW( uirow, 2, value );
7165 MSI_RecordSetInteger( uirow, 3, action );
7166 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7167 msiobj_release( &uirow->hdr );
7169 if (env) RegCloseKey( env );
7170 msi_free( deformatted );
7171 return r;
7174 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7176 static const WCHAR query[] = {
7177 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7178 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7179 MSIQUERY *view;
7180 UINT rc;
7182 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7183 if (rc != ERROR_SUCCESS)
7184 return ERROR_SUCCESS;
7186 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7187 msiobj_release( &view->hdr );
7188 return rc;
7191 UINT msi_validate_product_id( MSIPACKAGE *package )
7193 LPWSTR key, template, id;
7194 UINT r = ERROR_SUCCESS;
7196 id = msi_dup_property( package->db, szProductID );
7197 if (id)
7199 msi_free( id );
7200 return ERROR_SUCCESS;
7202 template = msi_dup_property( package->db, szPIDTemplate );
7203 key = msi_dup_property( package->db, szPIDKEY );
7204 if (key && template)
7206 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7207 r = msi_set_property( package->db, szProductID, key, -1 );
7209 msi_free( template );
7210 msi_free( key );
7211 return r;
7214 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7216 return msi_validate_product_id( package );
7219 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7221 TRACE("\n");
7222 package->need_reboot_at_end = 1;
7223 return ERROR_SUCCESS;
7226 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7228 static const WCHAR szAvailableFreeReg[] =
7229 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7230 MSIRECORD *uirow;
7231 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7233 TRACE("%p %d kilobytes\n", package, space);
7235 uirow = MSI_CreateRecord( 1 );
7236 MSI_RecordSetInteger( uirow, 1, space );
7237 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7238 msiobj_release( &uirow->hdr );
7240 return ERROR_SUCCESS;
7243 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7245 TRACE("%p\n", package);
7247 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7248 return ERROR_SUCCESS;
7251 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7253 FIXME("%p\n", package);
7254 return ERROR_SUCCESS;
7257 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7259 static const WCHAR driver_query[] = {
7260 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7261 'O','D','B','C','D','r','i','v','e','r',0};
7262 static const WCHAR translator_query[] = {
7263 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7264 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7265 MSIQUERY *view;
7266 UINT r, count;
7268 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7269 if (r == ERROR_SUCCESS)
7271 count = 0;
7272 r = MSI_IterateRecords( view, &count, NULL, package );
7273 msiobj_release( &view->hdr );
7274 if (r != ERROR_SUCCESS)
7275 return r;
7276 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7278 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7279 if (r == ERROR_SUCCESS)
7281 count = 0;
7282 r = MSI_IterateRecords( view, &count, NULL, package );
7283 msiobj_release( &view->hdr );
7284 if (r != ERROR_SUCCESS)
7285 return r;
7286 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7288 return ERROR_SUCCESS;
7291 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7293 static const WCHAR fmtW[] =
7294 {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7295 MSIPACKAGE *package = param;
7296 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7297 int attrs = MSI_RecordGetInteger( rec, 5 );
7298 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7299 WCHAR *product, *features, *cmd;
7300 STARTUPINFOW si;
7301 PROCESS_INFORMATION info;
7302 BOOL ret;
7304 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7305 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7307 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7309 len += strlenW( product );
7310 if (features)
7311 len += strlenW( features );
7312 else
7313 len += sizeof(szAll) / sizeof(szAll[0]);
7315 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7317 msi_free( product );
7318 msi_free( features );
7319 return ERROR_OUTOFMEMORY;
7321 sprintfW( cmd, fmtW, product, features ? features : szAll );
7322 msi_free( product );
7323 msi_free( features );
7325 memset( &si, 0, sizeof(STARTUPINFOW) );
7326 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7327 msi_free( cmd );
7328 if (!ret) return GetLastError();
7329 CloseHandle( info.hThread );
7331 WaitForSingleObject( info.hProcess, INFINITE );
7332 CloseHandle( info.hProcess );
7333 return ERROR_SUCCESS;
7336 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7338 static const WCHAR query[] = {
7339 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7340 MSIQUERY *view;
7341 UINT r;
7343 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7344 if (r == ERROR_SUCCESS)
7346 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7347 msiobj_release( &view->hdr );
7348 if (r != ERROR_SUCCESS)
7349 return r;
7351 return ERROR_SUCCESS;
7354 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7356 MSIPACKAGE *package = param;
7357 int attributes = MSI_RecordGetInteger( rec, 5 );
7359 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7361 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7362 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7363 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7364 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7365 HKEY hkey;
7366 UINT r;
7368 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7370 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7371 if (r != ERROR_SUCCESS)
7372 return ERROR_SUCCESS;
7374 else
7376 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7377 if (r != ERROR_SUCCESS)
7378 return ERROR_SUCCESS;
7380 RegCloseKey( hkey );
7382 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7383 debugstr_w(upgrade_code), debugstr_w(version_min),
7384 debugstr_w(version_max), debugstr_w(language));
7386 return ERROR_SUCCESS;
7389 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7391 static const WCHAR query[] = {
7392 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7393 'U','p','g','r','a','d','e',0};
7394 MSIQUERY *view;
7395 UINT r;
7397 if (msi_get_property_int( package->db, szInstalled, 0 ))
7399 TRACE("product is installed, skipping action\n");
7400 return ERROR_SUCCESS;
7402 if (msi_get_property_int( package->db, szPreselected, 0 ))
7404 TRACE("Preselected property is set, not migrating feature states\n");
7405 return ERROR_SUCCESS;
7407 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7408 if (r == ERROR_SUCCESS)
7410 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7411 msiobj_release( &view->hdr );
7412 if (r != ERROR_SUCCESS)
7413 return r;
7415 return ERROR_SUCCESS;
7418 static void bind_image( const char *filename, const char *path )
7420 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7422 WARN("failed to bind image %u\n", GetLastError());
7426 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7428 UINT i;
7429 MSIFILE *file;
7430 MSIPACKAGE *package = param;
7431 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7432 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7433 char *filenameA, *pathA;
7434 WCHAR *pathW, **path_list;
7436 if (!(file = msi_get_loaded_file( package, key )))
7438 WARN("file %s not found\n", debugstr_w(key));
7439 return ERROR_SUCCESS;
7441 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7442 path_list = msi_split_string( paths, ';' );
7443 if (!path_list) bind_image( filenameA, NULL );
7444 else
7446 for (i = 0; path_list[i] && path_list[i][0]; i++)
7448 deformat_string( package, path_list[i], &pathW );
7449 if ((pathA = strdupWtoA( pathW )))
7451 bind_image( filenameA, pathA );
7452 msi_free( pathA );
7454 msi_free( pathW );
7457 msi_free( path_list );
7458 msi_free( filenameA );
7459 return ERROR_SUCCESS;
7462 static UINT ACTION_BindImage( MSIPACKAGE *package )
7464 static const WCHAR query[] = {
7465 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7466 'B','i','n','d','I','m','a','g','e',0};
7467 MSIQUERY *view;
7468 UINT r;
7470 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7471 if (r == ERROR_SUCCESS)
7473 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7474 msiobj_release( &view->hdr );
7475 if (r != ERROR_SUCCESS)
7476 return r;
7478 return ERROR_SUCCESS;
7481 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7483 static const WCHAR query[] = {
7484 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7485 MSIQUERY *view;
7486 DWORD count = 0;
7487 UINT r;
7489 r = MSI_OpenQuery( package->db, &view, query, table );
7490 if (r == ERROR_SUCCESS)
7492 r = MSI_IterateRecords(view, &count, NULL, package);
7493 msiobj_release(&view->hdr);
7494 if (r != ERROR_SUCCESS)
7495 return r;
7497 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7498 return ERROR_SUCCESS;
7501 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7503 static const WCHAR table[] = {
7504 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7505 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7508 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7510 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7511 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7514 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7516 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7517 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7520 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7522 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7523 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7526 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7528 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7529 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7532 static const struct
7534 const WCHAR *action;
7535 UINT (*handler)(MSIPACKAGE *);
7536 const WCHAR *action_rollback;
7538 StandardActions[] =
7540 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7541 { szAppSearch, ACTION_AppSearch, NULL },
7542 { szBindImage, ACTION_BindImage, NULL },
7543 { szCCPSearch, ACTION_CCPSearch, NULL },
7544 { szCostFinalize, ACTION_CostFinalize, NULL },
7545 { szCostInitialize, ACTION_CostInitialize, NULL },
7546 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7547 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7548 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7549 { szDisableRollback, ACTION_DisableRollback, NULL },
7550 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7551 { szExecuteAction, ACTION_ExecuteAction, NULL },
7552 { szFileCost, ACTION_FileCost, NULL },
7553 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7554 { szForceReboot, ACTION_ForceReboot, NULL },
7555 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7556 { szInstallExecute, ACTION_InstallExecute, NULL },
7557 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7558 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7559 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7560 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7561 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7562 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7563 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7564 { szInstallValidate, ACTION_InstallValidate, NULL },
7565 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7566 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7567 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7568 { szMoveFiles, ACTION_MoveFiles, NULL },
7569 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7570 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7571 { szPatchFiles, ACTION_PatchFiles, NULL },
7572 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7573 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7574 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7575 { szPublishProduct, ACTION_PublishProduct, NULL },
7576 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7577 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7578 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7579 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7580 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7581 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7582 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7583 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7584 { szRegisterUser, ACTION_RegisterUser, NULL },
7585 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7586 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7587 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7588 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7589 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7590 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7591 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7592 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7593 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7594 { szResolveSource, ACTION_ResolveSource, NULL },
7595 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7596 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7597 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7598 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7599 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7600 { szStartServices, ACTION_StartServices, szStopServices },
7601 { szStopServices, ACTION_StopServices, szStartServices },
7602 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7603 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7604 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7605 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7606 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7607 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7608 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7609 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7610 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7611 { szValidateProductID, ACTION_ValidateProductID, NULL },
7612 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7613 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7614 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7615 { NULL, NULL, NULL }
7618 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7620 BOOL ret = FALSE;
7621 UINT i;
7623 i = 0;
7624 while (StandardActions[i].action != NULL)
7626 if (!strcmpW( StandardActions[i].action, action ))
7628 ui_actionstart( package, action );
7629 if (StandardActions[i].handler)
7631 ui_actioninfo( package, action, TRUE, 0 );
7632 *rc = StandardActions[i].handler( package );
7633 ui_actioninfo( package, action, FALSE, *rc );
7635 if (StandardActions[i].action_rollback && !package->need_rollback)
7637 TRACE("scheduling rollback action\n");
7638 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7641 else
7643 FIXME("unhandled standard action %s\n", debugstr_w(action));
7644 *rc = ERROR_SUCCESS;
7646 ret = TRUE;
7647 break;
7649 i++;
7651 return ret;
7654 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7656 UINT rc = ERROR_SUCCESS;
7657 BOOL handled;
7659 TRACE("Performing action (%s)\n", debugstr_w(action));
7661 handled = ACTION_HandleStandardAction(package, action, &rc);
7663 if (!handled)
7664 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7666 if (!handled)
7668 WARN("unhandled msi action %s\n", debugstr_w(action));
7669 rc = ERROR_FUNCTION_NOT_CALLED;
7672 return rc;
7675 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7677 UINT rc = ERROR_SUCCESS;
7678 BOOL handled = FALSE;
7680 TRACE("Performing action (%s)\n", debugstr_w(action));
7682 package->action_progress_increment = 0;
7683 handled = ACTION_HandleStandardAction(package, action, &rc);
7685 if (!handled)
7686 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7688 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7689 handled = TRUE;
7691 if (!handled)
7693 WARN("unhandled msi action %s\n", debugstr_w(action));
7694 rc = ERROR_FUNCTION_NOT_CALLED;
7697 return rc;
7700 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7702 UINT rc = ERROR_SUCCESS;
7703 MSIRECORD *row;
7705 static const WCHAR query[] =
7706 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7707 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7708 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7709 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7710 static const WCHAR ui_query[] =
7711 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7712 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7713 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7714 ' ', '=',' ','%','i',0};
7716 if (needs_ui_sequence(package))
7717 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7718 else
7719 row = MSI_QueryGetRecord(package->db, query, seq);
7721 if (row)
7723 LPCWSTR action, cond;
7725 TRACE("Running the actions\n");
7727 /* check conditions */
7728 cond = MSI_RecordGetString(row, 2);
7730 /* this is a hack to skip errors in the condition code */
7731 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7733 msiobj_release(&row->hdr);
7734 return ERROR_SUCCESS;
7737 action = MSI_RecordGetString(row, 1);
7738 if (!action)
7740 ERR("failed to fetch action\n");
7741 msiobj_release(&row->hdr);
7742 return ERROR_FUNCTION_FAILED;
7745 if (needs_ui_sequence(package))
7746 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7747 else
7748 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7750 msiobj_release(&row->hdr);
7753 return rc;
7756 /****************************************************
7757 * TOP level entry points
7758 *****************************************************/
7760 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7761 LPCWSTR szCommandLine )
7763 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7764 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7765 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7766 WCHAR *reinstall, *remove, *patch, *productcode;
7767 BOOL ui_exists;
7768 UINT rc;
7770 msi_set_property( package->db, szAction, szInstall, -1 );
7772 package->script->InWhatSequence = SEQUENCE_INSTALL;
7774 if (szPackagePath)
7776 LPWSTR p, dir;
7777 LPCWSTR file;
7779 dir = strdupW(szPackagePath);
7780 p = strrchrW(dir, '\\');
7781 if (p)
7783 *(++p) = 0;
7784 file = szPackagePath + (p - dir);
7786 else
7788 msi_free(dir);
7789 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7790 GetCurrentDirectoryW(MAX_PATH, dir);
7791 lstrcatW(dir, szBackSlash);
7792 file = szPackagePath;
7795 msi_free( package->PackagePath );
7796 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7797 if (!package->PackagePath)
7799 msi_free(dir);
7800 return ERROR_OUTOFMEMORY;
7803 lstrcpyW(package->PackagePath, dir);
7804 lstrcatW(package->PackagePath, file);
7805 msi_free(dir);
7807 msi_set_sourcedir_props(package, FALSE);
7810 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7811 if (rc != ERROR_SUCCESS)
7812 return rc;
7814 msi_apply_transforms( package );
7815 msi_apply_patches( package );
7817 patch = msi_dup_property( package->db, szPatch );
7818 remove = msi_dup_property( package->db, szRemove );
7819 reinstall = msi_dup_property( package->db, szReinstall );
7820 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7822 TRACE("setting REINSTALL property to ALL\n");
7823 msi_set_property( package->db, szReinstall, szAll, -1 );
7824 package->full_reinstall = 1;
7827 msi_set_original_database_property( package->db, szPackagePath );
7828 msi_parse_command_line( package, szCommandLine, FALSE );
7829 msi_adjust_privilege_properties( package );
7830 msi_set_context( package );
7832 productcode = msi_dup_property( package->db, szProductCode );
7833 if (strcmpiW( productcode, package->ProductCode ))
7835 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
7836 msi_free( package->ProductCode );
7837 package->ProductCode = productcode;
7839 else msi_free( productcode );
7841 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7843 TRACE("disabling rollback\n");
7844 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7847 if (needs_ui_sequence( package))
7849 package->script->InWhatSequence |= SEQUENCE_UI;
7850 rc = ACTION_ProcessUISequence(package);
7851 ui_exists = ui_sequence_exists(package);
7852 if (rc == ERROR_SUCCESS || !ui_exists)
7854 package->script->InWhatSequence |= SEQUENCE_EXEC;
7855 rc = ACTION_ProcessExecSequence(package, ui_exists);
7858 else
7859 rc = ACTION_ProcessExecSequence(package, FALSE);
7861 /* process the ending type action */
7862 if (rc == ERROR_SUCCESS)
7863 ACTION_PerformActionSequence(package, -1);
7864 else if (rc == ERROR_INSTALL_USEREXIT)
7865 ACTION_PerformActionSequence(package, -2);
7866 else if (rc == ERROR_INSTALL_SUSPEND)
7867 ACTION_PerformActionSequence(package, -4);
7868 else /* failed */
7870 ACTION_PerformActionSequence(package, -3);
7871 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7873 package->need_rollback = TRUE;
7877 /* finish up running custom actions */
7878 ACTION_FinishCustomActions(package);
7880 if (package->need_rollback && !reinstall)
7882 WARN("installation failed, running rollback script\n");
7883 execute_script( package, SCRIPT_ROLLBACK );
7885 msi_free( reinstall );
7886 msi_free( remove );
7887 msi_free( patch );
7889 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7890 return ERROR_SUCCESS_REBOOT_REQUIRED;
7892 return rc;