msi: Call execute_script directly in InstallFinalize.
[wine.git] / dlls / msi / action.c
blob939e42669292172f9106b29759c49868a2da27d0
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "shlwapi.h"
39 #include "imagehlp.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
48 static const WCHAR szCreateFolders[] =
49 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
50 static const WCHAR szCostFinalize[] =
51 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
52 static const WCHAR szWriteRegistryValues[] =
53 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
54 static const WCHAR szFileCost[] =
55 {'F','i','l','e','C','o','s','t',0};
56 static const WCHAR szInstallInitialize[] =
57 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
58 static const WCHAR szInstallValidate[] =
59 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
60 static const WCHAR szLaunchConditions[] =
61 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
62 static const WCHAR szProcessComponents[] =
63 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
64 static const WCHAR szRegisterTypeLibraries[] =
65 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
66 static const WCHAR szCreateShortcuts[] =
67 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
68 static const WCHAR szPublishProduct[] =
69 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
70 static const WCHAR szWriteIniValues[] =
71 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
72 static const WCHAR szSelfRegModules[] =
73 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
74 static const WCHAR szPublishFeatures[] =
75 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
76 static const WCHAR szRegisterProduct[] =
77 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
78 static const WCHAR szInstallExecute[] =
79 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
80 static const WCHAR szInstallExecuteAgain[] =
81 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
82 static const WCHAR szInstallFinalize[] =
83 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
84 static const WCHAR szForceReboot[] =
85 {'F','o','r','c','e','R','e','b','o','o','t',0};
86 static const WCHAR szResolveSource[] =
87 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
88 static const WCHAR szAllocateRegistrySpace[] =
89 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
90 static const WCHAR szBindImage[] =
91 {'B','i','n','d','I','m','a','g','e',0};
92 static const WCHAR szDeleteServices[] =
93 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
94 static const WCHAR szDisableRollback[] =
95 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
96 static const WCHAR szExecuteAction[] =
97 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
98 static const WCHAR szInstallAdminPackage[] =
99 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
100 static const WCHAR szInstallSFPCatalogFile[] =
101 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
102 static const WCHAR szIsolateComponents[] =
103 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
104 static const WCHAR szMigrateFeatureStates[] =
105 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
106 static const WCHAR szMsiUnpublishAssemblies[] =
107 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
108 static const WCHAR szInstallODBC[] =
109 {'I','n','s','t','a','l','l','O','D','B','C',0};
110 static const WCHAR szInstallServices[] =
111 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
112 static const WCHAR szPublishComponents[] =
113 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
114 static const WCHAR szRegisterComPlus[] =
115 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
116 static const WCHAR szRegisterUser[] =
117 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
118 static const WCHAR szRemoveEnvironmentStrings[] =
119 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
120 static const WCHAR szRemoveExistingProducts[] =
121 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
122 static const WCHAR szRemoveFolders[] =
123 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
124 static const WCHAR szRemoveIniValues[] =
125 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
126 static const WCHAR szRemoveODBC[] =
127 {'R','e','m','o','v','e','O','D','B','C',0};
128 static const WCHAR szRemoveRegistryValues[] =
129 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
130 static const WCHAR szRemoveShortcuts[] =
131 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
132 static const WCHAR szRMCCPSearch[] =
133 {'R','M','C','C','P','S','e','a','r','c','h',0};
134 static const WCHAR szScheduleReboot[] =
135 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
136 static const WCHAR szSelfUnregModules[] =
137 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
138 static const WCHAR szSetODBCFolders[] =
139 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
140 static const WCHAR szStartServices[] =
141 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
142 static const WCHAR szStopServices[] =
143 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
144 static const WCHAR szUnpublishComponents[] =
145 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
146 static const WCHAR szUnpublishFeatures[] =
147 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
148 static const WCHAR szUnregisterComPlus[] =
149 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
150 static const WCHAR szUnregisterTypeLibraries[] =
151 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
152 static const WCHAR szValidateProductID[] =
153 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
154 static const WCHAR szWriteEnvironmentStrings[] =
155 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
157 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
159 static const WCHAR Query_t[] =
160 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
161 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
162 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
163 ' ','\'','%','s','\'',0};
164 MSIRECORD * row;
166 row = MSI_QueryGetRecord( package->db, Query_t, action );
167 if (!row)
168 return;
169 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
170 msiobj_release(&row->hdr);
173 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
174 UINT rc)
176 MSIRECORD * row;
177 static const WCHAR template_s[]=
178 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
179 '%','s', '.',0};
180 static const WCHAR template_e[]=
181 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
182 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
183 '%','i','.',0};
184 static const WCHAR format[] =
185 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
186 WCHAR message[1024];
187 WCHAR timet[0x100];
189 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
190 if (start)
191 sprintfW(message,template_s,timet,action);
192 else
193 sprintfW(message,template_e,timet,action,rc);
195 row = MSI_CreateRecord(1);
196 MSI_RecordSetStringW(row,1,message);
198 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
199 msiobj_release(&row->hdr);
202 enum parse_state
204 state_whitespace,
205 state_token,
206 state_quote
209 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
211 enum parse_state state = state_quote;
212 const WCHAR *p;
213 WCHAR *out = value;
214 BOOL ignore, in_quotes = FALSE;
215 int count = 0, len = 0;
217 for (p = str; *p; p++)
219 ignore = FALSE;
220 switch (state)
222 case state_whitespace:
223 switch (*p)
225 case ' ':
226 in_quotes = TRUE;
227 ignore = TRUE;
228 len++;
229 break;
230 case '"':
231 state = state_quote;
232 if (in_quotes && p[1] != '\"') count--;
233 else count++;
234 break;
235 default:
236 state = state_token;
237 in_quotes = TRUE;
238 len++;
239 break;
241 break;
243 case state_token:
244 switch (*p)
246 case '"':
247 state = state_quote;
248 if (in_quotes) count--;
249 else count++;
250 break;
251 case ' ':
252 state = state_whitespace;
253 if (!count) goto done;
254 in_quotes = TRUE;
255 len++;
256 break;
257 default:
258 if (!count) in_quotes = FALSE;
259 else in_quotes = TRUE;
260 len++;
261 break;
263 break;
265 case state_quote:
266 switch (*p)
268 case '"':
269 if (in_quotes && p[1] != '\"') count--;
270 else count++;
271 break;
272 case ' ':
273 state = state_whitespace;
274 if (!count || (count > 1 && !len)) goto done;
275 in_quotes = TRUE;
276 len++;
277 break;
278 default:
279 state = state_token;
280 if (!count) in_quotes = FALSE;
281 else in_quotes = TRUE;
282 len++;
283 break;
285 break;
287 default: break;
289 if (!ignore) *out++ = *p;
292 done:
293 if (!len) *value = 0;
294 else *out = 0;
296 *quotes = count;
297 return p - str;
300 static void remove_quotes( WCHAR *str )
302 WCHAR *p = str;
303 int len = strlenW( str );
305 while ((p = strchrW( p, '"' )))
307 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
308 p++;
312 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
313 BOOL preserve_case )
315 LPCWSTR ptr, ptr2;
316 int num_quotes;
317 DWORD len;
318 WCHAR *prop, *val;
319 UINT r;
321 if (!szCommandLine)
322 return ERROR_SUCCESS;
324 ptr = szCommandLine;
325 while (*ptr)
327 while (*ptr == ' ') ptr++;
328 if (!*ptr) break;
330 ptr2 = strchrW( ptr, '=' );
331 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
333 len = ptr2 - ptr;
334 if (!len) return ERROR_INVALID_COMMAND_LINE;
336 while (ptr[len - 1] == ' ') len--;
338 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
339 memcpy( prop, ptr, len * sizeof(WCHAR) );
340 prop[len] = 0;
341 if (!preserve_case) struprW( prop );
343 ptr2++;
344 while (*ptr2 == ' ') ptr2++;
346 num_quotes = 0;
347 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
348 len = parse_prop( ptr2, val, &num_quotes );
349 if (num_quotes % 2)
351 WARN("unbalanced quotes\n");
352 msi_free( val );
353 msi_free( prop );
354 return ERROR_INVALID_COMMAND_LINE;
356 remove_quotes( val );
357 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
359 r = msi_set_property( package->db, prop, val, -1 );
360 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
361 msi_reset_folders( package, TRUE );
363 msi_free( val );
364 msi_free( prop );
366 ptr = ptr2 + len;
369 return ERROR_SUCCESS;
372 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
374 LPCWSTR pc;
375 LPWSTR p, *ret = NULL;
376 UINT count = 0;
378 if (!str)
379 return ret;
381 /* count the number of substrings */
382 for ( pc = str, count = 0; pc; count++ )
384 pc = strchrW( pc, sep );
385 if (pc)
386 pc++;
389 /* allocate space for an array of substring pointers and the substrings */
390 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
391 (lstrlenW(str)+1) * sizeof(WCHAR) );
392 if (!ret)
393 return ret;
395 /* copy the string and set the pointers */
396 p = (LPWSTR) &ret[count+1];
397 lstrcpyW( p, str );
398 for( count = 0; (ret[count] = p); count++ )
400 p = strchrW( p, sep );
401 if (p)
402 *p++ = 0;
405 return ret;
408 static BOOL ui_sequence_exists( MSIPACKAGE *package )
410 static const WCHAR query [] = {
411 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
412 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
413 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',0};
414 MSIQUERY *view;
415 DWORD count = 0;
417 if (!(MSI_DatabaseOpenViewW( package->db, query, &view )))
419 MSI_IterateRecords( view, &count, NULL, package );
420 msiobj_release( &view->hdr );
422 return count != 0;
425 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
427 WCHAR *source, *check, *p, *db;
428 DWORD len;
430 if (!(db = msi_dup_property( package->db, szOriginalDatabase )))
431 return ERROR_OUTOFMEMORY;
433 if (!(p = strrchrW( db, '\\' )) && !(p = strrchrW( db, '/' )))
435 msi_free(db);
436 return ERROR_SUCCESS;
438 len = p - db + 2;
439 source = msi_alloc( len * sizeof(WCHAR) );
440 lstrcpynW( source, db, len );
441 msi_free( db );
443 check = msi_dup_property( package->db, szSourceDir );
444 if (!check || replace)
446 UINT r = msi_set_property( package->db, szSourceDir, source, -1 );
447 if (r == ERROR_SUCCESS)
448 msi_reset_folders( package, TRUE );
450 msi_free( check );
452 check = msi_dup_property( package->db, szSOURCEDIR );
453 if (!check || replace)
454 msi_set_property( package->db, szSOURCEDIR, source, -1 );
456 msi_free( check );
457 msi_free( source );
459 return ERROR_SUCCESS;
462 static BOOL needs_ui_sequence(MSIPACKAGE *package)
464 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
467 UINT msi_set_context(MSIPACKAGE *package)
469 UINT r = msi_locate_product( package->ProductCode, &package->Context );
470 if (r != ERROR_SUCCESS)
472 int num = msi_get_property_int( package->db, szAllUsers, 0 );
473 if (num == 1 || num == 2)
474 package->Context = MSIINSTALLCONTEXT_MACHINE;
475 else
476 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
478 return ERROR_SUCCESS;
481 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
483 UINT rc;
484 LPCWSTR cond, action;
485 MSIPACKAGE *package = param;
487 action = MSI_RecordGetString(row,1);
488 if (!action)
490 ERR("Error is retrieving action name\n");
491 return ERROR_FUNCTION_FAILED;
494 /* check conditions */
495 cond = MSI_RecordGetString(row,2);
497 /* this is a hack to skip errors in the condition code */
498 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
500 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
501 return ERROR_SUCCESS;
504 if (needs_ui_sequence(package))
505 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
506 else
507 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
509 msi_dialog_check_messages( NULL );
511 if (package->CurrentInstallState != ERROR_SUCCESS)
512 rc = package->CurrentInstallState;
514 if (rc == ERROR_FUNCTION_NOT_CALLED)
515 rc = ERROR_SUCCESS;
517 if (rc != ERROR_SUCCESS)
518 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
520 if (package->need_reboot_now)
522 TRACE("action %s asked for immediate reboot, suspending installation\n",
523 debugstr_w(action));
524 rc = ACTION_ForceReboot( package );
526 return rc;
529 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
531 static const WCHAR query[] = {
532 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
533 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
534 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
535 '`','S','e','q','u','e','n','c','e','`',0};
536 MSIQUERY *view;
537 UINT r;
539 TRACE("%p %s\n", package, debugstr_w(table));
541 r = MSI_OpenQuery( package->db, &view, query, table );
542 if (r == ERROR_SUCCESS)
544 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
545 msiobj_release(&view->hdr);
547 return r;
550 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
552 static const WCHAR query[] = {
553 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
554 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
555 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
556 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
557 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
558 static const WCHAR query_validate[] = {
559 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
560 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
561 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
562 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
563 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
564 MSIQUERY *view;
565 INT seq = 0;
566 UINT rc;
568 if (package->script->ExecuteSequenceRun)
570 TRACE("Execute Sequence already Run\n");
571 return ERROR_SUCCESS;
574 package->script->ExecuteSequenceRun = TRUE;
576 /* get the sequence number */
577 if (UIran)
579 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
580 if (!row) return ERROR_FUNCTION_FAILED;
581 seq = MSI_RecordGetInteger(row,1);
582 msiobj_release(&row->hdr);
584 rc = MSI_OpenQuery(package->db, &view, query, seq);
585 if (rc == ERROR_SUCCESS)
587 TRACE("Running the actions\n");
589 msi_set_property( package->db, szSourceDir, NULL, -1 );
590 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
591 msiobj_release(&view->hdr);
593 return rc;
596 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
598 static const WCHAR query[] = {
599 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
600 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
601 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
602 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
603 MSIQUERY *view;
604 UINT rc;
606 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
607 if (rc == ERROR_SUCCESS)
609 TRACE("Running the actions\n");
610 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
611 msiobj_release(&view->hdr);
613 return rc;
616 /********************************************************
617 * ACTION helper functions and functions that perform the actions
618 *******************************************************/
619 static BOOL ACTION_HandleCustomAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc, UINT script )
621 BOOL ret=FALSE;
622 UINT arc;
624 arc = ACTION_CustomAction( package, action, script );
625 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
627 *rc = arc;
628 ret = TRUE;
630 return ret;
633 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
635 MSICOMPONENT *comp;
637 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
639 if (!strcmpW( Component, comp->Component )) return comp;
641 return NULL;
644 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
646 MSIFEATURE *feature;
648 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
650 if (!strcmpW( Feature, feature->Feature )) return feature;
652 return NULL;
655 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
657 MSIFILE *file;
659 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
661 if (!strcmpW( key, file->File )) return file;
663 return NULL;
666 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
668 MSIFOLDER *folder;
670 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
672 if (!strcmpW( dir, folder->Directory )) return folder;
674 return NULL;
678 * Recursively create all directories in the path.
679 * shamelessly stolen from setupapi/queue.c
681 BOOL msi_create_full_path( const WCHAR *path )
683 BOOL ret = TRUE;
684 WCHAR *new_path;
685 int len;
687 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
688 strcpyW( new_path, path );
690 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
691 new_path[len - 1] = 0;
693 while (!CreateDirectoryW( new_path, NULL ))
695 WCHAR *slash;
696 DWORD last_error = GetLastError();
697 if (last_error == ERROR_ALREADY_EXISTS) break;
698 if (last_error != ERROR_PATH_NOT_FOUND)
700 ret = FALSE;
701 break;
703 if (!(slash = strrchrW( new_path, '\\' )))
705 ret = FALSE;
706 break;
708 len = slash - new_path;
709 new_path[len] = 0;
710 if (!msi_create_full_path( new_path ))
712 ret = FALSE;
713 break;
715 new_path[len] = '\\';
717 msi_free( new_path );
718 return ret;
721 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
723 MSIRECORD *row;
725 row = MSI_CreateRecord( 4 );
726 MSI_RecordSetInteger( row, 1, a );
727 MSI_RecordSetInteger( row, 2, b );
728 MSI_RecordSetInteger( row, 3, c );
729 MSI_RecordSetInteger( row, 4, d );
730 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
731 msiobj_release( &row->hdr );
733 msi_dialog_check_messages( NULL );
736 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
738 static const WCHAR query[] =
739 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
740 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
741 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
742 WCHAR message[1024];
743 MSIRECORD *row = 0;
744 DWORD size;
746 if (!package->LastAction || strcmpW( package->LastAction, action ))
748 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
750 if (MSI_RecordIsNull( row, 3 ))
752 msiobj_release( &row->hdr );
753 return;
755 /* update the cached action format */
756 msi_free( package->ActionFormat );
757 package->ActionFormat = msi_dup_record_field( row, 3 );
758 msi_free( package->LastAction );
759 package->LastAction = strdupW( action );
760 msiobj_release( &row->hdr );
762 size = 1024;
763 MSI_RecordSetStringW( record, 0, package->ActionFormat );
764 MSI_FormatRecordW( package, record, message, &size );
765 row = MSI_CreateRecord( 1 );
766 MSI_RecordSetStringW( row, 1, message );
767 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
768 msiobj_release( &row->hdr );
771 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
773 if (!comp->Enabled)
775 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
776 return INSTALLSTATE_UNKNOWN;
778 if (package->need_rollback) return comp->Installed;
779 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
781 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
782 return INSTALLSTATE_UNKNOWN;
784 return comp->ActionRequest;
787 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
789 if (package->need_rollback) return feature->Installed;
790 return feature->ActionRequest;
793 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
795 MSIPACKAGE *package = param;
796 LPCWSTR dir, component, full_path;
797 MSIRECORD *uirow;
798 MSIFOLDER *folder;
799 MSICOMPONENT *comp;
801 component = MSI_RecordGetString(row, 2);
802 if (!component)
803 return ERROR_SUCCESS;
805 comp = msi_get_loaded_component(package, component);
806 if (!comp)
807 return ERROR_SUCCESS;
809 comp->Action = msi_get_component_action( package, comp );
810 if (comp->Action != INSTALLSTATE_LOCAL)
812 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
813 return ERROR_SUCCESS;
816 dir = MSI_RecordGetString(row,1);
817 if (!dir)
819 ERR("Unable to get folder id\n");
820 return ERROR_SUCCESS;
823 uirow = MSI_CreateRecord(1);
824 MSI_RecordSetStringW(uirow, 1, dir);
825 msi_ui_actiondata(package, szCreateFolders, uirow);
826 msiobj_release(&uirow->hdr);
828 full_path = msi_get_target_folder( package, dir );
829 if (!full_path)
831 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
832 return ERROR_SUCCESS;
834 TRACE("folder is %s\n", debugstr_w(full_path));
836 folder = msi_get_loaded_folder( package, dir );
837 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
838 folder->State = FOLDER_STATE_CREATED;
839 return ERROR_SUCCESS;
842 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
844 static const WCHAR query[] = {
845 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
846 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
847 MSIQUERY *view;
848 UINT rc;
850 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
851 if (rc != ERROR_SUCCESS)
852 return ERROR_SUCCESS;
854 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
855 msiobj_release(&view->hdr);
856 return rc;
859 static void remove_persistent_folder( MSIFOLDER *folder )
861 FolderList *fl;
863 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
865 remove_persistent_folder( fl->folder );
867 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
869 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
873 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
875 MSIPACKAGE *package = param;
876 LPCWSTR dir, component, full_path;
877 MSIRECORD *uirow;
878 MSIFOLDER *folder;
879 MSICOMPONENT *comp;
881 component = MSI_RecordGetString(row, 2);
882 if (!component)
883 return ERROR_SUCCESS;
885 comp = msi_get_loaded_component(package, component);
886 if (!comp)
887 return ERROR_SUCCESS;
889 comp->Action = msi_get_component_action( package, comp );
890 if (comp->Action != INSTALLSTATE_ABSENT)
892 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
893 return ERROR_SUCCESS;
896 dir = MSI_RecordGetString( row, 1 );
897 if (!dir)
899 ERR("Unable to get folder id\n");
900 return ERROR_SUCCESS;
903 full_path = msi_get_target_folder( package, dir );
904 if (!full_path)
906 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
907 return ERROR_SUCCESS;
909 TRACE("folder is %s\n", debugstr_w(full_path));
911 uirow = MSI_CreateRecord( 1 );
912 MSI_RecordSetStringW( uirow, 1, dir );
913 msi_ui_actiondata( package, szRemoveFolders, uirow );
914 msiobj_release( &uirow->hdr );
916 folder = msi_get_loaded_folder( package, dir );
917 remove_persistent_folder( folder );
918 return ERROR_SUCCESS;
921 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
923 static const WCHAR query[] = {
924 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
925 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
926 MSIQUERY *view;
927 UINT rc;
929 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
930 if (rc != ERROR_SUCCESS)
931 return ERROR_SUCCESS;
933 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
934 msiobj_release( &view->hdr );
935 return rc;
938 static UINT load_component( MSIRECORD *row, LPVOID param )
940 MSIPACKAGE *package = param;
941 MSICOMPONENT *comp;
943 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
944 if (!comp)
945 return ERROR_FUNCTION_FAILED;
947 list_add_tail( &package->components, &comp->entry );
949 /* fill in the data */
950 comp->Component = msi_dup_record_field( row, 1 );
952 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
954 comp->ComponentId = msi_dup_record_field( row, 2 );
955 comp->Directory = msi_dup_record_field( row, 3 );
956 comp->Attributes = MSI_RecordGetInteger(row,4);
957 comp->Condition = msi_dup_record_field( row, 5 );
958 comp->KeyPath = msi_dup_record_field( row, 6 );
960 comp->Installed = INSTALLSTATE_UNKNOWN;
961 comp->Action = INSTALLSTATE_UNKNOWN;
962 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
964 comp->assembly = msi_load_assembly( package, comp );
965 return ERROR_SUCCESS;
968 UINT msi_load_all_components( MSIPACKAGE *package )
970 static const WCHAR query[] = {
971 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
972 '`','C','o','m','p','o','n','e','n','t','`',0};
973 MSIQUERY *view;
974 UINT r;
976 if (!list_empty(&package->components))
977 return ERROR_SUCCESS;
979 r = MSI_DatabaseOpenViewW( package->db, query, &view );
980 if (r != ERROR_SUCCESS)
981 return r;
983 if (!msi_init_assembly_caches( package ))
985 ERR("can't initialize assembly caches\n");
986 msiobj_release( &view->hdr );
987 return ERROR_FUNCTION_FAILED;
990 r = MSI_IterateRecords(view, NULL, load_component, package);
991 msiobj_release(&view->hdr);
992 return r;
995 typedef struct {
996 MSIPACKAGE *package;
997 MSIFEATURE *feature;
998 } _ilfs;
1000 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1002 ComponentList *cl;
1004 cl = msi_alloc( sizeof (*cl) );
1005 if ( !cl )
1006 return ERROR_NOT_ENOUGH_MEMORY;
1007 cl->component = comp;
1008 list_add_tail( &feature->Components, &cl->entry );
1010 return ERROR_SUCCESS;
1013 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1015 FeatureList *fl;
1017 fl = msi_alloc( sizeof(*fl) );
1018 if ( !fl )
1019 return ERROR_NOT_ENOUGH_MEMORY;
1020 fl->feature = child;
1021 list_add_tail( &parent->Children, &fl->entry );
1023 return ERROR_SUCCESS;
1026 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1028 _ilfs* ilfs = param;
1029 LPCWSTR component;
1030 MSICOMPONENT *comp;
1032 component = MSI_RecordGetString(row,1);
1034 /* check to see if the component is already loaded */
1035 comp = msi_get_loaded_component( ilfs->package, component );
1036 if (!comp)
1038 WARN("ignoring unknown component %s\n", debugstr_w(component));
1039 return ERROR_SUCCESS;
1041 add_feature_component( ilfs->feature, comp );
1042 comp->Enabled = TRUE;
1044 return ERROR_SUCCESS;
1047 static UINT load_feature(MSIRECORD * row, LPVOID param)
1049 static const WCHAR query[] = {
1050 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1051 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1052 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1053 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1054 MSIPACKAGE *package = param;
1055 MSIFEATURE *feature;
1056 MSIQUERY *view;
1057 _ilfs ilfs;
1058 UINT rc;
1060 /* fill in the data */
1062 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1063 if (!feature)
1064 return ERROR_NOT_ENOUGH_MEMORY;
1066 list_init( &feature->Children );
1067 list_init( &feature->Components );
1069 feature->Feature = msi_dup_record_field( row, 1 );
1071 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1073 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1074 feature->Title = msi_dup_record_field( row, 3 );
1075 feature->Description = msi_dup_record_field( row, 4 );
1077 if (!MSI_RecordIsNull(row,5))
1078 feature->Display = MSI_RecordGetInteger(row,5);
1080 feature->Level= MSI_RecordGetInteger(row,6);
1081 feature->Directory = msi_dup_record_field( row, 7 );
1082 feature->Attributes = MSI_RecordGetInteger(row,8);
1084 feature->Installed = INSTALLSTATE_UNKNOWN;
1085 feature->Action = INSTALLSTATE_UNKNOWN;
1086 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1088 list_add_tail( &package->features, &feature->entry );
1090 /* load feature components */
1092 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1093 if (rc != ERROR_SUCCESS)
1094 return ERROR_SUCCESS;
1096 ilfs.package = package;
1097 ilfs.feature = feature;
1099 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1100 msiobj_release(&view->hdr);
1101 return rc;
1104 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1106 MSIPACKAGE *package = param;
1107 MSIFEATURE *parent, *child;
1109 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1110 if (!child)
1111 return ERROR_FUNCTION_FAILED;
1113 if (!child->Feature_Parent)
1114 return ERROR_SUCCESS;
1116 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1117 if (!parent)
1118 return ERROR_FUNCTION_FAILED;
1120 add_feature_child( parent, child );
1121 return ERROR_SUCCESS;
1124 UINT msi_load_all_features( MSIPACKAGE *package )
1126 static const WCHAR query[] = {
1127 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1128 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1129 '`','D','i','s','p','l','a','y','`',0};
1130 MSIQUERY *view;
1131 UINT r;
1133 if (!list_empty(&package->features))
1134 return ERROR_SUCCESS;
1136 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1137 if (r != ERROR_SUCCESS)
1138 return r;
1140 r = MSI_IterateRecords( view, NULL, load_feature, package );
1141 if (r != ERROR_SUCCESS)
1143 msiobj_release( &view->hdr );
1144 return r;
1146 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1147 msiobj_release( &view->hdr );
1148 return r;
1151 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1153 if (!p)
1154 return p;
1155 p = strchrW(p, ch);
1156 if (!p)
1157 return p;
1158 *p = 0;
1159 return p+1;
1162 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1164 static const WCHAR query[] = {
1165 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1166 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1167 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1168 MSIQUERY *view = NULL;
1169 MSIRECORD *row = NULL;
1170 UINT r;
1172 TRACE("%s\n", debugstr_w(file->File));
1174 r = MSI_OpenQuery(package->db, &view, query, file->File);
1175 if (r != ERROR_SUCCESS)
1176 goto done;
1178 r = MSI_ViewExecute(view, NULL);
1179 if (r != ERROR_SUCCESS)
1180 goto done;
1182 r = MSI_ViewFetch(view, &row);
1183 if (r != ERROR_SUCCESS)
1184 goto done;
1186 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1187 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1188 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1189 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1190 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1192 done:
1193 if (view) msiobj_release(&view->hdr);
1194 if (row) msiobj_release(&row->hdr);
1195 return r;
1198 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1200 MSIRECORD *row;
1201 static const WCHAR query[] = {
1202 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1203 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1204 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1206 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1207 if (!row)
1209 WARN("query failed\n");
1210 return ERROR_FUNCTION_FAILED;
1213 file->disk_id = MSI_RecordGetInteger( row, 1 );
1214 msiobj_release( &row->hdr );
1215 return ERROR_SUCCESS;
1218 static UINT load_file(MSIRECORD *row, LPVOID param)
1220 MSIPACKAGE* package = param;
1221 LPCWSTR component;
1222 MSIFILE *file;
1224 /* fill in the data */
1226 file = msi_alloc_zero( sizeof (MSIFILE) );
1227 if (!file)
1228 return ERROR_NOT_ENOUGH_MEMORY;
1230 file->File = msi_dup_record_field( row, 1 );
1232 component = MSI_RecordGetString( row, 2 );
1233 file->Component = msi_get_loaded_component( package, component );
1235 if (!file->Component)
1237 WARN("Component not found: %s\n", debugstr_w(component));
1238 msi_free(file->File);
1239 msi_free(file);
1240 return ERROR_SUCCESS;
1243 file->FileName = msi_dup_record_field( row, 3 );
1244 msi_reduce_to_long_filename( file->FileName );
1246 file->ShortName = msi_dup_record_field( row, 3 );
1247 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1249 file->FileSize = MSI_RecordGetInteger( row, 4 );
1250 file->Version = msi_dup_record_field( row, 5 );
1251 file->Language = msi_dup_record_field( row, 6 );
1252 file->Attributes = MSI_RecordGetInteger( row, 7 );
1253 file->Sequence = MSI_RecordGetInteger( row, 8 );
1255 file->state = msifs_invalid;
1257 /* if the compressed bits are not set in the file attributes,
1258 * then read the information from the package word count property
1260 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1262 file->IsCompressed = FALSE;
1264 else if (file->Attributes &
1265 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1267 file->IsCompressed = TRUE;
1269 else if (file->Attributes & msidbFileAttributesNoncompressed)
1271 file->IsCompressed = FALSE;
1273 else
1275 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1278 load_file_hash(package, file);
1279 load_file_disk_id(package, file);
1281 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1283 list_add_tail( &package->files, &file->entry );
1285 return ERROR_SUCCESS;
1288 static UINT load_all_files(MSIPACKAGE *package)
1290 static const WCHAR query[] = {
1291 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1292 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1293 '`','S','e','q','u','e','n','c','e','`', 0};
1294 MSIQUERY *view;
1295 UINT rc;
1297 if (!list_empty(&package->files))
1298 return ERROR_SUCCESS;
1300 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1301 if (rc != ERROR_SUCCESS)
1302 return ERROR_SUCCESS;
1304 rc = MSI_IterateRecords(view, NULL, load_file, package);
1305 msiobj_release(&view->hdr);
1306 return rc;
1309 static UINT load_media( MSIRECORD *row, LPVOID param )
1311 MSIPACKAGE *package = param;
1312 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1313 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1315 /* FIXME: load external cabinets and directory sources too */
1316 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1317 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1318 return ERROR_SUCCESS;
1321 static UINT load_all_media( MSIPACKAGE *package )
1323 static const WCHAR query[] = {
1324 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1325 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1326 '`','D','i','s','k','I','d','`',0};
1327 MSIQUERY *view;
1328 UINT r;
1330 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1331 if (r != ERROR_SUCCESS)
1332 return ERROR_SUCCESS;
1334 r = MSI_IterateRecords( view, NULL, load_media, package );
1335 msiobj_release( &view->hdr );
1336 return r;
1339 static UINT load_patch(MSIRECORD *row, LPVOID param)
1341 MSIPACKAGE *package = param;
1342 MSIFILEPATCH *patch;
1343 LPWSTR file_key;
1345 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1346 if (!patch)
1347 return ERROR_NOT_ENOUGH_MEMORY;
1349 file_key = msi_dup_record_field( row, 1 );
1350 patch->File = msi_get_loaded_file( package, file_key );
1351 msi_free(file_key);
1353 if( !patch->File )
1355 ERR("Failed to find target for patch in File table\n");
1356 msi_free(patch);
1357 return ERROR_FUNCTION_FAILED;
1360 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1362 /* FIXME: The database should be properly transformed */
1363 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1365 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1366 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1367 patch->IsApplied = FALSE;
1369 /* FIXME:
1370 * Header field - for patch validation.
1371 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1374 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1376 list_add_tail( &package->filepatches, &patch->entry );
1378 return ERROR_SUCCESS;
1381 static UINT load_all_patches(MSIPACKAGE *package)
1383 static const WCHAR query[] = {
1384 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1385 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1386 '`','S','e','q','u','e','n','c','e','`',0};
1387 MSIQUERY *view;
1388 UINT rc;
1390 if (!list_empty(&package->filepatches))
1391 return ERROR_SUCCESS;
1393 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1394 if (rc != ERROR_SUCCESS)
1395 return ERROR_SUCCESS;
1397 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1398 msiobj_release(&view->hdr);
1399 return rc;
1402 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1404 static const WCHAR query[] = {
1405 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1406 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1407 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1408 MSIQUERY *view;
1410 folder->persistent = FALSE;
1411 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1413 if (!MSI_ViewExecute( view, NULL ))
1415 MSIRECORD *rec;
1416 if (!MSI_ViewFetch( view, &rec ))
1418 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1419 folder->persistent = TRUE;
1420 msiobj_release( &rec->hdr );
1423 msiobj_release( &view->hdr );
1425 return ERROR_SUCCESS;
1428 static UINT load_folder( MSIRECORD *row, LPVOID param )
1430 MSIPACKAGE *package = param;
1431 static WCHAR szEmpty[] = { 0 };
1432 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1433 MSIFOLDER *folder;
1435 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1436 list_init( &folder->children );
1437 folder->Directory = msi_dup_record_field( row, 1 );
1438 folder->Parent = msi_dup_record_field( row, 2 );
1439 p = msi_dup_record_field(row, 3);
1441 TRACE("%s\n", debugstr_w(folder->Directory));
1443 /* split src and target dir */
1444 tgt_short = p;
1445 src_short = folder_split_path( p, ':' );
1447 /* split the long and short paths */
1448 tgt_long = folder_split_path( tgt_short, '|' );
1449 src_long = folder_split_path( src_short, '|' );
1451 /* check for no-op dirs */
1452 if (tgt_short && !strcmpW( szDot, tgt_short ))
1453 tgt_short = szEmpty;
1454 if (src_short && !strcmpW( szDot, src_short ))
1455 src_short = szEmpty;
1457 if (!tgt_long)
1458 tgt_long = tgt_short;
1460 if (!src_short) {
1461 src_short = tgt_short;
1462 src_long = tgt_long;
1465 if (!src_long)
1466 src_long = src_short;
1468 /* FIXME: use the target short path too */
1469 folder->TargetDefault = strdupW(tgt_long);
1470 folder->SourceShortPath = strdupW(src_short);
1471 folder->SourceLongPath = strdupW(src_long);
1472 msi_free(p);
1474 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1475 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1476 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1478 load_folder_persistence( package, folder );
1480 list_add_tail( &package->folders, &folder->entry );
1481 return ERROR_SUCCESS;
1484 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1486 FolderList *fl;
1488 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1489 fl->folder = child;
1490 list_add_tail( &parent->children, &fl->entry );
1491 return ERROR_SUCCESS;
1494 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1496 MSIPACKAGE *package = param;
1497 MSIFOLDER *parent, *child;
1499 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1500 return ERROR_FUNCTION_FAILED;
1502 if (!child->Parent) return ERROR_SUCCESS;
1504 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1505 return ERROR_FUNCTION_FAILED;
1507 return add_folder_child( parent, child );
1510 static UINT load_all_folders( MSIPACKAGE *package )
1512 static const WCHAR query[] = {
1513 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1514 '`','D','i','r','e','c','t','o','r','y','`',0};
1515 MSIQUERY *view;
1516 UINT r;
1518 if (!list_empty(&package->folders))
1519 return ERROR_SUCCESS;
1521 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1522 if (r != ERROR_SUCCESS)
1523 return r;
1525 r = MSI_IterateRecords( view, NULL, load_folder, package );
1526 if (r != ERROR_SUCCESS)
1528 msiobj_release( &view->hdr );
1529 return r;
1531 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1532 msiobj_release( &view->hdr );
1533 return r;
1536 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1538 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1539 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1541 load_all_folders( package );
1542 msi_load_all_components( package );
1543 msi_load_all_features( package );
1544 load_all_files( package );
1545 load_all_patches( package );
1546 load_all_media( package );
1548 return ERROR_SUCCESS;
1551 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1553 const WCHAR *action = package->script->Actions[script][index];
1554 ui_actionstart( package, action );
1555 TRACE("executing %s\n", debugstr_w(action));
1556 return ACTION_PerformAction( package, action, script );
1559 static UINT execute_script( MSIPACKAGE *package, UINT script )
1561 UINT i, rc = ERROR_SUCCESS;
1563 TRACE("executing script %u\n", script);
1565 if (!package->script)
1567 ERR("no script!\n");
1568 return ERROR_FUNCTION_FAILED;
1570 if (script == SCRIPT_ROLLBACK)
1572 for (i = package->script->ActionCount[script]; i > 0; i--)
1574 rc = execute_script_action( package, script, i - 1 );
1575 if (rc != ERROR_SUCCESS) break;
1578 else
1580 for (i = 0; i < package->script->ActionCount[script]; i++)
1582 rc = execute_script_action( package, script, i );
1583 if (rc != ERROR_SUCCESS) break;
1586 msi_free_action_script(package, script);
1587 return rc;
1590 static UINT ACTION_FileCost(MSIPACKAGE *package)
1592 return ERROR_SUCCESS;
1595 static void get_client_counts( MSIPACKAGE *package )
1597 MSICOMPONENT *comp;
1598 HKEY hkey;
1600 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1602 if (!comp->ComponentId) continue;
1604 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1605 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1607 comp->num_clients = 0;
1608 continue;
1610 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1611 NULL, NULL, NULL, NULL );
1612 RegCloseKey( hkey );
1616 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1618 MSICOMPONENT *comp;
1619 UINT r;
1621 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1623 if (!comp->ComponentId) continue;
1625 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1626 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1627 &comp->Installed );
1628 if (r == ERROR_SUCCESS) continue;
1630 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1631 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1632 &comp->Installed );
1633 if (r == ERROR_SUCCESS) continue;
1635 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1636 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1637 &comp->Installed );
1638 if (r == ERROR_SUCCESS) continue;
1640 comp->Installed = INSTALLSTATE_ABSENT;
1644 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1646 MSIFEATURE *feature;
1648 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1650 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1652 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1653 feature->Installed = INSTALLSTATE_ABSENT;
1654 else
1655 feature->Installed = state;
1659 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1661 return (feature->Level > 0 && feature->Level <= level);
1664 static BOOL process_state_property(MSIPACKAGE* package, int level,
1665 LPCWSTR property, INSTALLSTATE state)
1667 LPWSTR override;
1668 MSIFEATURE *feature;
1669 BOOL remove = !strcmpW(property, szRemove);
1670 BOOL reinstall = !strcmpW(property, szReinstall);
1672 override = msi_dup_property( package->db, property );
1673 if (!override)
1674 return FALSE;
1676 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1678 if (feature->Level <= 0)
1679 continue;
1681 if (reinstall)
1682 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1683 else if (remove)
1684 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1686 if (!strcmpiW( override, szAll ))
1688 feature->Action = state;
1689 feature->ActionRequest = state;
1691 else
1693 LPWSTR ptr = override;
1694 LPWSTR ptr2 = strchrW(override,',');
1696 while (ptr)
1698 int len = ptr2 - ptr;
1700 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1701 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1703 feature->Action = state;
1704 feature->ActionRequest = state;
1705 break;
1707 if (ptr2)
1709 ptr=ptr2+1;
1710 ptr2 = strchrW(ptr,',');
1712 else
1713 break;
1717 msi_free(override);
1718 return TRUE;
1721 static BOOL process_overrides( MSIPACKAGE *package, int level )
1723 static const WCHAR szAddLocal[] =
1724 {'A','D','D','L','O','C','A','L',0};
1725 static const WCHAR szAddSource[] =
1726 {'A','D','D','S','O','U','R','C','E',0};
1727 static const WCHAR szAdvertise[] =
1728 {'A','D','V','E','R','T','I','S','E',0};
1729 BOOL ret = FALSE;
1731 /* all these activation/deactivation things happen in order and things
1732 * later on the list override things earlier on the list.
1734 * 0 INSTALLLEVEL processing
1735 * 1 ADDLOCAL
1736 * 2 REMOVE
1737 * 3 ADDSOURCE
1738 * 4 ADDDEFAULT
1739 * 5 REINSTALL
1740 * 6 ADVERTISE
1741 * 7 COMPADDLOCAL
1742 * 8 COMPADDSOURCE
1743 * 9 FILEADDLOCAL
1744 * 10 FILEADDSOURCE
1745 * 11 FILEADDDEFAULT
1747 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1748 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1749 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1750 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1751 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1753 if (ret && !package->full_reinstall)
1754 msi_set_property( package->db, szPreselected, szOne, -1 );
1756 return ret;
1759 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1761 int level;
1762 MSICOMPONENT* component;
1763 MSIFEATURE *feature;
1765 TRACE("Checking Install Level\n");
1767 level = msi_get_property_int(package->db, szInstallLevel, 1);
1769 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1771 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1773 if (!is_feature_selected( feature, level )) continue;
1775 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1777 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1779 feature->Action = INSTALLSTATE_SOURCE;
1780 feature->ActionRequest = INSTALLSTATE_SOURCE;
1782 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1784 feature->Action = INSTALLSTATE_ADVERTISED;
1785 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1787 else
1789 feature->Action = INSTALLSTATE_LOCAL;
1790 feature->ActionRequest = INSTALLSTATE_LOCAL;
1794 /* disable child features of unselected parent or follow parent */
1795 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1797 FeatureList *fl;
1799 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1801 if (!is_feature_selected( feature, level ))
1803 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1804 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1806 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1808 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1809 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1810 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1811 fl->feature->Action = feature->Action;
1812 fl->feature->ActionRequest = feature->ActionRequest;
1817 else /* preselected */
1819 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1821 if (!is_feature_selected( feature, level )) continue;
1823 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1825 if (feature->Installed == INSTALLSTATE_ABSENT)
1827 feature->Action = INSTALLSTATE_UNKNOWN;
1828 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1830 else
1832 feature->Action = feature->Installed;
1833 feature->ActionRequest = feature->Installed;
1837 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1839 FeatureList *fl;
1841 if (!is_feature_selected( feature, level )) continue;
1843 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1845 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1846 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1848 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1849 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1850 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1851 fl->feature->Action = feature->Action;
1852 fl->feature->ActionRequest = feature->ActionRequest;
1858 /* now we want to set component state based based on feature state */
1859 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1861 ComponentList *cl;
1863 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1864 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1865 feature->ActionRequest, feature->Action);
1867 /* features with components that have compressed files are made local */
1868 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1870 if (cl->component->ForceLocalState &&
1871 feature->ActionRequest == INSTALLSTATE_SOURCE)
1873 feature->Action = INSTALLSTATE_LOCAL;
1874 feature->ActionRequest = INSTALLSTATE_LOCAL;
1875 break;
1879 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1881 component = cl->component;
1883 switch (feature->ActionRequest)
1885 case INSTALLSTATE_ABSENT:
1886 component->anyAbsent = 1;
1887 break;
1888 case INSTALLSTATE_ADVERTISED:
1889 component->hasAdvertisedFeature = 1;
1890 break;
1891 case INSTALLSTATE_SOURCE:
1892 component->hasSourceFeature = 1;
1893 break;
1894 case INSTALLSTATE_LOCAL:
1895 component->hasLocalFeature = 1;
1896 break;
1897 case INSTALLSTATE_DEFAULT:
1898 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1899 component->hasAdvertisedFeature = 1;
1900 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1901 component->hasSourceFeature = 1;
1902 else
1903 component->hasLocalFeature = 1;
1904 break;
1905 default:
1906 break;
1911 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1913 /* check if it's local or source */
1914 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1915 (component->hasLocalFeature || component->hasSourceFeature))
1917 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1918 !component->ForceLocalState)
1920 component->Action = INSTALLSTATE_SOURCE;
1921 component->ActionRequest = INSTALLSTATE_SOURCE;
1923 else
1925 component->Action = INSTALLSTATE_LOCAL;
1926 component->ActionRequest = INSTALLSTATE_LOCAL;
1928 continue;
1931 /* if any feature is local, the component must be local too */
1932 if (component->hasLocalFeature)
1934 component->Action = INSTALLSTATE_LOCAL;
1935 component->ActionRequest = INSTALLSTATE_LOCAL;
1936 continue;
1938 if (component->hasSourceFeature)
1940 component->Action = INSTALLSTATE_SOURCE;
1941 component->ActionRequest = INSTALLSTATE_SOURCE;
1942 continue;
1944 if (component->hasAdvertisedFeature)
1946 component->Action = INSTALLSTATE_ADVERTISED;
1947 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1948 continue;
1950 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1951 if (component->anyAbsent && component->ComponentId)
1953 component->Action = INSTALLSTATE_ABSENT;
1954 component->ActionRequest = INSTALLSTATE_ABSENT;
1958 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1960 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1962 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1963 component->Action = INSTALLSTATE_LOCAL;
1964 component->ActionRequest = INSTALLSTATE_LOCAL;
1967 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1968 component->Installed == INSTALLSTATE_SOURCE &&
1969 component->hasSourceFeature)
1971 component->Action = INSTALLSTATE_UNKNOWN;
1972 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1975 TRACE("component %s (installed %d request %d action %d)\n",
1976 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1978 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1979 component->num_clients++;
1980 else if (component->Action == INSTALLSTATE_ABSENT)
1981 component->num_clients--;
1984 return ERROR_SUCCESS;
1987 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1989 MSIPACKAGE *package = param;
1990 LPCWSTR name;
1991 MSIFEATURE *feature;
1993 name = MSI_RecordGetString( row, 1 );
1995 feature = msi_get_loaded_feature( package, name );
1996 if (!feature)
1997 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1998 else
2000 LPCWSTR Condition;
2001 Condition = MSI_RecordGetString(row,3);
2003 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2005 int level = MSI_RecordGetInteger(row,2);
2006 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2007 feature->Level = level;
2010 return ERROR_SUCCESS;
2013 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2015 static const WCHAR name[] = {'\\',0};
2016 VS_FIXEDFILEINFO *ptr, *ret;
2017 LPVOID version;
2018 DWORD versize, handle;
2019 UINT sz;
2021 versize = GetFileVersionInfoSizeW( filename, &handle );
2022 if (!versize)
2023 return NULL;
2025 version = msi_alloc( versize );
2026 if (!version)
2027 return NULL;
2029 GetFileVersionInfoW( filename, 0, versize, version );
2031 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2033 msi_free( version );
2034 return NULL;
2037 ret = msi_alloc( sz );
2038 memcpy( ret, ptr, sz );
2040 msi_free( version );
2041 return ret;
2044 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2046 DWORD ms, ls;
2048 msi_parse_version_string( version, &ms, &ls );
2050 if (fi->dwFileVersionMS > ms) return 1;
2051 else if (fi->dwFileVersionMS < ms) return -1;
2052 else if (fi->dwFileVersionLS > ls) return 1;
2053 else if (fi->dwFileVersionLS < ls) return -1;
2054 return 0;
2057 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2059 DWORD ms1, ms2;
2061 msi_parse_version_string( ver1, &ms1, NULL );
2062 msi_parse_version_string( ver2, &ms2, NULL );
2064 if (ms1 > ms2) return 1;
2065 else if (ms1 < ms2) return -1;
2066 return 0;
2069 DWORD msi_get_disk_file_size( LPCWSTR filename )
2071 HANDLE file;
2072 DWORD size;
2074 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2075 if (file == INVALID_HANDLE_VALUE)
2076 return INVALID_FILE_SIZE;
2078 size = GetFileSize( file, NULL );
2079 TRACE("size is %u\n", size);
2080 CloseHandle( file );
2081 return size;
2084 BOOL msi_file_hash_matches( MSIFILE *file )
2086 UINT r;
2087 MSIFILEHASHINFO hash;
2089 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2090 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2091 if (r != ERROR_SUCCESS)
2092 return FALSE;
2094 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2097 static WCHAR *get_temp_dir( void )
2099 static UINT id;
2100 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2102 GetTempPathW( MAX_PATH, tmp );
2103 for (;;)
2105 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2106 if (CreateDirectoryW( dir, NULL )) break;
2108 return strdupW( dir );
2112 * msi_build_directory_name()
2114 * This function is to save messing round with directory names
2115 * It handles adding backslashes between path segments,
2116 * and can add \ at the end of the directory name if told to.
2118 * It takes a variable number of arguments.
2119 * It always allocates a new string for the result, so make sure
2120 * to free the return value when finished with it.
2122 * The first arg is the number of path segments that follow.
2123 * The arguments following count are a list of path segments.
2124 * A path segment may be NULL.
2126 * Path segments will be added with a \ separating them.
2127 * A \ will not be added after the last segment, however if the
2128 * last segment is NULL, then the last character will be a \
2130 WCHAR *msi_build_directory_name( DWORD count, ... )
2132 DWORD sz = 1, i;
2133 WCHAR *dir;
2134 va_list va;
2136 va_start( va, count );
2137 for (i = 0; i < count; i++)
2139 const WCHAR *str = va_arg( va, const WCHAR * );
2140 if (str) sz += strlenW( str ) + 1;
2142 va_end( va );
2144 dir = msi_alloc( sz * sizeof(WCHAR) );
2145 dir[0] = 0;
2147 va_start( va, count );
2148 for (i = 0; i < count; i++)
2150 const WCHAR *str = va_arg( va, const WCHAR * );
2151 if (!str) continue;
2152 strcatW( dir, str );
2153 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2155 va_end( va );
2156 return dir;
2159 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2161 MSIASSEMBLY *assembly = file->Component->assembly;
2163 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2165 msi_free( file->TargetPath );
2166 if (assembly && !assembly->application)
2168 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2169 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2170 msi_track_tempfile( package, file->TargetPath );
2172 else
2174 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2175 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2178 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2181 static UINT calculate_file_cost( MSIPACKAGE *package )
2183 VS_FIXEDFILEINFO *file_version;
2184 WCHAR *font_version;
2185 MSIFILE *file;
2187 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2189 MSICOMPONENT *comp = file->Component;
2190 DWORD file_size;
2192 if (!comp->Enabled) continue;
2194 if (file->IsCompressed)
2195 comp->ForceLocalState = TRUE;
2197 set_target_path( package, file );
2199 if ((comp->assembly && !comp->assembly->installed) ||
2200 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2202 comp->Cost += file->FileSize;
2203 continue;
2205 file_size = msi_get_disk_file_size( file->TargetPath );
2207 if (file->Version)
2209 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2211 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2213 comp->Cost += file->FileSize - file_size;
2215 msi_free( file_version );
2216 continue;
2218 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2220 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2222 comp->Cost += file->FileSize - file_size;
2224 msi_free( font_version );
2225 continue;
2228 if (file_size != file->FileSize)
2230 comp->Cost += file->FileSize - file_size;
2233 return ERROR_SUCCESS;
2236 WCHAR *msi_normalize_path( const WCHAR *in )
2238 const WCHAR *p = in;
2239 WCHAR *q, *ret;
2240 int n, len = strlenW( in ) + 2;
2242 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2244 len = 0;
2245 while (1)
2247 /* copy until the end of the string or a space */
2248 while (*p != ' ' && (*q = *p))
2250 p++, len++;
2251 /* reduce many backslashes to one */
2252 if (*p != '\\' || *q != '\\')
2253 q++;
2256 /* quit at the end of the string */
2257 if (!*p)
2258 break;
2260 /* count the number of spaces */
2261 n = 0;
2262 while (p[n] == ' ')
2263 n++;
2265 /* if it's leading or trailing space, skip it */
2266 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2267 p += n;
2268 else /* copy n spaces */
2269 while (n && (*q++ = *p++)) n--;
2271 while (q - ret > 0 && q[-1] == ' ') q--;
2272 if (q - ret > 0 && q[-1] != '\\')
2274 q[0] = '\\';
2275 q[1] = 0;
2277 return ret;
2280 static WCHAR *get_install_location( MSIPACKAGE *package )
2282 HKEY hkey;
2283 WCHAR *path;
2285 if (!package->ProductCode) return NULL;
2286 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2287 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2289 msi_free( path );
2290 path = NULL;
2292 RegCloseKey( hkey );
2293 return path;
2296 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2298 FolderList *fl;
2299 MSIFOLDER *folder, *parent, *child;
2300 WCHAR *path, *normalized_path;
2302 TRACE("resolving %s\n", debugstr_w(name));
2304 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2306 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2308 if (!(path = get_install_location( package )) &&
2309 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2311 path = msi_dup_property( package->db, szRootDrive );
2314 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2316 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2318 parent = msi_get_loaded_folder( package, folder->Parent );
2319 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2321 else
2322 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2324 normalized_path = msi_normalize_path( path );
2325 msi_free( path );
2326 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2328 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2329 msi_free( normalized_path );
2330 return;
2332 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2333 msi_free( folder->ResolvedTarget );
2334 folder->ResolvedTarget = normalized_path;
2336 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2338 child = fl->folder;
2339 msi_resolve_target_folder( package, child->Directory, load_prop );
2341 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2344 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2346 static const WCHAR query[] =
2347 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2348 '`','C','o','n','d','i','t','i','o','n','`',0};
2349 static const WCHAR szOutOfDiskSpace[] =
2350 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2351 static const WCHAR szPrimaryFolder[] =
2352 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2353 static const WCHAR szPrimaryVolumePath[] =
2354 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2355 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2356 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2357 'A','v','a','i','l','a','b','l','e',0};
2358 static const WCHAR szOutOfNoRbDiskSpace[] =
2359 {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0};
2360 MSICOMPONENT *comp;
2361 MSIQUERY *view;
2362 WCHAR *level, *primary_key, *primary_folder;
2363 UINT rc;
2365 TRACE("Building directory properties\n");
2366 msi_resolve_target_folder( package, szTargetDir, TRUE );
2368 TRACE("Evaluating component conditions\n");
2369 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2371 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2373 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2374 comp->Enabled = FALSE;
2376 else
2377 comp->Enabled = TRUE;
2379 get_client_counts( package );
2381 /* read components states from the registry */
2382 ACTION_GetComponentInstallStates(package);
2383 ACTION_GetFeatureInstallStates(package);
2385 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2387 TRACE("Evaluating feature conditions\n");
2389 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2390 if (rc == ERROR_SUCCESS)
2392 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2393 msiobj_release( &view->hdr );
2394 if (rc != ERROR_SUCCESS)
2395 return rc;
2399 TRACE("Calculating file cost\n");
2400 calculate_file_cost( package );
2402 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2403 /* set default run level if not set */
2404 level = msi_dup_property( package->db, szInstallLevel );
2405 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2406 msi_free(level);
2408 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2410 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2412 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2413 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2415 ULARGE_INTEGER free;
2417 primary_folder[2] = 0;
2418 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2420 static const WCHAR fmtW[] = {'%','l','u',0};
2421 WCHAR buf[21];
2423 sprintfW( buf, fmtW, free.QuadPart / 512 );
2424 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2426 toupperW( primary_folder[0] );
2427 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2429 msi_free( primary_folder );
2431 msi_free( primary_key );
2434 /* FIXME: check volume disk space */
2435 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2436 msi_set_property( package->db, szOutOfNoRbDiskSpace, szZero, -1 );
2438 return MSI_SetFeatureStates(package);
2441 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size )
2443 BYTE *data;
2445 if (!value)
2447 *size = sizeof(WCHAR);
2448 *type = REG_SZ;
2449 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2450 return data;
2452 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2454 if (value[1]=='x')
2456 LPWSTR ptr;
2457 CHAR byte[5];
2458 LPWSTR deformated = NULL;
2459 int count;
2461 deformat_string(package, &value[2], &deformated);
2463 /* binary value type */
2464 ptr = deformated;
2465 *type = REG_BINARY;
2466 if (strlenW(ptr)%2)
2467 *size = (strlenW(ptr)/2)+1;
2468 else
2469 *size = strlenW(ptr)/2;
2471 data = msi_alloc(*size);
2473 byte[0] = '0';
2474 byte[1] = 'x';
2475 byte[4] = 0;
2476 count = 0;
2477 /* if uneven pad with a zero in front */
2478 if (strlenW(ptr)%2)
2480 byte[2]= '0';
2481 byte[3]= *ptr;
2482 ptr++;
2483 data[count] = (BYTE)strtol(byte,NULL,0);
2484 count ++;
2485 TRACE("Uneven byte count\n");
2487 while (*ptr)
2489 byte[2]= *ptr;
2490 ptr++;
2491 byte[3]= *ptr;
2492 ptr++;
2493 data[count] = (BYTE)strtol(byte,NULL,0);
2494 count ++;
2496 msi_free(deformated);
2498 TRACE("Data %i bytes(%i)\n",*size,count);
2500 else
2502 LPWSTR deformated;
2503 LPWSTR p;
2504 DWORD d = 0;
2505 deformat_string(package, &value[1], &deformated);
2507 *type=REG_DWORD;
2508 *size = sizeof(DWORD);
2509 data = msi_alloc(*size);
2510 p = deformated;
2511 if (*p == '-')
2512 p++;
2513 while (*p)
2515 if ( (*p < '0') || (*p > '9') )
2516 break;
2517 d *= 10;
2518 d += (*p - '0');
2519 p++;
2521 if (deformated[0] == '-')
2522 d = -d;
2523 *(LPDWORD)data = d;
2524 TRACE("DWORD %i\n",*(LPDWORD)data);
2526 msi_free(deformated);
2529 else
2531 const WCHAR *ptr = value;
2533 *type = REG_SZ;
2534 if (value[0] == '#')
2536 ptr++; len--;
2537 if (value[1] == '%')
2539 ptr++; len--;
2540 *type = REG_EXPAND_SZ;
2543 data = (BYTE *)msi_strdupW( ptr, len );
2544 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2545 *size = (len + 1) * sizeof(WCHAR);
2547 return data;
2550 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2552 const WCHAR *ret;
2554 switch (root)
2556 case -1:
2557 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2559 *root_key = HKEY_LOCAL_MACHINE;
2560 ret = szHLM;
2562 else
2564 *root_key = HKEY_CURRENT_USER;
2565 ret = szHCU;
2567 break;
2568 case 0:
2569 *root_key = HKEY_CLASSES_ROOT;
2570 ret = szHCR;
2571 break;
2572 case 1:
2573 *root_key = HKEY_CURRENT_USER;
2574 ret = szHCU;
2575 break;
2576 case 2:
2577 *root_key = HKEY_LOCAL_MACHINE;
2578 ret = szHLM;
2579 break;
2580 case 3:
2581 *root_key = HKEY_USERS;
2582 ret = szHU;
2583 break;
2584 default:
2585 ERR("Unknown root %i\n", root);
2586 return NULL;
2589 return ret;
2592 static WCHAR *get_keypath( MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2594 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2595 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2597 if ((is_64bit || is_wow64) &&
2598 !(comp->Attributes & msidbComponentAttributes64bit) &&
2599 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2601 UINT size;
2602 WCHAR *path_32node;
2604 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2605 if (!(path_32node = msi_alloc( size ))) return NULL;
2607 memcpy( path_32node, path, len * sizeof(WCHAR) );
2608 strcpyW( path_32node + len, szWow6432Node );
2609 strcatW( path_32node, szBackSlash );
2610 strcatW( path_32node, path + len );
2611 return path_32node;
2613 return strdupW( path );
2616 static HKEY open_key( HKEY root, const WCHAR *path, BOOL create )
2618 REGSAM access = KEY_ALL_ACCESS;
2619 WCHAR *subkey, *p, *q;
2620 HKEY hkey, ret = NULL;
2621 LONG res;
2623 if (is_wow64) access |= KEY_WOW64_64KEY;
2625 if (!(subkey = strdupW( path ))) return NULL;
2626 p = subkey;
2627 if ((q = strchrW( p, '\\' ))) *q = 0;
2628 if (create)
2629 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2630 else
2631 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2632 if (res)
2634 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2635 msi_free( subkey );
2636 return NULL;
2638 if (q && q[1])
2640 ret = open_key( hkey, q + 1, create );
2641 RegCloseKey( hkey );
2643 else ret = hkey;
2644 msi_free( subkey );
2645 return ret;
2648 static BOOL is_special_entry( const WCHAR *name )
2650 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2653 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2655 const WCHAR *p = str;
2656 WCHAR **ret;
2657 int i = 0;
2659 *count = 0;
2660 if (!str) return NULL;
2661 while ((p - str) < len)
2663 p += strlenW( p ) + 1;
2664 (*count)++;
2666 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2667 p = str;
2668 while ((p - str) < len)
2670 if (!(ret[i] = strdupW( p )))
2672 for (; i >= 0; i--) msi_free( ret[i] );
2673 msi_free( ret );
2674 return NULL;
2676 p += strlenW( p ) + 1;
2677 i++;
2679 return ret;
2682 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2683 WCHAR **right, DWORD right_count, DWORD *size )
2685 WCHAR *ret, *p;
2686 unsigned int i;
2688 *size = sizeof(WCHAR);
2689 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2690 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2692 if (!(ret = p = msi_alloc( *size ))) return NULL;
2694 for (i = 0; i < left_count; i++)
2696 strcpyW( p, left[i] );
2697 p += strlenW( p ) + 1;
2699 for (i = 0; i < right_count; i++)
2701 strcpyW( p, right[i] );
2702 p += strlenW( p ) + 1;
2704 *p = 0;
2705 return ret;
2708 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2709 WCHAR **new, DWORD new_count )
2711 DWORD ret = old_count;
2712 unsigned int i, j, k;
2714 for (i = 0; i < new_count; i++)
2716 for (j = 0; j < old_count; j++)
2718 if (old[j] && !strcmpW( new[i], old[j] ))
2720 msi_free( old[j] );
2721 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2722 old[k] = NULL;
2723 ret--;
2727 return ret;
2730 enum join_op
2732 JOIN_OP_APPEND,
2733 JOIN_OP_PREPEND,
2734 JOIN_OP_REPLACE
2737 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2738 WCHAR **new, DWORD new_count, DWORD *size )
2740 switch (op)
2742 case JOIN_OP_APPEND:
2743 old_count = remove_duplicate_values( old, old_count, new, new_count );
2744 return flatten_multi_string_values( old, old_count, new, new_count, size );
2746 case JOIN_OP_PREPEND:
2747 old_count = remove_duplicate_values( old, old_count, new, new_count );
2748 return flatten_multi_string_values( new, new_count, old, old_count, size );
2750 case JOIN_OP_REPLACE:
2751 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2753 default:
2754 ERR("unhandled join op %u\n", op);
2755 return NULL;
2759 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2760 BYTE *new_value, DWORD new_size, DWORD *size )
2762 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2763 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2764 enum join_op op = JOIN_OP_REPLACE;
2765 WCHAR **old = NULL, **new = NULL;
2766 BYTE *ret;
2768 if (new_size / sizeof(WCHAR) - 1 > 1)
2770 new_ptr = (const WCHAR *)new_value;
2771 new_len = new_size / sizeof(WCHAR) - 1;
2773 if (!new_ptr[0] && new_ptr[new_len - 1])
2775 op = JOIN_OP_APPEND;
2776 new_len--;
2777 new_ptr++;
2779 else if (new_ptr[0] && !new_ptr[new_len - 1])
2781 op = JOIN_OP_PREPEND;
2782 new_len--;
2784 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2786 op = JOIN_OP_REPLACE;
2787 new_len -= 2;
2788 new_ptr++;
2790 new = split_multi_string_values( new_ptr, new_len, &new_count );
2792 if (old_size / sizeof(WCHAR) - 1 > 1)
2794 old_ptr = (const WCHAR *)old_value;
2795 old_len = old_size / sizeof(WCHAR) - 1;
2796 old = split_multi_string_values( old_ptr, old_len, &old_count );
2798 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2799 for (i = 0; i < old_count; i++) msi_free( old[i] );
2800 for (i = 0; i < new_count; i++) msi_free( new[i] );
2801 msi_free( old );
2802 msi_free( new );
2803 return ret;
2806 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2808 BYTE *ret;
2809 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2810 if (!(ret = msi_alloc( *size ))) return NULL;
2811 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2812 return ret;
2815 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2817 MSIPACKAGE *package = param;
2818 BYTE *new_value, *old_value = NULL;
2819 HKEY root_key, hkey;
2820 DWORD type, old_type, new_size, old_size = 0;
2821 LPWSTR deformated, uikey, keypath;
2822 const WCHAR *szRoot, *component, *name, *key, *str;
2823 MSICOMPONENT *comp;
2824 MSIRECORD * uirow;
2825 INT root;
2826 BOOL check_first = FALSE;
2827 int len;
2829 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2831 component = MSI_RecordGetString(row, 6);
2832 comp = msi_get_loaded_component(package,component);
2833 if (!comp)
2834 return ERROR_SUCCESS;
2836 comp->Action = msi_get_component_action( package, comp );
2837 if (comp->Action != INSTALLSTATE_LOCAL)
2839 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2840 return ERROR_SUCCESS;
2843 name = MSI_RecordGetString(row, 4);
2844 if( MSI_RecordIsNull(row,5) && name )
2846 /* null values can have special meanings */
2847 if (name[0]=='-' && name[1] == 0)
2848 return ERROR_SUCCESS;
2849 if ((name[0] == '+' || name[0] == '*') && !name[1])
2850 check_first = TRUE;
2853 root = MSI_RecordGetInteger(row,2);
2854 key = MSI_RecordGetString(row, 3);
2856 szRoot = get_root_key( package, root, &root_key );
2857 if (!szRoot)
2858 return ERROR_SUCCESS;
2860 deformat_string(package, key , &deformated);
2861 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2862 strcpyW(uikey,szRoot);
2863 strcatW(uikey,deformated);
2865 keypath = get_keypath( comp, root_key, deformated );
2866 msi_free( deformated );
2867 if (!(hkey = open_key( root_key, keypath, TRUE )))
2869 ERR("Could not create key %s\n", debugstr_w(keypath));
2870 msi_free(uikey);
2871 msi_free(keypath);
2872 return ERROR_FUNCTION_FAILED;
2874 str = msi_record_get_string( row, 5, NULL );
2875 len = deformat_string( package, str, &deformated );
2876 new_value = parse_value( package, deformated, len, &type, &new_size );
2878 msi_free( deformated );
2879 deformat_string(package, name, &deformated);
2881 if (!is_special_entry( name ))
2883 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2884 if (type == REG_MULTI_SZ)
2886 BYTE *new;
2887 if (old_value && old_type != REG_MULTI_SZ)
2889 msi_free( old_value );
2890 old_value = NULL;
2891 old_size = 0;
2893 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2894 msi_free( new_value );
2895 new_value = new;
2897 if (!check_first)
2899 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2900 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2902 else if (!old_value)
2904 if (deformated || new_size)
2906 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2907 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2910 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2912 RegCloseKey(hkey);
2914 uirow = MSI_CreateRecord(3);
2915 MSI_RecordSetStringW(uirow,2,deformated);
2916 MSI_RecordSetStringW(uirow,1,uikey);
2917 if (type == REG_SZ || type == REG_EXPAND_SZ)
2918 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2919 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2920 msiobj_release( &uirow->hdr );
2922 msi_free(new_value);
2923 msi_free(old_value);
2924 msi_free(deformated);
2925 msi_free(uikey);
2926 msi_free(keypath);
2928 return ERROR_SUCCESS;
2931 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2933 static const WCHAR query[] = {
2934 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2935 '`','R','e','g','i','s','t','r','y','`',0};
2936 MSIQUERY *view;
2937 UINT rc;
2939 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2940 if (rc != ERROR_SUCCESS)
2941 return ERROR_SUCCESS;
2943 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2944 msiobj_release(&view->hdr);
2945 return rc;
2948 static void delete_key( HKEY root, const WCHAR *path )
2950 REGSAM access = 0;
2951 WCHAR *subkey, *p;
2952 HKEY hkey;
2953 LONG res;
2955 if (is_wow64) access |= KEY_WOW64_64KEY;
2957 if (!(subkey = strdupW( path ))) return;
2958 for (;;)
2960 if ((p = strrchrW( subkey, '\\' ))) *p = 0;
2961 hkey = open_key( root, subkey, FALSE );
2962 if (!hkey) break;
2963 if (p && p[1])
2964 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2965 else
2966 res = RegDeleteKeyExW( root, subkey, access, 0 );
2967 if (res)
2969 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
2970 break;
2972 if (p && p[1]) RegCloseKey( hkey );
2973 else break;
2975 msi_free( subkey );
2978 static void delete_value( HKEY root, const WCHAR *path, const WCHAR *value )
2980 LONG res;
2981 HKEY hkey;
2982 DWORD num_subkeys, num_values;
2984 if ((hkey = open_key( root, path, FALSE )))
2986 if ((res = RegDeleteValueW( hkey, value )))
2987 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2989 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2990 NULL, NULL, NULL, NULL );
2991 RegCloseKey( hkey );
2992 if (!res && !num_subkeys && !num_values)
2994 TRACE("removing empty key %s\n", debugstr_w(path));
2995 delete_key( root, path );
3000 static void delete_tree( HKEY root, const WCHAR *path )
3002 LONG res;
3003 HKEY hkey;
3005 if (!(hkey = open_key( root, path, FALSE ))) return;
3006 res = RegDeleteTreeW( hkey, NULL );
3007 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3008 delete_key( root, path );
3009 RegCloseKey( hkey );
3012 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3014 MSIPACKAGE *package = param;
3015 LPCWSTR component, name, key_str, root_key_str;
3016 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3017 MSICOMPONENT *comp;
3018 MSIRECORD *uirow;
3019 BOOL delete_key = FALSE;
3020 HKEY hkey_root;
3021 UINT size;
3022 INT root;
3024 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3026 component = MSI_RecordGetString( row, 6 );
3027 comp = msi_get_loaded_component( package, component );
3028 if (!comp)
3029 return ERROR_SUCCESS;
3031 comp->Action = msi_get_component_action( package, comp );
3032 if (comp->Action != INSTALLSTATE_ABSENT)
3034 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3035 return ERROR_SUCCESS;
3038 name = MSI_RecordGetString( row, 4 );
3039 if (MSI_RecordIsNull( row, 5 ) && name )
3041 if (name[0] == '+' && !name[1])
3042 return ERROR_SUCCESS;
3043 if ((name[0] == '-' || name[0] == '*') && !name[1])
3045 delete_key = TRUE;
3046 name = NULL;
3050 root = MSI_RecordGetInteger( row, 2 );
3051 key_str = MSI_RecordGetString( row, 3 );
3053 root_key_str = get_root_key( package, root, &hkey_root );
3054 if (!root_key_str)
3055 return ERROR_SUCCESS;
3057 deformat_string( package, key_str, &deformated_key );
3058 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3059 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3060 strcpyW( ui_key_str, root_key_str );
3061 strcatW( ui_key_str, deformated_key );
3063 deformat_string( package, name, &deformated_name );
3065 keypath = get_keypath( comp, hkey_root, deformated_key );
3066 msi_free( deformated_key );
3067 if (delete_key) delete_tree( hkey_root, keypath );
3068 else delete_value( hkey_root, keypath, deformated_name );
3069 msi_free( keypath );
3071 uirow = MSI_CreateRecord( 2 );
3072 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3073 MSI_RecordSetStringW( uirow, 2, deformated_name );
3074 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3075 msiobj_release( &uirow->hdr );
3077 msi_free( ui_key_str );
3078 msi_free( deformated_name );
3079 return ERROR_SUCCESS;
3082 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3084 MSIPACKAGE *package = param;
3085 LPCWSTR component, name, key_str, root_key_str;
3086 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3087 MSICOMPONENT *comp;
3088 MSIRECORD *uirow;
3089 BOOL delete_key = FALSE;
3090 HKEY hkey_root;
3091 UINT size;
3092 INT root;
3094 component = MSI_RecordGetString( row, 5 );
3095 comp = msi_get_loaded_component( package, component );
3096 if (!comp)
3097 return ERROR_SUCCESS;
3099 comp->Action = msi_get_component_action( package, comp );
3100 if (comp->Action != INSTALLSTATE_LOCAL)
3102 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3103 return ERROR_SUCCESS;
3106 if ((name = MSI_RecordGetString( row, 4 )))
3108 if (name[0] == '-' && !name[1])
3110 delete_key = TRUE;
3111 name = NULL;
3115 root = MSI_RecordGetInteger( row, 2 );
3116 key_str = MSI_RecordGetString( row, 3 );
3118 root_key_str = get_root_key( package, root, &hkey_root );
3119 if (!root_key_str)
3120 return ERROR_SUCCESS;
3122 deformat_string( package, key_str, &deformated_key );
3123 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3124 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3125 strcpyW( ui_key_str, root_key_str );
3126 strcatW( ui_key_str, deformated_key );
3128 deformat_string( package, name, &deformated_name );
3130 keypath = get_keypath( comp, hkey_root, deformated_key );
3131 msi_free( deformated_key );
3132 if (delete_key) delete_tree( hkey_root, keypath );
3133 else delete_value( hkey_root, keypath, deformated_name );
3134 msi_free( keypath );
3136 uirow = MSI_CreateRecord( 2 );
3137 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3138 MSI_RecordSetStringW( uirow, 2, deformated_name );
3139 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3140 msiobj_release( &uirow->hdr );
3142 msi_free( ui_key_str );
3143 msi_free( deformated_name );
3144 return ERROR_SUCCESS;
3147 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3149 static const WCHAR registry_query[] = {
3150 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3151 '`','R','e','g','i','s','t','r','y','`',0};
3152 static const WCHAR remove_registry_query[] = {
3153 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3154 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3155 MSIQUERY *view;
3156 UINT rc;
3158 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3159 if (rc == ERROR_SUCCESS)
3161 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3162 msiobj_release( &view->hdr );
3163 if (rc != ERROR_SUCCESS)
3164 return rc;
3166 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3167 if (rc == ERROR_SUCCESS)
3169 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3170 msiobj_release( &view->hdr );
3171 if (rc != ERROR_SUCCESS)
3172 return rc;
3174 return ERROR_SUCCESS;
3177 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3179 return ERROR_SUCCESS;
3183 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3185 static const WCHAR query[]= {
3186 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3187 '`','R','e','g','i','s','t','r','y','`',0};
3188 MSICOMPONENT *comp;
3189 DWORD total = 0, count = 0;
3190 MSIQUERY *view;
3191 MSIFEATURE *feature;
3192 MSIFILE *file;
3193 UINT rc;
3195 TRACE("InstallValidate\n");
3197 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3198 if (rc == ERROR_SUCCESS)
3200 rc = MSI_IterateRecords( view, &count, NULL, package );
3201 msiobj_release( &view->hdr );
3202 if (rc != ERROR_SUCCESS)
3203 return rc;
3204 total += count * REG_PROGRESS_VALUE;
3206 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3207 total += COMPONENT_PROGRESS_VALUE;
3209 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3210 total += file->FileSize;
3212 msi_ui_progress( package, 0, total, 0, 0 );
3214 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3216 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3217 debugstr_w(feature->Feature), feature->Installed,
3218 feature->ActionRequest, feature->Action);
3220 return ERROR_SUCCESS;
3223 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3225 MSIPACKAGE* package = param;
3226 LPCWSTR cond = NULL;
3227 LPCWSTR message = NULL;
3228 UINT r;
3230 static const WCHAR title[]=
3231 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3233 cond = MSI_RecordGetString(row,1);
3235 r = MSI_EvaluateConditionW(package,cond);
3236 if (r == MSICONDITION_FALSE)
3238 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3240 LPWSTR deformated;
3241 message = MSI_RecordGetString(row,2);
3242 deformat_string(package,message,&deformated);
3243 MessageBoxW(NULL,deformated,title,MB_OK);
3244 msi_free(deformated);
3247 return ERROR_INSTALL_FAILURE;
3250 return ERROR_SUCCESS;
3253 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3255 static const WCHAR query[] = {
3256 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3257 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3258 MSIQUERY *view;
3259 UINT rc;
3261 TRACE("Checking launch conditions\n");
3263 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3264 if (rc != ERROR_SUCCESS)
3265 return ERROR_SUCCESS;
3267 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3268 msiobj_release(&view->hdr);
3269 return rc;
3272 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3275 if (!cmp->KeyPath)
3276 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3278 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3280 static const WCHAR query[] = {
3281 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3282 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3283 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3284 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3285 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3286 MSIRECORD *row;
3287 UINT root, len;
3288 LPWSTR deformated, buffer, deformated_name;
3289 LPCWSTR key, name;
3291 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3292 if (!row)
3293 return NULL;
3295 root = MSI_RecordGetInteger(row,2);
3296 key = MSI_RecordGetString(row, 3);
3297 name = MSI_RecordGetString(row, 4);
3298 deformat_string(package, key , &deformated);
3299 deformat_string(package, name, &deformated_name);
3301 len = strlenW(deformated) + 6;
3302 if (deformated_name)
3303 len+=strlenW(deformated_name);
3305 buffer = msi_alloc( len *sizeof(WCHAR));
3307 if (deformated_name)
3308 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3309 else
3310 sprintfW(buffer,fmt,root,deformated);
3312 msi_free(deformated);
3313 msi_free(deformated_name);
3314 msiobj_release(&row->hdr);
3316 return buffer;
3318 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3320 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3321 return NULL;
3323 else
3325 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3327 if (file)
3328 return strdupW( file->TargetPath );
3330 return NULL;
3333 static HKEY openSharedDLLsKey(void)
3335 HKEY hkey=0;
3336 static const WCHAR path[] =
3337 {'S','o','f','t','w','a','r','e','\\',
3338 'M','i','c','r','o','s','o','f','t','\\',
3339 'W','i','n','d','o','w','s','\\',
3340 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3341 'S','h','a','r','e','d','D','L','L','s',0};
3343 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3344 return hkey;
3347 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3349 HKEY hkey;
3350 DWORD count=0;
3351 DWORD type;
3352 DWORD sz = sizeof(count);
3353 DWORD rc;
3355 hkey = openSharedDLLsKey();
3356 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3357 if (rc != ERROR_SUCCESS)
3358 count = 0;
3359 RegCloseKey(hkey);
3360 return count;
3363 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3365 HKEY hkey;
3367 hkey = openSharedDLLsKey();
3368 if (count > 0)
3369 msi_reg_set_val_dword( hkey, path, count );
3370 else
3371 RegDeleteValueW(hkey,path);
3372 RegCloseKey(hkey);
3373 return count;
3376 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3378 MSIFEATURE *feature;
3379 INT count = 0;
3380 BOOL write = FALSE;
3382 /* only refcount DLLs */
3383 if (comp->KeyPath == NULL ||
3384 comp->assembly ||
3385 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3386 comp->Attributes & msidbComponentAttributesODBCDataSource)
3387 write = FALSE;
3388 else
3390 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3391 write = (count > 0);
3393 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3394 write = TRUE;
3397 /* increment counts */
3398 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3400 ComponentList *cl;
3402 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3403 continue;
3405 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3407 if ( cl->component == comp )
3408 count++;
3412 /* decrement counts */
3413 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3415 ComponentList *cl;
3417 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3418 continue;
3420 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3422 if ( cl->component == comp )
3423 count--;
3427 /* ref count all the files in the component */
3428 if (write)
3430 MSIFILE *file;
3432 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3434 if (file->Component == comp)
3435 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3439 /* add a count for permanent */
3440 if (comp->Attributes & msidbComponentAttributesPermanent)
3441 count ++;
3443 comp->RefCount = count;
3445 if (write)
3446 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3449 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3451 if (comp->assembly)
3453 const WCHAR prefixW[] = {'<','\\',0};
3454 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3455 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3457 if (keypath)
3459 strcpyW( keypath, prefixW );
3460 strcatW( keypath, comp->assembly->display_name );
3462 return keypath;
3464 return resolve_keypath( package, comp );
3467 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3469 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3470 UINT rc;
3471 MSICOMPONENT *comp;
3472 HKEY hkey;
3474 TRACE("\n");
3476 squash_guid(package->ProductCode,squished_pc);
3477 msi_set_sourcedir_props(package, FALSE);
3479 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3481 MSIRECORD *uirow;
3482 INSTALLSTATE action;
3484 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3485 if (!comp->ComponentId)
3486 continue;
3488 squash_guid( comp->ComponentId, squished_cc );
3489 msi_free( comp->FullKeypath );
3490 comp->FullKeypath = build_full_keypath( package, comp );
3492 ACTION_RefCountComponent( package, comp );
3494 if (package->need_rollback) action = comp->Installed;
3495 else action = comp->ActionRequest;
3497 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3498 debugstr_w(comp->Component), debugstr_w(squished_cc),
3499 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3501 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3503 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3504 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3505 else
3506 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3508 if (rc != ERROR_SUCCESS)
3509 continue;
3511 if (comp->Attributes & msidbComponentAttributesPermanent)
3513 static const WCHAR szPermKey[] =
3514 { '0','0','0','0','0','0','0','0','0','0','0','0',
3515 '0','0','0','0','0','0','0','0','0','0','0','0',
3516 '0','0','0','0','0','0','0','0',0 };
3518 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3520 if (action == INSTALLSTATE_LOCAL)
3521 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3522 else
3524 MSIFILE *file;
3525 MSIRECORD *row;
3526 LPWSTR ptr, ptr2;
3527 WCHAR source[MAX_PATH];
3528 WCHAR base[MAX_PATH];
3529 LPWSTR sourcepath;
3531 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3532 static const WCHAR query[] = {
3533 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3534 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3535 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3536 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3537 '`','D','i','s','k','I','d','`',0};
3539 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3540 continue;
3542 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3543 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3544 ptr2 = strrchrW(source, '\\') + 1;
3545 msiobj_release(&row->hdr);
3547 lstrcpyW(base, package->PackagePath);
3548 ptr = strrchrW(base, '\\');
3549 *(ptr + 1) = '\0';
3551 sourcepath = msi_resolve_file_source(package, file);
3552 ptr = sourcepath + lstrlenW(base);
3553 lstrcpyW(ptr2, ptr);
3554 msi_free(sourcepath);
3556 msi_reg_set_val_str(hkey, squished_pc, source);
3558 RegCloseKey(hkey);
3560 else if (action == INSTALLSTATE_ABSENT)
3562 if (comp->num_clients <= 0)
3564 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3565 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3566 else
3567 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3569 if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
3571 else
3573 LONG res;
3575 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3576 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE );
3577 else
3578 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );
3580 if (rc != ERROR_SUCCESS)
3582 WARN( "failed to open component key %u\n", rc );
3583 continue;
3585 res = RegDeleteValueW( hkey, squished_pc );
3586 RegCloseKey(hkey);
3587 if (res) WARN( "failed to delete component value %d\n", res );
3591 /* UI stuff */
3592 uirow = MSI_CreateRecord(3);
3593 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3594 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3595 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3596 msi_ui_actiondata( package, szProcessComponents, uirow );
3597 msiobj_release( &uirow->hdr );
3599 return ERROR_SUCCESS;
3602 typedef struct {
3603 CLSID clsid;
3604 LPWSTR source;
3606 LPWSTR path;
3607 ITypeLib *ptLib;
3608 } typelib_struct;
3610 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3611 LPWSTR lpszName, LONG_PTR lParam)
3613 TLIBATTR *attr;
3614 typelib_struct *tl_struct = (typelib_struct*) lParam;
3615 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3616 int sz;
3617 HRESULT res;
3619 if (!IS_INTRESOURCE(lpszName))
3621 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3622 return TRUE;
3625 sz = strlenW(tl_struct->source)+4;
3626 sz *= sizeof(WCHAR);
3628 if ((INT_PTR)lpszName == 1)
3629 tl_struct->path = strdupW(tl_struct->source);
3630 else
3632 tl_struct->path = msi_alloc(sz);
3633 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3636 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3637 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3638 if (FAILED(res))
3640 msi_free(tl_struct->path);
3641 tl_struct->path = NULL;
3643 return TRUE;
3646 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3647 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3649 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3650 return FALSE;
3653 msi_free(tl_struct->path);
3654 tl_struct->path = NULL;
3656 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3657 ITypeLib_Release(tl_struct->ptLib);
3659 return TRUE;
3662 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3664 MSIPACKAGE* package = param;
3665 LPCWSTR component;
3666 MSICOMPONENT *comp;
3667 MSIFILE *file;
3668 typelib_struct tl_struct;
3669 ITypeLib *tlib;
3670 HMODULE module;
3671 HRESULT hr;
3673 component = MSI_RecordGetString(row,3);
3674 comp = msi_get_loaded_component(package,component);
3675 if (!comp)
3676 return ERROR_SUCCESS;
3678 comp->Action = msi_get_component_action( package, comp );
3679 if (comp->Action != INSTALLSTATE_LOCAL)
3681 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3682 return ERROR_SUCCESS;
3685 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3687 TRACE("component has no key path\n");
3688 return ERROR_SUCCESS;
3690 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3692 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3693 if (module)
3695 LPCWSTR guid;
3696 guid = MSI_RecordGetString(row,1);
3697 CLSIDFromString( guid, &tl_struct.clsid);
3698 tl_struct.source = strdupW( file->TargetPath );
3699 tl_struct.path = NULL;
3701 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3702 (LONG_PTR)&tl_struct);
3704 if (tl_struct.path)
3706 LPCWSTR helpid, help_path = NULL;
3707 HRESULT res;
3709 helpid = MSI_RecordGetString(row,6);
3711 if (helpid) help_path = msi_get_target_folder( package, helpid );
3712 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3714 if (FAILED(res))
3715 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3716 else
3717 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3719 ITypeLib_Release(tl_struct.ptLib);
3720 msi_free(tl_struct.path);
3722 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3724 FreeLibrary(module);
3725 msi_free(tl_struct.source);
3727 else
3729 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3730 if (FAILED(hr))
3732 ERR("Failed to load type library: %08x\n", hr);
3733 return ERROR_INSTALL_FAILURE;
3736 ITypeLib_Release(tlib);
3739 return ERROR_SUCCESS;
3742 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3744 static const WCHAR query[] = {
3745 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3746 '`','T','y','p','e','L','i','b','`',0};
3747 MSIQUERY *view;
3748 UINT rc;
3750 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3751 if (rc != ERROR_SUCCESS)
3752 return ERROR_SUCCESS;
3754 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3755 msiobj_release(&view->hdr);
3756 return rc;
3759 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3761 MSIPACKAGE *package = param;
3762 LPCWSTR component, guid;
3763 MSICOMPONENT *comp;
3764 GUID libid;
3765 UINT version;
3766 LCID language;
3767 SYSKIND syskind;
3768 HRESULT hr;
3770 component = MSI_RecordGetString( row, 3 );
3771 comp = msi_get_loaded_component( package, component );
3772 if (!comp)
3773 return ERROR_SUCCESS;
3775 comp->Action = msi_get_component_action( package, comp );
3776 if (comp->Action != INSTALLSTATE_ABSENT)
3778 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3779 return ERROR_SUCCESS;
3781 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3783 guid = MSI_RecordGetString( row, 1 );
3784 CLSIDFromString( guid, &libid );
3785 version = MSI_RecordGetInteger( row, 4 );
3786 language = MSI_RecordGetInteger( row, 2 );
3788 #ifdef _WIN64
3789 syskind = SYS_WIN64;
3790 #else
3791 syskind = SYS_WIN32;
3792 #endif
3794 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3795 if (FAILED(hr))
3797 WARN("Failed to unregister typelib: %08x\n", hr);
3800 return ERROR_SUCCESS;
3803 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3805 static const WCHAR query[] = {
3806 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3807 '`','T','y','p','e','L','i','b','`',0};
3808 MSIQUERY *view;
3809 UINT rc;
3811 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3812 if (rc != ERROR_SUCCESS)
3813 return ERROR_SUCCESS;
3815 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3816 msiobj_release( &view->hdr );
3817 return rc;
3820 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3822 static const WCHAR szlnk[] = {'.','l','n','k',0};
3823 LPCWSTR directory, extension, link_folder;
3824 LPWSTR link_file, filename;
3826 directory = MSI_RecordGetString( row, 2 );
3827 link_folder = msi_get_target_folder( package, directory );
3828 if (!link_folder)
3830 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3831 return NULL;
3833 /* may be needed because of a bug somewhere else */
3834 msi_create_full_path( link_folder );
3836 filename = msi_dup_record_field( row, 3 );
3837 msi_reduce_to_long_filename( filename );
3839 extension = strrchrW( filename, '.' );
3840 if (!extension || strcmpiW( extension, szlnk ))
3842 int len = strlenW( filename );
3843 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3844 memcpy( filename + len, szlnk, sizeof(szlnk) );
3846 link_file = msi_build_directory_name( 2, link_folder, filename );
3847 msi_free( filename );
3849 return link_file;
3852 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3854 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3855 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3856 WCHAR *folder, *dest, *path;
3858 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3859 folder = msi_dup_property( package->db, szWindowsFolder );
3860 else
3862 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3863 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3864 msi_free( appdata );
3866 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3867 msi_create_full_path( dest );
3868 path = msi_build_directory_name( 2, dest, icon_name );
3869 msi_free( folder );
3870 msi_free( dest );
3871 return path;
3874 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3876 MSIPACKAGE *package = param;
3877 LPWSTR link_file, deformated, path;
3878 LPCWSTR component, target;
3879 MSICOMPONENT *comp;
3880 IShellLinkW *sl = NULL;
3881 IPersistFile *pf = NULL;
3882 HRESULT res;
3884 component = MSI_RecordGetString(row, 4);
3885 comp = msi_get_loaded_component(package, component);
3886 if (!comp)
3887 return ERROR_SUCCESS;
3889 comp->Action = msi_get_component_action( package, comp );
3890 if (comp->Action != INSTALLSTATE_LOCAL)
3892 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3893 return ERROR_SUCCESS;
3895 msi_ui_actiondata( package, szCreateShortcuts, row );
3897 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3898 &IID_IShellLinkW, (LPVOID *) &sl );
3900 if (FAILED( res ))
3902 ERR("CLSID_ShellLink not available\n");
3903 goto err;
3906 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3907 if (FAILED( res ))
3909 ERR("QueryInterface(IID_IPersistFile) failed\n");
3910 goto err;
3913 target = MSI_RecordGetString(row, 5);
3914 if (strchrW(target, '['))
3916 deformat_string( package, target, &path );
3917 TRACE("target path is %s\n", debugstr_w(path));
3918 IShellLinkW_SetPath( sl, path );
3919 msi_free( path );
3921 else
3923 FIXME("poorly handled shortcut format, advertised shortcut\n");
3924 path = resolve_keypath( package, comp );
3925 IShellLinkW_SetPath( sl, path );
3926 msi_free( path );
3929 if (!MSI_RecordIsNull(row,6))
3931 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3932 deformat_string(package, arguments, &deformated);
3933 IShellLinkW_SetArguments(sl,deformated);
3934 msi_free(deformated);
3937 if (!MSI_RecordIsNull(row,7))
3939 LPCWSTR description = MSI_RecordGetString(row, 7);
3940 IShellLinkW_SetDescription(sl, description);
3943 if (!MSI_RecordIsNull(row,8))
3944 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3946 if (!MSI_RecordIsNull(row,9))
3948 INT index;
3949 LPCWSTR icon = MSI_RecordGetString(row, 9);
3951 path = msi_build_icon_path(package, icon);
3952 index = MSI_RecordGetInteger(row,10);
3954 /* no value means 0 */
3955 if (index == MSI_NULL_INTEGER)
3956 index = 0;
3958 IShellLinkW_SetIconLocation(sl, path, index);
3959 msi_free(path);
3962 if (!MSI_RecordIsNull(row,11))
3963 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3965 if (!MSI_RecordIsNull(row,12))
3967 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3968 full_path = msi_get_target_folder( package, wkdir );
3969 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3971 link_file = get_link_file(package, row);
3973 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3974 IPersistFile_Save(pf, link_file, FALSE);
3975 msi_free(link_file);
3977 err:
3978 if (pf)
3979 IPersistFile_Release( pf );
3980 if (sl)
3981 IShellLinkW_Release( sl );
3983 return ERROR_SUCCESS;
3986 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3988 static const WCHAR query[] = {
3989 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3990 '`','S','h','o','r','t','c','u','t','`',0};
3991 MSIQUERY *view;
3992 HRESULT res;
3993 UINT rc;
3995 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3996 if (rc != ERROR_SUCCESS)
3997 return ERROR_SUCCESS;
3999 res = CoInitialize( NULL );
4001 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4002 msiobj_release(&view->hdr);
4004 if (SUCCEEDED(res)) CoUninitialize();
4005 return rc;
4008 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4010 MSIPACKAGE *package = param;
4011 LPWSTR link_file;
4012 LPCWSTR component;
4013 MSICOMPONENT *comp;
4015 component = MSI_RecordGetString( row, 4 );
4016 comp = msi_get_loaded_component( package, component );
4017 if (!comp)
4018 return ERROR_SUCCESS;
4020 comp->Action = msi_get_component_action( package, comp );
4021 if (comp->Action != INSTALLSTATE_ABSENT)
4023 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4024 return ERROR_SUCCESS;
4026 msi_ui_actiondata( package, szRemoveShortcuts, row );
4028 link_file = get_link_file( package, row );
4030 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4031 if (!DeleteFileW( link_file ))
4033 WARN("Failed to remove shortcut file %u\n", GetLastError());
4035 msi_free( link_file );
4037 return ERROR_SUCCESS;
4040 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4042 static const WCHAR query[] = {
4043 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4044 '`','S','h','o','r','t','c','u','t','`',0};
4045 MSIQUERY *view;
4046 UINT rc;
4048 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4049 if (rc != ERROR_SUCCESS)
4050 return ERROR_SUCCESS;
4052 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4053 msiobj_release( &view->hdr );
4054 return rc;
4057 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4059 MSIPACKAGE* package = param;
4060 HANDLE the_file;
4061 LPWSTR FilePath;
4062 LPCWSTR FileName;
4063 CHAR buffer[1024];
4064 DWORD sz;
4065 UINT rc;
4067 FileName = MSI_RecordGetString(row,1);
4068 if (!FileName)
4070 ERR("Unable to get FileName\n");
4071 return ERROR_SUCCESS;
4074 FilePath = msi_build_icon_path(package, FileName);
4076 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4078 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4079 FILE_ATTRIBUTE_NORMAL, NULL);
4081 if (the_file == INVALID_HANDLE_VALUE)
4083 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4084 msi_free(FilePath);
4085 return ERROR_SUCCESS;
4090 DWORD write;
4091 sz = 1024;
4092 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4093 if (rc != ERROR_SUCCESS)
4095 ERR("Failed to get stream\n");
4096 CloseHandle(the_file);
4097 DeleteFileW(FilePath);
4098 break;
4100 WriteFile(the_file,buffer,sz,&write,NULL);
4101 } while (sz == 1024);
4103 msi_free(FilePath);
4104 CloseHandle(the_file);
4106 return ERROR_SUCCESS;
4109 static UINT msi_publish_icons(MSIPACKAGE *package)
4111 static const WCHAR query[]= {
4112 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4113 '`','I','c','o','n','`',0};
4114 MSIQUERY *view;
4115 UINT r;
4117 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4118 if (r == ERROR_SUCCESS)
4120 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4121 msiobj_release(&view->hdr);
4122 if (r != ERROR_SUCCESS)
4123 return r;
4125 return ERROR_SUCCESS;
4128 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4130 UINT r;
4131 HKEY source;
4132 LPWSTR buffer;
4133 MSIMEDIADISK *disk;
4134 MSISOURCELISTINFO *info;
4136 r = RegCreateKeyW(hkey, szSourceList, &source);
4137 if (r != ERROR_SUCCESS)
4138 return r;
4140 RegCloseKey(source);
4142 buffer = strrchrW(package->PackagePath, '\\') + 1;
4143 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4144 package->Context, MSICODE_PRODUCT,
4145 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4146 if (r != ERROR_SUCCESS)
4147 return r;
4149 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4150 package->Context, MSICODE_PRODUCT,
4151 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4152 if (r != ERROR_SUCCESS)
4153 return r;
4155 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4156 package->Context, MSICODE_PRODUCT,
4157 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4158 if (r != ERROR_SUCCESS)
4159 return r;
4161 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4163 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4164 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4165 info->options, info->value);
4166 else
4167 MsiSourceListSetInfoW(package->ProductCode, NULL,
4168 info->context, info->options,
4169 info->property, info->value);
4172 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4174 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4175 disk->context, disk->options,
4176 disk->disk_id, disk->volume_label, disk->disk_prompt);
4179 return ERROR_SUCCESS;
4182 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4184 MSIHANDLE hdb, suminfo;
4185 WCHAR guids[MAX_PATH];
4186 WCHAR packcode[SQUISH_GUID_SIZE];
4187 LPWSTR buffer;
4188 LPWSTR ptr;
4189 DWORD langid;
4190 DWORD size;
4191 UINT r;
4193 static const WCHAR szARPProductIcon[] =
4194 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4195 static const WCHAR szAssignment[] =
4196 {'A','s','s','i','g','n','m','e','n','t',0};
4197 static const WCHAR szAdvertiseFlags[] =
4198 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4199 static const WCHAR szClients[] =
4200 {'C','l','i','e','n','t','s',0};
4201 static const WCHAR szColon[] = {':',0};
4203 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4204 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4205 msi_free(buffer);
4207 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4208 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4210 /* FIXME */
4211 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4213 buffer = msi_dup_property(package->db, szARPProductIcon);
4214 if (buffer)
4216 LPWSTR path = msi_build_icon_path(package, buffer);
4217 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4218 msi_free(path);
4219 msi_free(buffer);
4222 buffer = msi_dup_property(package->db, szProductVersion);
4223 if (buffer)
4225 DWORD verdword = msi_version_str_to_dword(buffer);
4226 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4227 msi_free(buffer);
4230 msi_reg_set_val_dword(hkey, szAssignment, 0);
4231 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4232 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4233 msi_reg_set_val_str(hkey, szClients, szColon);
4235 hdb = alloc_msihandle(&package->db->hdr);
4236 if (!hdb)
4237 return ERROR_NOT_ENOUGH_MEMORY;
4239 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4240 MsiCloseHandle(hdb);
4241 if (r != ERROR_SUCCESS)
4242 goto done;
4244 size = MAX_PATH;
4245 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4246 NULL, guids, &size);
4247 if (r != ERROR_SUCCESS)
4248 goto done;
4250 ptr = strchrW(guids, ';');
4251 if (ptr) *ptr = 0;
4252 squash_guid(guids, packcode);
4253 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4255 done:
4256 MsiCloseHandle(suminfo);
4257 return ERROR_SUCCESS;
4260 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4262 UINT r;
4263 HKEY hkey;
4264 LPWSTR upgrade;
4265 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4267 upgrade = msi_dup_property(package->db, szUpgradeCode);
4268 if (!upgrade)
4269 return ERROR_SUCCESS;
4271 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4272 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4273 else
4274 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4276 if (r != ERROR_SUCCESS)
4278 WARN("failed to open upgrade code key\n");
4279 msi_free(upgrade);
4280 return ERROR_SUCCESS;
4282 squash_guid(package->ProductCode, squashed_pc);
4283 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4284 RegCloseKey(hkey);
4285 msi_free(upgrade);
4286 return ERROR_SUCCESS;
4289 static BOOL msi_check_publish(MSIPACKAGE *package)
4291 MSIFEATURE *feature;
4293 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4295 feature->Action = msi_get_feature_action( package, feature );
4296 if (feature->Action == INSTALLSTATE_LOCAL)
4297 return TRUE;
4300 return FALSE;
4303 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4305 MSIFEATURE *feature;
4307 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4309 feature->Action = msi_get_feature_action( package, feature );
4310 if (feature->Action != INSTALLSTATE_ABSENT)
4311 return FALSE;
4314 return TRUE;
4317 static UINT msi_publish_patches( MSIPACKAGE *package )
4319 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4320 WCHAR patch_squashed[GUID_SIZE];
4321 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4322 LONG res;
4323 MSIPATCHINFO *patch;
4324 UINT r;
4325 WCHAR *p, *all_patches = NULL;
4326 DWORD len = 0;
4328 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4329 if (r != ERROR_SUCCESS)
4330 return ERROR_FUNCTION_FAILED;
4332 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4333 if (res != ERROR_SUCCESS)
4335 r = ERROR_FUNCTION_FAILED;
4336 goto done;
4339 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4340 if (r != ERROR_SUCCESS)
4341 goto done;
4343 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4345 squash_guid( patch->patchcode, patch_squashed );
4346 len += strlenW( patch_squashed ) + 1;
4349 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4350 if (!all_patches)
4351 goto done;
4353 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4355 HKEY patch_key;
4357 squash_guid( patch->patchcode, p );
4358 p += strlenW( p ) + 1;
4360 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4361 (const BYTE *)patch->transforms,
4362 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4363 if (res != ERROR_SUCCESS)
4364 goto done;
4366 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4367 if (r != ERROR_SUCCESS)
4368 goto done;
4370 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4371 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4372 RegCloseKey( patch_key );
4373 if (res != ERROR_SUCCESS)
4374 goto done;
4376 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4378 res = GetLastError();
4379 ERR("Unable to copy patch package %d\n", res);
4380 goto done;
4382 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4383 if (res != ERROR_SUCCESS)
4384 goto done;
4386 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4387 RegCloseKey( patch_key );
4388 if (res != ERROR_SUCCESS)
4389 goto done;
4392 all_patches[len] = 0;
4393 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4394 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4395 if (res != ERROR_SUCCESS)
4396 goto done;
4398 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4399 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4400 if (res != ERROR_SUCCESS)
4401 r = ERROR_FUNCTION_FAILED;
4403 done:
4404 RegCloseKey( product_patches_key );
4405 RegCloseKey( patches_key );
4406 RegCloseKey( product_key );
4407 msi_free( all_patches );
4408 return r;
4411 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4413 UINT rc;
4414 HKEY hukey = NULL, hudkey = NULL;
4415 MSIRECORD *uirow;
4417 if (!list_empty(&package->patches))
4419 rc = msi_publish_patches(package);
4420 if (rc != ERROR_SUCCESS)
4421 goto end;
4424 /* FIXME: also need to publish if the product is in advertise mode */
4425 if (!msi_check_publish(package))
4426 return ERROR_SUCCESS;
4428 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4429 &hukey, TRUE);
4430 if (rc != ERROR_SUCCESS)
4431 goto end;
4433 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4434 NULL, &hudkey, TRUE);
4435 if (rc != ERROR_SUCCESS)
4436 goto end;
4438 rc = msi_publish_upgrade_code(package);
4439 if (rc != ERROR_SUCCESS)
4440 goto end;
4442 rc = msi_publish_product_properties(package, hukey);
4443 if (rc != ERROR_SUCCESS)
4444 goto end;
4446 rc = msi_publish_sourcelist(package, hukey);
4447 if (rc != ERROR_SUCCESS)
4448 goto end;
4450 rc = msi_publish_icons(package);
4452 end:
4453 uirow = MSI_CreateRecord( 1 );
4454 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4455 msi_ui_actiondata( package, szPublishProduct, uirow );
4456 msiobj_release( &uirow->hdr );
4458 RegCloseKey(hukey);
4459 RegCloseKey(hudkey);
4460 return rc;
4463 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4465 WCHAR *filename, *ptr, *folder, *ret;
4466 const WCHAR *dirprop;
4468 filename = msi_dup_record_field( row, 2 );
4469 if (filename && (ptr = strchrW( filename, '|' )))
4470 ptr++;
4471 else
4472 ptr = filename;
4474 dirprop = MSI_RecordGetString( row, 3 );
4475 if (dirprop)
4477 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4478 if (!folder) folder = msi_dup_property( package->db, dirprop );
4480 else
4481 folder = msi_dup_property( package->db, szWindowsFolder );
4483 if (!folder)
4485 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4486 msi_free( filename );
4487 return NULL;
4490 ret = msi_build_directory_name( 2, folder, ptr );
4492 msi_free( filename );
4493 msi_free( folder );
4494 return ret;
4497 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4499 MSIPACKAGE *package = param;
4500 LPCWSTR component, section, key, value, identifier;
4501 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4502 MSIRECORD * uirow;
4503 INT action;
4504 MSICOMPONENT *comp;
4506 component = MSI_RecordGetString(row, 8);
4507 comp = msi_get_loaded_component(package,component);
4508 if (!comp)
4509 return ERROR_SUCCESS;
4511 comp->Action = msi_get_component_action( package, comp );
4512 if (comp->Action != INSTALLSTATE_LOCAL)
4514 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4515 return ERROR_SUCCESS;
4518 identifier = MSI_RecordGetString(row,1);
4519 section = MSI_RecordGetString(row,4);
4520 key = MSI_RecordGetString(row,5);
4521 value = MSI_RecordGetString(row,6);
4522 action = MSI_RecordGetInteger(row,7);
4524 deformat_string(package,section,&deformated_section);
4525 deformat_string(package,key,&deformated_key);
4526 deformat_string(package,value,&deformated_value);
4528 fullname = get_ini_file_name(package, row);
4530 if (action == 0)
4532 TRACE("Adding value %s to section %s in %s\n",
4533 debugstr_w(deformated_key), debugstr_w(deformated_section),
4534 debugstr_w(fullname));
4535 WritePrivateProfileStringW(deformated_section, deformated_key,
4536 deformated_value, fullname);
4538 else if (action == 1)
4540 WCHAR returned[10];
4541 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4542 returned, 10, fullname);
4543 if (returned[0] == 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));
4549 WritePrivateProfileStringW(deformated_section, deformated_key,
4550 deformated_value, fullname);
4553 else if (action == 3)
4554 FIXME("Append to existing section not yet implemented\n");
4556 uirow = MSI_CreateRecord(4);
4557 MSI_RecordSetStringW(uirow,1,identifier);
4558 MSI_RecordSetStringW(uirow,2,deformated_section);
4559 MSI_RecordSetStringW(uirow,3,deformated_key);
4560 MSI_RecordSetStringW(uirow,4,deformated_value);
4561 msi_ui_actiondata( package, szWriteIniValues, uirow );
4562 msiobj_release( &uirow->hdr );
4564 msi_free(fullname);
4565 msi_free(deformated_key);
4566 msi_free(deformated_value);
4567 msi_free(deformated_section);
4568 return ERROR_SUCCESS;
4571 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4573 static const WCHAR query[] = {
4574 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4575 '`','I','n','i','F','i','l','e','`',0};
4576 MSIQUERY *view;
4577 UINT rc;
4579 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4580 if (rc != ERROR_SUCCESS)
4581 return ERROR_SUCCESS;
4583 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4584 msiobj_release(&view->hdr);
4585 return rc;
4588 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4590 MSIPACKAGE *package = param;
4591 LPCWSTR component, section, key, value, identifier;
4592 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4593 MSICOMPONENT *comp;
4594 MSIRECORD *uirow;
4595 INT action;
4597 component = MSI_RecordGetString( row, 8 );
4598 comp = msi_get_loaded_component( package, component );
4599 if (!comp)
4600 return ERROR_SUCCESS;
4602 comp->Action = msi_get_component_action( package, comp );
4603 if (comp->Action != INSTALLSTATE_ABSENT)
4605 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4606 return ERROR_SUCCESS;
4609 identifier = MSI_RecordGetString( row, 1 );
4610 section = MSI_RecordGetString( row, 4 );
4611 key = MSI_RecordGetString( row, 5 );
4612 value = MSI_RecordGetString( row, 6 );
4613 action = MSI_RecordGetInteger( row, 7 );
4615 deformat_string( package, section, &deformated_section );
4616 deformat_string( package, key, &deformated_key );
4617 deformat_string( package, value, &deformated_value );
4619 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4621 filename = get_ini_file_name( package, row );
4623 TRACE("Removing key %s from section %s in %s\n",
4624 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4626 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4628 WARN("Unable to remove key %u\n", GetLastError());
4630 msi_free( filename );
4632 else
4633 FIXME("Unsupported action %d\n", action);
4636 uirow = MSI_CreateRecord( 4 );
4637 MSI_RecordSetStringW( uirow, 1, identifier );
4638 MSI_RecordSetStringW( uirow, 2, deformated_section );
4639 MSI_RecordSetStringW( uirow, 3, deformated_key );
4640 MSI_RecordSetStringW( uirow, 4, deformated_value );
4641 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4642 msiobj_release( &uirow->hdr );
4644 msi_free( deformated_key );
4645 msi_free( deformated_value );
4646 msi_free( deformated_section );
4647 return ERROR_SUCCESS;
4650 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4652 MSIPACKAGE *package = param;
4653 LPCWSTR component, section, key, value, identifier;
4654 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4655 MSICOMPONENT *comp;
4656 MSIRECORD *uirow;
4657 INT action;
4659 component = MSI_RecordGetString( row, 8 );
4660 comp = msi_get_loaded_component( package, component );
4661 if (!comp)
4662 return ERROR_SUCCESS;
4664 comp->Action = msi_get_component_action( package, comp );
4665 if (comp->Action != INSTALLSTATE_LOCAL)
4667 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4668 return ERROR_SUCCESS;
4671 identifier = MSI_RecordGetString( row, 1 );
4672 section = MSI_RecordGetString( row, 4 );
4673 key = MSI_RecordGetString( row, 5 );
4674 value = MSI_RecordGetString( row, 6 );
4675 action = MSI_RecordGetInteger( row, 7 );
4677 deformat_string( package, section, &deformated_section );
4678 deformat_string( package, key, &deformated_key );
4679 deformat_string( package, value, &deformated_value );
4681 if (action == msidbIniFileActionRemoveLine)
4683 filename = get_ini_file_name( package, row );
4685 TRACE("Removing key %s from section %s in %s\n",
4686 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4688 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4690 WARN("Unable to remove key %u\n", GetLastError());
4692 msi_free( filename );
4694 else
4695 FIXME("Unsupported action %d\n", action);
4697 uirow = MSI_CreateRecord( 4 );
4698 MSI_RecordSetStringW( uirow, 1, identifier );
4699 MSI_RecordSetStringW( uirow, 2, deformated_section );
4700 MSI_RecordSetStringW( uirow, 3, deformated_key );
4701 MSI_RecordSetStringW( uirow, 4, deformated_value );
4702 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4703 msiobj_release( &uirow->hdr );
4705 msi_free( deformated_key );
4706 msi_free( deformated_value );
4707 msi_free( deformated_section );
4708 return ERROR_SUCCESS;
4711 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4713 static const WCHAR query[] = {
4714 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4715 '`','I','n','i','F','i','l','e','`',0};
4716 static const WCHAR remove_query[] = {
4717 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4718 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4719 MSIQUERY *view;
4720 UINT rc;
4722 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4723 if (rc == ERROR_SUCCESS)
4725 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4726 msiobj_release( &view->hdr );
4727 if (rc != ERROR_SUCCESS)
4728 return rc;
4730 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4731 if (rc == ERROR_SUCCESS)
4733 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4734 msiobj_release( &view->hdr );
4735 if (rc != ERROR_SUCCESS)
4736 return rc;
4738 return ERROR_SUCCESS;
4741 static void register_dll( const WCHAR *dll, BOOL unregister )
4743 static const WCHAR regW[] =
4744 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4745 static const WCHAR unregW[] =
4746 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4747 PROCESS_INFORMATION pi;
4748 STARTUPINFOW si;
4749 WCHAR *cmd;
4751 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4753 if (unregister) sprintfW( cmd, unregW, dll );
4754 else sprintfW( cmd, regW, dll );
4756 memset( &si, 0, sizeof(STARTUPINFOW) );
4757 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4759 CloseHandle( pi.hThread );
4760 msi_dialog_check_messages( pi.hProcess );
4761 CloseHandle( pi.hProcess );
4763 msi_free( cmd );
4766 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4768 MSIPACKAGE *package = param;
4769 LPCWSTR filename;
4770 MSIFILE *file;
4771 MSIRECORD *uirow;
4773 filename = MSI_RecordGetString( row, 1 );
4774 file = msi_get_loaded_file( package, filename );
4775 if (!file)
4777 WARN("unable to find file %s\n", debugstr_w(filename));
4778 return ERROR_SUCCESS;
4780 file->Component->Action = msi_get_component_action( package, file->Component );
4781 if (file->Component->Action != INSTALLSTATE_LOCAL)
4783 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4784 return ERROR_SUCCESS;
4787 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4788 register_dll( file->TargetPath, FALSE );
4790 uirow = MSI_CreateRecord( 2 );
4791 MSI_RecordSetStringW( uirow, 1, file->File );
4792 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4793 msi_ui_actiondata( package, szSelfRegModules, uirow );
4794 msiobj_release( &uirow->hdr );
4796 return ERROR_SUCCESS;
4799 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4801 static const WCHAR query[] = {
4802 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4803 '`','S','e','l','f','R','e','g','`',0};
4804 MSIQUERY *view;
4805 UINT rc;
4807 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4808 if (rc != ERROR_SUCCESS)
4809 return ERROR_SUCCESS;
4811 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4812 msiobj_release(&view->hdr);
4813 return rc;
4816 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4818 MSIPACKAGE *package = param;
4819 LPCWSTR filename;
4820 MSIFILE *file;
4821 MSIRECORD *uirow;
4823 filename = MSI_RecordGetString( row, 1 );
4824 file = msi_get_loaded_file( package, filename );
4825 if (!file)
4827 WARN("unable to find file %s\n", debugstr_w(filename));
4828 return ERROR_SUCCESS;
4830 file->Component->Action = msi_get_component_action( package, file->Component );
4831 if (file->Component->Action != INSTALLSTATE_ABSENT)
4833 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4834 return ERROR_SUCCESS;
4837 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4838 register_dll( file->TargetPath, TRUE );
4840 uirow = MSI_CreateRecord( 2 );
4841 MSI_RecordSetStringW( uirow, 1, file->File );
4842 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4843 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4844 msiobj_release( &uirow->hdr );
4846 return ERROR_SUCCESS;
4849 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4851 static const WCHAR query[] = {
4852 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4853 '`','S','e','l','f','R','e','g','`',0};
4854 MSIQUERY *view;
4855 UINT rc;
4857 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4858 if (rc != ERROR_SUCCESS)
4859 return ERROR_SUCCESS;
4861 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4862 msiobj_release( &view->hdr );
4863 return rc;
4866 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4868 MSIFEATURE *feature;
4869 UINT rc;
4870 HKEY hkey = NULL, userdata = NULL;
4872 if (!msi_check_publish(package))
4873 return ERROR_SUCCESS;
4875 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4876 &hkey, TRUE);
4877 if (rc != ERROR_SUCCESS)
4878 goto end;
4880 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4881 &userdata, TRUE);
4882 if (rc != ERROR_SUCCESS)
4883 goto end;
4885 /* here the guids are base 85 encoded */
4886 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4888 ComponentList *cl;
4889 LPWSTR data = NULL;
4890 GUID clsid;
4891 INT size;
4892 BOOL absent = FALSE;
4893 MSIRECORD *uirow;
4895 if (feature->Level <= 0) continue;
4897 if (feature->Action != INSTALLSTATE_LOCAL &&
4898 feature->Action != INSTALLSTATE_SOURCE &&
4899 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4901 size = 1;
4902 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4904 size += 21;
4906 if (feature->Feature_Parent)
4907 size += strlenW( feature->Feature_Parent )+2;
4909 data = msi_alloc(size * sizeof(WCHAR));
4911 data[0] = 0;
4912 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4914 MSICOMPONENT* component = cl->component;
4915 WCHAR buf[21];
4917 buf[0] = 0;
4918 if (component->ComponentId)
4920 TRACE("From %s\n",debugstr_w(component->ComponentId));
4921 CLSIDFromString(component->ComponentId, &clsid);
4922 encode_base85_guid(&clsid,buf);
4923 TRACE("to %s\n",debugstr_w(buf));
4924 strcatW(data,buf);
4928 if (feature->Feature_Parent)
4930 static const WCHAR sep[] = {'\2',0};
4931 strcatW(data,sep);
4932 strcatW(data,feature->Feature_Parent);
4935 msi_reg_set_val_str( userdata, feature->Feature, data );
4936 msi_free(data);
4938 size = 0;
4939 if (feature->Feature_Parent)
4940 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4941 if (!absent)
4943 size += sizeof(WCHAR);
4944 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4945 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4947 else
4949 size += 2*sizeof(WCHAR);
4950 data = msi_alloc(size);
4951 data[0] = 0x6;
4952 data[1] = 0;
4953 if (feature->Feature_Parent)
4954 strcpyW( &data[1], feature->Feature_Parent );
4955 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4956 (LPBYTE)data,size);
4957 msi_free(data);
4960 /* the UI chunk */
4961 uirow = MSI_CreateRecord( 1 );
4962 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4963 msi_ui_actiondata( package, szPublishFeatures, uirow );
4964 msiobj_release( &uirow->hdr );
4965 /* FIXME: call msi_ui_progress? */
4968 end:
4969 RegCloseKey(hkey);
4970 RegCloseKey(userdata);
4971 return rc;
4974 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4976 UINT r;
4977 HKEY hkey;
4978 MSIRECORD *uirow;
4980 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4982 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4983 &hkey, FALSE);
4984 if (r == ERROR_SUCCESS)
4986 RegDeleteValueW(hkey, feature->Feature);
4987 RegCloseKey(hkey);
4990 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4991 &hkey, FALSE);
4992 if (r == ERROR_SUCCESS)
4994 RegDeleteValueW(hkey, feature->Feature);
4995 RegCloseKey(hkey);
4998 uirow = MSI_CreateRecord( 1 );
4999 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5000 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
5001 msiobj_release( &uirow->hdr );
5003 return ERROR_SUCCESS;
5006 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5008 MSIFEATURE *feature;
5010 if (!msi_check_unpublish(package))
5011 return ERROR_SUCCESS;
5013 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5015 msi_unpublish_feature(package, feature);
5018 return ERROR_SUCCESS;
5021 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5023 SYSTEMTIME systime;
5024 DWORD size, langid;
5025 WCHAR date[9], *val, *buffer;
5026 const WCHAR *prop, *key;
5028 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5029 static const WCHAR modpath_fmt[] =
5030 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5031 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5032 static const WCHAR szModifyPath[] =
5033 {'M','o','d','i','f','y','P','a','t','h',0};
5034 static const WCHAR szUninstallString[] =
5035 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5036 static const WCHAR szEstimatedSize[] =
5037 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5038 static const WCHAR szDisplayVersion[] =
5039 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5040 static const WCHAR szInstallSource[] =
5041 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5042 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5043 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5044 static const WCHAR szAuthorizedCDFPrefix[] =
5045 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5046 static const WCHAR szARPCONTACT[] =
5047 {'A','R','P','C','O','N','T','A','C','T',0};
5048 static const WCHAR szContact[] =
5049 {'C','o','n','t','a','c','t',0};
5050 static const WCHAR szARPCOMMENTS[] =
5051 {'A','R','P','C','O','M','M','E','N','T','S',0};
5052 static const WCHAR szComments[] =
5053 {'C','o','m','m','e','n','t','s',0};
5054 static const WCHAR szProductName[] =
5055 {'P','r','o','d','u','c','t','N','a','m','e',0};
5056 static const WCHAR szDisplayName[] =
5057 {'D','i','s','p','l','a','y','N','a','m','e',0};
5058 static const WCHAR szARPHELPLINK[] =
5059 {'A','R','P','H','E','L','P','L','I','N','K',0};
5060 static const WCHAR szHelpLink[] =
5061 {'H','e','l','p','L','i','n','k',0};
5062 static const WCHAR szARPHELPTELEPHONE[] =
5063 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5064 static const WCHAR szHelpTelephone[] =
5065 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5066 static const WCHAR szARPINSTALLLOCATION[] =
5067 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5068 static const WCHAR szManufacturer[] =
5069 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5070 static const WCHAR szPublisher[] =
5071 {'P','u','b','l','i','s','h','e','r',0};
5072 static const WCHAR szARPREADME[] =
5073 {'A','R','P','R','E','A','D','M','E',0};
5074 static const WCHAR szReadme[] =
5075 {'R','e','a','d','M','e',0};
5076 static const WCHAR szARPSIZE[] =
5077 {'A','R','P','S','I','Z','E',0};
5078 static const WCHAR szSize[] =
5079 {'S','i','z','e',0};
5080 static const WCHAR szARPURLINFOABOUT[] =
5081 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5082 static const WCHAR szURLInfoAbout[] =
5083 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5084 static const WCHAR szARPURLUPDATEINFO[] =
5085 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5086 static const WCHAR szURLUpdateInfo[] =
5087 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5088 static const WCHAR szARPSYSTEMCOMPONENT[] =
5089 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5090 static const WCHAR szSystemComponent[] =
5091 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5093 static const WCHAR *propval[] = {
5094 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5095 szARPCONTACT, szContact,
5096 szARPCOMMENTS, szComments,
5097 szProductName, szDisplayName,
5098 szARPHELPLINK, szHelpLink,
5099 szARPHELPTELEPHONE, szHelpTelephone,
5100 szARPINSTALLLOCATION, szInstallLocation,
5101 szSourceDir, szInstallSource,
5102 szManufacturer, szPublisher,
5103 szARPREADME, szReadme,
5104 szARPSIZE, szSize,
5105 szARPURLINFOABOUT, szURLInfoAbout,
5106 szARPURLUPDATEINFO, szURLUpdateInfo,
5107 NULL
5109 const WCHAR **p = propval;
5111 while (*p)
5113 prop = *p++;
5114 key = *p++;
5115 val = msi_dup_property(package->db, prop);
5116 msi_reg_set_val_str(hkey, key, val);
5117 msi_free(val);
5120 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5121 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5123 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5125 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5126 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5127 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5128 msi_free(buffer);
5130 /* FIXME: Write real Estimated Size when we have it */
5131 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5133 GetLocalTime(&systime);
5134 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5135 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5137 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5138 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5140 buffer = msi_dup_property(package->db, szProductVersion);
5141 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5142 if (buffer)
5144 DWORD verdword = msi_version_str_to_dword(buffer);
5146 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5147 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5148 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5149 msi_free(buffer);
5152 return ERROR_SUCCESS;
5155 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5157 WCHAR squashed_pc[SQUISH_GUID_SIZE];
5158 MSIRECORD *uirow;
5159 LPWSTR upgrade_code;
5160 HKEY hkey, props, upgrade_key;
5161 UINT rc;
5163 /* FIXME: also need to publish if the product is in advertise mode */
5164 if (!msi_check_publish(package))
5165 return ERROR_SUCCESS;
5167 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5168 if (rc != ERROR_SUCCESS)
5169 return rc;
5171 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5172 if (rc != ERROR_SUCCESS)
5173 goto done;
5175 rc = msi_publish_install_properties(package, hkey);
5176 if (rc != ERROR_SUCCESS)
5177 goto done;
5179 rc = msi_publish_install_properties(package, props);
5180 if (rc != ERROR_SUCCESS)
5181 goto done;
5183 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5184 if (upgrade_code)
5186 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5187 if (rc == ERROR_SUCCESS)
5189 squash_guid( package->ProductCode, squashed_pc );
5190 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5191 RegCloseKey( upgrade_key );
5193 msi_free( upgrade_code );
5195 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5196 package->delete_on_close = FALSE;
5198 done:
5199 uirow = MSI_CreateRecord( 1 );
5200 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5201 msi_ui_actiondata( package, szRegisterProduct, uirow );
5202 msiobj_release( &uirow->hdr );
5204 RegCloseKey(hkey);
5205 return ERROR_SUCCESS;
5208 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5210 return execute_script(package, SCRIPT_INSTALL);
5213 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5215 MSIPACKAGE *package = param;
5216 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5217 WCHAR *p, *icon_path;
5219 if (!icon) return ERROR_SUCCESS;
5220 if ((icon_path = msi_build_icon_path( package, icon )))
5222 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5223 DeleteFileW( icon_path );
5224 if ((p = strrchrW( icon_path, '\\' )))
5226 *p = 0;
5227 RemoveDirectoryW( icon_path );
5229 msi_free( icon_path );
5231 return ERROR_SUCCESS;
5234 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5236 static const WCHAR query[]= {
5237 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5238 MSIQUERY *view;
5239 UINT r;
5241 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5242 if (r == ERROR_SUCCESS)
5244 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5245 msiobj_release( &view->hdr );
5246 if (r != ERROR_SUCCESS)
5247 return r;
5249 return ERROR_SUCCESS;
5252 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
5254 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
5255 WCHAR *upgrade, **features;
5256 BOOL full_uninstall = TRUE;
5257 MSIFEATURE *feature;
5258 MSIPATCHINFO *patch;
5259 UINT i;
5261 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5263 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
5265 features = msi_split_string( remove, ',' );
5266 for (i = 0; features && features[i]; i++)
5268 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
5270 msi_free(features);
5272 if (!full_uninstall)
5273 return ERROR_SUCCESS;
5275 MSIREG_DeleteProductKey(package->ProductCode);
5276 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5277 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5279 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5280 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5281 MSIREG_DeleteUserProductKey(package->ProductCode);
5282 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5284 upgrade = msi_dup_property(package->db, szUpgradeCode);
5285 if (upgrade)
5287 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5288 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5289 msi_free(upgrade);
5292 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5294 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5295 if (!strcmpW( package->ProductCode, patch->products ))
5297 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5298 patch->delete_on_close = TRUE;
5300 /* FIXME: remove local patch package if this is the last product */
5302 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5303 package->delete_on_close = TRUE;
5305 msi_unpublish_icons( package );
5306 return ERROR_SUCCESS;
5309 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5311 UINT rc;
5312 WCHAR *remove;
5314 /* first do the same as an InstallExecute */
5315 rc = execute_script(package, SCRIPT_INSTALL);
5316 if (rc != ERROR_SUCCESS)
5317 return rc;
5319 /* then handle commit actions */
5320 rc = execute_script(package, SCRIPT_COMMIT);
5321 if (rc != ERROR_SUCCESS)
5322 return rc;
5324 remove = msi_dup_property(package->db, szRemove);
5325 rc = msi_unpublish_product(package, remove);
5326 msi_free(remove);
5327 return rc;
5330 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5332 static const WCHAR RunOnce[] = {
5333 'S','o','f','t','w','a','r','e','\\',
5334 'M','i','c','r','o','s','o','f','t','\\',
5335 'W','i','n','d','o','w','s','\\',
5336 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5337 'R','u','n','O','n','c','e',0};
5338 static const WCHAR InstallRunOnce[] = {
5339 'S','o','f','t','w','a','r','e','\\',
5340 'M','i','c','r','o','s','o','f','t','\\',
5341 'W','i','n','d','o','w','s','\\',
5342 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5343 'I','n','s','t','a','l','l','e','r','\\',
5344 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5346 static const WCHAR msiexec_fmt[] = {
5347 '%','s',
5348 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5349 '\"','%','s','\"',0};
5350 static const WCHAR install_fmt[] = {
5351 '/','I',' ','\"','%','s','\"',' ',
5352 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5353 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5354 WCHAR buffer[256], sysdir[MAX_PATH];
5355 HKEY hkey;
5356 WCHAR squished_pc[100];
5358 squash_guid(package->ProductCode,squished_pc);
5360 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5361 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5362 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5363 squished_pc);
5365 msi_reg_set_val_str( hkey, squished_pc, buffer );
5366 RegCloseKey(hkey);
5368 TRACE("Reboot command %s\n",debugstr_w(buffer));
5370 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5371 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5373 msi_reg_set_val_str( hkey, squished_pc, buffer );
5374 RegCloseKey(hkey);
5376 return ERROR_INSTALL_SUSPEND;
5379 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5381 static const WCHAR query[] =
5382 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5383 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5384 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5385 MSIRECORD *rec, *row;
5386 DWORD i, size = 0;
5387 va_list va;
5388 const WCHAR *str;
5389 WCHAR *data;
5391 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5393 rec = MSI_CreateRecord( count + 2 );
5394 str = MSI_RecordGetString( row, 1 );
5395 MSI_RecordSetStringW( rec, 0, str );
5396 msiobj_release( &row->hdr );
5397 MSI_RecordSetInteger( rec, 1, error );
5399 va_start( va, count );
5400 for (i = 0; i < count; i++)
5402 str = va_arg( va, const WCHAR *);
5403 MSI_RecordSetStringW( rec, i + 2, str );
5405 va_end( va );
5407 MSI_FormatRecordW( package, rec, NULL, &size );
5408 size++;
5409 data = msi_alloc( size * sizeof(WCHAR) );
5410 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5411 else data[0] = 0;
5412 msiobj_release( &rec->hdr );
5413 return data;
5416 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5418 DWORD attrib;
5419 UINT rc;
5422 * We are currently doing what should be done here in the top level Install
5423 * however for Administrative and uninstalls this step will be needed
5425 if (!package->PackagePath)
5426 return ERROR_SUCCESS;
5428 msi_set_sourcedir_props(package, TRUE);
5430 attrib = GetFileAttributesW(package->db->path);
5431 if (attrib == INVALID_FILE_ATTRIBUTES)
5433 LPWSTR prompt, msg;
5434 DWORD size = 0;
5436 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5437 package->Context, MSICODE_PRODUCT,
5438 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5439 if (rc == ERROR_MORE_DATA)
5441 prompt = msi_alloc(size * sizeof(WCHAR));
5442 MsiSourceListGetInfoW(package->ProductCode, NULL,
5443 package->Context, MSICODE_PRODUCT,
5444 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5446 else
5447 prompt = strdupW(package->db->path);
5449 msg = msi_build_error_string(package, 1302, 1, prompt);
5450 msi_free(prompt);
5451 while(attrib == INVALID_FILE_ATTRIBUTES)
5453 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5454 if (rc == IDCANCEL)
5456 msi_free(msg);
5457 return ERROR_INSTALL_USEREXIT;
5459 attrib = GetFileAttributesW(package->db->path);
5461 msi_free(msg);
5462 rc = ERROR_SUCCESS;
5464 else
5465 return ERROR_SUCCESS;
5467 return rc;
5470 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5472 HKEY hkey = 0;
5473 LPWSTR buffer, productid = NULL;
5474 UINT i, rc = ERROR_SUCCESS;
5475 MSIRECORD *uirow;
5477 static const WCHAR szPropKeys[][80] =
5479 {'P','r','o','d','u','c','t','I','D',0},
5480 {'U','S','E','R','N','A','M','E',0},
5481 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5482 {0},
5485 static const WCHAR szRegKeys[][80] =
5487 {'P','r','o','d','u','c','t','I','D',0},
5488 {'R','e','g','O','w','n','e','r',0},
5489 {'R','e','g','C','o','m','p','a','n','y',0},
5490 {0},
5493 if (msi_check_unpublish(package))
5495 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5496 goto end;
5499 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5500 if (!productid)
5501 goto end;
5503 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5504 NULL, &hkey, TRUE);
5505 if (rc != ERROR_SUCCESS)
5506 goto end;
5508 for( i = 0; szPropKeys[i][0]; i++ )
5510 buffer = msi_dup_property( package->db, szPropKeys[i] );
5511 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5512 msi_free( buffer );
5515 end:
5516 uirow = MSI_CreateRecord( 1 );
5517 MSI_RecordSetStringW( uirow, 1, productid );
5518 msi_ui_actiondata( package, szRegisterUser, uirow );
5519 msiobj_release( &uirow->hdr );
5521 msi_free(productid);
5522 RegCloseKey(hkey);
5523 return rc;
5527 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5529 UINT rc;
5531 package->script->InWhatSequence |= SEQUENCE_EXEC;
5532 rc = ACTION_ProcessExecSequence(package,FALSE);
5533 return rc;
5536 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5538 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5539 WCHAR productid_85[21], component_85[21], *ret;
5540 GUID clsid;
5541 DWORD sz;
5543 /* > is used if there is a component GUID and < if not. */
5545 productid_85[0] = 0;
5546 component_85[0] = 0;
5547 CLSIDFromString( package->ProductCode, &clsid );
5549 encode_base85_guid( &clsid, productid_85 );
5550 if (component)
5552 CLSIDFromString( component->ComponentId, &clsid );
5553 encode_base85_guid( &clsid, component_85 );
5556 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5557 debugstr_w(component_85));
5559 sz = 20 + strlenW( feature ) + 20 + 3;
5560 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5561 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5562 return ret;
5565 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5567 MSIPACKAGE *package = param;
5568 LPCWSTR compgroupid, component, feature, qualifier, text;
5569 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5570 HKEY hkey = NULL;
5571 UINT rc;
5572 MSICOMPONENT *comp;
5573 MSIFEATURE *feat;
5574 DWORD sz;
5575 MSIRECORD *uirow;
5576 int len;
5578 feature = MSI_RecordGetString(rec, 5);
5579 feat = msi_get_loaded_feature(package, feature);
5580 if (!feat)
5581 return ERROR_SUCCESS;
5583 feat->Action = msi_get_feature_action( package, feat );
5584 if (feat->Action != INSTALLSTATE_LOCAL &&
5585 feat->Action != INSTALLSTATE_SOURCE &&
5586 feat->Action != INSTALLSTATE_ADVERTISED)
5588 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5589 return ERROR_SUCCESS;
5592 component = MSI_RecordGetString(rec, 3);
5593 comp = msi_get_loaded_component(package, component);
5594 if (!comp)
5595 return ERROR_SUCCESS;
5597 compgroupid = MSI_RecordGetString(rec,1);
5598 qualifier = MSI_RecordGetString(rec,2);
5600 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5601 if (rc != ERROR_SUCCESS)
5602 goto end;
5604 advertise = msi_create_component_advertise_string( package, comp, feature );
5605 text = MSI_RecordGetString( rec, 4 );
5606 if (text)
5608 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5609 strcpyW( p, advertise );
5610 strcatW( p, text );
5611 msi_free( advertise );
5612 advertise = p;
5614 existing = msi_reg_get_val_str( hkey, qualifier );
5616 sz = strlenW( advertise ) + 1;
5617 if (existing)
5619 for (p = existing; *p; p += len)
5621 len = strlenW( p ) + 1;
5622 if (strcmpW( advertise, p )) sz += len;
5625 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5627 rc = ERROR_OUTOFMEMORY;
5628 goto end;
5630 q = output;
5631 if (existing)
5633 for (p = existing; *p; p += len)
5635 len = strlenW( p ) + 1;
5636 if (strcmpW( advertise, p ))
5638 memcpy( q, p, len * sizeof(WCHAR) );
5639 q += len;
5643 strcpyW( q, advertise );
5644 q[strlenW( q ) + 1] = 0;
5646 msi_reg_set_val_multi_str( hkey, qualifier, output );
5648 end:
5649 RegCloseKey(hkey);
5650 msi_free( output );
5651 msi_free( advertise );
5652 msi_free( existing );
5654 /* the UI chunk */
5655 uirow = MSI_CreateRecord( 2 );
5656 MSI_RecordSetStringW( uirow, 1, compgroupid );
5657 MSI_RecordSetStringW( uirow, 2, qualifier);
5658 msi_ui_actiondata( package, szPublishComponents, uirow );
5659 msiobj_release( &uirow->hdr );
5660 /* FIXME: call ui_progress? */
5662 return rc;
5666 * At present I am ignorning the advertised components part of this and only
5667 * focusing on the qualified component sets
5669 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5671 static const WCHAR query[] = {
5672 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5673 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5674 MSIQUERY *view;
5675 UINT rc;
5677 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5678 if (rc != ERROR_SUCCESS)
5679 return ERROR_SUCCESS;
5681 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5682 msiobj_release(&view->hdr);
5683 return rc;
5686 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5688 static const WCHAR szInstallerComponents[] = {
5689 'S','o','f','t','w','a','r','e','\\',
5690 'M','i','c','r','o','s','o','f','t','\\',
5691 'I','n','s','t','a','l','l','e','r','\\',
5692 'C','o','m','p','o','n','e','n','t','s','\\',0};
5694 MSIPACKAGE *package = param;
5695 LPCWSTR compgroupid, component, feature, qualifier;
5696 MSICOMPONENT *comp;
5697 MSIFEATURE *feat;
5698 MSIRECORD *uirow;
5699 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5700 LONG res;
5702 feature = MSI_RecordGetString( rec, 5 );
5703 feat = msi_get_loaded_feature( package, feature );
5704 if (!feat)
5705 return ERROR_SUCCESS;
5707 feat->Action = msi_get_feature_action( package, feat );
5708 if (feat->Action != INSTALLSTATE_ABSENT)
5710 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5711 return ERROR_SUCCESS;
5714 component = MSI_RecordGetString( rec, 3 );
5715 comp = msi_get_loaded_component( package, component );
5716 if (!comp)
5717 return ERROR_SUCCESS;
5719 compgroupid = MSI_RecordGetString( rec, 1 );
5720 qualifier = MSI_RecordGetString( rec, 2 );
5722 squash_guid( compgroupid, squashed );
5723 strcpyW( keypath, szInstallerComponents );
5724 strcatW( keypath, squashed );
5726 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5727 if (res != ERROR_SUCCESS)
5729 WARN("Unable to delete component key %d\n", res);
5732 uirow = MSI_CreateRecord( 2 );
5733 MSI_RecordSetStringW( uirow, 1, compgroupid );
5734 MSI_RecordSetStringW( uirow, 2, qualifier );
5735 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5736 msiobj_release( &uirow->hdr );
5738 return ERROR_SUCCESS;
5741 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5743 static const WCHAR query[] = {
5744 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5745 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5746 MSIQUERY *view;
5747 UINT rc;
5749 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5750 if (rc != ERROR_SUCCESS)
5751 return ERROR_SUCCESS;
5753 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5754 msiobj_release( &view->hdr );
5755 return rc;
5758 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5760 static const WCHAR query[] =
5761 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5762 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5763 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5764 MSIPACKAGE *package = param;
5765 MSICOMPONENT *component;
5766 MSIRECORD *row;
5767 MSIFILE *file;
5768 SC_HANDLE hscm = NULL, service = NULL;
5769 LPCWSTR comp, key;
5770 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5771 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5772 DWORD serv_type, start_type, err_control;
5773 SERVICE_DESCRIPTIONW sd = {NULL};
5774 UINT ret = ERROR_SUCCESS;
5776 comp = MSI_RecordGetString( rec, 12 );
5777 component = msi_get_loaded_component( package, comp );
5778 if (!component)
5780 WARN("service component not found\n");
5781 goto done;
5783 component->Action = msi_get_component_action( package, component );
5784 if (component->Action != INSTALLSTATE_LOCAL)
5786 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5787 goto done;
5789 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5790 if (!hscm)
5792 ERR("Failed to open the SC Manager!\n");
5793 goto done;
5796 start_type = MSI_RecordGetInteger(rec, 5);
5797 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5798 goto done;
5800 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5801 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5802 serv_type = MSI_RecordGetInteger(rec, 4);
5803 err_control = MSI_RecordGetInteger(rec, 6);
5804 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5805 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5806 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5807 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5808 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5809 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5811 /* fetch the service path */
5812 row = MSI_QueryGetRecord(package->db, query, comp);
5813 if (!row)
5815 ERR("Query failed\n");
5816 goto done;
5818 if (!(key = MSI_RecordGetString(row, 6)))
5820 msiobj_release(&row->hdr);
5821 goto done;
5823 file = msi_get_loaded_file(package, key);
5824 msiobj_release(&row->hdr);
5825 if (!file)
5827 ERR("Failed to load the service file\n");
5828 goto done;
5831 if (!args || !args[0]) image_path = file->TargetPath;
5832 else
5834 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5835 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5837 ret = ERROR_OUTOFMEMORY;
5838 goto done;
5841 strcpyW(image_path, file->TargetPath);
5842 strcatW(image_path, szSpace);
5843 strcatW(image_path, args);
5845 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5846 start_type, err_control, image_path, load_order,
5847 NULL, depends, serv_name, pass);
5849 if (!service)
5851 if (GetLastError() != ERROR_SERVICE_EXISTS)
5852 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5854 else if (sd.lpDescription)
5856 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5857 WARN("failed to set service description %u\n", GetLastError());
5860 if (image_path != file->TargetPath) msi_free(image_path);
5861 done:
5862 CloseServiceHandle(service);
5863 CloseServiceHandle(hscm);
5864 msi_free(name);
5865 msi_free(disp);
5866 msi_free(sd.lpDescription);
5867 msi_free(load_order);
5868 msi_free(serv_name);
5869 msi_free(pass);
5870 msi_free(depends);
5871 msi_free(args);
5873 return ret;
5876 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5878 static const WCHAR query[] = {
5879 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5880 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5881 MSIQUERY *view;
5882 UINT rc;
5884 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5885 if (rc != ERROR_SUCCESS)
5886 return ERROR_SUCCESS;
5888 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5889 msiobj_release(&view->hdr);
5890 return rc;
5893 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5894 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5896 LPCWSTR *vector, *temp_vector;
5897 LPWSTR p, q;
5898 DWORD sep_len;
5900 static const WCHAR separator[] = {'[','~',']',0};
5902 *numargs = 0;
5903 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5905 if (!args)
5906 return NULL;
5908 vector = msi_alloc(sizeof(LPWSTR));
5909 if (!vector)
5910 return NULL;
5912 p = args;
5915 (*numargs)++;
5916 vector[*numargs - 1] = p;
5918 if ((q = strstrW(p, separator)))
5920 *q = '\0';
5922 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5923 if (!temp_vector)
5925 msi_free(vector);
5926 return NULL;
5928 vector = temp_vector;
5930 p = q + sep_len;
5932 } while (q);
5934 return vector;
5937 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5939 MSIPACKAGE *package = param;
5940 MSICOMPONENT *comp;
5941 MSIRECORD *uirow;
5942 SC_HANDLE scm = NULL, service = NULL;
5943 LPCWSTR component, *vector = NULL;
5944 LPWSTR name, args, display_name = NULL;
5945 DWORD event, numargs, len, wait, dummy;
5946 UINT r = ERROR_FUNCTION_FAILED;
5947 SERVICE_STATUS_PROCESS status;
5948 ULONGLONG start_time;
5950 component = MSI_RecordGetString(rec, 6);
5951 comp = msi_get_loaded_component(package, component);
5952 if (!comp)
5953 return ERROR_SUCCESS;
5955 comp->Action = msi_get_component_action( package, comp );
5956 if (comp->Action != INSTALLSTATE_LOCAL)
5958 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5959 return ERROR_SUCCESS;
5962 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5963 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5964 event = MSI_RecordGetInteger(rec, 3);
5965 wait = MSI_RecordGetInteger(rec, 5);
5967 if (!(event & msidbServiceControlEventStart))
5969 r = ERROR_SUCCESS;
5970 goto done;
5973 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5974 if (!scm)
5976 ERR("Failed to open the service control manager\n");
5977 goto done;
5980 len = 0;
5981 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5982 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5984 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5985 GetServiceDisplayNameW( scm, name, display_name, &len );
5988 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5989 if (!service)
5991 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5992 goto done;
5995 vector = msi_service_args_to_vector(args, &numargs);
5997 if (!StartServiceW(service, numargs, vector) &&
5998 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6000 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6001 goto done;
6004 r = ERROR_SUCCESS;
6005 if (wait)
6007 /* wait for at most 30 seconds for the service to be up and running */
6008 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6009 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6011 TRACE("failed to query service status (%u)\n", GetLastError());
6012 goto done;
6014 start_time = GetTickCount64();
6015 while (status.dwCurrentState == SERVICE_START_PENDING)
6017 if (GetTickCount64() - start_time > 30000) break;
6018 Sleep(1000);
6019 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6020 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6022 TRACE("failed to query service status (%u)\n", GetLastError());
6023 goto done;
6026 if (status.dwCurrentState != SERVICE_RUNNING)
6028 WARN("service failed to start %u\n", status.dwCurrentState);
6029 r = ERROR_FUNCTION_FAILED;
6033 done:
6034 uirow = MSI_CreateRecord( 2 );
6035 MSI_RecordSetStringW( uirow, 1, display_name );
6036 MSI_RecordSetStringW( uirow, 2, name );
6037 msi_ui_actiondata( package, szStartServices, uirow );
6038 msiobj_release( &uirow->hdr );
6040 CloseServiceHandle(service);
6041 CloseServiceHandle(scm);
6043 msi_free(name);
6044 msi_free(args);
6045 msi_free(vector);
6046 msi_free(display_name);
6047 return r;
6050 static UINT ACTION_StartServices( MSIPACKAGE *package )
6052 static const WCHAR query[] = {
6053 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6054 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6055 MSIQUERY *view;
6056 UINT rc;
6058 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6059 if (rc != ERROR_SUCCESS)
6060 return ERROR_SUCCESS;
6062 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6063 msiobj_release(&view->hdr);
6064 return rc;
6067 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6069 DWORD i, needed, count;
6070 ENUM_SERVICE_STATUSW *dependencies;
6071 SERVICE_STATUS ss;
6072 SC_HANDLE depserv;
6073 BOOL stopped, ret = FALSE;
6075 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6076 0, &needed, &count))
6077 return TRUE;
6079 if (GetLastError() != ERROR_MORE_DATA)
6080 return FALSE;
6082 dependencies = msi_alloc(needed);
6083 if (!dependencies)
6084 return FALSE;
6086 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6087 needed, &needed, &count))
6088 goto done;
6090 for (i = 0; i < count; i++)
6092 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6093 SERVICE_STOP | SERVICE_QUERY_STATUS);
6094 if (!depserv)
6095 goto done;
6097 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6098 CloseServiceHandle(depserv);
6099 if (!stopped)
6100 goto done;
6103 ret = TRUE;
6105 done:
6106 msi_free(dependencies);
6107 return ret;
6110 static UINT stop_service( LPCWSTR name )
6112 SC_HANDLE scm = NULL, service = NULL;
6113 SERVICE_STATUS status;
6114 SERVICE_STATUS_PROCESS ssp;
6115 DWORD needed;
6117 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6118 if (!scm)
6120 WARN("Failed to open the SCM: %d\n", GetLastError());
6121 goto done;
6124 service = OpenServiceW(scm, name,
6125 SERVICE_STOP |
6126 SERVICE_QUERY_STATUS |
6127 SERVICE_ENUMERATE_DEPENDENTS);
6128 if (!service)
6130 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6131 goto done;
6134 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6135 sizeof(SERVICE_STATUS_PROCESS), &needed))
6137 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6138 goto done;
6141 if (ssp.dwCurrentState == SERVICE_STOPPED)
6142 goto done;
6144 stop_service_dependents(scm, service);
6146 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6147 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6149 done:
6150 CloseServiceHandle(service);
6151 CloseServiceHandle(scm);
6153 return ERROR_SUCCESS;
6156 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6158 MSIPACKAGE *package = param;
6159 MSICOMPONENT *comp;
6160 MSIRECORD *uirow;
6161 LPCWSTR component;
6162 LPWSTR name = NULL, display_name = NULL;
6163 DWORD event, len;
6164 SC_HANDLE scm;
6166 event = MSI_RecordGetInteger( rec, 3 );
6167 if (!(event & msidbServiceControlEventStop))
6168 return ERROR_SUCCESS;
6170 component = MSI_RecordGetString( rec, 6 );
6171 comp = msi_get_loaded_component( package, component );
6172 if (!comp)
6173 return ERROR_SUCCESS;
6175 comp->Action = msi_get_component_action( package, comp );
6176 if (comp->Action != INSTALLSTATE_ABSENT)
6178 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6179 return ERROR_SUCCESS;
6182 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6183 if (!scm)
6185 ERR("Failed to open the service control manager\n");
6186 goto done;
6189 len = 0;
6190 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6191 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6193 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6194 GetServiceDisplayNameW( scm, name, display_name, &len );
6196 CloseServiceHandle( scm );
6198 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6199 stop_service( name );
6201 done:
6202 uirow = MSI_CreateRecord( 2 );
6203 MSI_RecordSetStringW( uirow, 1, display_name );
6204 MSI_RecordSetStringW( uirow, 2, name );
6205 msi_ui_actiondata( package, szStopServices, uirow );
6206 msiobj_release( &uirow->hdr );
6208 msi_free( name );
6209 msi_free( display_name );
6210 return ERROR_SUCCESS;
6213 static UINT ACTION_StopServices( MSIPACKAGE *package )
6215 static const WCHAR query[] = {
6216 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6217 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6218 MSIQUERY *view;
6219 UINT rc;
6221 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6222 if (rc != ERROR_SUCCESS)
6223 return ERROR_SUCCESS;
6225 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6226 msiobj_release(&view->hdr);
6227 return rc;
6230 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6232 MSIPACKAGE *package = param;
6233 MSICOMPONENT *comp;
6234 MSIRECORD *uirow;
6235 LPWSTR name = NULL, display_name = NULL;
6236 DWORD event, len;
6237 SC_HANDLE scm = NULL, service = NULL;
6239 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6240 if (!comp)
6241 return ERROR_SUCCESS;
6243 event = MSI_RecordGetInteger( rec, 3 );
6244 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6246 comp->Action = msi_get_component_action( package, comp );
6247 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6248 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6250 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6251 msi_free( name );
6252 return ERROR_SUCCESS;
6254 stop_service( name );
6256 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6257 if (!scm)
6259 WARN("Failed to open the SCM: %d\n", GetLastError());
6260 goto done;
6263 len = 0;
6264 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6265 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6267 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6268 GetServiceDisplayNameW( scm, name, display_name, &len );
6271 service = OpenServiceW( scm, name, DELETE );
6272 if (!service)
6274 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6275 goto done;
6278 if (!DeleteService( service ))
6279 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6281 done:
6282 uirow = MSI_CreateRecord( 2 );
6283 MSI_RecordSetStringW( uirow, 1, display_name );
6284 MSI_RecordSetStringW( uirow, 2, name );
6285 msi_ui_actiondata( package, szDeleteServices, uirow );
6286 msiobj_release( &uirow->hdr );
6288 CloseServiceHandle( service );
6289 CloseServiceHandle( scm );
6290 msi_free( name );
6291 msi_free( display_name );
6293 return ERROR_SUCCESS;
6296 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6298 static const WCHAR query[] = {
6299 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6300 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6301 MSIQUERY *view;
6302 UINT rc;
6304 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6305 if (rc != ERROR_SUCCESS)
6306 return ERROR_SUCCESS;
6308 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6309 msiobj_release( &view->hdr );
6310 return rc;
6313 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6315 MSIPACKAGE *package = param;
6316 LPWSTR driver, driver_path, ptr;
6317 WCHAR outpath[MAX_PATH];
6318 MSIFILE *driver_file = NULL, *setup_file = NULL;
6319 MSICOMPONENT *comp;
6320 MSIRECORD *uirow;
6321 LPCWSTR desc, file_key, component;
6322 DWORD len, usage;
6323 UINT r = ERROR_SUCCESS;
6325 static const WCHAR driver_fmt[] = {
6326 'D','r','i','v','e','r','=','%','s',0};
6327 static const WCHAR setup_fmt[] = {
6328 'S','e','t','u','p','=','%','s',0};
6329 static const WCHAR usage_fmt[] = {
6330 'F','i','l','e','U','s','a','g','e','=','1',0};
6332 component = MSI_RecordGetString( rec, 2 );
6333 comp = msi_get_loaded_component( package, component );
6334 if (!comp)
6335 return ERROR_SUCCESS;
6337 comp->Action = msi_get_component_action( package, comp );
6338 if (comp->Action != INSTALLSTATE_LOCAL)
6340 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6341 return ERROR_SUCCESS;
6343 desc = MSI_RecordGetString(rec, 3);
6345 file_key = MSI_RecordGetString( rec, 4 );
6346 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6348 file_key = MSI_RecordGetString( rec, 5 );
6349 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6351 if (!driver_file)
6353 ERR("ODBC Driver entry not found!\n");
6354 return ERROR_FUNCTION_FAILED;
6357 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6358 if (setup_file)
6359 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6360 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6362 driver = msi_alloc(len * sizeof(WCHAR));
6363 if (!driver)
6364 return ERROR_OUTOFMEMORY;
6366 ptr = driver;
6367 lstrcpyW(ptr, desc);
6368 ptr += lstrlenW(ptr) + 1;
6370 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6371 ptr += len + 1;
6373 if (setup_file)
6375 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6376 ptr += len + 1;
6379 lstrcpyW(ptr, usage_fmt);
6380 ptr += lstrlenW(ptr) + 1;
6381 *ptr = '\0';
6383 if (!driver_file->TargetPath)
6385 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6386 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6388 driver_path = strdupW(driver_file->TargetPath);
6389 ptr = strrchrW(driver_path, '\\');
6390 if (ptr) *ptr = '\0';
6392 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6393 NULL, ODBC_INSTALL_COMPLETE, &usage))
6395 ERR("Failed to install SQL driver!\n");
6396 r = ERROR_FUNCTION_FAILED;
6399 uirow = MSI_CreateRecord( 5 );
6400 MSI_RecordSetStringW( uirow, 1, desc );
6401 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6402 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6403 msi_ui_actiondata( package, szInstallODBC, uirow );
6404 msiobj_release( &uirow->hdr );
6406 msi_free(driver);
6407 msi_free(driver_path);
6409 return r;
6412 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6414 MSIPACKAGE *package = param;
6415 LPWSTR translator, translator_path, ptr;
6416 WCHAR outpath[MAX_PATH];
6417 MSIFILE *translator_file = NULL, *setup_file = NULL;
6418 MSICOMPONENT *comp;
6419 MSIRECORD *uirow;
6420 LPCWSTR desc, file_key, component;
6421 DWORD len, usage;
6422 UINT r = ERROR_SUCCESS;
6424 static const WCHAR translator_fmt[] = {
6425 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6426 static const WCHAR setup_fmt[] = {
6427 'S','e','t','u','p','=','%','s',0};
6429 component = MSI_RecordGetString( rec, 2 );
6430 comp = msi_get_loaded_component( package, component );
6431 if (!comp)
6432 return ERROR_SUCCESS;
6434 comp->Action = msi_get_component_action( package, comp );
6435 if (comp->Action != INSTALLSTATE_LOCAL)
6437 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6438 return ERROR_SUCCESS;
6440 desc = MSI_RecordGetString(rec, 3);
6442 file_key = MSI_RecordGetString( rec, 4 );
6443 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6445 file_key = MSI_RecordGetString( rec, 5 );
6446 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6448 if (!translator_file)
6450 ERR("ODBC Translator entry not found!\n");
6451 return ERROR_FUNCTION_FAILED;
6454 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6455 if (setup_file)
6456 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6458 translator = msi_alloc(len * sizeof(WCHAR));
6459 if (!translator)
6460 return ERROR_OUTOFMEMORY;
6462 ptr = translator;
6463 lstrcpyW(ptr, desc);
6464 ptr += lstrlenW(ptr) + 1;
6466 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6467 ptr += len + 1;
6469 if (setup_file)
6471 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6472 ptr += len + 1;
6474 *ptr = '\0';
6476 translator_path = strdupW(translator_file->TargetPath);
6477 ptr = strrchrW(translator_path, '\\');
6478 if (ptr) *ptr = '\0';
6480 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6481 NULL, ODBC_INSTALL_COMPLETE, &usage))
6483 ERR("Failed to install SQL translator!\n");
6484 r = ERROR_FUNCTION_FAILED;
6487 uirow = MSI_CreateRecord( 5 );
6488 MSI_RecordSetStringW( uirow, 1, desc );
6489 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6490 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6491 msi_ui_actiondata( package, szInstallODBC, uirow );
6492 msiobj_release( &uirow->hdr );
6494 msi_free(translator);
6495 msi_free(translator_path);
6497 return r;
6500 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6502 MSIPACKAGE *package = param;
6503 MSICOMPONENT *comp;
6504 LPWSTR attrs;
6505 LPCWSTR desc, driver, component;
6506 WORD request = ODBC_ADD_SYS_DSN;
6507 INT registration;
6508 DWORD len;
6509 UINT r = ERROR_SUCCESS;
6510 MSIRECORD *uirow;
6512 static const WCHAR attrs_fmt[] = {
6513 'D','S','N','=','%','s',0 };
6515 component = MSI_RecordGetString( rec, 2 );
6516 comp = msi_get_loaded_component( package, component );
6517 if (!comp)
6518 return ERROR_SUCCESS;
6520 comp->Action = msi_get_component_action( package, comp );
6521 if (comp->Action != INSTALLSTATE_LOCAL)
6523 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6524 return ERROR_SUCCESS;
6527 desc = MSI_RecordGetString(rec, 3);
6528 driver = MSI_RecordGetString(rec, 4);
6529 registration = MSI_RecordGetInteger(rec, 5);
6531 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6532 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6534 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6535 attrs = msi_alloc(len * sizeof(WCHAR));
6536 if (!attrs)
6537 return ERROR_OUTOFMEMORY;
6539 len = sprintfW(attrs, attrs_fmt, desc);
6540 attrs[len + 1] = 0;
6542 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6544 ERR("Failed to install SQL data source!\n");
6545 r = ERROR_FUNCTION_FAILED;
6548 uirow = MSI_CreateRecord( 5 );
6549 MSI_RecordSetStringW( uirow, 1, desc );
6550 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6551 MSI_RecordSetInteger( uirow, 3, request );
6552 msi_ui_actiondata( package, szInstallODBC, uirow );
6553 msiobj_release( &uirow->hdr );
6555 msi_free(attrs);
6557 return r;
6560 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6562 static const WCHAR driver_query[] = {
6563 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6564 'O','D','B','C','D','r','i','v','e','r',0};
6565 static const WCHAR translator_query[] = {
6566 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6567 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6568 static const WCHAR source_query[] = {
6569 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6570 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6571 MSIQUERY *view;
6572 UINT rc;
6574 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6575 if (rc == ERROR_SUCCESS)
6577 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6578 msiobj_release(&view->hdr);
6579 if (rc != ERROR_SUCCESS)
6580 return rc;
6582 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6583 if (rc == ERROR_SUCCESS)
6585 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6586 msiobj_release(&view->hdr);
6587 if (rc != ERROR_SUCCESS)
6588 return rc;
6590 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6591 if (rc == ERROR_SUCCESS)
6593 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6594 msiobj_release(&view->hdr);
6595 if (rc != ERROR_SUCCESS)
6596 return rc;
6598 return ERROR_SUCCESS;
6601 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6603 MSIPACKAGE *package = param;
6604 MSICOMPONENT *comp;
6605 MSIRECORD *uirow;
6606 DWORD usage;
6607 LPCWSTR desc, component;
6609 component = MSI_RecordGetString( rec, 2 );
6610 comp = msi_get_loaded_component( package, component );
6611 if (!comp)
6612 return ERROR_SUCCESS;
6614 comp->Action = msi_get_component_action( package, comp );
6615 if (comp->Action != INSTALLSTATE_ABSENT)
6617 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6618 return ERROR_SUCCESS;
6621 desc = MSI_RecordGetString( rec, 3 );
6622 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6624 WARN("Failed to remove ODBC driver\n");
6626 else if (!usage)
6628 FIXME("Usage count reached 0\n");
6631 uirow = MSI_CreateRecord( 2 );
6632 MSI_RecordSetStringW( uirow, 1, desc );
6633 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6634 msi_ui_actiondata( package, szRemoveODBC, uirow );
6635 msiobj_release( &uirow->hdr );
6637 return ERROR_SUCCESS;
6640 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6642 MSIPACKAGE *package = param;
6643 MSICOMPONENT *comp;
6644 MSIRECORD *uirow;
6645 DWORD usage;
6646 LPCWSTR desc, component;
6648 component = MSI_RecordGetString( rec, 2 );
6649 comp = msi_get_loaded_component( package, component );
6650 if (!comp)
6651 return ERROR_SUCCESS;
6653 comp->Action = msi_get_component_action( package, comp );
6654 if (comp->Action != INSTALLSTATE_ABSENT)
6656 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6657 return ERROR_SUCCESS;
6660 desc = MSI_RecordGetString( rec, 3 );
6661 if (!SQLRemoveTranslatorW( desc, &usage ))
6663 WARN("Failed to remove ODBC translator\n");
6665 else if (!usage)
6667 FIXME("Usage count reached 0\n");
6670 uirow = MSI_CreateRecord( 2 );
6671 MSI_RecordSetStringW( uirow, 1, desc );
6672 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6673 msi_ui_actiondata( package, szRemoveODBC, uirow );
6674 msiobj_release( &uirow->hdr );
6676 return ERROR_SUCCESS;
6679 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6681 MSIPACKAGE *package = param;
6682 MSICOMPONENT *comp;
6683 MSIRECORD *uirow;
6684 LPWSTR attrs;
6685 LPCWSTR desc, driver, component;
6686 WORD request = ODBC_REMOVE_SYS_DSN;
6687 INT registration;
6688 DWORD len;
6690 static const WCHAR attrs_fmt[] = {
6691 'D','S','N','=','%','s',0 };
6693 component = MSI_RecordGetString( rec, 2 );
6694 comp = msi_get_loaded_component( package, component );
6695 if (!comp)
6696 return ERROR_SUCCESS;
6698 comp->Action = msi_get_component_action( package, comp );
6699 if (comp->Action != INSTALLSTATE_ABSENT)
6701 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6702 return ERROR_SUCCESS;
6705 desc = MSI_RecordGetString( rec, 3 );
6706 driver = MSI_RecordGetString( rec, 4 );
6707 registration = MSI_RecordGetInteger( rec, 5 );
6709 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6710 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6712 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6713 attrs = msi_alloc( len * sizeof(WCHAR) );
6714 if (!attrs)
6715 return ERROR_OUTOFMEMORY;
6717 FIXME("Use ODBCSourceAttribute table\n");
6719 len = sprintfW( attrs, attrs_fmt, desc );
6720 attrs[len + 1] = 0;
6722 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6724 WARN("Failed to remove ODBC data source\n");
6726 msi_free( attrs );
6728 uirow = MSI_CreateRecord( 3 );
6729 MSI_RecordSetStringW( uirow, 1, desc );
6730 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6731 MSI_RecordSetInteger( uirow, 3, request );
6732 msi_ui_actiondata( package, szRemoveODBC, uirow );
6733 msiobj_release( &uirow->hdr );
6735 return ERROR_SUCCESS;
6738 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6740 static const WCHAR driver_query[] = {
6741 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6742 'O','D','B','C','D','r','i','v','e','r',0};
6743 static const WCHAR translator_query[] = {
6744 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6745 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6746 static const WCHAR source_query[] = {
6747 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6748 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6749 MSIQUERY *view;
6750 UINT rc;
6752 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6753 if (rc == ERROR_SUCCESS)
6755 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6756 msiobj_release( &view->hdr );
6757 if (rc != ERROR_SUCCESS)
6758 return rc;
6760 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6761 if (rc == ERROR_SUCCESS)
6763 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6764 msiobj_release( &view->hdr );
6765 if (rc != ERROR_SUCCESS)
6766 return rc;
6768 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6769 if (rc == ERROR_SUCCESS)
6771 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6772 msiobj_release( &view->hdr );
6773 if (rc != ERROR_SUCCESS)
6774 return rc;
6776 return ERROR_SUCCESS;
6779 #define ENV_ACT_SETALWAYS 0x1
6780 #define ENV_ACT_SETABSENT 0x2
6781 #define ENV_ACT_REMOVE 0x4
6782 #define ENV_ACT_REMOVEMATCH 0x8
6784 #define ENV_MOD_MACHINE 0x20000000
6785 #define ENV_MOD_APPEND 0x40000000
6786 #define ENV_MOD_PREFIX 0x80000000
6787 #define ENV_MOD_MASK 0xC0000000
6789 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6791 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6793 LPCWSTR cptr = *name;
6795 static const WCHAR prefix[] = {'[','~',']',0};
6796 static const int prefix_len = 3;
6798 *flags = 0;
6799 while (*cptr)
6801 if (*cptr == '=')
6802 *flags |= ENV_ACT_SETALWAYS;
6803 else if (*cptr == '+')
6804 *flags |= ENV_ACT_SETABSENT;
6805 else if (*cptr == '-')
6806 *flags |= ENV_ACT_REMOVE;
6807 else if (*cptr == '!')
6808 *flags |= ENV_ACT_REMOVEMATCH;
6809 else if (*cptr == '*')
6810 *flags |= ENV_MOD_MACHINE;
6811 else
6812 break;
6814 cptr++;
6815 (*name)++;
6818 if (!*cptr)
6820 ERR("Missing environment variable\n");
6821 return ERROR_FUNCTION_FAILED;
6824 if (*value)
6826 LPCWSTR ptr = *value;
6827 if (!strncmpW(ptr, prefix, prefix_len))
6829 if (ptr[prefix_len] == szSemiColon[0])
6831 *flags |= ENV_MOD_APPEND;
6832 *value += lstrlenW(prefix);
6834 else
6836 *value = NULL;
6839 else if (lstrlenW(*value) >= prefix_len)
6841 ptr += lstrlenW(ptr) - prefix_len;
6842 if (!strcmpW( ptr, prefix ))
6844 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6846 *flags |= ENV_MOD_PREFIX;
6847 /* the "[~]" will be removed by deformat_string */;
6849 else
6851 *value = NULL;
6857 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6858 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6859 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6860 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6862 ERR("Invalid flags: %08x\n", *flags);
6863 return ERROR_FUNCTION_FAILED;
6866 if (!*flags)
6867 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6869 return ERROR_SUCCESS;
6872 static UINT open_env_key( DWORD flags, HKEY *key )
6874 static const WCHAR user_env[] =
6875 {'E','n','v','i','r','o','n','m','e','n','t',0};
6876 static const WCHAR machine_env[] =
6877 {'S','y','s','t','e','m','\\',
6878 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6879 'C','o','n','t','r','o','l','\\',
6880 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6881 'E','n','v','i','r','o','n','m','e','n','t',0};
6882 const WCHAR *env;
6883 HKEY root;
6884 LONG res;
6886 if (flags & ENV_MOD_MACHINE)
6888 env = machine_env;
6889 root = HKEY_LOCAL_MACHINE;
6891 else
6893 env = user_env;
6894 root = HKEY_CURRENT_USER;
6897 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6898 if (res != ERROR_SUCCESS)
6900 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6901 return ERROR_FUNCTION_FAILED;
6904 return ERROR_SUCCESS;
6907 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6909 MSIPACKAGE *package = param;
6910 LPCWSTR name, value, component;
6911 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6912 DWORD flags, type, size;
6913 UINT res;
6914 HKEY env = NULL;
6915 MSICOMPONENT *comp;
6916 MSIRECORD *uirow;
6917 int action = 0;
6919 component = MSI_RecordGetString(rec, 4);
6920 comp = msi_get_loaded_component(package, component);
6921 if (!comp)
6922 return ERROR_SUCCESS;
6924 comp->Action = msi_get_component_action( package, comp );
6925 if (comp->Action != INSTALLSTATE_LOCAL)
6927 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6928 return ERROR_SUCCESS;
6930 name = MSI_RecordGetString(rec, 2);
6931 value = MSI_RecordGetString(rec, 3);
6933 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6935 res = env_parse_flags(&name, &value, &flags);
6936 if (res != ERROR_SUCCESS || !value)
6937 goto done;
6939 if (value && !deformat_string(package, value, &deformatted))
6941 res = ERROR_OUTOFMEMORY;
6942 goto done;
6945 value = deformatted;
6947 res = open_env_key( flags, &env );
6948 if (res != ERROR_SUCCESS)
6949 goto done;
6951 if (flags & ENV_MOD_MACHINE)
6952 action |= 0x20000000;
6954 size = 0;
6955 type = REG_SZ;
6956 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6957 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6958 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6959 goto done;
6961 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6963 action = 0x2;
6965 /* Nothing to do. */
6966 if (!value)
6968 res = ERROR_SUCCESS;
6969 goto done;
6972 /* If we are appending but the string was empty, strip ; */
6973 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6975 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6976 newval = strdupW(value);
6977 if (!newval)
6979 res = ERROR_OUTOFMEMORY;
6980 goto done;
6983 else
6985 action = 0x1;
6987 /* Contrary to MSDN, +-variable to [~];path works */
6988 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6990 res = ERROR_SUCCESS;
6991 goto done;
6994 data = msi_alloc(size);
6995 if (!data)
6997 RegCloseKey(env);
6998 return ERROR_OUTOFMEMORY;
7001 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
7002 if (res != ERROR_SUCCESS)
7003 goto done;
7005 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7007 action = 0x4;
7008 res = RegDeleteValueW(env, name);
7009 if (res != ERROR_SUCCESS)
7010 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7011 goto done;
7014 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
7015 if (flags & ENV_MOD_MASK)
7017 DWORD mod_size;
7018 int multiplier = 0;
7019 if (flags & ENV_MOD_APPEND) multiplier++;
7020 if (flags & ENV_MOD_PREFIX) multiplier++;
7021 mod_size = lstrlenW(value) * multiplier;
7022 size += mod_size * sizeof(WCHAR);
7025 newval = msi_alloc(size);
7026 ptr = newval;
7027 if (!newval)
7029 res = ERROR_OUTOFMEMORY;
7030 goto done;
7033 if (flags & ENV_MOD_PREFIX)
7035 lstrcpyW(newval, value);
7036 ptr = newval + lstrlenW(value);
7037 action |= 0x80000000;
7040 lstrcpyW(ptr, data);
7042 if (flags & ENV_MOD_APPEND)
7044 lstrcatW(newval, value);
7045 action |= 0x40000000;
7048 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7049 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
7050 if (res)
7052 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7055 done:
7056 uirow = MSI_CreateRecord( 3 );
7057 MSI_RecordSetStringW( uirow, 1, name );
7058 MSI_RecordSetStringW( uirow, 2, newval );
7059 MSI_RecordSetInteger( uirow, 3, action );
7060 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7061 msiobj_release( &uirow->hdr );
7063 if (env) RegCloseKey(env);
7064 msi_free(deformatted);
7065 msi_free(data);
7066 msi_free(newval);
7067 return res;
7070 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7072 static const WCHAR query[] = {
7073 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7074 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7075 MSIQUERY *view;
7076 UINT rc;
7078 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7079 if (rc != ERROR_SUCCESS)
7080 return ERROR_SUCCESS;
7082 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7083 msiobj_release(&view->hdr);
7084 return rc;
7087 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7089 MSIPACKAGE *package = param;
7090 LPCWSTR name, value, component;
7091 LPWSTR deformatted = NULL;
7092 DWORD flags;
7093 HKEY env;
7094 MSICOMPONENT *comp;
7095 MSIRECORD *uirow;
7096 int action = 0;
7097 LONG res;
7098 UINT r;
7100 component = MSI_RecordGetString( rec, 4 );
7101 comp = msi_get_loaded_component( package, component );
7102 if (!comp)
7103 return ERROR_SUCCESS;
7105 comp->Action = msi_get_component_action( package, comp );
7106 if (comp->Action != INSTALLSTATE_ABSENT)
7108 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7109 return ERROR_SUCCESS;
7111 name = MSI_RecordGetString( rec, 2 );
7112 value = MSI_RecordGetString( rec, 3 );
7114 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7116 r = env_parse_flags( &name, &value, &flags );
7117 if (r != ERROR_SUCCESS)
7118 return r;
7120 if (!(flags & ENV_ACT_REMOVE))
7122 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7123 return ERROR_SUCCESS;
7126 if (value && !deformat_string( package, value, &deformatted ))
7127 return ERROR_OUTOFMEMORY;
7129 value = deformatted;
7131 r = open_env_key( flags, &env );
7132 if (r != ERROR_SUCCESS)
7134 r = ERROR_SUCCESS;
7135 goto done;
7138 if (flags & ENV_MOD_MACHINE)
7139 action |= 0x20000000;
7141 TRACE("Removing %s\n", debugstr_w(name));
7143 res = RegDeleteValueW( env, name );
7144 if (res != ERROR_SUCCESS)
7146 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
7147 r = ERROR_SUCCESS;
7150 done:
7151 uirow = MSI_CreateRecord( 3 );
7152 MSI_RecordSetStringW( uirow, 1, name );
7153 MSI_RecordSetStringW( uirow, 2, value );
7154 MSI_RecordSetInteger( uirow, 3, action );
7155 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7156 msiobj_release( &uirow->hdr );
7158 if (env) RegCloseKey( env );
7159 msi_free( deformatted );
7160 return r;
7163 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7165 static const WCHAR query[] = {
7166 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7167 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7168 MSIQUERY *view;
7169 UINT rc;
7171 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7172 if (rc != ERROR_SUCCESS)
7173 return ERROR_SUCCESS;
7175 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7176 msiobj_release( &view->hdr );
7177 return rc;
7180 UINT msi_validate_product_id( MSIPACKAGE *package )
7182 LPWSTR key, template, id;
7183 UINT r = ERROR_SUCCESS;
7185 id = msi_dup_property( package->db, szProductID );
7186 if (id)
7188 msi_free( id );
7189 return ERROR_SUCCESS;
7191 template = msi_dup_property( package->db, szPIDTemplate );
7192 key = msi_dup_property( package->db, szPIDKEY );
7193 if (key && template)
7195 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7196 r = msi_set_property( package->db, szProductID, key, -1 );
7198 msi_free( template );
7199 msi_free( key );
7200 return r;
7203 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7205 return msi_validate_product_id( package );
7208 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7210 TRACE("\n");
7211 package->need_reboot_at_end = 1;
7212 return ERROR_SUCCESS;
7215 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7217 static const WCHAR szAvailableFreeReg[] =
7218 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7219 MSIRECORD *uirow;
7220 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7222 TRACE("%p %d kilobytes\n", package, space);
7224 uirow = MSI_CreateRecord( 1 );
7225 MSI_RecordSetInteger( uirow, 1, space );
7226 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7227 msiobj_release( &uirow->hdr );
7229 return ERROR_SUCCESS;
7232 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7234 TRACE("%p\n", package);
7236 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7237 return ERROR_SUCCESS;
7240 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7242 FIXME("%p\n", package);
7243 return ERROR_SUCCESS;
7246 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7248 static const WCHAR driver_query[] = {
7249 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7250 'O','D','B','C','D','r','i','v','e','r',0};
7251 static const WCHAR translator_query[] = {
7252 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7253 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7254 MSIQUERY *view;
7255 UINT r, count;
7257 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7258 if (r == ERROR_SUCCESS)
7260 count = 0;
7261 r = MSI_IterateRecords( view, &count, NULL, package );
7262 msiobj_release( &view->hdr );
7263 if (r != ERROR_SUCCESS)
7264 return r;
7265 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7267 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7268 if (r == ERROR_SUCCESS)
7270 count = 0;
7271 r = MSI_IterateRecords( view, &count, NULL, package );
7272 msiobj_release( &view->hdr );
7273 if (r != ERROR_SUCCESS)
7274 return r;
7275 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7277 return ERROR_SUCCESS;
7280 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7282 static const WCHAR fmtW[] =
7283 {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7284 MSIPACKAGE *package = param;
7285 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7286 int attrs = MSI_RecordGetInteger( rec, 5 );
7287 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7288 WCHAR *product, *features, *cmd;
7289 STARTUPINFOW si;
7290 PROCESS_INFORMATION info;
7291 BOOL ret;
7293 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7294 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7296 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7298 len += strlenW( product );
7299 if (features)
7300 len += strlenW( features );
7301 else
7302 len += sizeof(szAll) / sizeof(szAll[0]);
7304 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7306 msi_free( product );
7307 msi_free( features );
7308 return ERROR_OUTOFMEMORY;
7310 sprintfW( cmd, fmtW, product, features ? features : szAll );
7311 msi_free( product );
7312 msi_free( features );
7314 memset( &si, 0, sizeof(STARTUPINFOW) );
7315 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7316 msi_free( cmd );
7317 if (!ret) return GetLastError();
7318 CloseHandle( info.hThread );
7320 WaitForSingleObject( info.hProcess, INFINITE );
7321 CloseHandle( info.hProcess );
7322 return ERROR_SUCCESS;
7325 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7327 static const WCHAR query[] = {
7328 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7329 MSIQUERY *view;
7330 UINT r;
7332 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7333 if (r == ERROR_SUCCESS)
7335 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7336 msiobj_release( &view->hdr );
7337 if (r != ERROR_SUCCESS)
7338 return r;
7340 return ERROR_SUCCESS;
7343 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7345 MSIPACKAGE *package = param;
7346 int attributes = MSI_RecordGetInteger( rec, 5 );
7348 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7350 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7351 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7352 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7353 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7354 HKEY hkey;
7355 UINT r;
7357 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7359 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7360 if (r != ERROR_SUCCESS)
7361 return ERROR_SUCCESS;
7363 else
7365 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7366 if (r != ERROR_SUCCESS)
7367 return ERROR_SUCCESS;
7369 RegCloseKey( hkey );
7371 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7372 debugstr_w(upgrade_code), debugstr_w(version_min),
7373 debugstr_w(version_max), debugstr_w(language));
7375 return ERROR_SUCCESS;
7378 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7380 static const WCHAR query[] = {
7381 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7382 'U','p','g','r','a','d','e',0};
7383 MSIQUERY *view;
7384 UINT r;
7386 if (msi_get_property_int( package->db, szInstalled, 0 ))
7388 TRACE("product is installed, skipping action\n");
7389 return ERROR_SUCCESS;
7391 if (msi_get_property_int( package->db, szPreselected, 0 ))
7393 TRACE("Preselected property is set, not migrating feature states\n");
7394 return ERROR_SUCCESS;
7396 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7397 if (r == ERROR_SUCCESS)
7399 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7400 msiobj_release( &view->hdr );
7401 if (r != ERROR_SUCCESS)
7402 return r;
7404 return ERROR_SUCCESS;
7407 static void bind_image( const char *filename, const char *path )
7409 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7411 WARN("failed to bind image %u\n", GetLastError());
7415 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7417 UINT i;
7418 MSIFILE *file;
7419 MSIPACKAGE *package = param;
7420 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7421 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7422 char *filenameA, *pathA;
7423 WCHAR *pathW, **path_list;
7425 if (!(file = msi_get_loaded_file( package, key )))
7427 WARN("file %s not found\n", debugstr_w(key));
7428 return ERROR_SUCCESS;
7430 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7431 path_list = msi_split_string( paths, ';' );
7432 if (!path_list) bind_image( filenameA, NULL );
7433 else
7435 for (i = 0; path_list[i] && path_list[i][0]; i++)
7437 deformat_string( package, path_list[i], &pathW );
7438 if ((pathA = strdupWtoA( pathW )))
7440 bind_image( filenameA, pathA );
7441 msi_free( pathA );
7443 msi_free( pathW );
7446 msi_free( path_list );
7447 msi_free( filenameA );
7448 return ERROR_SUCCESS;
7451 static UINT ACTION_BindImage( MSIPACKAGE *package )
7453 static const WCHAR query[] = {
7454 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7455 'B','i','n','d','I','m','a','g','e',0};
7456 MSIQUERY *view;
7457 UINT r;
7459 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7460 if (r == ERROR_SUCCESS)
7462 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7463 msiobj_release( &view->hdr );
7464 if (r != ERROR_SUCCESS)
7465 return r;
7467 return ERROR_SUCCESS;
7470 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7472 static const WCHAR query[] = {
7473 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7474 MSIQUERY *view;
7475 DWORD count = 0;
7476 UINT r;
7478 r = MSI_OpenQuery( package->db, &view, query, table );
7479 if (r == ERROR_SUCCESS)
7481 r = MSI_IterateRecords(view, &count, NULL, package);
7482 msiobj_release(&view->hdr);
7483 if (r != ERROR_SUCCESS)
7484 return r;
7486 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7487 return ERROR_SUCCESS;
7490 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7492 static const WCHAR table[] = {
7493 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7494 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7497 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7499 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7500 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7503 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7505 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7506 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7509 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7511 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7512 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7515 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7517 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7518 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7521 static const struct
7523 const WCHAR *action;
7524 UINT (*handler)(MSIPACKAGE *);
7525 const WCHAR *action_rollback;
7527 StandardActions[] =
7529 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7530 { szAppSearch, ACTION_AppSearch, NULL },
7531 { szBindImage, ACTION_BindImage, NULL },
7532 { szCCPSearch, ACTION_CCPSearch, NULL },
7533 { szCostFinalize, ACTION_CostFinalize, NULL },
7534 { szCostInitialize, ACTION_CostInitialize, NULL },
7535 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7536 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7537 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7538 { szDisableRollback, ACTION_DisableRollback, NULL },
7539 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7540 { szExecuteAction, ACTION_ExecuteAction, NULL },
7541 { szFileCost, ACTION_FileCost, NULL },
7542 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7543 { szForceReboot, ACTION_ForceReboot, NULL },
7544 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7545 { szInstallExecute, ACTION_InstallExecute, NULL },
7546 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7547 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7548 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7549 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7550 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7551 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7552 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7553 { szInstallValidate, ACTION_InstallValidate, NULL },
7554 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7555 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7556 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7557 { szMoveFiles, ACTION_MoveFiles, NULL },
7558 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7559 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7560 { szPatchFiles, ACTION_PatchFiles, NULL },
7561 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7562 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7563 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7564 { szPublishProduct, ACTION_PublishProduct, NULL },
7565 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7566 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7567 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7568 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7569 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7570 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7571 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7572 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7573 { szRegisterUser, ACTION_RegisterUser, NULL },
7574 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7575 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7576 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7577 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7578 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7579 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7580 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7581 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7582 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7583 { szResolveSource, ACTION_ResolveSource, NULL },
7584 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7585 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7586 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7587 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7588 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7589 { szStartServices, ACTION_StartServices, szStopServices },
7590 { szStopServices, ACTION_StopServices, szStartServices },
7591 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7592 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7593 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7594 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7595 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7596 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7597 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7598 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7599 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7600 { szValidateProductID, ACTION_ValidateProductID, NULL },
7601 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7602 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7603 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7604 { NULL, NULL, NULL }
7607 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7609 BOOL ret = FALSE;
7610 UINT i;
7612 i = 0;
7613 while (StandardActions[i].action != NULL)
7615 if (!strcmpW( StandardActions[i].action, action ))
7617 ui_actionstart( package, action );
7618 if (StandardActions[i].handler)
7620 ui_actioninfo( package, action, TRUE, 0 );
7621 *rc = StandardActions[i].handler( package );
7622 ui_actioninfo( package, action, FALSE, *rc );
7624 if (StandardActions[i].action_rollback && !package->need_rollback)
7626 TRACE("scheduling rollback action\n");
7627 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7630 else
7632 FIXME("unhandled standard action %s\n", debugstr_w(action));
7633 *rc = ERROR_SUCCESS;
7635 ret = TRUE;
7636 break;
7638 i++;
7640 return ret;
7643 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7645 UINT rc = ERROR_SUCCESS;
7646 BOOL handled;
7648 TRACE("Performing action (%s)\n", debugstr_w(action));
7650 handled = ACTION_HandleStandardAction(package, action, &rc);
7652 if (!handled)
7653 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7655 if (!handled)
7657 WARN("unhandled msi action %s\n", debugstr_w(action));
7658 rc = ERROR_FUNCTION_NOT_CALLED;
7661 return rc;
7664 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7666 UINT rc = ERROR_SUCCESS;
7667 BOOL handled = FALSE;
7669 TRACE("Performing action (%s)\n", debugstr_w(action));
7671 package->action_progress_increment = 0;
7672 handled = ACTION_HandleStandardAction(package, action, &rc);
7674 if (!handled)
7675 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7677 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7678 handled = TRUE;
7680 if (!handled)
7682 WARN("unhandled msi action %s\n", debugstr_w(action));
7683 rc = ERROR_FUNCTION_NOT_CALLED;
7686 return rc;
7689 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7691 UINT rc = ERROR_SUCCESS;
7692 MSIRECORD *row;
7694 static const WCHAR query[] =
7695 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7696 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7697 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7698 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7699 static const WCHAR ui_query[] =
7700 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7701 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7702 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7703 ' ', '=',' ','%','i',0};
7705 if (needs_ui_sequence(package))
7706 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7707 else
7708 row = MSI_QueryGetRecord(package->db, query, seq);
7710 if (row)
7712 LPCWSTR action, cond;
7714 TRACE("Running the actions\n");
7716 /* check conditions */
7717 cond = MSI_RecordGetString(row, 2);
7719 /* this is a hack to skip errors in the condition code */
7720 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7722 msiobj_release(&row->hdr);
7723 return ERROR_SUCCESS;
7726 action = MSI_RecordGetString(row, 1);
7727 if (!action)
7729 ERR("failed to fetch action\n");
7730 msiobj_release(&row->hdr);
7731 return ERROR_FUNCTION_FAILED;
7734 if (needs_ui_sequence(package))
7735 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7736 else
7737 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7739 msiobj_release(&row->hdr);
7742 return rc;
7745 /****************************************************
7746 * TOP level entry points
7747 *****************************************************/
7749 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7750 LPCWSTR szCommandLine )
7752 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7753 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7754 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7755 WCHAR *reinstall, *remove, *patch, *productcode;
7756 BOOL ui_exists;
7757 UINT rc;
7759 msi_set_property( package->db, szAction, szInstall, -1 );
7761 package->script->InWhatSequence = SEQUENCE_INSTALL;
7763 if (szPackagePath)
7765 LPWSTR p, dir;
7766 LPCWSTR file;
7768 dir = strdupW(szPackagePath);
7769 p = strrchrW(dir, '\\');
7770 if (p)
7772 *(++p) = 0;
7773 file = szPackagePath + (p - dir);
7775 else
7777 msi_free(dir);
7778 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7779 GetCurrentDirectoryW(MAX_PATH, dir);
7780 lstrcatW(dir, szBackSlash);
7781 file = szPackagePath;
7784 msi_free( package->PackagePath );
7785 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7786 if (!package->PackagePath)
7788 msi_free(dir);
7789 return ERROR_OUTOFMEMORY;
7792 lstrcpyW(package->PackagePath, dir);
7793 lstrcatW(package->PackagePath, file);
7794 msi_free(dir);
7796 msi_set_sourcedir_props(package, FALSE);
7799 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7800 if (rc != ERROR_SUCCESS)
7801 return rc;
7803 msi_apply_transforms( package );
7804 msi_apply_patches( package );
7806 patch = msi_dup_property( package->db, szPatch );
7807 remove = msi_dup_property( package->db, szRemove );
7808 reinstall = msi_dup_property( package->db, szReinstall );
7809 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7811 TRACE("setting REINSTALL property to ALL\n");
7812 msi_set_property( package->db, szReinstall, szAll, -1 );
7813 package->full_reinstall = 1;
7816 msi_set_original_database_property( package->db, szPackagePath );
7817 msi_parse_command_line( package, szCommandLine, FALSE );
7818 msi_adjust_privilege_properties( package );
7819 msi_set_context( package );
7821 productcode = msi_dup_property( package->db, szProductCode );
7822 if (strcmpiW( productcode, package->ProductCode ))
7824 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
7825 msi_free( package->ProductCode );
7826 package->ProductCode = productcode;
7828 else msi_free( productcode );
7830 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7832 TRACE("disabling rollback\n");
7833 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7836 if (needs_ui_sequence( package))
7838 package->script->InWhatSequence |= SEQUENCE_UI;
7839 rc = ACTION_ProcessUISequence(package);
7840 ui_exists = ui_sequence_exists(package);
7841 if (rc == ERROR_SUCCESS || !ui_exists)
7843 package->script->InWhatSequence |= SEQUENCE_EXEC;
7844 rc = ACTION_ProcessExecSequence(package, ui_exists);
7847 else
7848 rc = ACTION_ProcessExecSequence(package, FALSE);
7850 /* process the ending type action */
7851 if (rc == ERROR_SUCCESS)
7852 ACTION_PerformActionSequence(package, -1);
7853 else if (rc == ERROR_INSTALL_USEREXIT)
7854 ACTION_PerformActionSequence(package, -2);
7855 else if (rc == ERROR_INSTALL_SUSPEND)
7856 ACTION_PerformActionSequence(package, -4);
7857 else /* failed */
7859 ACTION_PerformActionSequence(package, -3);
7860 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7862 package->need_rollback = TRUE;
7866 /* finish up running custom actions */
7867 ACTION_FinishCustomActions(package);
7869 if (package->need_rollback && !reinstall)
7871 WARN("installation failed, running rollback script\n");
7872 execute_script( package, SCRIPT_ROLLBACK );
7874 msi_free( reinstall );
7875 msi_free( remove );
7876 msi_free( patch );
7878 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7879 return ERROR_SUCCESS_REBOOT_REQUIRED;
7881 return rc;