msi: Properly handle service control events in Start/StopService.
[wine.git] / dlls / msi / action.c
blob1e527da8ecd88fee4727584700c2a2e97b68fa83
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 event = MSI_RecordGetInteger( rec, 3 );
5956 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5958 comp->Action = msi_get_component_action( package, comp );
5959 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
5960 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
5962 TRACE("not starting %s\n", debugstr_w(name));
5963 msi_free( name );
5964 return ERROR_SUCCESS;
5967 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5968 wait = MSI_RecordGetInteger(rec, 5);
5970 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5971 if (!scm)
5973 ERR("Failed to open the service control manager\n");
5974 goto done;
5977 len = 0;
5978 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5979 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5981 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5982 GetServiceDisplayNameW( scm, name, display_name, &len );
5985 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5986 if (!service)
5988 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5989 goto done;
5992 vector = msi_service_args_to_vector(args, &numargs);
5994 if (!StartServiceW(service, numargs, vector) &&
5995 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5997 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5998 goto done;
6001 r = ERROR_SUCCESS;
6002 if (wait)
6004 /* wait for at most 30 seconds for the service to be up and running */
6005 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6006 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6008 TRACE("failed to query service status (%u)\n", GetLastError());
6009 goto done;
6011 start_time = GetTickCount64();
6012 while (status.dwCurrentState == SERVICE_START_PENDING)
6014 if (GetTickCount64() - start_time > 30000) break;
6015 Sleep(1000);
6016 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6017 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6019 TRACE("failed to query service status (%u)\n", GetLastError());
6020 goto done;
6023 if (status.dwCurrentState != SERVICE_RUNNING)
6025 WARN("service failed to start %u\n", status.dwCurrentState);
6026 r = ERROR_FUNCTION_FAILED;
6030 done:
6031 uirow = MSI_CreateRecord( 2 );
6032 MSI_RecordSetStringW( uirow, 1, display_name );
6033 MSI_RecordSetStringW( uirow, 2, name );
6034 msi_ui_actiondata( package, szStartServices, uirow );
6035 msiobj_release( &uirow->hdr );
6037 CloseServiceHandle(service);
6038 CloseServiceHandle(scm);
6040 msi_free(name);
6041 msi_free(args);
6042 msi_free(vector);
6043 msi_free(display_name);
6044 return r;
6047 static UINT ACTION_StartServices( MSIPACKAGE *package )
6049 static const WCHAR query[] = {
6050 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6051 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6052 MSIQUERY *view;
6053 UINT rc;
6055 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6056 if (rc != ERROR_SUCCESS)
6057 return ERROR_SUCCESS;
6059 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6060 msiobj_release(&view->hdr);
6061 return rc;
6064 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6066 DWORD i, needed, count;
6067 ENUM_SERVICE_STATUSW *dependencies;
6068 SERVICE_STATUS ss;
6069 SC_HANDLE depserv;
6070 BOOL stopped, ret = FALSE;
6072 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6073 0, &needed, &count))
6074 return TRUE;
6076 if (GetLastError() != ERROR_MORE_DATA)
6077 return FALSE;
6079 dependencies = msi_alloc(needed);
6080 if (!dependencies)
6081 return FALSE;
6083 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6084 needed, &needed, &count))
6085 goto done;
6087 for (i = 0; i < count; i++)
6089 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6090 SERVICE_STOP | SERVICE_QUERY_STATUS);
6091 if (!depserv)
6092 goto done;
6094 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6095 CloseServiceHandle(depserv);
6096 if (!stopped)
6097 goto done;
6100 ret = TRUE;
6102 done:
6103 msi_free(dependencies);
6104 return ret;
6107 static UINT stop_service( LPCWSTR name )
6109 SC_HANDLE scm = NULL, service = NULL;
6110 SERVICE_STATUS status;
6111 SERVICE_STATUS_PROCESS ssp;
6112 DWORD needed;
6114 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6115 if (!scm)
6117 WARN("Failed to open the SCM: %d\n", GetLastError());
6118 goto done;
6121 service = OpenServiceW(scm, name,
6122 SERVICE_STOP |
6123 SERVICE_QUERY_STATUS |
6124 SERVICE_ENUMERATE_DEPENDENTS);
6125 if (!service)
6127 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6128 goto done;
6131 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6132 sizeof(SERVICE_STATUS_PROCESS), &needed))
6134 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6135 goto done;
6138 if (ssp.dwCurrentState == SERVICE_STOPPED)
6139 goto done;
6141 stop_service_dependents(scm, service);
6143 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6144 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6146 done:
6147 CloseServiceHandle(service);
6148 CloseServiceHandle(scm);
6150 return ERROR_SUCCESS;
6153 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6155 MSIPACKAGE *package = param;
6156 MSICOMPONENT *comp;
6157 MSIRECORD *uirow;
6158 LPCWSTR component;
6159 WCHAR *name, *display_name = NULL;
6160 DWORD event, len;
6161 SC_HANDLE scm;
6163 component = MSI_RecordGetString( rec, 6 );
6164 comp = msi_get_loaded_component( package, component );
6165 if (!comp)
6166 return ERROR_SUCCESS;
6168 event = MSI_RecordGetInteger( rec, 3 );
6169 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6171 comp->Action = msi_get_component_action( package, comp );
6172 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6173 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6175 TRACE("not stopping %s\n", debugstr_w(name));
6176 msi_free( name );
6177 return ERROR_SUCCESS;
6180 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6181 if (!scm)
6183 ERR("Failed to open the service control manager\n");
6184 goto done;
6187 len = 0;
6188 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6189 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6191 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6192 GetServiceDisplayNameW( scm, name, display_name, &len );
6194 CloseServiceHandle( scm );
6196 stop_service( name );
6198 done:
6199 uirow = MSI_CreateRecord( 2 );
6200 MSI_RecordSetStringW( uirow, 1, display_name );
6201 MSI_RecordSetStringW( uirow, 2, name );
6202 msi_ui_actiondata( package, szStopServices, uirow );
6203 msiobj_release( &uirow->hdr );
6205 msi_free( name );
6206 msi_free( display_name );
6207 return ERROR_SUCCESS;
6210 static UINT ACTION_StopServices( MSIPACKAGE *package )
6212 static const WCHAR query[] = {
6213 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6214 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6215 MSIQUERY *view;
6216 UINT rc;
6218 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6219 if (rc != ERROR_SUCCESS)
6220 return ERROR_SUCCESS;
6222 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6223 msiobj_release(&view->hdr);
6224 return rc;
6227 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6229 MSIPACKAGE *package = param;
6230 MSICOMPONENT *comp;
6231 MSIRECORD *uirow;
6232 LPWSTR name = NULL, display_name = NULL;
6233 DWORD event, len;
6234 SC_HANDLE scm = NULL, service = NULL;
6236 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6237 if (!comp)
6238 return ERROR_SUCCESS;
6240 event = MSI_RecordGetInteger( rec, 3 );
6241 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6243 comp->Action = msi_get_component_action( package, comp );
6244 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6245 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6247 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6248 msi_free( name );
6249 return ERROR_SUCCESS;
6251 stop_service( name );
6253 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6254 if (!scm)
6256 WARN("Failed to open the SCM: %d\n", GetLastError());
6257 goto done;
6260 len = 0;
6261 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6262 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6264 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6265 GetServiceDisplayNameW( scm, name, display_name, &len );
6268 service = OpenServiceW( scm, name, DELETE );
6269 if (!service)
6271 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6272 goto done;
6275 if (!DeleteService( service ))
6276 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6278 done:
6279 uirow = MSI_CreateRecord( 2 );
6280 MSI_RecordSetStringW( uirow, 1, display_name );
6281 MSI_RecordSetStringW( uirow, 2, name );
6282 msi_ui_actiondata( package, szDeleteServices, uirow );
6283 msiobj_release( &uirow->hdr );
6285 CloseServiceHandle( service );
6286 CloseServiceHandle( scm );
6287 msi_free( name );
6288 msi_free( display_name );
6290 return ERROR_SUCCESS;
6293 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6295 static const WCHAR query[] = {
6296 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6297 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6298 MSIQUERY *view;
6299 UINT rc;
6301 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6302 if (rc != ERROR_SUCCESS)
6303 return ERROR_SUCCESS;
6305 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6306 msiobj_release( &view->hdr );
6307 return rc;
6310 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6312 MSIPACKAGE *package = param;
6313 LPWSTR driver, driver_path, ptr;
6314 WCHAR outpath[MAX_PATH];
6315 MSIFILE *driver_file = NULL, *setup_file = NULL;
6316 MSICOMPONENT *comp;
6317 MSIRECORD *uirow;
6318 LPCWSTR desc, file_key, component;
6319 DWORD len, usage;
6320 UINT r = ERROR_SUCCESS;
6322 static const WCHAR driver_fmt[] = {
6323 'D','r','i','v','e','r','=','%','s',0};
6324 static const WCHAR setup_fmt[] = {
6325 'S','e','t','u','p','=','%','s',0};
6326 static const WCHAR usage_fmt[] = {
6327 'F','i','l','e','U','s','a','g','e','=','1',0};
6329 component = MSI_RecordGetString( rec, 2 );
6330 comp = msi_get_loaded_component( package, component );
6331 if (!comp)
6332 return ERROR_SUCCESS;
6334 comp->Action = msi_get_component_action( package, comp );
6335 if (comp->Action != INSTALLSTATE_LOCAL)
6337 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6338 return ERROR_SUCCESS;
6340 desc = MSI_RecordGetString(rec, 3);
6342 file_key = MSI_RecordGetString( rec, 4 );
6343 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6345 file_key = MSI_RecordGetString( rec, 5 );
6346 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6348 if (!driver_file)
6350 ERR("ODBC Driver entry not found!\n");
6351 return ERROR_FUNCTION_FAILED;
6354 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6355 if (setup_file)
6356 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6357 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6359 driver = msi_alloc(len * sizeof(WCHAR));
6360 if (!driver)
6361 return ERROR_OUTOFMEMORY;
6363 ptr = driver;
6364 lstrcpyW(ptr, desc);
6365 ptr += lstrlenW(ptr) + 1;
6367 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6368 ptr += len + 1;
6370 if (setup_file)
6372 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6373 ptr += len + 1;
6376 lstrcpyW(ptr, usage_fmt);
6377 ptr += lstrlenW(ptr) + 1;
6378 *ptr = '\0';
6380 if (!driver_file->TargetPath)
6382 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6383 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6385 driver_path = strdupW(driver_file->TargetPath);
6386 ptr = strrchrW(driver_path, '\\');
6387 if (ptr) *ptr = '\0';
6389 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6390 NULL, ODBC_INSTALL_COMPLETE, &usage))
6392 ERR("Failed to install SQL driver!\n");
6393 r = ERROR_FUNCTION_FAILED;
6396 uirow = MSI_CreateRecord( 5 );
6397 MSI_RecordSetStringW( uirow, 1, desc );
6398 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6399 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6400 msi_ui_actiondata( package, szInstallODBC, uirow );
6401 msiobj_release( &uirow->hdr );
6403 msi_free(driver);
6404 msi_free(driver_path);
6406 return r;
6409 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6411 MSIPACKAGE *package = param;
6412 LPWSTR translator, translator_path, ptr;
6413 WCHAR outpath[MAX_PATH];
6414 MSIFILE *translator_file = NULL, *setup_file = NULL;
6415 MSICOMPONENT *comp;
6416 MSIRECORD *uirow;
6417 LPCWSTR desc, file_key, component;
6418 DWORD len, usage;
6419 UINT r = ERROR_SUCCESS;
6421 static const WCHAR translator_fmt[] = {
6422 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6423 static const WCHAR setup_fmt[] = {
6424 'S','e','t','u','p','=','%','s',0};
6426 component = MSI_RecordGetString( rec, 2 );
6427 comp = msi_get_loaded_component( package, component );
6428 if (!comp)
6429 return ERROR_SUCCESS;
6431 comp->Action = msi_get_component_action( package, comp );
6432 if (comp->Action != INSTALLSTATE_LOCAL)
6434 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6435 return ERROR_SUCCESS;
6437 desc = MSI_RecordGetString(rec, 3);
6439 file_key = MSI_RecordGetString( rec, 4 );
6440 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6442 file_key = MSI_RecordGetString( rec, 5 );
6443 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6445 if (!translator_file)
6447 ERR("ODBC Translator entry not found!\n");
6448 return ERROR_FUNCTION_FAILED;
6451 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6452 if (setup_file)
6453 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6455 translator = msi_alloc(len * sizeof(WCHAR));
6456 if (!translator)
6457 return ERROR_OUTOFMEMORY;
6459 ptr = translator;
6460 lstrcpyW(ptr, desc);
6461 ptr += lstrlenW(ptr) + 1;
6463 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6464 ptr += len + 1;
6466 if (setup_file)
6468 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6469 ptr += len + 1;
6471 *ptr = '\0';
6473 translator_path = strdupW(translator_file->TargetPath);
6474 ptr = strrchrW(translator_path, '\\');
6475 if (ptr) *ptr = '\0';
6477 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6478 NULL, ODBC_INSTALL_COMPLETE, &usage))
6480 ERR("Failed to install SQL translator!\n");
6481 r = ERROR_FUNCTION_FAILED;
6484 uirow = MSI_CreateRecord( 5 );
6485 MSI_RecordSetStringW( uirow, 1, desc );
6486 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6487 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6488 msi_ui_actiondata( package, szInstallODBC, uirow );
6489 msiobj_release( &uirow->hdr );
6491 msi_free(translator);
6492 msi_free(translator_path);
6494 return r;
6497 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6499 MSIPACKAGE *package = param;
6500 MSICOMPONENT *comp;
6501 LPWSTR attrs;
6502 LPCWSTR desc, driver, component;
6503 WORD request = ODBC_ADD_SYS_DSN;
6504 INT registration;
6505 DWORD len;
6506 UINT r = ERROR_SUCCESS;
6507 MSIRECORD *uirow;
6509 static const WCHAR attrs_fmt[] = {
6510 'D','S','N','=','%','s',0 };
6512 component = MSI_RecordGetString( rec, 2 );
6513 comp = msi_get_loaded_component( package, component );
6514 if (!comp)
6515 return ERROR_SUCCESS;
6517 comp->Action = msi_get_component_action( package, comp );
6518 if (comp->Action != INSTALLSTATE_LOCAL)
6520 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6521 return ERROR_SUCCESS;
6524 desc = MSI_RecordGetString(rec, 3);
6525 driver = MSI_RecordGetString(rec, 4);
6526 registration = MSI_RecordGetInteger(rec, 5);
6528 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6529 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6531 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6532 attrs = msi_alloc(len * sizeof(WCHAR));
6533 if (!attrs)
6534 return ERROR_OUTOFMEMORY;
6536 len = sprintfW(attrs, attrs_fmt, desc);
6537 attrs[len + 1] = 0;
6539 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6541 ERR("Failed to install SQL data source!\n");
6542 r = ERROR_FUNCTION_FAILED;
6545 uirow = MSI_CreateRecord( 5 );
6546 MSI_RecordSetStringW( uirow, 1, desc );
6547 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6548 MSI_RecordSetInteger( uirow, 3, request );
6549 msi_ui_actiondata( package, szInstallODBC, uirow );
6550 msiobj_release( &uirow->hdr );
6552 msi_free(attrs);
6554 return r;
6557 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6559 static const WCHAR driver_query[] = {
6560 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6561 'O','D','B','C','D','r','i','v','e','r',0};
6562 static const WCHAR translator_query[] = {
6563 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6564 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6565 static const WCHAR source_query[] = {
6566 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6567 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6568 MSIQUERY *view;
6569 UINT rc;
6571 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6572 if (rc == ERROR_SUCCESS)
6574 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6575 msiobj_release(&view->hdr);
6576 if (rc != ERROR_SUCCESS)
6577 return rc;
6579 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6580 if (rc == ERROR_SUCCESS)
6582 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6583 msiobj_release(&view->hdr);
6584 if (rc != ERROR_SUCCESS)
6585 return rc;
6587 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6588 if (rc == ERROR_SUCCESS)
6590 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6591 msiobj_release(&view->hdr);
6592 if (rc != ERROR_SUCCESS)
6593 return rc;
6595 return ERROR_SUCCESS;
6598 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6600 MSIPACKAGE *package = param;
6601 MSICOMPONENT *comp;
6602 MSIRECORD *uirow;
6603 DWORD usage;
6604 LPCWSTR desc, component;
6606 component = MSI_RecordGetString( rec, 2 );
6607 comp = msi_get_loaded_component( package, component );
6608 if (!comp)
6609 return ERROR_SUCCESS;
6611 comp->Action = msi_get_component_action( package, comp );
6612 if (comp->Action != INSTALLSTATE_ABSENT)
6614 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6615 return ERROR_SUCCESS;
6618 desc = MSI_RecordGetString( rec, 3 );
6619 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6621 WARN("Failed to remove ODBC driver\n");
6623 else if (!usage)
6625 FIXME("Usage count reached 0\n");
6628 uirow = MSI_CreateRecord( 2 );
6629 MSI_RecordSetStringW( uirow, 1, desc );
6630 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6631 msi_ui_actiondata( package, szRemoveODBC, uirow );
6632 msiobj_release( &uirow->hdr );
6634 return ERROR_SUCCESS;
6637 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6639 MSIPACKAGE *package = param;
6640 MSICOMPONENT *comp;
6641 MSIRECORD *uirow;
6642 DWORD usage;
6643 LPCWSTR desc, component;
6645 component = MSI_RecordGetString( rec, 2 );
6646 comp = msi_get_loaded_component( package, component );
6647 if (!comp)
6648 return ERROR_SUCCESS;
6650 comp->Action = msi_get_component_action( package, comp );
6651 if (comp->Action != INSTALLSTATE_ABSENT)
6653 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6654 return ERROR_SUCCESS;
6657 desc = MSI_RecordGetString( rec, 3 );
6658 if (!SQLRemoveTranslatorW( desc, &usage ))
6660 WARN("Failed to remove ODBC translator\n");
6662 else if (!usage)
6664 FIXME("Usage count reached 0\n");
6667 uirow = MSI_CreateRecord( 2 );
6668 MSI_RecordSetStringW( uirow, 1, desc );
6669 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6670 msi_ui_actiondata( package, szRemoveODBC, uirow );
6671 msiobj_release( &uirow->hdr );
6673 return ERROR_SUCCESS;
6676 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6678 MSIPACKAGE *package = param;
6679 MSICOMPONENT *comp;
6680 MSIRECORD *uirow;
6681 LPWSTR attrs;
6682 LPCWSTR desc, driver, component;
6683 WORD request = ODBC_REMOVE_SYS_DSN;
6684 INT registration;
6685 DWORD len;
6687 static const WCHAR attrs_fmt[] = {
6688 'D','S','N','=','%','s',0 };
6690 component = MSI_RecordGetString( rec, 2 );
6691 comp = msi_get_loaded_component( package, component );
6692 if (!comp)
6693 return ERROR_SUCCESS;
6695 comp->Action = msi_get_component_action( package, comp );
6696 if (comp->Action != INSTALLSTATE_ABSENT)
6698 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6699 return ERROR_SUCCESS;
6702 desc = MSI_RecordGetString( rec, 3 );
6703 driver = MSI_RecordGetString( rec, 4 );
6704 registration = MSI_RecordGetInteger( rec, 5 );
6706 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6707 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6709 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6710 attrs = msi_alloc( len * sizeof(WCHAR) );
6711 if (!attrs)
6712 return ERROR_OUTOFMEMORY;
6714 FIXME("Use ODBCSourceAttribute table\n");
6716 len = sprintfW( attrs, attrs_fmt, desc );
6717 attrs[len + 1] = 0;
6719 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6721 WARN("Failed to remove ODBC data source\n");
6723 msi_free( attrs );
6725 uirow = MSI_CreateRecord( 3 );
6726 MSI_RecordSetStringW( uirow, 1, desc );
6727 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6728 MSI_RecordSetInteger( uirow, 3, request );
6729 msi_ui_actiondata( package, szRemoveODBC, uirow );
6730 msiobj_release( &uirow->hdr );
6732 return ERROR_SUCCESS;
6735 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6737 static const WCHAR driver_query[] = {
6738 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6739 'O','D','B','C','D','r','i','v','e','r',0};
6740 static const WCHAR translator_query[] = {
6741 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6742 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6743 static const WCHAR source_query[] = {
6744 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6745 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6746 MSIQUERY *view;
6747 UINT rc;
6749 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6750 if (rc == ERROR_SUCCESS)
6752 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6753 msiobj_release( &view->hdr );
6754 if (rc != ERROR_SUCCESS)
6755 return rc;
6757 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6758 if (rc == ERROR_SUCCESS)
6760 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6761 msiobj_release( &view->hdr );
6762 if (rc != ERROR_SUCCESS)
6763 return rc;
6765 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6766 if (rc == ERROR_SUCCESS)
6768 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6769 msiobj_release( &view->hdr );
6770 if (rc != ERROR_SUCCESS)
6771 return rc;
6773 return ERROR_SUCCESS;
6776 #define ENV_ACT_SETALWAYS 0x1
6777 #define ENV_ACT_SETABSENT 0x2
6778 #define ENV_ACT_REMOVE 0x4
6779 #define ENV_ACT_REMOVEMATCH 0x8
6781 #define ENV_MOD_MACHINE 0x20000000
6782 #define ENV_MOD_APPEND 0x40000000
6783 #define ENV_MOD_PREFIX 0x80000000
6784 #define ENV_MOD_MASK 0xC0000000
6786 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6788 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6790 LPCWSTR cptr = *name;
6792 static const WCHAR prefix[] = {'[','~',']',0};
6793 static const int prefix_len = 3;
6795 *flags = 0;
6796 while (*cptr)
6798 if (*cptr == '=')
6799 *flags |= ENV_ACT_SETALWAYS;
6800 else if (*cptr == '+')
6801 *flags |= ENV_ACT_SETABSENT;
6802 else if (*cptr == '-')
6803 *flags |= ENV_ACT_REMOVE;
6804 else if (*cptr == '!')
6805 *flags |= ENV_ACT_REMOVEMATCH;
6806 else if (*cptr == '*')
6807 *flags |= ENV_MOD_MACHINE;
6808 else
6809 break;
6811 cptr++;
6812 (*name)++;
6815 if (!*cptr)
6817 ERR("Missing environment variable\n");
6818 return ERROR_FUNCTION_FAILED;
6821 if (*value)
6823 LPCWSTR ptr = *value;
6824 if (!strncmpW(ptr, prefix, prefix_len))
6826 if (ptr[prefix_len] == szSemiColon[0])
6828 *flags |= ENV_MOD_APPEND;
6829 *value += lstrlenW(prefix);
6831 else
6833 *value = NULL;
6836 else if (lstrlenW(*value) >= prefix_len)
6838 ptr += lstrlenW(ptr) - prefix_len;
6839 if (!strcmpW( ptr, prefix ))
6841 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6843 *flags |= ENV_MOD_PREFIX;
6844 /* the "[~]" will be removed by deformat_string */;
6846 else
6848 *value = NULL;
6854 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6855 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6856 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6857 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6859 ERR("Invalid flags: %08x\n", *flags);
6860 return ERROR_FUNCTION_FAILED;
6863 if (!*flags)
6864 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6866 return ERROR_SUCCESS;
6869 static UINT open_env_key( DWORD flags, HKEY *key )
6871 static const WCHAR user_env[] =
6872 {'E','n','v','i','r','o','n','m','e','n','t',0};
6873 static const WCHAR machine_env[] =
6874 {'S','y','s','t','e','m','\\',
6875 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6876 'C','o','n','t','r','o','l','\\',
6877 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6878 'E','n','v','i','r','o','n','m','e','n','t',0};
6879 const WCHAR *env;
6880 HKEY root;
6881 LONG res;
6883 if (flags & ENV_MOD_MACHINE)
6885 env = machine_env;
6886 root = HKEY_LOCAL_MACHINE;
6888 else
6890 env = user_env;
6891 root = HKEY_CURRENT_USER;
6894 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6895 if (res != ERROR_SUCCESS)
6897 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6898 return ERROR_FUNCTION_FAILED;
6901 return ERROR_SUCCESS;
6904 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6906 MSIPACKAGE *package = param;
6907 LPCWSTR name, value, component;
6908 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6909 DWORD flags, type, size;
6910 UINT res;
6911 HKEY env = NULL;
6912 MSICOMPONENT *comp;
6913 MSIRECORD *uirow;
6914 int action = 0;
6916 component = MSI_RecordGetString(rec, 4);
6917 comp = msi_get_loaded_component(package, component);
6918 if (!comp)
6919 return ERROR_SUCCESS;
6921 comp->Action = msi_get_component_action( package, comp );
6922 if (comp->Action != INSTALLSTATE_LOCAL)
6924 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6925 return ERROR_SUCCESS;
6927 name = MSI_RecordGetString(rec, 2);
6928 value = MSI_RecordGetString(rec, 3);
6930 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6932 res = env_parse_flags(&name, &value, &flags);
6933 if (res != ERROR_SUCCESS || !value)
6934 goto done;
6936 if (value && !deformat_string(package, value, &deformatted))
6938 res = ERROR_OUTOFMEMORY;
6939 goto done;
6942 value = deformatted;
6944 res = open_env_key( flags, &env );
6945 if (res != ERROR_SUCCESS)
6946 goto done;
6948 if (flags & ENV_MOD_MACHINE)
6949 action |= 0x20000000;
6951 size = 0;
6952 type = REG_SZ;
6953 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6954 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6955 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6956 goto done;
6958 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6960 action = 0x2;
6962 /* Nothing to do. */
6963 if (!value)
6965 res = ERROR_SUCCESS;
6966 goto done;
6969 /* If we are appending but the string was empty, strip ; */
6970 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6972 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6973 newval = strdupW(value);
6974 if (!newval)
6976 res = ERROR_OUTOFMEMORY;
6977 goto done;
6980 else
6982 action = 0x1;
6984 /* Contrary to MSDN, +-variable to [~];path works */
6985 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6987 res = ERROR_SUCCESS;
6988 goto done;
6991 data = msi_alloc(size);
6992 if (!data)
6994 RegCloseKey(env);
6995 return ERROR_OUTOFMEMORY;
6998 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6999 if (res != ERROR_SUCCESS)
7000 goto done;
7002 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7004 action = 0x4;
7005 res = RegDeleteValueW(env, name);
7006 if (res != ERROR_SUCCESS)
7007 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7008 goto done;
7011 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
7012 if (flags & ENV_MOD_MASK)
7014 DWORD mod_size;
7015 int multiplier = 0;
7016 if (flags & ENV_MOD_APPEND) multiplier++;
7017 if (flags & ENV_MOD_PREFIX) multiplier++;
7018 mod_size = lstrlenW(value) * multiplier;
7019 size += mod_size * sizeof(WCHAR);
7022 newval = msi_alloc(size);
7023 ptr = newval;
7024 if (!newval)
7026 res = ERROR_OUTOFMEMORY;
7027 goto done;
7030 if (flags & ENV_MOD_PREFIX)
7032 lstrcpyW(newval, value);
7033 ptr = newval + lstrlenW(value);
7034 action |= 0x80000000;
7037 lstrcpyW(ptr, data);
7039 if (flags & ENV_MOD_APPEND)
7041 lstrcatW(newval, value);
7042 action |= 0x40000000;
7045 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7046 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
7047 if (res)
7049 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7052 done:
7053 uirow = MSI_CreateRecord( 3 );
7054 MSI_RecordSetStringW( uirow, 1, name );
7055 MSI_RecordSetStringW( uirow, 2, newval );
7056 MSI_RecordSetInteger( uirow, 3, action );
7057 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7058 msiobj_release( &uirow->hdr );
7060 if (env) RegCloseKey(env);
7061 msi_free(deformatted);
7062 msi_free(data);
7063 msi_free(newval);
7064 return res;
7067 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7069 static const WCHAR query[] = {
7070 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7071 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7072 MSIQUERY *view;
7073 UINT rc;
7075 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7076 if (rc != ERROR_SUCCESS)
7077 return ERROR_SUCCESS;
7079 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7080 msiobj_release(&view->hdr);
7081 return rc;
7084 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7086 MSIPACKAGE *package = param;
7087 LPCWSTR name, value, component;
7088 LPWSTR deformatted = NULL;
7089 DWORD flags;
7090 HKEY env;
7091 MSICOMPONENT *comp;
7092 MSIRECORD *uirow;
7093 int action = 0;
7094 LONG res;
7095 UINT r;
7097 component = MSI_RecordGetString( rec, 4 );
7098 comp = msi_get_loaded_component( package, component );
7099 if (!comp)
7100 return ERROR_SUCCESS;
7102 comp->Action = msi_get_component_action( package, comp );
7103 if (comp->Action != INSTALLSTATE_ABSENT)
7105 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7106 return ERROR_SUCCESS;
7108 name = MSI_RecordGetString( rec, 2 );
7109 value = MSI_RecordGetString( rec, 3 );
7111 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7113 r = env_parse_flags( &name, &value, &flags );
7114 if (r != ERROR_SUCCESS)
7115 return r;
7117 if (!(flags & ENV_ACT_REMOVE))
7119 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7120 return ERROR_SUCCESS;
7123 if (value && !deformat_string( package, value, &deformatted ))
7124 return ERROR_OUTOFMEMORY;
7126 value = deformatted;
7128 r = open_env_key( flags, &env );
7129 if (r != ERROR_SUCCESS)
7131 r = ERROR_SUCCESS;
7132 goto done;
7135 if (flags & ENV_MOD_MACHINE)
7136 action |= 0x20000000;
7138 TRACE("Removing %s\n", debugstr_w(name));
7140 res = RegDeleteValueW( env, name );
7141 if (res != ERROR_SUCCESS)
7143 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
7144 r = ERROR_SUCCESS;
7147 done:
7148 uirow = MSI_CreateRecord( 3 );
7149 MSI_RecordSetStringW( uirow, 1, name );
7150 MSI_RecordSetStringW( uirow, 2, value );
7151 MSI_RecordSetInteger( uirow, 3, action );
7152 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7153 msiobj_release( &uirow->hdr );
7155 if (env) RegCloseKey( env );
7156 msi_free( deformatted );
7157 return r;
7160 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7162 static const WCHAR query[] = {
7163 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7164 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7165 MSIQUERY *view;
7166 UINT rc;
7168 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7169 if (rc != ERROR_SUCCESS)
7170 return ERROR_SUCCESS;
7172 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7173 msiobj_release( &view->hdr );
7174 return rc;
7177 UINT msi_validate_product_id( MSIPACKAGE *package )
7179 LPWSTR key, template, id;
7180 UINT r = ERROR_SUCCESS;
7182 id = msi_dup_property( package->db, szProductID );
7183 if (id)
7185 msi_free( id );
7186 return ERROR_SUCCESS;
7188 template = msi_dup_property( package->db, szPIDTemplate );
7189 key = msi_dup_property( package->db, szPIDKEY );
7190 if (key && template)
7192 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7193 r = msi_set_property( package->db, szProductID, key, -1 );
7195 msi_free( template );
7196 msi_free( key );
7197 return r;
7200 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7202 return msi_validate_product_id( package );
7205 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7207 TRACE("\n");
7208 package->need_reboot_at_end = 1;
7209 return ERROR_SUCCESS;
7212 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7214 static const WCHAR szAvailableFreeReg[] =
7215 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7216 MSIRECORD *uirow;
7217 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7219 TRACE("%p %d kilobytes\n", package, space);
7221 uirow = MSI_CreateRecord( 1 );
7222 MSI_RecordSetInteger( uirow, 1, space );
7223 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7224 msiobj_release( &uirow->hdr );
7226 return ERROR_SUCCESS;
7229 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7231 TRACE("%p\n", package);
7233 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7234 return ERROR_SUCCESS;
7237 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7239 FIXME("%p\n", package);
7240 return ERROR_SUCCESS;
7243 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7245 static const WCHAR driver_query[] = {
7246 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7247 'O','D','B','C','D','r','i','v','e','r',0};
7248 static const WCHAR translator_query[] = {
7249 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7250 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7251 MSIQUERY *view;
7252 UINT r, count;
7254 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7255 if (r == ERROR_SUCCESS)
7257 count = 0;
7258 r = MSI_IterateRecords( view, &count, NULL, package );
7259 msiobj_release( &view->hdr );
7260 if (r != ERROR_SUCCESS)
7261 return r;
7262 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7264 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7265 if (r == ERROR_SUCCESS)
7267 count = 0;
7268 r = MSI_IterateRecords( view, &count, NULL, package );
7269 msiobj_release( &view->hdr );
7270 if (r != ERROR_SUCCESS)
7271 return r;
7272 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7274 return ERROR_SUCCESS;
7277 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7279 static const WCHAR fmtW[] =
7280 {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7281 MSIPACKAGE *package = param;
7282 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7283 int attrs = MSI_RecordGetInteger( rec, 5 );
7284 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7285 WCHAR *product, *features, *cmd;
7286 STARTUPINFOW si;
7287 PROCESS_INFORMATION info;
7288 BOOL ret;
7290 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7291 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7293 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7295 len += strlenW( product );
7296 if (features)
7297 len += strlenW( features );
7298 else
7299 len += sizeof(szAll) / sizeof(szAll[0]);
7301 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7303 msi_free( product );
7304 msi_free( features );
7305 return ERROR_OUTOFMEMORY;
7307 sprintfW( cmd, fmtW, product, features ? features : szAll );
7308 msi_free( product );
7309 msi_free( features );
7311 memset( &si, 0, sizeof(STARTUPINFOW) );
7312 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7313 msi_free( cmd );
7314 if (!ret) return GetLastError();
7315 CloseHandle( info.hThread );
7317 WaitForSingleObject( info.hProcess, INFINITE );
7318 CloseHandle( info.hProcess );
7319 return ERROR_SUCCESS;
7322 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7324 static const WCHAR query[] = {
7325 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7326 MSIQUERY *view;
7327 UINT r;
7329 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7330 if (r == ERROR_SUCCESS)
7332 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7333 msiobj_release( &view->hdr );
7334 if (r != ERROR_SUCCESS)
7335 return r;
7337 return ERROR_SUCCESS;
7340 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7342 MSIPACKAGE *package = param;
7343 int attributes = MSI_RecordGetInteger( rec, 5 );
7345 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7347 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7348 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7349 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7350 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7351 HKEY hkey;
7352 UINT r;
7354 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7356 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7357 if (r != ERROR_SUCCESS)
7358 return ERROR_SUCCESS;
7360 else
7362 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7363 if (r != ERROR_SUCCESS)
7364 return ERROR_SUCCESS;
7366 RegCloseKey( hkey );
7368 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7369 debugstr_w(upgrade_code), debugstr_w(version_min),
7370 debugstr_w(version_max), debugstr_w(language));
7372 return ERROR_SUCCESS;
7375 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7377 static const WCHAR query[] = {
7378 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7379 'U','p','g','r','a','d','e',0};
7380 MSIQUERY *view;
7381 UINT r;
7383 if (msi_get_property_int( package->db, szInstalled, 0 ))
7385 TRACE("product is installed, skipping action\n");
7386 return ERROR_SUCCESS;
7388 if (msi_get_property_int( package->db, szPreselected, 0 ))
7390 TRACE("Preselected property is set, not migrating feature states\n");
7391 return ERROR_SUCCESS;
7393 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7394 if (r == ERROR_SUCCESS)
7396 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7397 msiobj_release( &view->hdr );
7398 if (r != ERROR_SUCCESS)
7399 return r;
7401 return ERROR_SUCCESS;
7404 static void bind_image( const char *filename, const char *path )
7406 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7408 WARN("failed to bind image %u\n", GetLastError());
7412 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7414 UINT i;
7415 MSIFILE *file;
7416 MSIPACKAGE *package = param;
7417 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7418 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7419 char *filenameA, *pathA;
7420 WCHAR *pathW, **path_list;
7422 if (!(file = msi_get_loaded_file( package, key )))
7424 WARN("file %s not found\n", debugstr_w(key));
7425 return ERROR_SUCCESS;
7427 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7428 path_list = msi_split_string( paths, ';' );
7429 if (!path_list) bind_image( filenameA, NULL );
7430 else
7432 for (i = 0; path_list[i] && path_list[i][0]; i++)
7434 deformat_string( package, path_list[i], &pathW );
7435 if ((pathA = strdupWtoA( pathW )))
7437 bind_image( filenameA, pathA );
7438 msi_free( pathA );
7440 msi_free( pathW );
7443 msi_free( path_list );
7444 msi_free( filenameA );
7445 return ERROR_SUCCESS;
7448 static UINT ACTION_BindImage( MSIPACKAGE *package )
7450 static const WCHAR query[] = {
7451 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7452 'B','i','n','d','I','m','a','g','e',0};
7453 MSIQUERY *view;
7454 UINT r;
7456 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7457 if (r == ERROR_SUCCESS)
7459 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7460 msiobj_release( &view->hdr );
7461 if (r != ERROR_SUCCESS)
7462 return r;
7464 return ERROR_SUCCESS;
7467 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7469 static const WCHAR query[] = {
7470 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7471 MSIQUERY *view;
7472 DWORD count = 0;
7473 UINT r;
7475 r = MSI_OpenQuery( package->db, &view, query, table );
7476 if (r == ERROR_SUCCESS)
7478 r = MSI_IterateRecords(view, &count, NULL, package);
7479 msiobj_release(&view->hdr);
7480 if (r != ERROR_SUCCESS)
7481 return r;
7483 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7484 return ERROR_SUCCESS;
7487 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7489 static const WCHAR table[] = {
7490 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7491 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7494 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7496 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7497 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7500 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7502 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7503 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7506 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7508 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7509 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7512 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7514 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7515 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7518 static const struct
7520 const WCHAR *action;
7521 UINT (*handler)(MSIPACKAGE *);
7522 const WCHAR *action_rollback;
7524 StandardActions[] =
7526 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7527 { szAppSearch, ACTION_AppSearch, NULL },
7528 { szBindImage, ACTION_BindImage, NULL },
7529 { szCCPSearch, ACTION_CCPSearch, NULL },
7530 { szCostFinalize, ACTION_CostFinalize, NULL },
7531 { szCostInitialize, ACTION_CostInitialize, NULL },
7532 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7533 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7534 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7535 { szDisableRollback, ACTION_DisableRollback, NULL },
7536 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7537 { szExecuteAction, ACTION_ExecuteAction, NULL },
7538 { szFileCost, ACTION_FileCost, NULL },
7539 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7540 { szForceReboot, ACTION_ForceReboot, NULL },
7541 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7542 { szInstallExecute, ACTION_InstallExecute, NULL },
7543 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7544 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7545 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7546 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7547 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7548 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7549 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7550 { szInstallValidate, ACTION_InstallValidate, NULL },
7551 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7552 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7553 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7554 { szMoveFiles, ACTION_MoveFiles, NULL },
7555 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7556 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7557 { szPatchFiles, ACTION_PatchFiles, NULL },
7558 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7559 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7560 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7561 { szPublishProduct, ACTION_PublishProduct, NULL },
7562 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7563 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7564 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7565 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7566 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7567 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7568 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7569 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7570 { szRegisterUser, ACTION_RegisterUser, NULL },
7571 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7572 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7573 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7574 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7575 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7576 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7577 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7578 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7579 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7580 { szResolveSource, ACTION_ResolveSource, NULL },
7581 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7582 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7583 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7584 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7585 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7586 { szStartServices, ACTION_StartServices, szStopServices },
7587 { szStopServices, ACTION_StopServices, szStartServices },
7588 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7589 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7590 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7591 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7592 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7593 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7594 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7595 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7596 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7597 { szValidateProductID, ACTION_ValidateProductID, NULL },
7598 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7599 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7600 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7601 { NULL, NULL, NULL }
7604 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7606 BOOL ret = FALSE;
7607 UINT i;
7609 i = 0;
7610 while (StandardActions[i].action != NULL)
7612 if (!strcmpW( StandardActions[i].action, action ))
7614 ui_actionstart( package, action );
7615 if (StandardActions[i].handler)
7617 ui_actioninfo( package, action, TRUE, 0 );
7618 *rc = StandardActions[i].handler( package );
7619 ui_actioninfo( package, action, FALSE, *rc );
7621 if (StandardActions[i].action_rollback && !package->need_rollback)
7623 TRACE("scheduling rollback action\n");
7624 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7627 else
7629 FIXME("unhandled standard action %s\n", debugstr_w(action));
7630 *rc = ERROR_SUCCESS;
7632 ret = TRUE;
7633 break;
7635 i++;
7637 return ret;
7640 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7642 UINT rc = ERROR_SUCCESS;
7643 BOOL handled;
7645 TRACE("Performing action (%s)\n", debugstr_w(action));
7647 handled = ACTION_HandleStandardAction(package, action, &rc);
7649 if (!handled)
7650 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7652 if (!handled)
7654 WARN("unhandled msi action %s\n", debugstr_w(action));
7655 rc = ERROR_FUNCTION_NOT_CALLED;
7658 return rc;
7661 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7663 UINT rc = ERROR_SUCCESS;
7664 BOOL handled = FALSE;
7666 TRACE("Performing action (%s)\n", debugstr_w(action));
7668 package->action_progress_increment = 0;
7669 handled = ACTION_HandleStandardAction(package, action, &rc);
7671 if (!handled)
7672 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7674 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7675 handled = TRUE;
7677 if (!handled)
7679 WARN("unhandled msi action %s\n", debugstr_w(action));
7680 rc = ERROR_FUNCTION_NOT_CALLED;
7683 return rc;
7686 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7688 UINT rc = ERROR_SUCCESS;
7689 MSIRECORD *row;
7691 static const WCHAR query[] =
7692 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7693 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7694 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7695 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7696 static const WCHAR ui_query[] =
7697 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7698 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7699 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7700 ' ', '=',' ','%','i',0};
7702 if (needs_ui_sequence(package))
7703 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7704 else
7705 row = MSI_QueryGetRecord(package->db, query, seq);
7707 if (row)
7709 LPCWSTR action, cond;
7711 TRACE("Running the actions\n");
7713 /* check conditions */
7714 cond = MSI_RecordGetString(row, 2);
7716 /* this is a hack to skip errors in the condition code */
7717 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7719 msiobj_release(&row->hdr);
7720 return ERROR_SUCCESS;
7723 action = MSI_RecordGetString(row, 1);
7724 if (!action)
7726 ERR("failed to fetch action\n");
7727 msiobj_release(&row->hdr);
7728 return ERROR_FUNCTION_FAILED;
7731 if (needs_ui_sequence(package))
7732 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7733 else
7734 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7736 msiobj_release(&row->hdr);
7739 return rc;
7742 /****************************************************
7743 * TOP level entry points
7744 *****************************************************/
7746 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7747 LPCWSTR szCommandLine )
7749 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7750 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7751 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7752 WCHAR *reinstall, *remove, *patch, *productcode;
7753 BOOL ui_exists;
7754 UINT rc;
7756 msi_set_property( package->db, szAction, szInstall, -1 );
7758 package->script->InWhatSequence = SEQUENCE_INSTALL;
7760 if (szPackagePath)
7762 LPWSTR p, dir;
7763 LPCWSTR file;
7765 dir = strdupW(szPackagePath);
7766 p = strrchrW(dir, '\\');
7767 if (p)
7769 *(++p) = 0;
7770 file = szPackagePath + (p - dir);
7772 else
7774 msi_free(dir);
7775 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7776 GetCurrentDirectoryW(MAX_PATH, dir);
7777 lstrcatW(dir, szBackSlash);
7778 file = szPackagePath;
7781 msi_free( package->PackagePath );
7782 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7783 if (!package->PackagePath)
7785 msi_free(dir);
7786 return ERROR_OUTOFMEMORY;
7789 lstrcpyW(package->PackagePath, dir);
7790 lstrcatW(package->PackagePath, file);
7791 msi_free(dir);
7793 msi_set_sourcedir_props(package, FALSE);
7796 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7797 if (rc != ERROR_SUCCESS)
7798 return rc;
7800 msi_apply_transforms( package );
7801 msi_apply_patches( package );
7803 patch = msi_dup_property( package->db, szPatch );
7804 remove = msi_dup_property( package->db, szRemove );
7805 reinstall = msi_dup_property( package->db, szReinstall );
7806 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7808 TRACE("setting REINSTALL property to ALL\n");
7809 msi_set_property( package->db, szReinstall, szAll, -1 );
7810 package->full_reinstall = 1;
7813 msi_set_original_database_property( package->db, szPackagePath );
7814 msi_parse_command_line( package, szCommandLine, FALSE );
7815 msi_adjust_privilege_properties( package );
7816 msi_set_context( package );
7818 productcode = msi_dup_property( package->db, szProductCode );
7819 if (strcmpiW( productcode, package->ProductCode ))
7821 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
7822 msi_free( package->ProductCode );
7823 package->ProductCode = productcode;
7825 else msi_free( productcode );
7827 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7829 TRACE("disabling rollback\n");
7830 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7833 if (needs_ui_sequence( package))
7835 package->script->InWhatSequence |= SEQUENCE_UI;
7836 rc = ACTION_ProcessUISequence(package);
7837 ui_exists = ui_sequence_exists(package);
7838 if (rc == ERROR_SUCCESS || !ui_exists)
7840 package->script->InWhatSequence |= SEQUENCE_EXEC;
7841 rc = ACTION_ProcessExecSequence(package, ui_exists);
7844 else
7845 rc = ACTION_ProcessExecSequence(package, FALSE);
7847 /* process the ending type action */
7848 if (rc == ERROR_SUCCESS)
7849 ACTION_PerformActionSequence(package, -1);
7850 else if (rc == ERROR_INSTALL_USEREXIT)
7851 ACTION_PerformActionSequence(package, -2);
7852 else if (rc == ERROR_INSTALL_SUSPEND)
7853 ACTION_PerformActionSequence(package, -4);
7854 else /* failed */
7856 ACTION_PerformActionSequence(package, -3);
7857 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7859 package->need_rollback = TRUE;
7863 /* finish up running custom actions */
7864 ACTION_FinishCustomActions(package);
7866 if (package->need_rollback && !reinstall)
7868 WARN("installation failed, running rollback script\n");
7869 execute_script( package, SCRIPT_ROLLBACK );
7871 msi_free( reinstall );
7872 msi_free( remove );
7873 msi_free( patch );
7875 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7876 return ERROR_SUCCESS_REBOOT_REQUIRED;
7878 return rc;