d3d8: TRACE fixes.
[wine.git] / dlls / msi / action.c
blob93e85c3d820fa10379b22b14dd2d9854f0b1dadb
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "shlwapi.h"
39 #include "imagehlp.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
48 static const WCHAR szCreateFolders[] =
49 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
50 static const WCHAR szCostFinalize[] =
51 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
52 static const WCHAR szWriteRegistryValues[] =
53 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
54 static const WCHAR szFileCost[] =
55 {'F','i','l','e','C','o','s','t',0};
56 static const WCHAR szInstallInitialize[] =
57 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
58 static const WCHAR szInstallValidate[] =
59 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
60 static const WCHAR szLaunchConditions[] =
61 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
62 static const WCHAR szProcessComponents[] =
63 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
64 static const WCHAR szRegisterTypeLibraries[] =
65 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
66 static const WCHAR szCreateShortcuts[] =
67 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
68 static const WCHAR szPublishProduct[] =
69 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
70 static const WCHAR szWriteIniValues[] =
71 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
72 static const WCHAR szSelfRegModules[] =
73 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
74 static const WCHAR szPublishFeatures[] =
75 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
76 static const WCHAR szRegisterProduct[] =
77 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
78 static const WCHAR szInstallExecute[] =
79 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
80 static const WCHAR szInstallExecuteAgain[] =
81 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
82 static const WCHAR szInstallFinalize[] =
83 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
84 static const WCHAR szForceReboot[] =
85 {'F','o','r','c','e','R','e','b','o','o','t',0};
86 static const WCHAR szResolveSource[] =
87 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
88 static const WCHAR szAllocateRegistrySpace[] =
89 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
90 static const WCHAR szBindImage[] =
91 {'B','i','n','d','I','m','a','g','e',0};
92 static const WCHAR szDeleteServices[] =
93 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
94 static const WCHAR szDisableRollback[] =
95 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
96 static const WCHAR szExecuteAction[] =
97 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
98 static const WCHAR szInstallAdminPackage[] =
99 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
100 static const WCHAR szInstallSFPCatalogFile[] =
101 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
102 static const WCHAR szIsolateComponents[] =
103 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
104 static const WCHAR szMigrateFeatureStates[] =
105 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
106 static const WCHAR szMsiUnpublishAssemblies[] =
107 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
108 static const WCHAR szInstallODBC[] =
109 {'I','n','s','t','a','l','l','O','D','B','C',0};
110 static const WCHAR szInstallServices[] =
111 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
112 static const WCHAR szPublishComponents[] =
113 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
114 static const WCHAR szRegisterComPlus[] =
115 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
116 static const WCHAR szRegisterUser[] =
117 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
118 static const WCHAR szRemoveEnvironmentStrings[] =
119 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
120 static const WCHAR szRemoveExistingProducts[] =
121 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
122 static const WCHAR szRemoveFolders[] =
123 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
124 static const WCHAR szRemoveIniValues[] =
125 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
126 static const WCHAR szRemoveODBC[] =
127 {'R','e','m','o','v','e','O','D','B','C',0};
128 static const WCHAR szRemoveRegistryValues[] =
129 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
130 static const WCHAR szRemoveShortcuts[] =
131 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
132 static const WCHAR szRMCCPSearch[] =
133 {'R','M','C','C','P','S','e','a','r','c','h',0};
134 static const WCHAR szScheduleReboot[] =
135 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
136 static const WCHAR szSelfUnregModules[] =
137 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
138 static const WCHAR szSetODBCFolders[] =
139 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
140 static const WCHAR szStartServices[] =
141 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
142 static const WCHAR szStopServices[] =
143 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
144 static const WCHAR szUnpublishComponents[] =
145 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
146 static const WCHAR szUnpublishFeatures[] =
147 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
148 static const WCHAR szUnregisterComPlus[] =
149 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
150 static const WCHAR szUnregisterTypeLibraries[] =
151 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
152 static const WCHAR szValidateProductID[] =
153 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
154 static const WCHAR szWriteEnvironmentStrings[] =
155 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
157 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
159 static const WCHAR Query_t[] =
160 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
161 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
162 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
163 ' ','\'','%','s','\'',0};
164 MSIRECORD * row;
166 row = MSI_QueryGetRecord( package->db, Query_t, action );
167 if (!row)
168 return;
169 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
170 msiobj_release(&row->hdr);
173 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
174 UINT rc)
176 MSIRECORD * row;
177 static const WCHAR template_s[]=
178 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
179 '%','s', '.',0};
180 static const WCHAR template_e[]=
181 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
182 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
183 '%','i','.',0};
184 static const WCHAR format[] =
185 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
186 WCHAR message[1024];
187 WCHAR timet[0x100];
189 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
190 if (start)
191 sprintfW(message,template_s,timet,action);
192 else
193 sprintfW(message,template_e,timet,action,rc);
195 row = MSI_CreateRecord(1);
196 MSI_RecordSetStringW(row,1,message);
198 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
199 msiobj_release(&row->hdr);
202 enum parse_state
204 state_whitespace,
205 state_token,
206 state_quote
209 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
211 enum parse_state state = state_quote;
212 const WCHAR *p;
213 WCHAR *out = value;
214 BOOL ignore, in_quotes = FALSE;
215 int count = 0, len = 0;
217 for (p = str; *p; p++)
219 ignore = FALSE;
220 switch (state)
222 case state_whitespace:
223 switch (*p)
225 case ' ':
226 in_quotes = TRUE;
227 ignore = TRUE;
228 len++;
229 break;
230 case '"':
231 state = state_quote;
232 if (in_quotes && p[1] != '\"') count--;
233 else count++;
234 break;
235 default:
236 state = state_token;
237 in_quotes = TRUE;
238 len++;
239 break;
241 break;
243 case state_token:
244 switch (*p)
246 case '"':
247 state = state_quote;
248 if (in_quotes) count--;
249 else count++;
250 break;
251 case ' ':
252 state = state_whitespace;
253 if (!count) goto done;
254 in_quotes = TRUE;
255 len++;
256 break;
257 default:
258 if (!count) in_quotes = FALSE;
259 else in_quotes = TRUE;
260 len++;
261 break;
263 break;
265 case state_quote:
266 switch (*p)
268 case '"':
269 if (in_quotes && p[1] != '\"') count--;
270 else count++;
271 break;
272 case ' ':
273 state = state_whitespace;
274 if (!count || (count > 1 && !len)) goto done;
275 in_quotes = TRUE;
276 len++;
277 break;
278 default:
279 state = state_token;
280 if (!count) in_quotes = FALSE;
281 else in_quotes = TRUE;
282 len++;
283 break;
285 break;
287 default: break;
289 if (!ignore) *out++ = *p;
292 done:
293 if (!len) *value = 0;
294 else *out = 0;
296 *quotes = count;
297 return p - str;
300 static void remove_quotes( WCHAR *str )
302 WCHAR *p = str;
303 int len = strlenW( str );
305 while ((p = strchrW( p, '"' )))
307 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
308 p++;
312 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
313 BOOL preserve_case )
315 LPCWSTR ptr, ptr2;
316 int num_quotes;
317 DWORD len;
318 WCHAR *prop, *val;
319 UINT r;
321 if (!szCommandLine)
322 return ERROR_SUCCESS;
324 ptr = szCommandLine;
325 while (*ptr)
327 while (*ptr == ' ') ptr++;
328 if (!*ptr) break;
330 ptr2 = strchrW( ptr, '=' );
331 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
333 len = ptr2 - ptr;
334 if (!len) return ERROR_INVALID_COMMAND_LINE;
336 while (ptr[len - 1] == ' ') len--;
338 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
339 memcpy( prop, ptr, len * sizeof(WCHAR) );
340 prop[len] = 0;
341 if (!preserve_case) struprW( prop );
343 ptr2++;
344 while (*ptr2 == ' ') ptr2++;
346 num_quotes = 0;
347 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
348 len = parse_prop( ptr2, val, &num_quotes );
349 if (num_quotes % 2)
351 WARN("unbalanced quotes\n");
352 msi_free( val );
353 msi_free( prop );
354 return ERROR_INVALID_COMMAND_LINE;
356 remove_quotes( val );
357 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
359 r = msi_set_property( package->db, prop, val, -1 );
360 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
361 msi_reset_folders( package, TRUE );
363 msi_free( val );
364 msi_free( prop );
366 ptr = ptr2 + len;
369 return ERROR_SUCCESS;
372 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
374 LPCWSTR pc;
375 LPWSTR p, *ret = NULL;
376 UINT count = 0;
378 if (!str)
379 return ret;
381 /* count the number of substrings */
382 for ( pc = str, count = 0; pc; count++ )
384 pc = strchrW( pc, sep );
385 if (pc)
386 pc++;
389 /* allocate space for an array of substring pointers and the substrings */
390 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
391 (lstrlenW(str)+1) * sizeof(WCHAR) );
392 if (!ret)
393 return ret;
395 /* copy the string and set the pointers */
396 p = (LPWSTR) &ret[count+1];
397 lstrcpyW( p, str );
398 for( count = 0; (ret[count] = p); count++ )
400 p = strchrW( p, sep );
401 if (p)
402 *p++ = 0;
405 return ret;
408 static BOOL ui_sequence_exists( MSIPACKAGE *package )
410 static const WCHAR query [] = {
411 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
412 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
413 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',0};
414 MSIQUERY *view;
415 DWORD count = 0;
417 if (!(MSI_DatabaseOpenViewW( package->db, query, &view )))
419 MSI_IterateRecords( view, &count, NULL, package );
420 msiobj_release( &view->hdr );
422 return count != 0;
425 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
427 WCHAR *source, *check, *p, *db;
428 DWORD len;
430 if (!(db = msi_dup_property( package->db, szOriginalDatabase )))
431 return ERROR_OUTOFMEMORY;
433 if (!(p = strrchrW( db, '\\' )) && !(p = strrchrW( db, '/' )))
435 msi_free(db);
436 return ERROR_SUCCESS;
438 len = p - db + 2;
439 source = msi_alloc( len * sizeof(WCHAR) );
440 lstrcpynW( source, db, len );
441 msi_free( db );
443 check = msi_dup_property( package->db, szSourceDir );
444 if (!check || replace)
446 UINT r = msi_set_property( package->db, szSourceDir, source, -1 );
447 if (r == ERROR_SUCCESS)
448 msi_reset_folders( package, TRUE );
450 msi_free( check );
452 check = msi_dup_property( package->db, szSOURCEDIR );
453 if (!check || replace)
454 msi_set_property( package->db, szSOURCEDIR, source, -1 );
456 msi_free( check );
457 msi_free( source );
459 return ERROR_SUCCESS;
462 static BOOL needs_ui_sequence(MSIPACKAGE *package)
464 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
467 UINT msi_set_context(MSIPACKAGE *package)
469 UINT r = msi_locate_product( package->ProductCode, &package->Context );
470 if (r != ERROR_SUCCESS)
472 int num = msi_get_property_int( package->db, szAllUsers, 0 );
473 if (num == 1 || num == 2)
474 package->Context = MSIINSTALLCONTEXT_MACHINE;
475 else
476 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
478 return ERROR_SUCCESS;
481 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
483 UINT rc;
484 LPCWSTR cond, action;
485 MSIPACKAGE *package = param;
487 action = MSI_RecordGetString(row,1);
488 if (!action)
490 ERR("Error is retrieving action name\n");
491 return ERROR_FUNCTION_FAILED;
494 /* check conditions */
495 cond = MSI_RecordGetString(row,2);
497 /* this is a hack to skip errors in the condition code */
498 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
500 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
501 return ERROR_SUCCESS;
504 if (needs_ui_sequence(package))
505 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
506 else
507 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
509 msi_dialog_check_messages( NULL );
511 if (package->CurrentInstallState != ERROR_SUCCESS)
512 rc = package->CurrentInstallState;
514 if (rc == ERROR_FUNCTION_NOT_CALLED)
515 rc = ERROR_SUCCESS;
517 if (rc != ERROR_SUCCESS)
518 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
520 if (package->need_reboot_now)
522 TRACE("action %s asked for immediate reboot, suspending installation\n",
523 debugstr_w(action));
524 rc = ACTION_ForceReboot( package );
526 return rc;
529 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
531 static const WCHAR query[] = {
532 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
533 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
534 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
535 '`','S','e','q','u','e','n','c','e','`',0};
536 MSIQUERY *view;
537 UINT r;
539 TRACE("%p %s\n", package, debugstr_w(table));
541 r = MSI_OpenQuery( package->db, &view, query, table );
542 if (r == ERROR_SUCCESS)
544 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
545 msiobj_release(&view->hdr);
547 return r;
550 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
552 static const WCHAR query[] = {
553 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
554 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
555 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
556 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
557 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
558 static const WCHAR query_validate[] = {
559 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
560 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
561 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
562 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
563 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
564 MSIQUERY *view;
565 INT seq = 0;
566 UINT rc;
568 if (package->script->ExecuteSequenceRun)
570 TRACE("Execute Sequence already Run\n");
571 return ERROR_SUCCESS;
574 package->script->ExecuteSequenceRun = TRUE;
576 /* get the sequence number */
577 if (UIran)
579 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
580 if (!row) return ERROR_FUNCTION_FAILED;
581 seq = MSI_RecordGetInteger(row,1);
582 msiobj_release(&row->hdr);
584 rc = MSI_OpenQuery(package->db, &view, query, seq);
585 if (rc == ERROR_SUCCESS)
587 TRACE("Running the actions\n");
589 msi_set_property( package->db, szSourceDir, NULL, -1 );
590 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
591 msiobj_release(&view->hdr);
593 return rc;
596 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
598 static const WCHAR query[] = {
599 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
600 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
601 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
602 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
603 MSIQUERY *view;
604 UINT rc;
606 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
607 if (rc == ERROR_SUCCESS)
609 TRACE("Running the actions\n");
610 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
611 msiobj_release(&view->hdr);
613 return rc;
616 /********************************************************
617 * ACTION helper functions and functions that perform the actions
618 *******************************************************/
619 static BOOL ACTION_HandleCustomAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc, UINT script )
621 BOOL ret=FALSE;
622 UINT arc;
624 arc = ACTION_CustomAction( package, action, script );
625 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
627 *rc = arc;
628 ret = TRUE;
630 return ret;
633 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
635 MSICOMPONENT *comp;
637 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
639 if (!strcmpW( Component, comp->Component )) return comp;
641 return NULL;
644 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
646 MSIFEATURE *feature;
648 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
650 if (!strcmpW( Feature, feature->Feature )) return feature;
652 return NULL;
655 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
657 MSIFILE *file;
659 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
661 if (!strcmpW( key, file->File )) return file;
663 return NULL;
666 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
668 MSIFOLDER *folder;
670 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
672 if (!strcmpW( dir, folder->Directory )) return folder;
674 return NULL;
678 * Recursively create all directories in the path.
679 * shamelessly stolen from setupapi/queue.c
681 BOOL msi_create_full_path( const WCHAR *path )
683 BOOL ret = TRUE;
684 WCHAR *new_path;
685 int len;
687 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
688 strcpyW( new_path, path );
690 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
691 new_path[len - 1] = 0;
693 while (!CreateDirectoryW( new_path, NULL ))
695 WCHAR *slash;
696 DWORD last_error = GetLastError();
697 if (last_error == ERROR_ALREADY_EXISTS) break;
698 if (last_error != ERROR_PATH_NOT_FOUND)
700 ret = FALSE;
701 break;
703 if (!(slash = strrchrW( new_path, '\\' )))
705 ret = FALSE;
706 break;
708 len = slash - new_path;
709 new_path[len] = 0;
710 if (!msi_create_full_path( new_path ))
712 ret = FALSE;
713 break;
715 new_path[len] = '\\';
717 msi_free( new_path );
718 return ret;
721 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
723 MSIRECORD *row;
725 row = MSI_CreateRecord( 4 );
726 MSI_RecordSetInteger( row, 1, a );
727 MSI_RecordSetInteger( row, 2, b );
728 MSI_RecordSetInteger( row, 3, c );
729 MSI_RecordSetInteger( row, 4, d );
730 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
731 msiobj_release( &row->hdr );
733 msi_dialog_check_messages( NULL );
736 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
738 static const WCHAR query[] =
739 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
740 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
741 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
742 WCHAR message[1024];
743 MSIRECORD *row = 0;
744 DWORD size;
746 if (!package->LastAction || strcmpW( package->LastAction, action ))
748 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
750 if (MSI_RecordIsNull( row, 3 ))
752 msiobj_release( &row->hdr );
753 return;
755 /* update the cached action format */
756 msi_free( package->ActionFormat );
757 package->ActionFormat = msi_dup_record_field( row, 3 );
758 msi_free( package->LastAction );
759 package->LastAction = strdupW( action );
760 msiobj_release( &row->hdr );
762 size = 1024;
763 MSI_RecordSetStringW( record, 0, package->ActionFormat );
764 MSI_FormatRecordW( package, record, message, &size );
765 row = MSI_CreateRecord( 1 );
766 MSI_RecordSetStringW( row, 1, message );
767 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
768 msiobj_release( &row->hdr );
771 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
773 if (!comp->Enabled)
775 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
776 return INSTALLSTATE_UNKNOWN;
778 if (package->need_rollback) return comp->Installed;
779 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
781 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
782 return INSTALLSTATE_UNKNOWN;
784 return comp->ActionRequest;
787 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
789 if (package->need_rollback) return feature->Installed;
790 return feature->ActionRequest;
793 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
795 MSIPACKAGE *package = param;
796 LPCWSTR dir, component, full_path;
797 MSIRECORD *uirow;
798 MSIFOLDER *folder;
799 MSICOMPONENT *comp;
801 component = MSI_RecordGetString(row, 2);
802 if (!component)
803 return ERROR_SUCCESS;
805 comp = msi_get_loaded_component(package, component);
806 if (!comp)
807 return ERROR_SUCCESS;
809 comp->Action = msi_get_component_action( package, comp );
810 if (comp->Action != INSTALLSTATE_LOCAL)
812 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
813 return ERROR_SUCCESS;
816 dir = MSI_RecordGetString(row,1);
817 if (!dir)
819 ERR("Unable to get folder id\n");
820 return ERROR_SUCCESS;
823 uirow = MSI_CreateRecord(1);
824 MSI_RecordSetStringW(uirow, 1, dir);
825 msi_ui_actiondata(package, szCreateFolders, uirow);
826 msiobj_release(&uirow->hdr);
828 full_path = msi_get_target_folder( package, dir );
829 if (!full_path)
831 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
832 return ERROR_SUCCESS;
834 TRACE("folder is %s\n", debugstr_w(full_path));
836 folder = msi_get_loaded_folder( package, dir );
837 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
838 folder->State = FOLDER_STATE_CREATED;
839 return ERROR_SUCCESS;
842 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
844 static const WCHAR query[] = {
845 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
846 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
847 MSIQUERY *view;
848 UINT rc;
850 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
851 if (rc != ERROR_SUCCESS)
852 return ERROR_SUCCESS;
854 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
855 msiobj_release(&view->hdr);
856 return rc;
859 static void remove_persistent_folder( MSIFOLDER *folder )
861 FolderList *fl;
863 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
865 remove_persistent_folder( fl->folder );
867 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
869 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
873 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
875 MSIPACKAGE *package = param;
876 LPCWSTR dir, component, full_path;
877 MSIRECORD *uirow;
878 MSIFOLDER *folder;
879 MSICOMPONENT *comp;
881 component = MSI_RecordGetString(row, 2);
882 if (!component)
883 return ERROR_SUCCESS;
885 comp = msi_get_loaded_component(package, component);
886 if (!comp)
887 return ERROR_SUCCESS;
889 comp->Action = msi_get_component_action( package, comp );
890 if (comp->Action != INSTALLSTATE_ABSENT)
892 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
893 return ERROR_SUCCESS;
896 dir = MSI_RecordGetString( row, 1 );
897 if (!dir)
899 ERR("Unable to get folder id\n");
900 return ERROR_SUCCESS;
903 full_path = msi_get_target_folder( package, dir );
904 if (!full_path)
906 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
907 return ERROR_SUCCESS;
909 TRACE("folder is %s\n", debugstr_w(full_path));
911 uirow = MSI_CreateRecord( 1 );
912 MSI_RecordSetStringW( uirow, 1, dir );
913 msi_ui_actiondata( package, szRemoveFolders, uirow );
914 msiobj_release( &uirow->hdr );
916 folder = msi_get_loaded_folder( package, dir );
917 remove_persistent_folder( folder );
918 return ERROR_SUCCESS;
921 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
923 static const WCHAR query[] = {
924 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
925 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
926 MSIQUERY *view;
927 UINT rc;
929 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
930 if (rc != ERROR_SUCCESS)
931 return ERROR_SUCCESS;
933 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
934 msiobj_release( &view->hdr );
935 return rc;
938 static UINT load_component( MSIRECORD *row, LPVOID param )
940 MSIPACKAGE *package = param;
941 MSICOMPONENT *comp;
943 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
944 if (!comp)
945 return ERROR_FUNCTION_FAILED;
947 list_add_tail( &package->components, &comp->entry );
949 /* fill in the data */
950 comp->Component = msi_dup_record_field( row, 1 );
952 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
954 comp->ComponentId = msi_dup_record_field( row, 2 );
955 comp->Directory = msi_dup_record_field( row, 3 );
956 comp->Attributes = MSI_RecordGetInteger(row,4);
957 comp->Condition = msi_dup_record_field( row, 5 );
958 comp->KeyPath = msi_dup_record_field( row, 6 );
960 comp->Installed = INSTALLSTATE_UNKNOWN;
961 comp->Action = INSTALLSTATE_UNKNOWN;
962 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
964 comp->assembly = msi_load_assembly( package, comp );
965 return ERROR_SUCCESS;
968 UINT msi_load_all_components( MSIPACKAGE *package )
970 static const WCHAR query[] = {
971 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
972 '`','C','o','m','p','o','n','e','n','t','`',0};
973 MSIQUERY *view;
974 UINT r;
976 if (!list_empty(&package->components))
977 return ERROR_SUCCESS;
979 r = MSI_DatabaseOpenViewW( package->db, query, &view );
980 if (r != ERROR_SUCCESS)
981 return r;
983 if (!msi_init_assembly_caches( package ))
985 ERR("can't initialize assembly caches\n");
986 msiobj_release( &view->hdr );
987 return ERROR_FUNCTION_FAILED;
990 r = MSI_IterateRecords(view, NULL, load_component, package);
991 msiobj_release(&view->hdr);
992 return r;
995 typedef struct {
996 MSIPACKAGE *package;
997 MSIFEATURE *feature;
998 } _ilfs;
1000 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1002 ComponentList *cl;
1004 cl = msi_alloc( sizeof (*cl) );
1005 if ( !cl )
1006 return ERROR_NOT_ENOUGH_MEMORY;
1007 cl->component = comp;
1008 list_add_tail( &feature->Components, &cl->entry );
1010 return ERROR_SUCCESS;
1013 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1015 FeatureList *fl;
1017 fl = msi_alloc( sizeof(*fl) );
1018 if ( !fl )
1019 return ERROR_NOT_ENOUGH_MEMORY;
1020 fl->feature = child;
1021 list_add_tail( &parent->Children, &fl->entry );
1023 return ERROR_SUCCESS;
1026 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1028 _ilfs* ilfs = param;
1029 LPCWSTR component;
1030 MSICOMPONENT *comp;
1032 component = MSI_RecordGetString(row,1);
1034 /* check to see if the component is already loaded */
1035 comp = msi_get_loaded_component( ilfs->package, component );
1036 if (!comp)
1038 WARN("ignoring unknown component %s\n", debugstr_w(component));
1039 return ERROR_SUCCESS;
1041 add_feature_component( ilfs->feature, comp );
1042 comp->Enabled = TRUE;
1044 return ERROR_SUCCESS;
1047 static UINT load_feature(MSIRECORD * row, LPVOID param)
1049 static const WCHAR query[] = {
1050 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1051 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1052 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1053 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1054 MSIPACKAGE *package = param;
1055 MSIFEATURE *feature;
1056 MSIQUERY *view;
1057 _ilfs ilfs;
1058 UINT rc;
1060 /* fill in the data */
1062 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1063 if (!feature)
1064 return ERROR_NOT_ENOUGH_MEMORY;
1066 list_init( &feature->Children );
1067 list_init( &feature->Components );
1069 feature->Feature = msi_dup_record_field( row, 1 );
1071 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1073 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1074 feature->Title = msi_dup_record_field( row, 3 );
1075 feature->Description = msi_dup_record_field( row, 4 );
1077 if (!MSI_RecordIsNull(row,5))
1078 feature->Display = MSI_RecordGetInteger(row,5);
1080 feature->Level= MSI_RecordGetInteger(row,6);
1081 feature->Directory = msi_dup_record_field( row, 7 );
1082 feature->Attributes = MSI_RecordGetInteger(row,8);
1084 feature->Installed = INSTALLSTATE_UNKNOWN;
1085 feature->Action = INSTALLSTATE_UNKNOWN;
1086 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1088 list_add_tail( &package->features, &feature->entry );
1090 /* load feature components */
1092 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1093 if (rc != ERROR_SUCCESS)
1094 return ERROR_SUCCESS;
1096 ilfs.package = package;
1097 ilfs.feature = feature;
1099 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1100 msiobj_release(&view->hdr);
1101 return rc;
1104 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1106 MSIPACKAGE *package = param;
1107 MSIFEATURE *parent, *child;
1109 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1110 if (!child)
1111 return ERROR_FUNCTION_FAILED;
1113 if (!child->Feature_Parent)
1114 return ERROR_SUCCESS;
1116 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1117 if (!parent)
1118 return ERROR_FUNCTION_FAILED;
1120 add_feature_child( parent, child );
1121 return ERROR_SUCCESS;
1124 UINT msi_load_all_features( MSIPACKAGE *package )
1126 static const WCHAR query[] = {
1127 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1128 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1129 '`','D','i','s','p','l','a','y','`',0};
1130 MSIQUERY *view;
1131 UINT r;
1133 if (!list_empty(&package->features))
1134 return ERROR_SUCCESS;
1136 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1137 if (r != ERROR_SUCCESS)
1138 return r;
1140 r = MSI_IterateRecords( view, NULL, load_feature, package );
1141 if (r != ERROR_SUCCESS)
1143 msiobj_release( &view->hdr );
1144 return r;
1146 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1147 msiobj_release( &view->hdr );
1148 return r;
1151 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1153 if (!p)
1154 return p;
1155 p = strchrW(p, ch);
1156 if (!p)
1157 return p;
1158 *p = 0;
1159 return p+1;
1162 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1164 static const WCHAR query[] = {
1165 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1166 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1167 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1168 MSIQUERY *view = NULL;
1169 MSIRECORD *row = NULL;
1170 UINT r;
1172 TRACE("%s\n", debugstr_w(file->File));
1174 r = MSI_OpenQuery(package->db, &view, query, file->File);
1175 if (r != ERROR_SUCCESS)
1176 goto done;
1178 r = MSI_ViewExecute(view, NULL);
1179 if (r != ERROR_SUCCESS)
1180 goto done;
1182 r = MSI_ViewFetch(view, &row);
1183 if (r != ERROR_SUCCESS)
1184 goto done;
1186 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1187 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1188 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1189 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1190 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1192 done:
1193 if (view) msiobj_release(&view->hdr);
1194 if (row) msiobj_release(&row->hdr);
1195 return r;
1198 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1200 MSIRECORD *row;
1201 static const WCHAR query[] = {
1202 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1203 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1204 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1206 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1207 if (!row)
1209 WARN("query failed\n");
1210 return ERROR_FUNCTION_FAILED;
1213 file->disk_id = MSI_RecordGetInteger( row, 1 );
1214 msiobj_release( &row->hdr );
1215 return ERROR_SUCCESS;
1218 static UINT load_file(MSIRECORD *row, LPVOID param)
1220 MSIPACKAGE* package = param;
1221 LPCWSTR component;
1222 MSIFILE *file;
1224 /* fill in the data */
1226 file = msi_alloc_zero( sizeof (MSIFILE) );
1227 if (!file)
1228 return ERROR_NOT_ENOUGH_MEMORY;
1230 file->File = msi_dup_record_field( row, 1 );
1232 component = MSI_RecordGetString( row, 2 );
1233 file->Component = msi_get_loaded_component( package, component );
1235 if (!file->Component)
1237 WARN("Component not found: %s\n", debugstr_w(component));
1238 msi_free(file->File);
1239 msi_free(file);
1240 return ERROR_SUCCESS;
1243 file->FileName = msi_dup_record_field( row, 3 );
1244 msi_reduce_to_long_filename( file->FileName );
1246 file->ShortName = msi_dup_record_field( row, 3 );
1247 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1249 file->FileSize = MSI_RecordGetInteger( row, 4 );
1250 file->Version = msi_dup_record_field( row, 5 );
1251 file->Language = msi_dup_record_field( row, 6 );
1252 file->Attributes = MSI_RecordGetInteger( row, 7 );
1253 file->Sequence = MSI_RecordGetInteger( row, 8 );
1255 file->state = msifs_invalid;
1257 /* if the compressed bits are not set in the file attributes,
1258 * then read the information from the package word count property
1260 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1262 file->IsCompressed = FALSE;
1264 else if (file->Attributes &
1265 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1267 file->IsCompressed = TRUE;
1269 else if (file->Attributes & msidbFileAttributesNoncompressed)
1271 file->IsCompressed = FALSE;
1273 else
1275 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1278 load_file_hash(package, file);
1279 load_file_disk_id(package, file);
1281 TRACE("File loaded (file %s sequence %u)\n", debugstr_w(file->File), file->Sequence);
1283 list_add_tail( &package->files, &file->entry );
1285 return ERROR_SUCCESS;
1288 static UINT load_all_files(MSIPACKAGE *package)
1290 static const WCHAR query[] = {
1291 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1292 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1293 '`','S','e','q','u','e','n','c','e','`', 0};
1294 MSIQUERY *view;
1295 UINT rc;
1297 if (!list_empty(&package->files))
1298 return ERROR_SUCCESS;
1300 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1301 if (rc != ERROR_SUCCESS)
1302 return ERROR_SUCCESS;
1304 rc = MSI_IterateRecords(view, NULL, load_file, package);
1305 msiobj_release(&view->hdr);
1306 return rc;
1309 static UINT load_media( MSIRECORD *row, LPVOID param )
1311 MSIPACKAGE *package = param;
1312 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1313 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1315 /* FIXME: load external cabinets and directory sources too */
1316 if (!cabinet || cabinet[0] != '#' || disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
1317 return ERROR_SUCCESS;
1319 return msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1322 static UINT load_all_media( MSIPACKAGE *package )
1324 static const WCHAR query[] = {
1325 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1326 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1327 '`','D','i','s','k','I','d','`',0};
1328 MSIQUERY *view;
1329 UINT r;
1331 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1332 if (r != ERROR_SUCCESS)
1333 return ERROR_SUCCESS;
1335 r = MSI_IterateRecords( view, NULL, load_media, package );
1336 msiobj_release( &view->hdr );
1337 return r;
1340 static UINT load_patch_disk_id( MSIPACKAGE *package, MSIFILEPATCH *patch )
1342 static const WCHAR query[] =
1343 {'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1344 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1345 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','u',0};
1346 MSIRECORD *rec;
1348 if (!(rec = MSI_QueryGetRecord( package->db, query, patch->Sequence )))
1350 WARN("query failed\n");
1351 return ERROR_FUNCTION_FAILED;
1354 patch->disk_id = MSI_RecordGetInteger( rec, 1 );
1355 msiobj_release( &rec->hdr );
1356 return ERROR_SUCCESS;
1359 static UINT load_patch(MSIRECORD *row, LPVOID param)
1361 MSIPACKAGE *package = param;
1362 MSIFILEPATCH *patch;
1363 const WCHAR *file_key;
1365 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1366 if (!patch)
1367 return ERROR_NOT_ENOUGH_MEMORY;
1369 file_key = MSI_RecordGetString( row, 1 );
1370 patch->File = msi_get_loaded_file( package, file_key );
1371 if (!patch->File)
1373 ERR("Failed to find target for patch in File table\n");
1374 msi_free(patch);
1375 return ERROR_FUNCTION_FAILED;
1378 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1379 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1380 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1382 /* FIXME:
1383 * Header field - for patch validation.
1384 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1387 load_patch_disk_id( package, patch );
1389 TRACE("Patch loaded (file %s sequence %u)\n", debugstr_w(patch->File->File), patch->Sequence);
1391 list_add_tail( &package->filepatches, &patch->entry );
1393 return ERROR_SUCCESS;
1396 static UINT load_all_patches(MSIPACKAGE *package)
1398 static const WCHAR query[] = {
1399 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1400 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1401 '`','S','e','q','u','e','n','c','e','`',0};
1402 MSIQUERY *view;
1403 UINT rc;
1405 if (!list_empty(&package->filepatches))
1406 return ERROR_SUCCESS;
1408 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1409 if (rc != ERROR_SUCCESS)
1410 return ERROR_SUCCESS;
1412 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1413 msiobj_release(&view->hdr);
1414 return rc;
1417 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1419 static const WCHAR query[] = {
1420 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1421 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1422 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1423 MSIQUERY *view;
1425 folder->persistent = FALSE;
1426 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1428 if (!MSI_ViewExecute( view, NULL ))
1430 MSIRECORD *rec;
1431 if (!MSI_ViewFetch( view, &rec ))
1433 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1434 folder->persistent = TRUE;
1435 msiobj_release( &rec->hdr );
1438 msiobj_release( &view->hdr );
1440 return ERROR_SUCCESS;
1443 static UINT load_folder( MSIRECORD *row, LPVOID param )
1445 MSIPACKAGE *package = param;
1446 static WCHAR szEmpty[] = { 0 };
1447 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1448 MSIFOLDER *folder;
1450 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1451 list_init( &folder->children );
1452 folder->Directory = msi_dup_record_field( row, 1 );
1453 folder->Parent = msi_dup_record_field( row, 2 );
1454 p = msi_dup_record_field(row, 3);
1456 TRACE("%s\n", debugstr_w(folder->Directory));
1458 /* split src and target dir */
1459 tgt_short = p;
1460 src_short = folder_split_path( p, ':' );
1462 /* split the long and short paths */
1463 tgt_long = folder_split_path( tgt_short, '|' );
1464 src_long = folder_split_path( src_short, '|' );
1466 /* check for no-op dirs */
1467 if (tgt_short && !strcmpW( szDot, tgt_short ))
1468 tgt_short = szEmpty;
1469 if (src_short && !strcmpW( szDot, src_short ))
1470 src_short = szEmpty;
1472 if (!tgt_long)
1473 tgt_long = tgt_short;
1475 if (!src_short) {
1476 src_short = tgt_short;
1477 src_long = tgt_long;
1480 if (!src_long)
1481 src_long = src_short;
1483 /* FIXME: use the target short path too */
1484 folder->TargetDefault = strdupW(tgt_long);
1485 folder->SourceShortPath = strdupW(src_short);
1486 folder->SourceLongPath = strdupW(src_long);
1487 msi_free(p);
1489 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1490 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1491 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1493 load_folder_persistence( package, folder );
1495 list_add_tail( &package->folders, &folder->entry );
1496 return ERROR_SUCCESS;
1499 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1501 FolderList *fl;
1503 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1504 fl->folder = child;
1505 list_add_tail( &parent->children, &fl->entry );
1506 return ERROR_SUCCESS;
1509 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1511 MSIPACKAGE *package = param;
1512 MSIFOLDER *parent, *child;
1514 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1515 return ERROR_FUNCTION_FAILED;
1517 if (!child->Parent) return ERROR_SUCCESS;
1519 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1520 return ERROR_FUNCTION_FAILED;
1522 return add_folder_child( parent, child );
1525 static UINT load_all_folders( MSIPACKAGE *package )
1527 static const WCHAR query[] = {
1528 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1529 '`','D','i','r','e','c','t','o','r','y','`',0};
1530 MSIQUERY *view;
1531 UINT r;
1533 if (!list_empty(&package->folders))
1534 return ERROR_SUCCESS;
1536 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1537 if (r != ERROR_SUCCESS)
1538 return r;
1540 r = MSI_IterateRecords( view, NULL, load_folder, package );
1541 if (r != ERROR_SUCCESS)
1543 msiobj_release( &view->hdr );
1544 return r;
1546 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1547 msiobj_release( &view->hdr );
1548 return r;
1551 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1553 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1554 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1556 load_all_folders( package );
1557 msi_load_all_components( package );
1558 msi_load_all_features( package );
1559 load_all_files( package );
1560 load_all_patches( package );
1561 load_all_media( package );
1563 return ERROR_SUCCESS;
1566 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1568 const WCHAR *action = package->script->Actions[script][index];
1569 ui_actionstart( package, action );
1570 TRACE("executing %s\n", debugstr_w(action));
1571 return ACTION_PerformAction( package, action, script );
1574 static UINT execute_script( MSIPACKAGE *package, UINT script )
1576 UINT i, rc = ERROR_SUCCESS;
1578 TRACE("executing script %u\n", script);
1580 if (!package->script)
1582 ERR("no script!\n");
1583 return ERROR_FUNCTION_FAILED;
1585 if (script == SCRIPT_ROLLBACK)
1587 for (i = package->script->ActionCount[script]; i > 0; i--)
1589 rc = execute_script_action( package, script, i - 1 );
1590 if (rc != ERROR_SUCCESS) break;
1593 else
1595 for (i = 0; i < package->script->ActionCount[script]; i++)
1597 rc = execute_script_action( package, script, i );
1598 if (rc != ERROR_SUCCESS) break;
1601 msi_free_action_script(package, script);
1602 return rc;
1605 static UINT ACTION_FileCost(MSIPACKAGE *package)
1607 return ERROR_SUCCESS;
1610 static void get_client_counts( MSIPACKAGE *package )
1612 MSICOMPONENT *comp;
1613 HKEY hkey;
1615 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1617 if (!comp->ComponentId) continue;
1619 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1620 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1622 comp->num_clients = 0;
1623 continue;
1625 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1626 NULL, NULL, NULL, NULL );
1627 RegCloseKey( hkey );
1631 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1633 MSICOMPONENT *comp;
1634 UINT r;
1636 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1638 if (!comp->ComponentId) continue;
1640 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1641 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1642 &comp->Installed );
1643 if (r == ERROR_SUCCESS) continue;
1645 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1646 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1647 &comp->Installed );
1648 if (r == ERROR_SUCCESS) continue;
1650 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1651 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1652 &comp->Installed );
1653 if (r == ERROR_SUCCESS) continue;
1655 comp->Installed = INSTALLSTATE_ABSENT;
1659 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1661 MSIFEATURE *feature;
1663 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1665 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1667 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1668 feature->Installed = INSTALLSTATE_ABSENT;
1669 else
1670 feature->Installed = state;
1674 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1676 return (feature->Level > 0 && feature->Level <= level);
1679 static BOOL process_state_property(MSIPACKAGE* package, int level,
1680 LPCWSTR property, INSTALLSTATE state)
1682 LPWSTR override;
1683 MSIFEATURE *feature;
1684 BOOL remove = !strcmpW(property, szRemove);
1685 BOOL reinstall = !strcmpW(property, szReinstall);
1687 override = msi_dup_property( package->db, property );
1688 if (!override)
1689 return FALSE;
1691 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1693 if (feature->Level <= 0)
1694 continue;
1696 if (reinstall)
1697 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1698 else if (remove)
1699 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1701 if (!strcmpiW( override, szAll ))
1703 feature->Action = state;
1704 feature->ActionRequest = state;
1706 else
1708 LPWSTR ptr = override;
1709 LPWSTR ptr2 = strchrW(override,',');
1711 while (ptr)
1713 int len = ptr2 - ptr;
1715 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1716 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1718 feature->Action = state;
1719 feature->ActionRequest = state;
1720 break;
1722 if (ptr2)
1724 ptr=ptr2+1;
1725 ptr2 = strchrW(ptr,',');
1727 else
1728 break;
1732 msi_free(override);
1733 return TRUE;
1736 static BOOL process_overrides( MSIPACKAGE *package, int level )
1738 static const WCHAR szAddLocal[] =
1739 {'A','D','D','L','O','C','A','L',0};
1740 static const WCHAR szAddSource[] =
1741 {'A','D','D','S','O','U','R','C','E',0};
1742 static const WCHAR szAdvertise[] =
1743 {'A','D','V','E','R','T','I','S','E',0};
1744 BOOL ret = FALSE;
1746 /* all these activation/deactivation things happen in order and things
1747 * later on the list override things earlier on the list.
1749 * 0 INSTALLLEVEL processing
1750 * 1 ADDLOCAL
1751 * 2 REMOVE
1752 * 3 ADDSOURCE
1753 * 4 ADDDEFAULT
1754 * 5 REINSTALL
1755 * 6 ADVERTISE
1756 * 7 COMPADDLOCAL
1757 * 8 COMPADDSOURCE
1758 * 9 FILEADDLOCAL
1759 * 10 FILEADDSOURCE
1760 * 11 FILEADDDEFAULT
1762 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1763 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1764 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1765 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1766 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1768 if (ret && !package->full_reinstall)
1769 msi_set_property( package->db, szPreselected, szOne, -1 );
1771 return ret;
1774 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1776 int level;
1777 MSICOMPONENT* component;
1778 MSIFEATURE *feature;
1780 TRACE("Checking Install Level\n");
1782 level = msi_get_property_int(package->db, szInstallLevel, 1);
1784 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1786 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1788 if (!is_feature_selected( feature, level )) continue;
1790 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1792 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1794 feature->Action = INSTALLSTATE_SOURCE;
1795 feature->ActionRequest = INSTALLSTATE_SOURCE;
1797 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1799 feature->Action = INSTALLSTATE_ADVERTISED;
1800 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1802 else
1804 feature->Action = INSTALLSTATE_LOCAL;
1805 feature->ActionRequest = INSTALLSTATE_LOCAL;
1809 /* disable child features of unselected parent or follow parent */
1810 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1812 FeatureList *fl;
1814 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1816 if (!is_feature_selected( feature, level ))
1818 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1819 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1821 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1823 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1824 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1825 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1826 fl->feature->Action = feature->Action;
1827 fl->feature->ActionRequest = feature->ActionRequest;
1832 else /* preselected */
1834 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1836 if (!is_feature_selected( feature, level )) continue;
1838 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1840 if (feature->Installed == INSTALLSTATE_ABSENT)
1842 feature->Action = INSTALLSTATE_UNKNOWN;
1843 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1845 else
1847 feature->Action = feature->Installed;
1848 feature->ActionRequest = feature->Installed;
1852 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1854 FeatureList *fl;
1856 if (!is_feature_selected( feature, level )) continue;
1858 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1860 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1861 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1863 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1864 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1865 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1866 fl->feature->Action = feature->Action;
1867 fl->feature->ActionRequest = feature->ActionRequest;
1873 /* now we want to set component state based based on feature state */
1874 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1876 ComponentList *cl;
1878 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1879 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1880 feature->ActionRequest, feature->Action);
1882 /* features with components that have compressed files are made local */
1883 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1885 if (cl->component->ForceLocalState &&
1886 feature->ActionRequest == INSTALLSTATE_SOURCE)
1888 feature->Action = INSTALLSTATE_LOCAL;
1889 feature->ActionRequest = INSTALLSTATE_LOCAL;
1890 break;
1894 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1896 component = cl->component;
1898 switch (feature->ActionRequest)
1900 case INSTALLSTATE_ABSENT:
1901 component->anyAbsent = 1;
1902 break;
1903 case INSTALLSTATE_ADVERTISED:
1904 component->hasAdvertisedFeature = 1;
1905 break;
1906 case INSTALLSTATE_SOURCE:
1907 component->hasSourceFeature = 1;
1908 break;
1909 case INSTALLSTATE_LOCAL:
1910 component->hasLocalFeature = 1;
1911 break;
1912 case INSTALLSTATE_DEFAULT:
1913 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1914 component->hasAdvertisedFeature = 1;
1915 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1916 component->hasSourceFeature = 1;
1917 else
1918 component->hasLocalFeature = 1;
1919 break;
1920 default:
1921 break;
1926 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1928 /* check if it's local or source */
1929 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1930 (component->hasLocalFeature || component->hasSourceFeature))
1932 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1933 !component->ForceLocalState)
1935 component->Action = INSTALLSTATE_SOURCE;
1936 component->ActionRequest = INSTALLSTATE_SOURCE;
1938 else
1940 component->Action = INSTALLSTATE_LOCAL;
1941 component->ActionRequest = INSTALLSTATE_LOCAL;
1943 continue;
1946 /* if any feature is local, the component must be local too */
1947 if (component->hasLocalFeature)
1949 component->Action = INSTALLSTATE_LOCAL;
1950 component->ActionRequest = INSTALLSTATE_LOCAL;
1951 continue;
1953 if (component->hasSourceFeature)
1955 component->Action = INSTALLSTATE_SOURCE;
1956 component->ActionRequest = INSTALLSTATE_SOURCE;
1957 continue;
1959 if (component->hasAdvertisedFeature)
1961 component->Action = INSTALLSTATE_ADVERTISED;
1962 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1963 continue;
1965 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1966 if (component->anyAbsent && component->ComponentId)
1968 component->Action = INSTALLSTATE_ABSENT;
1969 component->ActionRequest = INSTALLSTATE_ABSENT;
1973 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1975 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1977 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1978 component->Action = INSTALLSTATE_LOCAL;
1979 component->ActionRequest = INSTALLSTATE_LOCAL;
1982 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1983 component->Installed == INSTALLSTATE_SOURCE &&
1984 component->hasSourceFeature)
1986 component->Action = INSTALLSTATE_UNKNOWN;
1987 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1990 TRACE("component %s (installed %d request %d action %d)\n",
1991 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1993 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1994 component->num_clients++;
1995 else if (component->Action == INSTALLSTATE_ABSENT)
1996 component->num_clients--;
1999 return ERROR_SUCCESS;
2002 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2004 MSIPACKAGE *package = param;
2005 LPCWSTR name;
2006 MSIFEATURE *feature;
2008 name = MSI_RecordGetString( row, 1 );
2010 feature = msi_get_loaded_feature( package, name );
2011 if (!feature)
2012 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2013 else
2015 LPCWSTR Condition;
2016 Condition = MSI_RecordGetString(row,3);
2018 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2020 int level = MSI_RecordGetInteger(row,2);
2021 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2022 feature->Level = level;
2025 return ERROR_SUCCESS;
2028 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2030 static const WCHAR name[] = {'\\',0};
2031 VS_FIXEDFILEINFO *ptr, *ret;
2032 LPVOID version;
2033 DWORD versize, handle;
2034 UINT sz;
2036 versize = GetFileVersionInfoSizeW( filename, &handle );
2037 if (!versize)
2038 return NULL;
2040 version = msi_alloc( versize );
2041 if (!version)
2042 return NULL;
2044 GetFileVersionInfoW( filename, 0, versize, version );
2046 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2048 msi_free( version );
2049 return NULL;
2052 ret = msi_alloc( sz );
2053 memcpy( ret, ptr, sz );
2055 msi_free( version );
2056 return ret;
2059 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2061 DWORD ms, ls;
2063 msi_parse_version_string( version, &ms, &ls );
2065 if (fi->dwFileVersionMS > ms) return 1;
2066 else if (fi->dwFileVersionMS < ms) return -1;
2067 else if (fi->dwFileVersionLS > ls) return 1;
2068 else if (fi->dwFileVersionLS < ls) return -1;
2069 return 0;
2072 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2074 DWORD ms1, ms2;
2076 msi_parse_version_string( ver1, &ms1, NULL );
2077 msi_parse_version_string( ver2, &ms2, NULL );
2079 if (ms1 > ms2) return 1;
2080 else if (ms1 < ms2) return -1;
2081 return 0;
2084 DWORD msi_get_disk_file_size( LPCWSTR filename )
2086 HANDLE file;
2087 DWORD size;
2089 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2090 if (file == INVALID_HANDLE_VALUE)
2091 return INVALID_FILE_SIZE;
2093 size = GetFileSize( file, NULL );
2094 CloseHandle( file );
2095 return size;
2098 BOOL msi_file_hash_matches( MSIFILE *file )
2100 UINT r;
2101 MSIFILEHASHINFO hash;
2103 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2104 r = msi_get_filehash( file->TargetPath, &hash );
2105 if (r != ERROR_SUCCESS)
2106 return FALSE;
2108 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2111 static WCHAR *create_temp_dir( MSIDATABASE *db )
2113 static UINT id;
2114 WCHAR *ret;
2116 if (!db->tempfolder)
2118 WCHAR tmp[MAX_PATH];
2119 UINT len = sizeof(tmp)/sizeof(tmp[0]);
2121 if (msi_get_property( db, szTempFolder, tmp, &len ) ||
2122 GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY)
2124 GetTempPathW( MAX_PATH, tmp );
2126 if (!(db->tempfolder = strdupW( tmp ))) return NULL;
2129 if ((ret = msi_alloc( (strlenW( db->tempfolder ) + 20) * sizeof(WCHAR) )))
2131 for (;;)
2133 if (!GetTempFileNameW( db->tempfolder, szMsi, ++id, ret ))
2135 msi_free( ret );
2136 return NULL;
2138 if (CreateDirectoryW( ret, NULL )) break;
2142 return ret;
2146 * msi_build_directory_name()
2148 * This function is to save messing round with directory names
2149 * It handles adding backslashes between path segments,
2150 * and can add \ at the end of the directory name if told to.
2152 * It takes a variable number of arguments.
2153 * It always allocates a new string for the result, so make sure
2154 * to free the return value when finished with it.
2156 * The first arg is the number of path segments that follow.
2157 * The arguments following count are a list of path segments.
2158 * A path segment may be NULL.
2160 * Path segments will be added with a \ separating them.
2161 * A \ will not be added after the last segment, however if the
2162 * last segment is NULL, then the last character will be a \
2164 WCHAR *msi_build_directory_name( DWORD count, ... )
2166 DWORD sz = 1, i;
2167 WCHAR *dir;
2168 va_list va;
2170 va_start( va, count );
2171 for (i = 0; i < count; i++)
2173 const WCHAR *str = va_arg( va, const WCHAR * );
2174 if (str) sz += strlenW( str ) + 1;
2176 va_end( va );
2178 dir = msi_alloc( sz * sizeof(WCHAR) );
2179 dir[0] = 0;
2181 va_start( va, count );
2182 for (i = 0; i < count; i++)
2184 const WCHAR *str = va_arg( va, const WCHAR * );
2185 if (!str) continue;
2186 strcatW( dir, str );
2187 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2189 va_end( va );
2190 return dir;
2193 BOOL msi_is_global_assembly( MSICOMPONENT *comp )
2195 return comp->assembly && !comp->assembly->application;
2198 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2200 msi_free( file->TargetPath );
2201 if (msi_is_global_assembly( file->Component ))
2203 MSIASSEMBLY *assembly = file->Component->assembly;
2205 if (!assembly->tempdir) assembly->tempdir = create_temp_dir( package->db );
2206 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2208 else
2210 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2211 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2214 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2217 static UINT calculate_file_cost( MSIPACKAGE *package )
2219 VS_FIXEDFILEINFO *file_version;
2220 WCHAR *font_version;
2221 MSIFILE *file;
2223 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2225 MSICOMPONENT *comp = file->Component;
2226 DWORD file_size;
2228 if (!comp->Enabled) continue;
2230 if (file->IsCompressed)
2231 comp->ForceLocalState = TRUE;
2233 set_target_path( package, file );
2235 if ((comp->assembly && !comp->assembly->installed) ||
2236 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2238 comp->Cost += file->FileSize;
2239 continue;
2241 file_size = msi_get_disk_file_size( file->TargetPath );
2242 TRACE("%s (size %u)\n", debugstr_w(file->TargetPath), file_size);
2244 if (file->Version)
2246 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2248 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2250 comp->Cost += file->FileSize - file_size;
2252 msi_free( file_version );
2253 continue;
2255 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2257 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2259 comp->Cost += file->FileSize - file_size;
2261 msi_free( font_version );
2262 continue;
2265 if (file_size != file->FileSize)
2267 comp->Cost += file->FileSize - file_size;
2270 return ERROR_SUCCESS;
2273 WCHAR *msi_normalize_path( const WCHAR *in )
2275 const WCHAR *p = in;
2276 WCHAR *q, *ret;
2277 int n, len = strlenW( in ) + 2;
2279 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2281 len = 0;
2282 while (1)
2284 /* copy until the end of the string or a space */
2285 while (*p != ' ' && (*q = *p))
2287 p++, len++;
2288 /* reduce many backslashes to one */
2289 if (*p != '\\' || *q != '\\')
2290 q++;
2293 /* quit at the end of the string */
2294 if (!*p)
2295 break;
2297 /* count the number of spaces */
2298 n = 0;
2299 while (p[n] == ' ')
2300 n++;
2302 /* if it's leading or trailing space, skip it */
2303 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2304 p += n;
2305 else /* copy n spaces */
2306 while (n && (*q++ = *p++)) n--;
2308 while (q - ret > 0 && q[-1] == ' ') q--;
2309 if (q - ret > 0 && q[-1] != '\\')
2311 q[0] = '\\';
2312 q[1] = 0;
2314 return ret;
2317 static WCHAR *get_install_location( MSIPACKAGE *package )
2319 HKEY hkey;
2320 WCHAR *path;
2322 if (!package->ProductCode) return NULL;
2323 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2324 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2326 msi_free( path );
2327 path = NULL;
2329 RegCloseKey( hkey );
2330 return path;
2333 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2335 FolderList *fl;
2336 MSIFOLDER *folder, *parent, *child;
2337 WCHAR *path, *normalized_path;
2339 TRACE("resolving %s\n", debugstr_w(name));
2341 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2343 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2345 if (!(path = get_install_location( package )) &&
2346 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2348 path = msi_dup_property( package->db, szRootDrive );
2351 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2353 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2355 parent = msi_get_loaded_folder( package, folder->Parent );
2356 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2358 else
2359 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2361 normalized_path = msi_normalize_path( path );
2362 msi_free( path );
2363 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2365 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2366 msi_free( normalized_path );
2367 return;
2369 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2370 msi_free( folder->ResolvedTarget );
2371 folder->ResolvedTarget = normalized_path;
2373 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2375 child = fl->folder;
2376 msi_resolve_target_folder( package, child->Directory, load_prop );
2378 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2381 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2383 static const WCHAR query[] =
2384 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2385 '`','C','o','n','d','i','t','i','o','n','`',0};
2386 static const WCHAR szOutOfDiskSpace[] =
2387 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2388 static const WCHAR szPrimaryFolder[] =
2389 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2390 static const WCHAR szPrimaryVolumePath[] =
2391 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2392 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2393 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2394 'A','v','a','i','l','a','b','l','e',0};
2395 static const WCHAR szOutOfNoRbDiskSpace[] =
2396 {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0};
2397 MSICOMPONENT *comp;
2398 MSIQUERY *view;
2399 WCHAR *level, *primary_key, *primary_folder;
2400 UINT rc;
2402 TRACE("Building directory properties\n");
2403 msi_resolve_target_folder( package, szTargetDir, TRUE );
2405 TRACE("Evaluating component conditions\n");
2406 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2408 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2410 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2411 comp->Enabled = FALSE;
2413 else
2414 comp->Enabled = TRUE;
2416 get_client_counts( package );
2418 /* read components states from the registry */
2419 ACTION_GetComponentInstallStates(package);
2420 ACTION_GetFeatureInstallStates(package);
2422 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2424 TRACE("Evaluating feature conditions\n");
2426 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2427 if (rc == ERROR_SUCCESS)
2429 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2430 msiobj_release( &view->hdr );
2431 if (rc != ERROR_SUCCESS)
2432 return rc;
2436 TRACE("Calculating file cost\n");
2437 calculate_file_cost( package );
2439 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2440 /* set default run level if not set */
2441 level = msi_dup_property( package->db, szInstallLevel );
2442 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2443 msi_free(level);
2445 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2447 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2449 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2450 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2452 ULARGE_INTEGER free;
2454 primary_folder[2] = 0;
2455 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2457 static const WCHAR fmtW[] = {'%','l','u',0};
2458 WCHAR buf[21];
2460 sprintfW( buf, fmtW, free.QuadPart / 512 );
2461 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2463 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2465 msi_free( primary_folder );
2467 msi_free( primary_key );
2470 /* FIXME: check volume disk space */
2471 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2472 msi_set_property( package->db, szOutOfNoRbDiskSpace, szZero, -1 );
2474 return MSI_SetFeatureStates(package);
2477 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size )
2479 BYTE *data;
2481 if (!value)
2483 *size = sizeof(WCHAR);
2484 *type = REG_SZ;
2485 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2486 return data;
2488 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2490 if (value[1]=='x')
2492 LPWSTR ptr;
2493 CHAR byte[5];
2494 LPWSTR deformated = NULL;
2495 int count;
2497 deformat_string(package, &value[2], &deformated);
2499 /* binary value type */
2500 ptr = deformated;
2501 *type = REG_BINARY;
2502 if (strlenW(ptr)%2)
2503 *size = (strlenW(ptr)/2)+1;
2504 else
2505 *size = strlenW(ptr)/2;
2507 data = msi_alloc(*size);
2509 byte[0] = '0';
2510 byte[1] = 'x';
2511 byte[4] = 0;
2512 count = 0;
2513 /* if uneven pad with a zero in front */
2514 if (strlenW(ptr)%2)
2516 byte[2]= '0';
2517 byte[3]= *ptr;
2518 ptr++;
2519 data[count] = (BYTE)strtol(byte,NULL,0);
2520 count ++;
2521 TRACE("Uneven byte count\n");
2523 while (*ptr)
2525 byte[2]= *ptr;
2526 ptr++;
2527 byte[3]= *ptr;
2528 ptr++;
2529 data[count] = (BYTE)strtol(byte,NULL,0);
2530 count ++;
2532 msi_free(deformated);
2534 TRACE("Data %i bytes(%i)\n",*size,count);
2536 else
2538 LPWSTR deformated;
2539 LPWSTR p;
2540 DWORD d = 0;
2541 deformat_string(package, &value[1], &deformated);
2543 *type=REG_DWORD;
2544 *size = sizeof(DWORD);
2545 data = msi_alloc(*size);
2546 p = deformated;
2547 if (*p == '-')
2548 p++;
2549 while (*p)
2551 if ( (*p < '0') || (*p > '9') )
2552 break;
2553 d *= 10;
2554 d += (*p - '0');
2555 p++;
2557 if (deformated[0] == '-')
2558 d = -d;
2559 *(LPDWORD)data = d;
2560 TRACE("DWORD %i\n",*(LPDWORD)data);
2562 msi_free(deformated);
2565 else
2567 const WCHAR *ptr = value;
2569 *type = REG_SZ;
2570 if (value[0] == '#')
2572 ptr++; len--;
2573 if (value[1] == '%')
2575 ptr++; len--;
2576 *type = REG_EXPAND_SZ;
2579 data = (BYTE *)msi_strdupW( ptr, len );
2580 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2581 *size = (len + 1) * sizeof(WCHAR);
2583 return data;
2586 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2588 const WCHAR *ret;
2590 switch (root)
2592 case -1:
2593 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2595 *root_key = HKEY_LOCAL_MACHINE;
2596 ret = szHLM;
2598 else
2600 *root_key = HKEY_CURRENT_USER;
2601 ret = szHCU;
2603 break;
2604 case 0:
2605 *root_key = HKEY_CLASSES_ROOT;
2606 ret = szHCR;
2607 break;
2608 case 1:
2609 *root_key = HKEY_CURRENT_USER;
2610 ret = szHCU;
2611 break;
2612 case 2:
2613 *root_key = HKEY_LOCAL_MACHINE;
2614 ret = szHLM;
2615 break;
2616 case 3:
2617 *root_key = HKEY_USERS;
2618 ret = szHU;
2619 break;
2620 default:
2621 ERR("Unknown root %i\n", root);
2622 return NULL;
2625 return ret;
2628 static inline REGSAM get_registry_view( const MSICOMPONENT *comp )
2630 REGSAM view = 0;
2631 if (is_wow64 || is_64bit)
2632 view |= (comp->Attributes & msidbComponentAttributes64bit) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
2633 return view;
2636 static HKEY open_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, BOOL create, REGSAM access )
2638 WCHAR *subkey, *p, *q;
2639 HKEY hkey, ret = NULL;
2640 LONG res;
2642 access |= get_registry_view( comp );
2644 if (!(subkey = strdupW( path ))) return NULL;
2645 p = subkey;
2646 if ((q = strchrW( p, '\\' ))) *q = 0;
2647 if (create)
2648 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2649 else
2650 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2651 if (res)
2653 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2654 msi_free( subkey );
2655 return NULL;
2657 if (q && q[1])
2659 ret = open_key( comp, hkey, q + 1, create, access );
2660 RegCloseKey( hkey );
2662 else ret = hkey;
2663 msi_free( subkey );
2664 return ret;
2667 static BOOL is_special_entry( const WCHAR *name )
2669 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2672 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2674 const WCHAR *p = str;
2675 WCHAR **ret;
2676 int i = 0;
2678 *count = 0;
2679 if (!str) return NULL;
2680 while ((p - str) < len)
2682 p += strlenW( p ) + 1;
2683 (*count)++;
2685 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2686 p = str;
2687 while ((p - str) < len)
2689 if (!(ret[i] = strdupW( p )))
2691 for (; i >= 0; i--) msi_free( ret[i] );
2692 msi_free( ret );
2693 return NULL;
2695 p += strlenW( p ) + 1;
2696 i++;
2698 return ret;
2701 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2702 WCHAR **right, DWORD right_count, DWORD *size )
2704 WCHAR *ret, *p;
2705 unsigned int i;
2707 *size = sizeof(WCHAR);
2708 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2709 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2711 if (!(ret = p = msi_alloc( *size ))) return NULL;
2713 for (i = 0; i < left_count; i++)
2715 strcpyW( p, left[i] );
2716 p += strlenW( p ) + 1;
2718 for (i = 0; i < right_count; i++)
2720 strcpyW( p, right[i] );
2721 p += strlenW( p ) + 1;
2723 *p = 0;
2724 return ret;
2727 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2728 WCHAR **new, DWORD new_count )
2730 DWORD ret = old_count;
2731 unsigned int i, j, k;
2733 for (i = 0; i < new_count; i++)
2735 for (j = 0; j < old_count; j++)
2737 if (old[j] && !strcmpW( new[i], old[j] ))
2739 msi_free( old[j] );
2740 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2741 old[k] = NULL;
2742 ret--;
2746 return ret;
2749 enum join_op
2751 JOIN_OP_APPEND,
2752 JOIN_OP_PREPEND,
2753 JOIN_OP_REPLACE
2756 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2757 WCHAR **new, DWORD new_count, DWORD *size )
2759 switch (op)
2761 case JOIN_OP_APPEND:
2762 old_count = remove_duplicate_values( old, old_count, new, new_count );
2763 return flatten_multi_string_values( old, old_count, new, new_count, size );
2765 case JOIN_OP_PREPEND:
2766 old_count = remove_duplicate_values( old, old_count, new, new_count );
2767 return flatten_multi_string_values( new, new_count, old, old_count, size );
2769 case JOIN_OP_REPLACE:
2770 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2772 default:
2773 ERR("unhandled join op %u\n", op);
2774 return NULL;
2778 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2779 BYTE *new_value, DWORD new_size, DWORD *size )
2781 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2782 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2783 enum join_op op = JOIN_OP_REPLACE;
2784 WCHAR **old = NULL, **new = NULL;
2785 BYTE *ret;
2787 if (new_size / sizeof(WCHAR) - 1 > 1)
2789 new_ptr = (const WCHAR *)new_value;
2790 new_len = new_size / sizeof(WCHAR) - 1;
2792 if (!new_ptr[0] && new_ptr[new_len - 1])
2794 op = JOIN_OP_APPEND;
2795 new_len--;
2796 new_ptr++;
2798 else if (new_ptr[0] && !new_ptr[new_len - 1])
2800 op = JOIN_OP_PREPEND;
2801 new_len--;
2803 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2805 op = JOIN_OP_REPLACE;
2806 new_len -= 2;
2807 new_ptr++;
2809 new = split_multi_string_values( new_ptr, new_len, &new_count );
2811 if (old_size / sizeof(WCHAR) - 1 > 1)
2813 old_ptr = (const WCHAR *)old_value;
2814 old_len = old_size / sizeof(WCHAR) - 1;
2815 old = split_multi_string_values( old_ptr, old_len, &old_count );
2817 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2818 for (i = 0; i < old_count; i++) msi_free( old[i] );
2819 for (i = 0; i < new_count; i++) msi_free( new[i] );
2820 msi_free( old );
2821 msi_free( new );
2822 return ret;
2825 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2827 BYTE *ret;
2828 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2829 if (!(ret = msi_alloc( *size ))) return NULL;
2830 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2831 return ret;
2834 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2836 MSIPACKAGE *package = param;
2837 BYTE *new_value, *old_value = NULL;
2838 HKEY root_key, hkey;
2839 DWORD type, old_type, new_size, old_size = 0;
2840 LPWSTR deformated, uikey;
2841 const WCHAR *szRoot, *component, *name, *key, *str;
2842 MSICOMPONENT *comp;
2843 MSIRECORD * uirow;
2844 INT root;
2845 BOOL check_first = FALSE;
2846 int len;
2848 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2850 component = MSI_RecordGetString(row, 6);
2851 comp = msi_get_loaded_component(package,component);
2852 if (!comp)
2853 return ERROR_SUCCESS;
2855 comp->Action = msi_get_component_action( package, comp );
2856 if (comp->Action != INSTALLSTATE_LOCAL)
2858 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2859 return ERROR_SUCCESS;
2862 name = MSI_RecordGetString(row, 4);
2863 if( MSI_RecordIsNull(row,5) && name )
2865 /* null values can have special meanings */
2866 if (name[0]=='-' && name[1] == 0)
2867 return ERROR_SUCCESS;
2868 if ((name[0] == '+' || name[0] == '*') && !name[1])
2869 check_first = TRUE;
2872 root = MSI_RecordGetInteger(row,2);
2873 key = MSI_RecordGetString(row, 3);
2875 szRoot = get_root_key( package, root, &root_key );
2876 if (!szRoot)
2877 return ERROR_SUCCESS;
2879 deformat_string(package, key , &deformated);
2880 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2881 strcpyW(uikey,szRoot);
2882 strcatW(uikey,deformated);
2884 if (!(hkey = open_key( comp, root_key, deformated, TRUE, KEY_QUERY_VALUE | KEY_SET_VALUE )))
2886 ERR("Could not create key %s\n", debugstr_w(deformated));
2887 msi_free(uikey);
2888 msi_free(deformated);
2889 return ERROR_FUNCTION_FAILED;
2891 msi_free( deformated );
2892 str = msi_record_get_string( row, 5, NULL );
2893 len = deformat_string( package, str, &deformated );
2894 new_value = parse_value( package, deformated, len, &type, &new_size );
2896 msi_free( deformated );
2897 deformat_string(package, name, &deformated);
2899 if (!is_special_entry( name ))
2901 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2902 if (type == REG_MULTI_SZ)
2904 BYTE *new;
2905 if (old_value && old_type != REG_MULTI_SZ)
2907 msi_free( old_value );
2908 old_value = NULL;
2909 old_size = 0;
2911 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2912 msi_free( new_value );
2913 new_value = new;
2915 if (!check_first)
2917 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2918 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2920 else if (!old_value)
2922 if (deformated || new_size)
2924 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2925 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2928 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2930 RegCloseKey(hkey);
2932 uirow = MSI_CreateRecord(3);
2933 MSI_RecordSetStringW(uirow,2,deformated);
2934 MSI_RecordSetStringW(uirow,1,uikey);
2935 if (type == REG_SZ || type == REG_EXPAND_SZ)
2936 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2937 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2938 msiobj_release( &uirow->hdr );
2940 msi_free(new_value);
2941 msi_free(old_value);
2942 msi_free(deformated);
2943 msi_free(uikey);
2945 return ERROR_SUCCESS;
2948 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2950 static const WCHAR query[] = {
2951 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2952 '`','R','e','g','i','s','t','r','y','`',0};
2953 MSIQUERY *view;
2954 UINT rc;
2956 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2957 if (rc != ERROR_SUCCESS)
2958 return ERROR_SUCCESS;
2960 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2961 msiobj_release(&view->hdr);
2962 return rc;
2965 static void delete_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2967 REGSAM access = 0;
2968 WCHAR *subkey, *p;
2969 HKEY hkey;
2970 LONG res;
2972 access |= get_registry_view( comp );
2974 if (!(subkey = strdupW( path ))) return;
2977 if ((p = strrchrW( subkey, '\\' )))
2979 *p = 0;
2980 if (!p[1]) continue; /* trailing backslash */
2981 hkey = open_key( comp, root, subkey, FALSE, access );
2982 if (!hkey) break;
2983 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2984 RegCloseKey( hkey );
2986 else
2987 res = RegDeleteKeyExW( root, subkey, access, 0 );
2988 if (res)
2990 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
2991 break;
2993 } while (p);
2994 msi_free( subkey );
2997 static void delete_value( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, const WCHAR *value )
2999 LONG res;
3000 HKEY hkey;
3001 DWORD num_subkeys, num_values;
3003 if ((hkey = open_key( comp, root, path, FALSE, KEY_SET_VALUE | KEY_QUERY_VALUE )))
3005 if ((res = RegDeleteValueW( hkey, value )))
3006 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
3008 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
3009 NULL, NULL, NULL, NULL );
3010 RegCloseKey( hkey );
3011 if (!res && !num_subkeys && !num_values)
3013 TRACE("removing empty key %s\n", debugstr_w(path));
3014 delete_key( comp, root, path );
3019 static void delete_tree( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
3021 LONG res;
3022 HKEY hkey;
3024 if (!(hkey = open_key( comp, root, path, FALSE, KEY_ALL_ACCESS ))) return;
3025 res = RegDeleteTreeW( hkey, NULL );
3026 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3027 delete_key( comp, root, path );
3028 RegCloseKey( hkey );
3031 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3033 MSIPACKAGE *package = param;
3034 LPCWSTR component, name, key_str, root_key_str;
3035 LPWSTR deformated_key, deformated_name, ui_key_str;
3036 MSICOMPONENT *comp;
3037 MSIRECORD *uirow;
3038 BOOL delete_key = FALSE;
3039 HKEY hkey_root;
3040 UINT size;
3041 INT root;
3043 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3045 component = MSI_RecordGetString( row, 6 );
3046 comp = msi_get_loaded_component( package, component );
3047 if (!comp)
3048 return ERROR_SUCCESS;
3050 comp->Action = msi_get_component_action( package, comp );
3051 if (comp->Action != INSTALLSTATE_ABSENT)
3053 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3054 return ERROR_SUCCESS;
3057 name = MSI_RecordGetString( row, 4 );
3058 if (MSI_RecordIsNull( row, 5 ) && name )
3060 if (name[0] == '+' && !name[1])
3061 return ERROR_SUCCESS;
3062 if ((name[0] == '-' || name[0] == '*') && !name[1])
3064 delete_key = TRUE;
3065 name = NULL;
3069 root = MSI_RecordGetInteger( row, 2 );
3070 key_str = MSI_RecordGetString( row, 3 );
3072 root_key_str = get_root_key( package, root, &hkey_root );
3073 if (!root_key_str)
3074 return ERROR_SUCCESS;
3076 deformat_string( package, key_str, &deformated_key );
3077 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3078 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3079 strcpyW( ui_key_str, root_key_str );
3080 strcatW( ui_key_str, deformated_key );
3082 deformat_string( package, name, &deformated_name );
3084 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3085 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3086 msi_free( deformated_key );
3088 uirow = MSI_CreateRecord( 2 );
3089 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3090 MSI_RecordSetStringW( uirow, 2, deformated_name );
3091 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3092 msiobj_release( &uirow->hdr );
3094 msi_free( ui_key_str );
3095 msi_free( deformated_name );
3096 return ERROR_SUCCESS;
3099 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3101 MSIPACKAGE *package = param;
3102 LPCWSTR component, name, key_str, root_key_str;
3103 LPWSTR deformated_key, deformated_name, ui_key_str;
3104 MSICOMPONENT *comp;
3105 MSIRECORD *uirow;
3106 BOOL delete_key = FALSE;
3107 HKEY hkey_root;
3108 UINT size;
3109 INT root;
3111 component = MSI_RecordGetString( row, 5 );
3112 comp = msi_get_loaded_component( package, component );
3113 if (!comp)
3114 return ERROR_SUCCESS;
3116 comp->Action = msi_get_component_action( package, comp );
3117 if (comp->Action != INSTALLSTATE_LOCAL)
3119 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3120 return ERROR_SUCCESS;
3123 if ((name = MSI_RecordGetString( row, 4 )))
3125 if (name[0] == '-' && !name[1])
3127 delete_key = TRUE;
3128 name = NULL;
3132 root = MSI_RecordGetInteger( row, 2 );
3133 key_str = MSI_RecordGetString( row, 3 );
3135 root_key_str = get_root_key( package, root, &hkey_root );
3136 if (!root_key_str)
3137 return ERROR_SUCCESS;
3139 deformat_string( package, key_str, &deformated_key );
3140 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3141 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3142 strcpyW( ui_key_str, root_key_str );
3143 strcatW( ui_key_str, deformated_key );
3145 deformat_string( package, name, &deformated_name );
3147 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3148 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3149 msi_free( deformated_key );
3151 uirow = MSI_CreateRecord( 2 );
3152 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3153 MSI_RecordSetStringW( uirow, 2, deformated_name );
3154 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3155 msiobj_release( &uirow->hdr );
3157 msi_free( ui_key_str );
3158 msi_free( deformated_name );
3159 return ERROR_SUCCESS;
3162 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3164 static const WCHAR registry_query[] = {
3165 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3166 '`','R','e','g','i','s','t','r','y','`',0};
3167 static const WCHAR remove_registry_query[] = {
3168 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3169 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3170 MSIQUERY *view;
3171 UINT rc;
3173 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3174 if (rc == ERROR_SUCCESS)
3176 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3177 msiobj_release( &view->hdr );
3178 if (rc != ERROR_SUCCESS)
3179 return rc;
3181 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3182 if (rc == ERROR_SUCCESS)
3184 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3185 msiobj_release( &view->hdr );
3186 if (rc != ERROR_SUCCESS)
3187 return rc;
3189 return ERROR_SUCCESS;
3192 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3194 return ERROR_SUCCESS;
3198 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3200 static const WCHAR query[]= {
3201 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3202 '`','R','e','g','i','s','t','r','y','`',0};
3203 MSICOMPONENT *comp;
3204 DWORD total = 0, count = 0;
3205 MSIQUERY *view;
3206 MSIFEATURE *feature;
3207 MSIFILE *file;
3208 UINT rc;
3210 TRACE("InstallValidate\n");
3212 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3213 if (rc == ERROR_SUCCESS)
3215 rc = MSI_IterateRecords( view, &count, NULL, package );
3216 msiobj_release( &view->hdr );
3217 if (rc != ERROR_SUCCESS)
3218 return rc;
3219 total += count * REG_PROGRESS_VALUE;
3221 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3222 total += COMPONENT_PROGRESS_VALUE;
3224 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3225 total += file->FileSize;
3227 msi_ui_progress( package, 0, total, 0, 0 );
3229 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3231 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3232 debugstr_w(feature->Feature), feature->Installed,
3233 feature->ActionRequest, feature->Action);
3235 return ERROR_SUCCESS;
3238 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3240 MSIPACKAGE* package = param;
3241 LPCWSTR cond = NULL;
3242 LPCWSTR message = NULL;
3243 UINT r;
3245 static const WCHAR title[]=
3246 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3248 cond = MSI_RecordGetString(row,1);
3250 r = MSI_EvaluateConditionW(package,cond);
3251 if (r == MSICONDITION_FALSE)
3253 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3255 LPWSTR deformated;
3256 message = MSI_RecordGetString(row,2);
3257 deformat_string(package,message,&deformated);
3258 MessageBoxW(NULL,deformated,title,MB_OK);
3259 msi_free(deformated);
3262 return ERROR_INSTALL_FAILURE;
3265 return ERROR_SUCCESS;
3268 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3270 static const WCHAR query[] = {
3271 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3272 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3273 MSIQUERY *view;
3274 UINT rc;
3276 TRACE("Checking launch conditions\n");
3278 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3279 if (rc != ERROR_SUCCESS)
3280 return ERROR_SUCCESS;
3282 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3283 msiobj_release(&view->hdr);
3284 return rc;
3287 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3290 if (!cmp->KeyPath)
3291 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3293 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3295 static const WCHAR query[] = {
3296 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3297 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3298 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3299 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3300 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3301 MSIRECORD *row;
3302 UINT root, len;
3303 LPWSTR deformated, buffer, deformated_name;
3304 LPCWSTR key, name;
3306 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3307 if (!row)
3308 return NULL;
3310 root = MSI_RecordGetInteger(row,2);
3311 key = MSI_RecordGetString(row, 3);
3312 name = MSI_RecordGetString(row, 4);
3313 deformat_string(package, key , &deformated);
3314 deformat_string(package, name, &deformated_name);
3316 len = strlenW(deformated) + 6;
3317 if (deformated_name)
3318 len+=strlenW(deformated_name);
3320 buffer = msi_alloc( len *sizeof(WCHAR));
3322 if (deformated_name)
3323 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3324 else
3325 sprintfW(buffer,fmt,root,deformated);
3327 msi_free(deformated);
3328 msi_free(deformated_name);
3329 msiobj_release(&row->hdr);
3331 return buffer;
3333 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3335 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3336 return NULL;
3338 else
3340 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3342 if (file)
3343 return strdupW( file->TargetPath );
3345 return NULL;
3348 static HKEY openSharedDLLsKey(void)
3350 HKEY hkey=0;
3351 static const WCHAR path[] =
3352 {'S','o','f','t','w','a','r','e','\\',
3353 'M','i','c','r','o','s','o','f','t','\\',
3354 'W','i','n','d','o','w','s','\\',
3355 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3356 'S','h','a','r','e','d','D','L','L','s',0};
3358 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3359 return hkey;
3362 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3364 HKEY hkey;
3365 DWORD count=0;
3366 DWORD type;
3367 DWORD sz = sizeof(count);
3368 DWORD rc;
3370 hkey = openSharedDLLsKey();
3371 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3372 if (rc != ERROR_SUCCESS)
3373 count = 0;
3374 RegCloseKey(hkey);
3375 return count;
3378 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3380 HKEY hkey;
3382 hkey = openSharedDLLsKey();
3383 if (count > 0)
3384 msi_reg_set_val_dword( hkey, path, count );
3385 else
3386 RegDeleteValueW(hkey,path);
3387 RegCloseKey(hkey);
3388 return count;
3391 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3393 MSIFEATURE *feature;
3394 INT count = 0;
3395 BOOL write = FALSE;
3397 /* only refcount DLLs */
3398 if (comp->KeyPath == NULL ||
3399 comp->assembly ||
3400 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3401 comp->Attributes & msidbComponentAttributesODBCDataSource)
3402 write = FALSE;
3403 else
3405 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3406 write = (count > 0);
3408 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3409 write = TRUE;
3412 /* increment counts */
3413 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3415 ComponentList *cl;
3417 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3418 continue;
3420 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3422 if ( cl->component == comp )
3423 count++;
3427 /* decrement counts */
3428 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3430 ComponentList *cl;
3432 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3433 continue;
3435 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3437 if ( cl->component == comp )
3438 count--;
3442 /* ref count all the files in the component */
3443 if (write)
3445 MSIFILE *file;
3447 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3449 if (file->Component == comp)
3450 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3454 /* add a count for permanent */
3455 if (comp->Attributes & msidbComponentAttributesPermanent)
3456 count ++;
3458 comp->RefCount = count;
3460 if (write)
3461 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3464 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3466 if (comp->assembly)
3468 const WCHAR prefixW[] = {'<','\\',0};
3469 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3470 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3472 if (keypath)
3474 strcpyW( keypath, prefixW );
3475 strcatW( keypath, comp->assembly->display_name );
3477 return keypath;
3479 return resolve_keypath( package, comp );
3482 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3484 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3485 UINT rc;
3486 MSICOMPONENT *comp;
3487 HKEY hkey;
3489 TRACE("\n");
3491 squash_guid(package->ProductCode,squished_pc);
3492 msi_set_sourcedir_props(package, FALSE);
3494 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3496 MSIRECORD *uirow;
3497 INSTALLSTATE action;
3499 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3500 if (!comp->ComponentId)
3501 continue;
3503 squash_guid( comp->ComponentId, squished_cc );
3504 msi_free( comp->FullKeypath );
3505 comp->FullKeypath = build_full_keypath( package, comp );
3507 ACTION_RefCountComponent( package, comp );
3509 if (package->need_rollback) action = comp->Installed;
3510 else action = comp->ActionRequest;
3512 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3513 debugstr_w(comp->Component), debugstr_w(squished_cc),
3514 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3516 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3518 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3519 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3520 else
3521 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3523 if (rc != ERROR_SUCCESS)
3524 continue;
3526 if (comp->Attributes & msidbComponentAttributesPermanent)
3528 static const WCHAR szPermKey[] =
3529 { '0','0','0','0','0','0','0','0','0','0','0','0',
3530 '0','0','0','0','0','0','0','0','0','0','0','0',
3531 '0','0','0','0','0','0','0','0',0 };
3533 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3535 if (action == INSTALLSTATE_LOCAL)
3536 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3537 else
3539 MSIFILE *file;
3540 MSIRECORD *row;
3541 LPWSTR ptr, ptr2;
3542 WCHAR source[MAX_PATH];
3543 WCHAR base[MAX_PATH];
3544 LPWSTR sourcepath;
3546 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3547 static const WCHAR query[] = {
3548 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3549 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3550 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3551 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3552 '`','D','i','s','k','I','d','`',0};
3554 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3555 continue;
3557 if (!(row = MSI_QueryGetRecord(package->db, query, file->Sequence)))
3558 return ERROR_FUNCTION_FAILED;
3560 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3561 ptr2 = strrchrW(source, '\\') + 1;
3562 msiobj_release(&row->hdr);
3564 lstrcpyW(base, package->PackagePath);
3565 ptr = strrchrW(base, '\\');
3566 *(ptr + 1) = '\0';
3568 sourcepath = msi_resolve_file_source(package, file);
3569 ptr = sourcepath + lstrlenW(base);
3570 lstrcpyW(ptr2, ptr);
3571 msi_free(sourcepath);
3573 msi_reg_set_val_str(hkey, squished_pc, source);
3575 RegCloseKey(hkey);
3577 else if (action == INSTALLSTATE_ABSENT)
3579 if (comp->num_clients <= 0)
3581 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3582 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3583 else
3584 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3586 if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
3588 else
3590 LONG res;
3592 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3593 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE );
3594 else
3595 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );
3597 if (rc != ERROR_SUCCESS)
3599 WARN( "failed to open component key %u\n", rc );
3600 continue;
3602 res = RegDeleteValueW( hkey, squished_pc );
3603 RegCloseKey(hkey);
3604 if (res) WARN( "failed to delete component value %d\n", res );
3608 /* UI stuff */
3609 uirow = MSI_CreateRecord(3);
3610 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3611 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3612 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3613 msi_ui_actiondata( package, szProcessComponents, uirow );
3614 msiobj_release( &uirow->hdr );
3616 return ERROR_SUCCESS;
3619 typedef struct {
3620 CLSID clsid;
3621 LPWSTR source;
3623 LPWSTR path;
3624 ITypeLib *ptLib;
3625 } typelib_struct;
3627 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3628 LPWSTR lpszName, LONG_PTR lParam)
3630 TLIBATTR *attr;
3631 typelib_struct *tl_struct = (typelib_struct*) lParam;
3632 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3633 int sz;
3634 HRESULT res;
3636 if (!IS_INTRESOURCE(lpszName))
3638 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3639 return TRUE;
3642 sz = strlenW(tl_struct->source)+4;
3643 sz *= sizeof(WCHAR);
3645 if ((INT_PTR)lpszName == 1)
3646 tl_struct->path = strdupW(tl_struct->source);
3647 else
3649 tl_struct->path = msi_alloc(sz);
3650 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3653 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3654 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3655 if (FAILED(res))
3657 msi_free(tl_struct->path);
3658 tl_struct->path = NULL;
3660 return TRUE;
3663 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3664 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3666 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3667 return FALSE;
3670 msi_free(tl_struct->path);
3671 tl_struct->path = NULL;
3673 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3674 ITypeLib_Release(tl_struct->ptLib);
3676 return TRUE;
3679 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3681 MSIPACKAGE* package = param;
3682 LPCWSTR component;
3683 MSICOMPONENT *comp;
3684 MSIFILE *file;
3685 typelib_struct tl_struct;
3686 ITypeLib *tlib;
3687 HMODULE module;
3688 HRESULT hr;
3690 component = MSI_RecordGetString(row,3);
3691 comp = msi_get_loaded_component(package,component);
3692 if (!comp)
3693 return ERROR_SUCCESS;
3695 comp->Action = msi_get_component_action( package, comp );
3696 if (comp->Action != INSTALLSTATE_LOCAL)
3698 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3699 return ERROR_SUCCESS;
3702 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3704 TRACE("component has no key path\n");
3705 return ERROR_SUCCESS;
3707 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3709 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3710 if (module)
3712 LPCWSTR guid;
3713 guid = MSI_RecordGetString(row,1);
3714 CLSIDFromString( guid, &tl_struct.clsid);
3715 tl_struct.source = strdupW( file->TargetPath );
3716 tl_struct.path = NULL;
3718 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3719 (LONG_PTR)&tl_struct);
3721 if (tl_struct.path)
3723 LPCWSTR helpid, help_path = NULL;
3724 HRESULT res;
3726 helpid = MSI_RecordGetString(row,6);
3728 if (helpid) help_path = msi_get_target_folder( package, helpid );
3729 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3731 if (FAILED(res))
3732 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3733 else
3734 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3736 ITypeLib_Release(tl_struct.ptLib);
3737 msi_free(tl_struct.path);
3739 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3741 FreeLibrary(module);
3742 msi_free(tl_struct.source);
3744 else
3746 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3747 if (FAILED(hr))
3749 ERR("Failed to load type library: %08x\n", hr);
3750 return ERROR_INSTALL_FAILURE;
3753 ITypeLib_Release(tlib);
3756 return ERROR_SUCCESS;
3759 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3761 static const WCHAR query[] = {
3762 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3763 '`','T','y','p','e','L','i','b','`',0};
3764 MSIQUERY *view;
3765 UINT rc;
3767 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3768 if (rc != ERROR_SUCCESS)
3769 return ERROR_SUCCESS;
3771 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3772 msiobj_release(&view->hdr);
3773 return rc;
3776 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3778 MSIPACKAGE *package = param;
3779 LPCWSTR component, guid;
3780 MSICOMPONENT *comp;
3781 GUID libid;
3782 UINT version;
3783 LCID language;
3784 SYSKIND syskind;
3785 HRESULT hr;
3787 component = MSI_RecordGetString( row, 3 );
3788 comp = msi_get_loaded_component( package, component );
3789 if (!comp)
3790 return ERROR_SUCCESS;
3792 comp->Action = msi_get_component_action( package, comp );
3793 if (comp->Action != INSTALLSTATE_ABSENT)
3795 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3796 return ERROR_SUCCESS;
3798 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3800 guid = MSI_RecordGetString( row, 1 );
3801 CLSIDFromString( guid, &libid );
3802 version = MSI_RecordGetInteger( row, 4 );
3803 language = MSI_RecordGetInteger( row, 2 );
3805 #ifdef _WIN64
3806 syskind = SYS_WIN64;
3807 #else
3808 syskind = SYS_WIN32;
3809 #endif
3811 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3812 if (FAILED(hr))
3814 WARN("Failed to unregister typelib: %08x\n", hr);
3817 return ERROR_SUCCESS;
3820 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3822 static const WCHAR query[] = {
3823 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3824 '`','T','y','p','e','L','i','b','`',0};
3825 MSIQUERY *view;
3826 UINT rc;
3828 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3829 if (rc != ERROR_SUCCESS)
3830 return ERROR_SUCCESS;
3832 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3833 msiobj_release( &view->hdr );
3834 return rc;
3837 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3839 static const WCHAR szlnk[] = {'.','l','n','k',0};
3840 LPCWSTR directory, extension, link_folder;
3841 LPWSTR link_file, filename;
3843 directory = MSI_RecordGetString( row, 2 );
3844 link_folder = msi_get_target_folder( package, directory );
3845 if (!link_folder)
3847 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3848 return NULL;
3850 /* may be needed because of a bug somewhere else */
3851 msi_create_full_path( link_folder );
3853 filename = msi_dup_record_field( row, 3 );
3854 msi_reduce_to_long_filename( filename );
3856 extension = strrchrW( filename, '.' );
3857 if (!extension || strcmpiW( extension, szlnk ))
3859 int len = strlenW( filename );
3860 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3861 memcpy( filename + len, szlnk, sizeof(szlnk) );
3863 link_file = msi_build_directory_name( 2, link_folder, filename );
3864 msi_free( filename );
3866 return link_file;
3869 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3871 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3872 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3873 WCHAR *folder, *dest, *path;
3875 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3876 folder = msi_dup_property( package->db, szWindowsFolder );
3877 else
3879 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3880 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3881 msi_free( appdata );
3883 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3884 msi_create_full_path( dest );
3885 path = msi_build_directory_name( 2, dest, icon_name );
3886 msi_free( folder );
3887 msi_free( dest );
3888 return path;
3891 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3893 MSIPACKAGE *package = param;
3894 LPWSTR link_file, deformated, path;
3895 LPCWSTR component, target;
3896 MSICOMPONENT *comp;
3897 IShellLinkW *sl = NULL;
3898 IPersistFile *pf = NULL;
3899 HRESULT res;
3901 component = MSI_RecordGetString(row, 4);
3902 comp = msi_get_loaded_component(package, component);
3903 if (!comp)
3904 return ERROR_SUCCESS;
3906 comp->Action = msi_get_component_action( package, comp );
3907 if (comp->Action != INSTALLSTATE_LOCAL)
3909 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3910 return ERROR_SUCCESS;
3912 msi_ui_actiondata( package, szCreateShortcuts, row );
3914 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3915 &IID_IShellLinkW, (LPVOID *) &sl );
3917 if (FAILED( res ))
3919 ERR("CLSID_ShellLink not available\n");
3920 goto err;
3923 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3924 if (FAILED( res ))
3926 ERR("QueryInterface(IID_IPersistFile) failed\n");
3927 goto err;
3930 target = MSI_RecordGetString(row, 5);
3931 if (strchrW(target, '['))
3933 deformat_string( package, target, &path );
3934 TRACE("target path is %s\n", debugstr_w(path));
3935 IShellLinkW_SetPath( sl, path );
3936 msi_free( path );
3938 else
3940 FIXME("poorly handled shortcut format, advertised shortcut\n");
3941 path = resolve_keypath( package, comp );
3942 IShellLinkW_SetPath( sl, path );
3943 msi_free( path );
3946 if (!MSI_RecordIsNull(row,6))
3948 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3949 deformat_string(package, arguments, &deformated);
3950 IShellLinkW_SetArguments(sl,deformated);
3951 msi_free(deformated);
3954 if (!MSI_RecordIsNull(row,7))
3956 LPCWSTR description = MSI_RecordGetString(row, 7);
3957 IShellLinkW_SetDescription(sl, description);
3960 if (!MSI_RecordIsNull(row,8))
3961 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3963 if (!MSI_RecordIsNull(row,9))
3965 INT index;
3966 LPCWSTR icon = MSI_RecordGetString(row, 9);
3968 path = msi_build_icon_path(package, icon);
3969 index = MSI_RecordGetInteger(row,10);
3971 /* no value means 0 */
3972 if (index == MSI_NULL_INTEGER)
3973 index = 0;
3975 IShellLinkW_SetIconLocation(sl, path, index);
3976 msi_free(path);
3979 if (!MSI_RecordIsNull(row,11))
3980 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3982 if (!MSI_RecordIsNull(row,12))
3984 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3985 full_path = msi_get_target_folder( package, wkdir );
3986 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3988 link_file = get_link_file(package, row);
3990 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3991 IPersistFile_Save(pf, link_file, FALSE);
3992 msi_free(link_file);
3994 err:
3995 if (pf)
3996 IPersistFile_Release( pf );
3997 if (sl)
3998 IShellLinkW_Release( sl );
4000 return ERROR_SUCCESS;
4003 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
4005 static const WCHAR query[] = {
4006 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4007 '`','S','h','o','r','t','c','u','t','`',0};
4008 MSIQUERY *view;
4009 HRESULT res;
4010 UINT rc;
4012 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4013 if (rc != ERROR_SUCCESS)
4014 return ERROR_SUCCESS;
4016 res = CoInitialize( NULL );
4018 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4019 msiobj_release(&view->hdr);
4021 if (SUCCEEDED(res)) CoUninitialize();
4022 return rc;
4025 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4027 MSIPACKAGE *package = param;
4028 LPWSTR link_file;
4029 LPCWSTR component;
4030 MSICOMPONENT *comp;
4032 component = MSI_RecordGetString( row, 4 );
4033 comp = msi_get_loaded_component( package, component );
4034 if (!comp)
4035 return ERROR_SUCCESS;
4037 comp->Action = msi_get_component_action( package, comp );
4038 if (comp->Action != INSTALLSTATE_ABSENT)
4040 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4041 return ERROR_SUCCESS;
4043 msi_ui_actiondata( package, szRemoveShortcuts, row );
4045 link_file = get_link_file( package, row );
4047 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4048 if (!DeleteFileW( link_file ))
4050 WARN("Failed to remove shortcut file %u\n", GetLastError());
4052 msi_free( link_file );
4054 return ERROR_SUCCESS;
4057 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4059 static const WCHAR query[] = {
4060 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4061 '`','S','h','o','r','t','c','u','t','`',0};
4062 MSIQUERY *view;
4063 UINT rc;
4065 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4066 if (rc != ERROR_SUCCESS)
4067 return ERROR_SUCCESS;
4069 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4070 msiobj_release( &view->hdr );
4071 return rc;
4074 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4076 MSIPACKAGE* package = param;
4077 HANDLE the_file;
4078 LPWSTR FilePath;
4079 LPCWSTR FileName;
4080 CHAR buffer[1024];
4081 DWORD sz;
4082 UINT rc;
4084 FileName = MSI_RecordGetString(row,1);
4085 if (!FileName)
4087 ERR("Unable to get FileName\n");
4088 return ERROR_SUCCESS;
4091 FilePath = msi_build_icon_path(package, FileName);
4093 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4095 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4096 FILE_ATTRIBUTE_NORMAL, NULL);
4098 if (the_file == INVALID_HANDLE_VALUE)
4100 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4101 msi_free(FilePath);
4102 return ERROR_SUCCESS;
4107 DWORD write;
4108 sz = 1024;
4109 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4110 if (rc != ERROR_SUCCESS)
4112 ERR("Failed to get stream\n");
4113 CloseHandle(the_file);
4114 DeleteFileW(FilePath);
4115 break;
4117 WriteFile(the_file,buffer,sz,&write,NULL);
4118 } while (sz == 1024);
4120 msi_free(FilePath);
4121 CloseHandle(the_file);
4123 return ERROR_SUCCESS;
4126 static UINT msi_publish_icons(MSIPACKAGE *package)
4128 static const WCHAR query[]= {
4129 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4130 '`','I','c','o','n','`',0};
4131 MSIQUERY *view;
4132 UINT r;
4134 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4135 if (r == ERROR_SUCCESS)
4137 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4138 msiobj_release(&view->hdr);
4139 if (r != ERROR_SUCCESS)
4140 return r;
4142 return ERROR_SUCCESS;
4145 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4147 UINT r;
4148 HKEY source;
4149 LPWSTR buffer;
4150 MSIMEDIADISK *disk;
4151 MSISOURCELISTINFO *info;
4153 r = RegCreateKeyW(hkey, szSourceList, &source);
4154 if (r != ERROR_SUCCESS)
4155 return r;
4157 RegCloseKey(source);
4159 buffer = strrchrW(package->PackagePath, '\\') + 1;
4160 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4161 package->Context, MSICODE_PRODUCT,
4162 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4163 if (r != ERROR_SUCCESS)
4164 return r;
4166 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4167 package->Context, MSICODE_PRODUCT,
4168 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4169 if (r != ERROR_SUCCESS)
4170 return r;
4172 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4173 package->Context, MSICODE_PRODUCT,
4174 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4175 if (r != ERROR_SUCCESS)
4176 return r;
4178 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4180 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4181 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4182 info->options, info->value);
4183 else
4184 MsiSourceListSetInfoW(package->ProductCode, NULL,
4185 info->context, info->options,
4186 info->property, info->value);
4189 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4191 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4192 disk->context, disk->options,
4193 disk->disk_id, disk->volume_label, disk->disk_prompt);
4196 return ERROR_SUCCESS;
4199 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4201 MSIHANDLE hdb, suminfo;
4202 WCHAR guids[MAX_PATH];
4203 WCHAR packcode[SQUISH_GUID_SIZE];
4204 LPWSTR buffer;
4205 LPWSTR ptr;
4206 DWORD langid;
4207 DWORD size;
4208 UINT r;
4210 static const WCHAR szARPProductIcon[] =
4211 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4212 static const WCHAR szAssignment[] =
4213 {'A','s','s','i','g','n','m','e','n','t',0};
4214 static const WCHAR szAdvertiseFlags[] =
4215 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4216 static const WCHAR szClients[] =
4217 {'C','l','i','e','n','t','s',0};
4218 static const WCHAR szColon[] = {':',0};
4220 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4221 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4222 msi_free(buffer);
4224 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4225 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4227 /* FIXME */
4228 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4230 buffer = msi_dup_property(package->db, szARPProductIcon);
4231 if (buffer)
4233 LPWSTR path = msi_build_icon_path(package, buffer);
4234 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4235 msi_free(path);
4236 msi_free(buffer);
4239 buffer = msi_dup_property(package->db, szProductVersion);
4240 if (buffer)
4242 DWORD verdword = msi_version_str_to_dword(buffer);
4243 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4244 msi_free(buffer);
4247 msi_reg_set_val_dword(hkey, szAssignment, 0);
4248 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4249 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4250 msi_reg_set_val_str(hkey, szClients, szColon);
4252 hdb = alloc_msihandle(&package->db->hdr);
4253 if (!hdb)
4254 return ERROR_NOT_ENOUGH_MEMORY;
4256 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4257 MsiCloseHandle(hdb);
4258 if (r != ERROR_SUCCESS)
4259 goto done;
4261 size = MAX_PATH;
4262 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4263 NULL, guids, &size);
4264 if (r != ERROR_SUCCESS)
4265 goto done;
4267 ptr = strchrW(guids, ';');
4268 if (ptr) *ptr = 0;
4269 squash_guid(guids, packcode);
4270 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4272 done:
4273 MsiCloseHandle(suminfo);
4274 return ERROR_SUCCESS;
4277 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4279 UINT r;
4280 HKEY hkey;
4281 LPWSTR upgrade;
4282 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4284 upgrade = msi_dup_property(package->db, szUpgradeCode);
4285 if (!upgrade)
4286 return ERROR_SUCCESS;
4288 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4289 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4290 else
4291 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4293 if (r != ERROR_SUCCESS)
4295 WARN("failed to open upgrade code key\n");
4296 msi_free(upgrade);
4297 return ERROR_SUCCESS;
4299 squash_guid(package->ProductCode, squashed_pc);
4300 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4301 RegCloseKey(hkey);
4302 msi_free(upgrade);
4303 return ERROR_SUCCESS;
4306 static BOOL msi_check_publish(MSIPACKAGE *package)
4308 MSIFEATURE *feature;
4310 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4312 feature->Action = msi_get_feature_action( package, feature );
4313 if (feature->Action == INSTALLSTATE_LOCAL)
4314 return TRUE;
4317 return FALSE;
4320 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4322 MSIFEATURE *feature;
4324 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4326 feature->Action = msi_get_feature_action( package, feature );
4327 if (feature->Action != INSTALLSTATE_ABSENT)
4328 return FALSE;
4331 return TRUE;
4334 static UINT msi_publish_patches( MSIPACKAGE *package )
4336 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4337 WCHAR patch_squashed[GUID_SIZE];
4338 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4339 LONG res;
4340 MSIPATCHINFO *patch;
4341 UINT r;
4342 WCHAR *p, *all_patches = NULL;
4343 DWORD len = 0;
4345 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4346 if (r != ERROR_SUCCESS)
4347 return ERROR_FUNCTION_FAILED;
4349 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4350 if (res != ERROR_SUCCESS)
4352 r = ERROR_FUNCTION_FAILED;
4353 goto done;
4356 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4357 if (r != ERROR_SUCCESS)
4358 goto done;
4360 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4362 squash_guid( patch->patchcode, patch_squashed );
4363 len += strlenW( patch_squashed ) + 1;
4366 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4367 if (!all_patches)
4368 goto done;
4370 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4372 HKEY patch_key;
4374 squash_guid( patch->patchcode, p );
4375 p += strlenW( p ) + 1;
4377 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4378 (const BYTE *)patch->transforms,
4379 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4380 if (res != ERROR_SUCCESS)
4381 goto done;
4383 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4384 if (r != ERROR_SUCCESS)
4385 goto done;
4387 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4388 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4389 RegCloseKey( patch_key );
4390 if (res != ERROR_SUCCESS)
4391 goto done;
4393 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4395 res = GetLastError();
4396 ERR("Unable to copy patch package %d\n", res);
4397 goto done;
4399 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4400 if (res != ERROR_SUCCESS)
4401 goto done;
4403 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4404 RegCloseKey( patch_key );
4405 if (res != ERROR_SUCCESS)
4406 goto done;
4409 all_patches[len] = 0;
4410 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4411 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4412 if (res != ERROR_SUCCESS)
4413 goto done;
4415 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4416 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4417 if (res != ERROR_SUCCESS)
4418 r = ERROR_FUNCTION_FAILED;
4420 done:
4421 RegCloseKey( product_patches_key );
4422 RegCloseKey( patches_key );
4423 RegCloseKey( product_key );
4424 msi_free( all_patches );
4425 return r;
4428 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4430 UINT rc;
4431 HKEY hukey = NULL, hudkey = NULL;
4432 MSIRECORD *uirow;
4434 if (!list_empty(&package->patches))
4436 rc = msi_publish_patches(package);
4437 if (rc != ERROR_SUCCESS)
4438 goto end;
4441 /* FIXME: also need to publish if the product is in advertise mode */
4442 if (!msi_check_publish(package))
4443 return ERROR_SUCCESS;
4445 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4446 &hukey, TRUE);
4447 if (rc != ERROR_SUCCESS)
4448 goto end;
4450 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4451 NULL, &hudkey, TRUE);
4452 if (rc != ERROR_SUCCESS)
4453 goto end;
4455 rc = msi_publish_upgrade_code(package);
4456 if (rc != ERROR_SUCCESS)
4457 goto end;
4459 rc = msi_publish_product_properties(package, hukey);
4460 if (rc != ERROR_SUCCESS)
4461 goto end;
4463 rc = msi_publish_sourcelist(package, hukey);
4464 if (rc != ERROR_SUCCESS)
4465 goto end;
4467 rc = msi_publish_icons(package);
4469 end:
4470 uirow = MSI_CreateRecord( 1 );
4471 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4472 msi_ui_actiondata( package, szPublishProduct, uirow );
4473 msiobj_release( &uirow->hdr );
4475 RegCloseKey(hukey);
4476 RegCloseKey(hudkey);
4477 return rc;
4480 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4482 WCHAR *filename, *ptr, *folder, *ret;
4483 const WCHAR *dirprop;
4485 filename = msi_dup_record_field( row, 2 );
4486 if (filename && (ptr = strchrW( filename, '|' )))
4487 ptr++;
4488 else
4489 ptr = filename;
4491 dirprop = MSI_RecordGetString( row, 3 );
4492 if (dirprop)
4494 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4495 if (!folder) folder = msi_dup_property( package->db, dirprop );
4497 else
4498 folder = msi_dup_property( package->db, szWindowsFolder );
4500 if (!folder)
4502 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4503 msi_free( filename );
4504 return NULL;
4507 ret = msi_build_directory_name( 2, folder, ptr );
4509 msi_free( filename );
4510 msi_free( folder );
4511 return ret;
4514 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4516 MSIPACKAGE *package = param;
4517 LPCWSTR component, section, key, value, identifier;
4518 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4519 MSIRECORD * uirow;
4520 INT action;
4521 MSICOMPONENT *comp;
4523 component = MSI_RecordGetString(row, 8);
4524 comp = msi_get_loaded_component(package,component);
4525 if (!comp)
4526 return ERROR_SUCCESS;
4528 comp->Action = msi_get_component_action( package, comp );
4529 if (comp->Action != INSTALLSTATE_LOCAL)
4531 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4532 return ERROR_SUCCESS;
4535 identifier = MSI_RecordGetString(row,1);
4536 section = MSI_RecordGetString(row,4);
4537 key = MSI_RecordGetString(row,5);
4538 value = MSI_RecordGetString(row,6);
4539 action = MSI_RecordGetInteger(row,7);
4541 deformat_string(package,section,&deformated_section);
4542 deformat_string(package,key,&deformated_key);
4543 deformat_string(package,value,&deformated_value);
4545 fullname = get_ini_file_name(package, row);
4547 if (action == 0)
4549 TRACE("Adding value %s to section %s in %s\n",
4550 debugstr_w(deformated_key), debugstr_w(deformated_section),
4551 debugstr_w(fullname));
4552 WritePrivateProfileStringW(deformated_section, deformated_key,
4553 deformated_value, fullname);
4555 else if (action == 1)
4557 WCHAR returned[10];
4558 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4559 returned, 10, fullname);
4560 if (returned[0] == 0)
4562 TRACE("Adding value %s to section %s in %s\n",
4563 debugstr_w(deformated_key), debugstr_w(deformated_section),
4564 debugstr_w(fullname));
4566 WritePrivateProfileStringW(deformated_section, deformated_key,
4567 deformated_value, fullname);
4570 else if (action == 3)
4571 FIXME("Append to existing section not yet implemented\n");
4573 uirow = MSI_CreateRecord(4);
4574 MSI_RecordSetStringW(uirow,1,identifier);
4575 MSI_RecordSetStringW(uirow,2,deformated_section);
4576 MSI_RecordSetStringW(uirow,3,deformated_key);
4577 MSI_RecordSetStringW(uirow,4,deformated_value);
4578 msi_ui_actiondata( package, szWriteIniValues, uirow );
4579 msiobj_release( &uirow->hdr );
4581 msi_free(fullname);
4582 msi_free(deformated_key);
4583 msi_free(deformated_value);
4584 msi_free(deformated_section);
4585 return ERROR_SUCCESS;
4588 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4590 static const WCHAR query[] = {
4591 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4592 '`','I','n','i','F','i','l','e','`',0};
4593 MSIQUERY *view;
4594 UINT rc;
4596 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4597 if (rc != ERROR_SUCCESS)
4598 return ERROR_SUCCESS;
4600 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4601 msiobj_release(&view->hdr);
4602 return rc;
4605 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4607 MSIPACKAGE *package = param;
4608 LPCWSTR component, section, key, value, identifier;
4609 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4610 MSICOMPONENT *comp;
4611 MSIRECORD *uirow;
4612 INT action;
4614 component = MSI_RecordGetString( row, 8 );
4615 comp = msi_get_loaded_component( package, component );
4616 if (!comp)
4617 return ERROR_SUCCESS;
4619 comp->Action = msi_get_component_action( package, comp );
4620 if (comp->Action != INSTALLSTATE_ABSENT)
4622 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4623 return ERROR_SUCCESS;
4626 identifier = MSI_RecordGetString( row, 1 );
4627 section = MSI_RecordGetString( row, 4 );
4628 key = MSI_RecordGetString( row, 5 );
4629 value = MSI_RecordGetString( row, 6 );
4630 action = MSI_RecordGetInteger( row, 7 );
4632 deformat_string( package, section, &deformated_section );
4633 deformat_string( package, key, &deformated_key );
4634 deformat_string( package, value, &deformated_value );
4636 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4638 filename = get_ini_file_name( package, row );
4640 TRACE("Removing key %s from section %s in %s\n",
4641 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4643 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4645 WARN("Unable to remove key %u\n", GetLastError());
4647 msi_free( filename );
4649 else
4650 FIXME("Unsupported action %d\n", action);
4653 uirow = MSI_CreateRecord( 4 );
4654 MSI_RecordSetStringW( uirow, 1, identifier );
4655 MSI_RecordSetStringW( uirow, 2, deformated_section );
4656 MSI_RecordSetStringW( uirow, 3, deformated_key );
4657 MSI_RecordSetStringW( uirow, 4, deformated_value );
4658 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4659 msiobj_release( &uirow->hdr );
4661 msi_free( deformated_key );
4662 msi_free( deformated_value );
4663 msi_free( deformated_section );
4664 return ERROR_SUCCESS;
4667 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4669 MSIPACKAGE *package = param;
4670 LPCWSTR component, section, key, value, identifier;
4671 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4672 MSICOMPONENT *comp;
4673 MSIRECORD *uirow;
4674 INT action;
4676 component = MSI_RecordGetString( row, 8 );
4677 comp = msi_get_loaded_component( package, component );
4678 if (!comp)
4679 return ERROR_SUCCESS;
4681 comp->Action = msi_get_component_action( package, comp );
4682 if (comp->Action != INSTALLSTATE_LOCAL)
4684 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4685 return ERROR_SUCCESS;
4688 identifier = MSI_RecordGetString( row, 1 );
4689 section = MSI_RecordGetString( row, 4 );
4690 key = MSI_RecordGetString( row, 5 );
4691 value = MSI_RecordGetString( row, 6 );
4692 action = MSI_RecordGetInteger( row, 7 );
4694 deformat_string( package, section, &deformated_section );
4695 deformat_string( package, key, &deformated_key );
4696 deformat_string( package, value, &deformated_value );
4698 if (action == msidbIniFileActionRemoveLine)
4700 filename = get_ini_file_name( package, row );
4702 TRACE("Removing key %s from section %s in %s\n",
4703 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4705 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4707 WARN("Unable to remove key %u\n", GetLastError());
4709 msi_free( filename );
4711 else
4712 FIXME("Unsupported action %d\n", action);
4714 uirow = MSI_CreateRecord( 4 );
4715 MSI_RecordSetStringW( uirow, 1, identifier );
4716 MSI_RecordSetStringW( uirow, 2, deformated_section );
4717 MSI_RecordSetStringW( uirow, 3, deformated_key );
4718 MSI_RecordSetStringW( uirow, 4, deformated_value );
4719 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4720 msiobj_release( &uirow->hdr );
4722 msi_free( deformated_key );
4723 msi_free( deformated_value );
4724 msi_free( deformated_section );
4725 return ERROR_SUCCESS;
4728 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4730 static const WCHAR query[] = {
4731 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4732 '`','I','n','i','F','i','l','e','`',0};
4733 static const WCHAR remove_query[] = {
4734 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4735 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4736 MSIQUERY *view;
4737 UINT rc;
4739 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4740 if (rc == ERROR_SUCCESS)
4742 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4743 msiobj_release( &view->hdr );
4744 if (rc != ERROR_SUCCESS)
4745 return rc;
4747 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4748 if (rc == ERROR_SUCCESS)
4750 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4751 msiobj_release( &view->hdr );
4752 if (rc != ERROR_SUCCESS)
4753 return rc;
4755 return ERROR_SUCCESS;
4758 static void register_dll( const WCHAR *dll, BOOL unregister )
4760 static const WCHAR regW[] =
4761 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4762 static const WCHAR unregW[] =
4763 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4764 PROCESS_INFORMATION pi;
4765 STARTUPINFOW si;
4766 WCHAR *cmd;
4768 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4770 if (unregister) sprintfW( cmd, unregW, dll );
4771 else sprintfW( cmd, regW, dll );
4773 memset( &si, 0, sizeof(STARTUPINFOW) );
4774 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4776 CloseHandle( pi.hThread );
4777 msi_dialog_check_messages( pi.hProcess );
4778 CloseHandle( pi.hProcess );
4780 msi_free( cmd );
4783 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4785 MSIPACKAGE *package = param;
4786 LPCWSTR filename;
4787 MSIFILE *file;
4788 MSIRECORD *uirow;
4790 filename = MSI_RecordGetString( row, 1 );
4791 file = msi_get_loaded_file( package, filename );
4792 if (!file)
4794 WARN("unable to find file %s\n", debugstr_w(filename));
4795 return ERROR_SUCCESS;
4797 file->Component->Action = msi_get_component_action( package, file->Component );
4798 if (file->Component->Action != INSTALLSTATE_LOCAL)
4800 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4801 return ERROR_SUCCESS;
4804 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4805 register_dll( file->TargetPath, FALSE );
4807 uirow = MSI_CreateRecord( 2 );
4808 MSI_RecordSetStringW( uirow, 1, file->File );
4809 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4810 msi_ui_actiondata( package, szSelfRegModules, uirow );
4811 msiobj_release( &uirow->hdr );
4813 return ERROR_SUCCESS;
4816 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4818 static const WCHAR query[] = {
4819 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4820 '`','S','e','l','f','R','e','g','`',0};
4821 MSIQUERY *view;
4822 UINT rc;
4824 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4825 if (rc != ERROR_SUCCESS)
4826 return ERROR_SUCCESS;
4828 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4829 msiobj_release(&view->hdr);
4830 return rc;
4833 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4835 MSIPACKAGE *package = param;
4836 LPCWSTR filename;
4837 MSIFILE *file;
4838 MSIRECORD *uirow;
4840 filename = MSI_RecordGetString( row, 1 );
4841 file = msi_get_loaded_file( package, filename );
4842 if (!file)
4844 WARN("unable to find file %s\n", debugstr_w(filename));
4845 return ERROR_SUCCESS;
4847 file->Component->Action = msi_get_component_action( package, file->Component );
4848 if (file->Component->Action != INSTALLSTATE_ABSENT)
4850 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4851 return ERROR_SUCCESS;
4854 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4855 register_dll( file->TargetPath, TRUE );
4857 uirow = MSI_CreateRecord( 2 );
4858 MSI_RecordSetStringW( uirow, 1, file->File );
4859 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4860 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4861 msiobj_release( &uirow->hdr );
4863 return ERROR_SUCCESS;
4866 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4868 static const WCHAR query[] = {
4869 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4870 '`','S','e','l','f','R','e','g','`',0};
4871 MSIQUERY *view;
4872 UINT rc;
4874 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4875 if (rc != ERROR_SUCCESS)
4876 return ERROR_SUCCESS;
4878 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4879 msiobj_release( &view->hdr );
4880 return rc;
4883 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4885 MSIFEATURE *feature;
4886 UINT rc;
4887 HKEY hkey = NULL, userdata = NULL;
4889 if (!msi_check_publish(package))
4890 return ERROR_SUCCESS;
4892 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4893 &hkey, TRUE);
4894 if (rc != ERROR_SUCCESS)
4895 goto end;
4897 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4898 &userdata, TRUE);
4899 if (rc != ERROR_SUCCESS)
4900 goto end;
4902 /* here the guids are base 85 encoded */
4903 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4905 ComponentList *cl;
4906 LPWSTR data = NULL;
4907 GUID clsid;
4908 INT size;
4909 BOOL absent = FALSE;
4910 MSIRECORD *uirow;
4912 if (feature->Level <= 0) continue;
4914 if (feature->Action != INSTALLSTATE_LOCAL &&
4915 feature->Action != INSTALLSTATE_SOURCE &&
4916 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4918 size = 1;
4919 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4921 size += 21;
4923 if (feature->Feature_Parent)
4924 size += strlenW( feature->Feature_Parent )+2;
4926 data = msi_alloc(size * sizeof(WCHAR));
4928 data[0] = 0;
4929 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4931 MSICOMPONENT* component = cl->component;
4932 WCHAR buf[21];
4934 buf[0] = 0;
4935 if (component->ComponentId)
4937 TRACE("From %s\n",debugstr_w(component->ComponentId));
4938 CLSIDFromString(component->ComponentId, &clsid);
4939 encode_base85_guid(&clsid,buf);
4940 TRACE("to %s\n",debugstr_w(buf));
4941 strcatW(data,buf);
4945 if (feature->Feature_Parent)
4947 static const WCHAR sep[] = {'\2',0};
4948 strcatW(data,sep);
4949 strcatW(data,feature->Feature_Parent);
4952 msi_reg_set_val_str( userdata, feature->Feature, data );
4953 msi_free(data);
4955 size = 0;
4956 if (feature->Feature_Parent)
4957 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4958 if (!absent)
4960 size += sizeof(WCHAR);
4961 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4962 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4964 else
4966 size += 2*sizeof(WCHAR);
4967 data = msi_alloc(size);
4968 data[0] = 0x6;
4969 data[1] = 0;
4970 if (feature->Feature_Parent)
4971 strcpyW( &data[1], feature->Feature_Parent );
4972 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4973 (LPBYTE)data,size);
4974 msi_free(data);
4977 /* the UI chunk */
4978 uirow = MSI_CreateRecord( 1 );
4979 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4980 msi_ui_actiondata( package, szPublishFeatures, uirow );
4981 msiobj_release( &uirow->hdr );
4982 /* FIXME: call msi_ui_progress? */
4985 end:
4986 RegCloseKey(hkey);
4987 RegCloseKey(userdata);
4988 return rc;
4991 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4993 UINT r;
4994 HKEY hkey;
4995 MSIRECORD *uirow;
4997 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4999 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
5000 &hkey, FALSE);
5001 if (r == ERROR_SUCCESS)
5003 RegDeleteValueW(hkey, feature->Feature);
5004 RegCloseKey(hkey);
5007 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
5008 &hkey, FALSE);
5009 if (r == ERROR_SUCCESS)
5011 RegDeleteValueW(hkey, feature->Feature);
5012 RegCloseKey(hkey);
5015 uirow = MSI_CreateRecord( 1 );
5016 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5017 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
5018 msiobj_release( &uirow->hdr );
5020 return ERROR_SUCCESS;
5023 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5025 MSIFEATURE *feature;
5027 if (!msi_check_unpublish(package))
5028 return ERROR_SUCCESS;
5030 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5032 msi_unpublish_feature(package, feature);
5035 return ERROR_SUCCESS;
5038 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5040 SYSTEMTIME systime;
5041 DWORD size, langid;
5042 WCHAR date[9], *val, *buffer;
5043 const WCHAR *prop, *key;
5045 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5046 static const WCHAR modpath_fmt[] =
5047 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5048 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5049 static const WCHAR szModifyPath[] =
5050 {'M','o','d','i','f','y','P','a','t','h',0};
5051 static const WCHAR szUninstallString[] =
5052 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5053 static const WCHAR szEstimatedSize[] =
5054 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5055 static const WCHAR szDisplayVersion[] =
5056 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5057 static const WCHAR szInstallSource[] =
5058 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5059 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5060 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5061 static const WCHAR szAuthorizedCDFPrefix[] =
5062 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5063 static const WCHAR szARPCONTACT[] =
5064 {'A','R','P','C','O','N','T','A','C','T',0};
5065 static const WCHAR szContact[] =
5066 {'C','o','n','t','a','c','t',0};
5067 static const WCHAR szARPCOMMENTS[] =
5068 {'A','R','P','C','O','M','M','E','N','T','S',0};
5069 static const WCHAR szComments[] =
5070 {'C','o','m','m','e','n','t','s',0};
5071 static const WCHAR szProductName[] =
5072 {'P','r','o','d','u','c','t','N','a','m','e',0};
5073 static const WCHAR szDisplayName[] =
5074 {'D','i','s','p','l','a','y','N','a','m','e',0};
5075 static const WCHAR szARPHELPLINK[] =
5076 {'A','R','P','H','E','L','P','L','I','N','K',0};
5077 static const WCHAR szHelpLink[] =
5078 {'H','e','l','p','L','i','n','k',0};
5079 static const WCHAR szARPHELPTELEPHONE[] =
5080 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5081 static const WCHAR szHelpTelephone[] =
5082 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5083 static const WCHAR szARPINSTALLLOCATION[] =
5084 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5085 static const WCHAR szManufacturer[] =
5086 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5087 static const WCHAR szPublisher[] =
5088 {'P','u','b','l','i','s','h','e','r',0};
5089 static const WCHAR szARPREADME[] =
5090 {'A','R','P','R','E','A','D','M','E',0};
5091 static const WCHAR szReadme[] =
5092 {'R','e','a','d','M','e',0};
5093 static const WCHAR szARPSIZE[] =
5094 {'A','R','P','S','I','Z','E',0};
5095 static const WCHAR szSize[] =
5096 {'S','i','z','e',0};
5097 static const WCHAR szARPURLINFOABOUT[] =
5098 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5099 static const WCHAR szURLInfoAbout[] =
5100 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5101 static const WCHAR szARPURLUPDATEINFO[] =
5102 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5103 static const WCHAR szURLUpdateInfo[] =
5104 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5105 static const WCHAR szARPSYSTEMCOMPONENT[] =
5106 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5107 static const WCHAR szSystemComponent[] =
5108 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5110 static const WCHAR *propval[] = {
5111 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5112 szARPCONTACT, szContact,
5113 szARPCOMMENTS, szComments,
5114 szProductName, szDisplayName,
5115 szARPHELPLINK, szHelpLink,
5116 szARPHELPTELEPHONE, szHelpTelephone,
5117 szARPINSTALLLOCATION, szInstallLocation,
5118 szSourceDir, szInstallSource,
5119 szManufacturer, szPublisher,
5120 szARPREADME, szReadme,
5121 szARPSIZE, szSize,
5122 szARPURLINFOABOUT, szURLInfoAbout,
5123 szARPURLUPDATEINFO, szURLUpdateInfo,
5124 NULL
5126 const WCHAR **p = propval;
5128 while (*p)
5130 prop = *p++;
5131 key = *p++;
5132 val = msi_dup_property(package->db, prop);
5133 msi_reg_set_val_str(hkey, key, val);
5134 msi_free(val);
5137 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5138 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5140 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5142 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5143 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5144 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5145 msi_free(buffer);
5147 /* FIXME: Write real Estimated Size when we have it */
5148 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5150 GetLocalTime(&systime);
5151 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5152 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5154 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5155 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5157 buffer = msi_dup_property(package->db, szProductVersion);
5158 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5159 if (buffer)
5161 DWORD verdword = msi_version_str_to_dword(buffer);
5163 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5164 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5165 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5166 msi_free(buffer);
5169 return ERROR_SUCCESS;
5172 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5174 WCHAR squashed_pc[SQUISH_GUID_SIZE];
5175 MSIRECORD *uirow;
5176 LPWSTR upgrade_code;
5177 HKEY hkey, props, upgrade_key;
5178 UINT rc;
5180 /* FIXME: also need to publish if the product is in advertise mode */
5181 if (!msi_check_publish(package))
5182 return ERROR_SUCCESS;
5184 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5185 if (rc != ERROR_SUCCESS)
5186 return rc;
5188 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5189 if (rc != ERROR_SUCCESS)
5190 goto done;
5192 rc = msi_publish_install_properties(package, hkey);
5193 if (rc != ERROR_SUCCESS)
5194 goto done;
5196 rc = msi_publish_install_properties(package, props);
5197 if (rc != ERROR_SUCCESS)
5198 goto done;
5200 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5201 if (upgrade_code)
5203 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5204 if (rc == ERROR_SUCCESS)
5206 squash_guid( package->ProductCode, squashed_pc );
5207 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5208 RegCloseKey( upgrade_key );
5210 msi_free( upgrade_code );
5212 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5213 package->delete_on_close = FALSE;
5215 done:
5216 uirow = MSI_CreateRecord( 1 );
5217 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5218 msi_ui_actiondata( package, szRegisterProduct, uirow );
5219 msiobj_release( &uirow->hdr );
5221 RegCloseKey(hkey);
5222 return ERROR_SUCCESS;
5225 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5227 return execute_script(package, SCRIPT_INSTALL);
5230 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5232 MSIPACKAGE *package = param;
5233 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5234 WCHAR *p, *icon_path;
5236 if (!icon) return ERROR_SUCCESS;
5237 if ((icon_path = msi_build_icon_path( package, icon )))
5239 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5240 DeleteFileW( icon_path );
5241 if ((p = strrchrW( icon_path, '\\' )))
5243 *p = 0;
5244 RemoveDirectoryW( icon_path );
5246 msi_free( icon_path );
5248 return ERROR_SUCCESS;
5251 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5253 static const WCHAR query[]= {
5254 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5255 MSIQUERY *view;
5256 UINT r;
5258 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5259 if (r == ERROR_SUCCESS)
5261 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5262 msiobj_release( &view->hdr );
5263 if (r != ERROR_SUCCESS)
5264 return r;
5266 return ERROR_SUCCESS;
5269 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
5271 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
5272 WCHAR *upgrade, **features;
5273 BOOL full_uninstall = TRUE;
5274 MSIFEATURE *feature;
5275 MSIPATCHINFO *patch;
5276 UINT i;
5278 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5280 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
5282 features = msi_split_string( remove, ',' );
5283 for (i = 0; features && features[i]; i++)
5285 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
5287 msi_free(features);
5289 if (!full_uninstall)
5290 return ERROR_SUCCESS;
5292 MSIREG_DeleteProductKey(package->ProductCode);
5293 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5294 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5296 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5297 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5298 MSIREG_DeleteUserProductKey(package->ProductCode);
5299 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5301 upgrade = msi_dup_property(package->db, szUpgradeCode);
5302 if (upgrade)
5304 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5305 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5306 msi_free(upgrade);
5309 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5311 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5312 if (!strcmpW( package->ProductCode, patch->products ))
5314 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5315 patch->delete_on_close = TRUE;
5317 /* FIXME: remove local patch package if this is the last product */
5319 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5320 package->delete_on_close = TRUE;
5322 msi_unpublish_icons( package );
5323 return ERROR_SUCCESS;
5326 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5328 UINT rc;
5329 WCHAR *remove;
5331 /* first do the same as an InstallExecute */
5332 rc = execute_script(package, SCRIPT_INSTALL);
5333 if (rc != ERROR_SUCCESS)
5334 return rc;
5336 /* then handle commit actions */
5337 rc = execute_script(package, SCRIPT_COMMIT);
5338 if (rc != ERROR_SUCCESS)
5339 return rc;
5341 remove = msi_dup_property(package->db, szRemove);
5342 rc = msi_unpublish_product(package, remove);
5343 msi_free(remove);
5344 return rc;
5347 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5349 static const WCHAR RunOnce[] = {
5350 'S','o','f','t','w','a','r','e','\\',
5351 'M','i','c','r','o','s','o','f','t','\\',
5352 'W','i','n','d','o','w','s','\\',
5353 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5354 'R','u','n','O','n','c','e',0};
5355 static const WCHAR InstallRunOnce[] = {
5356 'S','o','f','t','w','a','r','e','\\',
5357 'M','i','c','r','o','s','o','f','t','\\',
5358 'W','i','n','d','o','w','s','\\',
5359 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5360 'I','n','s','t','a','l','l','e','r','\\',
5361 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5363 static const WCHAR msiexec_fmt[] = {
5364 '%','s',
5365 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5366 '\"','%','s','\"',0};
5367 static const WCHAR install_fmt[] = {
5368 '/','I',' ','\"','%','s','\"',' ',
5369 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5370 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5371 WCHAR buffer[256], sysdir[MAX_PATH];
5372 HKEY hkey;
5373 WCHAR squished_pc[100];
5375 squash_guid(package->ProductCode,squished_pc);
5377 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5378 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5379 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5380 squished_pc);
5382 msi_reg_set_val_str( hkey, squished_pc, buffer );
5383 RegCloseKey(hkey);
5385 TRACE("Reboot command %s\n",debugstr_w(buffer));
5387 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5388 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5390 msi_reg_set_val_str( hkey, squished_pc, buffer );
5391 RegCloseKey(hkey);
5393 return ERROR_INSTALL_SUSPEND;
5396 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5398 static const WCHAR query[] =
5399 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5400 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5401 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5402 MSIRECORD *rec, *row;
5403 DWORD i, size = 0;
5404 va_list va;
5405 const WCHAR *str;
5406 WCHAR *data;
5408 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5410 rec = MSI_CreateRecord( count + 2 );
5411 str = MSI_RecordGetString( row, 1 );
5412 MSI_RecordSetStringW( rec, 0, str );
5413 msiobj_release( &row->hdr );
5414 MSI_RecordSetInteger( rec, 1, error );
5416 va_start( va, count );
5417 for (i = 0; i < count; i++)
5419 str = va_arg( va, const WCHAR *);
5420 MSI_RecordSetStringW( rec, i + 2, str );
5422 va_end( va );
5424 MSI_FormatRecordW( package, rec, NULL, &size );
5425 size++;
5426 data = msi_alloc( size * sizeof(WCHAR) );
5427 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5428 else data[0] = 0;
5429 msiobj_release( &rec->hdr );
5430 return data;
5433 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5435 DWORD attrib;
5436 UINT rc;
5439 * We are currently doing what should be done here in the top level Install
5440 * however for Administrative and uninstalls this step will be needed
5442 if (!package->PackagePath)
5443 return ERROR_SUCCESS;
5445 msi_set_sourcedir_props(package, TRUE);
5447 attrib = GetFileAttributesW(package->db->path);
5448 if (attrib == INVALID_FILE_ATTRIBUTES)
5450 LPWSTR prompt, msg;
5451 DWORD size = 0;
5453 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5454 package->Context, MSICODE_PRODUCT,
5455 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5456 if (rc == ERROR_MORE_DATA)
5458 prompt = msi_alloc(size * sizeof(WCHAR));
5459 MsiSourceListGetInfoW(package->ProductCode, NULL,
5460 package->Context, MSICODE_PRODUCT,
5461 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5463 else
5464 prompt = strdupW(package->db->path);
5466 msg = msi_build_error_string(package, 1302, 1, prompt);
5467 msi_free(prompt);
5468 while(attrib == INVALID_FILE_ATTRIBUTES)
5470 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5471 if (rc == IDCANCEL)
5473 msi_free(msg);
5474 return ERROR_INSTALL_USEREXIT;
5476 attrib = GetFileAttributesW(package->db->path);
5478 msi_free(msg);
5479 rc = ERROR_SUCCESS;
5481 else
5482 return ERROR_SUCCESS;
5484 return rc;
5487 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5489 HKEY hkey = 0;
5490 LPWSTR buffer, productid = NULL;
5491 UINT i, rc = ERROR_SUCCESS;
5492 MSIRECORD *uirow;
5494 static const WCHAR szPropKeys[][80] =
5496 {'P','r','o','d','u','c','t','I','D',0},
5497 {'U','S','E','R','N','A','M','E',0},
5498 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5499 {0},
5502 static const WCHAR szRegKeys[][80] =
5504 {'P','r','o','d','u','c','t','I','D',0},
5505 {'R','e','g','O','w','n','e','r',0},
5506 {'R','e','g','C','o','m','p','a','n','y',0},
5507 {0},
5510 if (msi_check_unpublish(package))
5512 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5513 goto end;
5516 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5517 if (!productid)
5518 goto end;
5520 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5521 NULL, &hkey, TRUE);
5522 if (rc != ERROR_SUCCESS)
5523 goto end;
5525 for( i = 0; szPropKeys[i][0]; i++ )
5527 buffer = msi_dup_property( package->db, szPropKeys[i] );
5528 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5529 msi_free( buffer );
5532 end:
5533 uirow = MSI_CreateRecord( 1 );
5534 MSI_RecordSetStringW( uirow, 1, productid );
5535 msi_ui_actiondata( package, szRegisterUser, uirow );
5536 msiobj_release( &uirow->hdr );
5538 msi_free(productid);
5539 RegCloseKey(hkey);
5540 return rc;
5544 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5546 UINT rc;
5548 package->script->InWhatSequence |= SEQUENCE_EXEC;
5549 rc = ACTION_ProcessExecSequence(package,FALSE);
5550 return rc;
5553 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5555 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5556 WCHAR productid_85[21], component_85[21], *ret;
5557 GUID clsid;
5558 DWORD sz;
5560 /* > is used if there is a component GUID and < if not. */
5562 productid_85[0] = 0;
5563 component_85[0] = 0;
5564 CLSIDFromString( package->ProductCode, &clsid );
5566 encode_base85_guid( &clsid, productid_85 );
5567 if (component)
5569 CLSIDFromString( component->ComponentId, &clsid );
5570 encode_base85_guid( &clsid, component_85 );
5573 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5574 debugstr_w(component_85));
5576 sz = 20 + strlenW( feature ) + 20 + 3;
5577 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5578 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5579 return ret;
5582 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5584 MSIPACKAGE *package = param;
5585 LPCWSTR compgroupid, component, feature, qualifier, text;
5586 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5587 HKEY hkey = NULL;
5588 UINT rc;
5589 MSICOMPONENT *comp;
5590 MSIFEATURE *feat;
5591 DWORD sz;
5592 MSIRECORD *uirow;
5593 int len;
5595 feature = MSI_RecordGetString(rec, 5);
5596 feat = msi_get_loaded_feature(package, feature);
5597 if (!feat)
5598 return ERROR_SUCCESS;
5600 feat->Action = msi_get_feature_action( package, feat );
5601 if (feat->Action != INSTALLSTATE_LOCAL &&
5602 feat->Action != INSTALLSTATE_SOURCE &&
5603 feat->Action != INSTALLSTATE_ADVERTISED)
5605 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5606 return ERROR_SUCCESS;
5609 component = MSI_RecordGetString(rec, 3);
5610 comp = msi_get_loaded_component(package, component);
5611 if (!comp)
5612 return ERROR_SUCCESS;
5614 compgroupid = MSI_RecordGetString(rec,1);
5615 qualifier = MSI_RecordGetString(rec,2);
5617 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5618 if (rc != ERROR_SUCCESS)
5619 goto end;
5621 advertise = msi_create_component_advertise_string( package, comp, feature );
5622 text = MSI_RecordGetString( rec, 4 );
5623 if (text)
5625 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5626 strcpyW( p, advertise );
5627 strcatW( p, text );
5628 msi_free( advertise );
5629 advertise = p;
5631 existing = msi_reg_get_val_str( hkey, qualifier );
5633 sz = strlenW( advertise ) + 1;
5634 if (existing)
5636 for (p = existing; *p; p += len)
5638 len = strlenW( p ) + 1;
5639 if (strcmpW( advertise, p )) sz += len;
5642 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5644 rc = ERROR_OUTOFMEMORY;
5645 goto end;
5647 q = output;
5648 if (existing)
5650 for (p = existing; *p; p += len)
5652 len = strlenW( p ) + 1;
5653 if (strcmpW( advertise, p ))
5655 memcpy( q, p, len * sizeof(WCHAR) );
5656 q += len;
5660 strcpyW( q, advertise );
5661 q[strlenW( q ) + 1] = 0;
5663 msi_reg_set_val_multi_str( hkey, qualifier, output );
5665 end:
5666 RegCloseKey(hkey);
5667 msi_free( output );
5668 msi_free( advertise );
5669 msi_free( existing );
5671 /* the UI chunk */
5672 uirow = MSI_CreateRecord( 2 );
5673 MSI_RecordSetStringW( uirow, 1, compgroupid );
5674 MSI_RecordSetStringW( uirow, 2, qualifier);
5675 msi_ui_actiondata( package, szPublishComponents, uirow );
5676 msiobj_release( &uirow->hdr );
5677 /* FIXME: call ui_progress? */
5679 return rc;
5683 * At present I am ignorning the advertised components part of this and only
5684 * focusing on the qualified component sets
5686 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5688 static const WCHAR query[] = {
5689 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5690 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5691 MSIQUERY *view;
5692 UINT rc;
5694 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5695 if (rc != ERROR_SUCCESS)
5696 return ERROR_SUCCESS;
5698 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5699 msiobj_release(&view->hdr);
5700 return rc;
5703 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5705 static const WCHAR szInstallerComponents[] = {
5706 'S','o','f','t','w','a','r','e','\\',
5707 'M','i','c','r','o','s','o','f','t','\\',
5708 'I','n','s','t','a','l','l','e','r','\\',
5709 'C','o','m','p','o','n','e','n','t','s','\\',0};
5711 MSIPACKAGE *package = param;
5712 LPCWSTR compgroupid, component, feature, qualifier;
5713 MSICOMPONENT *comp;
5714 MSIFEATURE *feat;
5715 MSIRECORD *uirow;
5716 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5717 LONG res;
5719 feature = MSI_RecordGetString( rec, 5 );
5720 feat = msi_get_loaded_feature( package, feature );
5721 if (!feat)
5722 return ERROR_SUCCESS;
5724 feat->Action = msi_get_feature_action( package, feat );
5725 if (feat->Action != INSTALLSTATE_ABSENT)
5727 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5728 return ERROR_SUCCESS;
5731 component = MSI_RecordGetString( rec, 3 );
5732 comp = msi_get_loaded_component( package, component );
5733 if (!comp)
5734 return ERROR_SUCCESS;
5736 compgroupid = MSI_RecordGetString( rec, 1 );
5737 qualifier = MSI_RecordGetString( rec, 2 );
5739 squash_guid( compgroupid, squashed );
5740 strcpyW( keypath, szInstallerComponents );
5741 strcatW( keypath, squashed );
5743 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5744 if (res != ERROR_SUCCESS)
5746 WARN("Unable to delete component key %d\n", res);
5749 uirow = MSI_CreateRecord( 2 );
5750 MSI_RecordSetStringW( uirow, 1, compgroupid );
5751 MSI_RecordSetStringW( uirow, 2, qualifier );
5752 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5753 msiobj_release( &uirow->hdr );
5755 return ERROR_SUCCESS;
5758 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5760 static const WCHAR query[] = {
5761 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5762 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5763 MSIQUERY *view;
5764 UINT rc;
5766 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5767 if (rc != ERROR_SUCCESS)
5768 return ERROR_SUCCESS;
5770 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5771 msiobj_release( &view->hdr );
5772 return rc;
5775 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5777 static const WCHAR query[] =
5778 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5779 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5780 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5781 MSIPACKAGE *package = param;
5782 MSICOMPONENT *component;
5783 MSIRECORD *row;
5784 MSIFILE *file;
5785 SC_HANDLE hscm = NULL, service = NULL;
5786 LPCWSTR comp, key;
5787 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5788 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5789 DWORD serv_type, start_type, err_control;
5790 SERVICE_DESCRIPTIONW sd = {NULL};
5791 UINT ret = ERROR_SUCCESS;
5793 comp = MSI_RecordGetString( rec, 12 );
5794 component = msi_get_loaded_component( package, comp );
5795 if (!component)
5797 WARN("service component not found\n");
5798 goto done;
5800 component->Action = msi_get_component_action( package, component );
5801 if (component->Action != INSTALLSTATE_LOCAL)
5803 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5804 goto done;
5806 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5807 if (!hscm)
5809 ERR("Failed to open the SC Manager!\n");
5810 goto done;
5813 start_type = MSI_RecordGetInteger(rec, 5);
5814 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5815 goto done;
5817 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5818 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5819 serv_type = MSI_RecordGetInteger(rec, 4);
5820 err_control = MSI_RecordGetInteger(rec, 6);
5821 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5822 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5823 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5824 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5825 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5826 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5828 /* fetch the service path */
5829 row = MSI_QueryGetRecord(package->db, query, comp);
5830 if (!row)
5832 ERR("Query failed\n");
5833 goto done;
5835 if (!(key = MSI_RecordGetString(row, 6)))
5837 msiobj_release(&row->hdr);
5838 goto done;
5840 file = msi_get_loaded_file(package, key);
5841 msiobj_release(&row->hdr);
5842 if (!file)
5844 ERR("Failed to load the service file\n");
5845 goto done;
5848 if (!args || !args[0]) image_path = file->TargetPath;
5849 else
5851 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5852 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5854 ret = ERROR_OUTOFMEMORY;
5855 goto done;
5858 strcpyW(image_path, file->TargetPath);
5859 strcatW(image_path, szSpace);
5860 strcatW(image_path, args);
5862 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5863 start_type, err_control, image_path, load_order,
5864 NULL, depends, serv_name, pass);
5866 if (!service)
5868 if (GetLastError() != ERROR_SERVICE_EXISTS)
5869 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5871 else if (sd.lpDescription)
5873 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5874 WARN("failed to set service description %u\n", GetLastError());
5877 if (image_path != file->TargetPath) msi_free(image_path);
5878 done:
5879 if (service) CloseServiceHandle(service);
5880 if (hscm) CloseServiceHandle(hscm);
5881 msi_free(name);
5882 msi_free(disp);
5883 msi_free(sd.lpDescription);
5884 msi_free(load_order);
5885 msi_free(serv_name);
5886 msi_free(pass);
5887 msi_free(depends);
5888 msi_free(args);
5890 return ret;
5893 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5895 static const WCHAR query[] = {
5896 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5897 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5898 MSIQUERY *view;
5899 UINT rc;
5901 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5902 if (rc != ERROR_SUCCESS)
5903 return ERROR_SUCCESS;
5905 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5906 msiobj_release(&view->hdr);
5907 return rc;
5910 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5911 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5913 LPCWSTR *vector, *temp_vector;
5914 LPWSTR p, q;
5915 DWORD sep_len;
5917 static const WCHAR separator[] = {'[','~',']',0};
5919 *numargs = 0;
5920 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5922 if (!args)
5923 return NULL;
5925 vector = msi_alloc(sizeof(LPWSTR));
5926 if (!vector)
5927 return NULL;
5929 p = args;
5932 (*numargs)++;
5933 vector[*numargs - 1] = p;
5935 if ((q = strstrW(p, separator)))
5937 *q = '\0';
5939 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5940 if (!temp_vector)
5942 msi_free(vector);
5943 return NULL;
5945 vector = temp_vector;
5947 p = q + sep_len;
5949 } while (q);
5951 return vector;
5954 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5956 MSIPACKAGE *package = param;
5957 MSICOMPONENT *comp;
5958 MSIRECORD *uirow;
5959 SC_HANDLE scm = NULL, service = NULL;
5960 LPCWSTR component, *vector = NULL;
5961 LPWSTR name, args, display_name = NULL;
5962 DWORD event, numargs, len, wait, dummy;
5963 UINT r = ERROR_FUNCTION_FAILED;
5964 SERVICE_STATUS_PROCESS status;
5965 ULONGLONG start_time;
5967 component = MSI_RecordGetString(rec, 6);
5968 comp = msi_get_loaded_component(package, component);
5969 if (!comp)
5970 return ERROR_SUCCESS;
5972 event = MSI_RecordGetInteger( rec, 3 );
5973 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5975 comp->Action = msi_get_component_action( package, comp );
5976 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
5977 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
5979 TRACE("not starting %s\n", debugstr_w(name));
5980 msi_free( name );
5981 return ERROR_SUCCESS;
5984 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5985 wait = MSI_RecordGetInteger(rec, 5);
5987 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5988 if (!scm)
5990 ERR("Failed to open the service control manager\n");
5991 goto done;
5994 len = 0;
5995 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5996 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5998 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5999 GetServiceDisplayNameW( scm, name, display_name, &len );
6002 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
6003 if (!service)
6005 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6006 goto done;
6009 vector = msi_service_args_to_vector(args, &numargs);
6011 if (!StartServiceW(service, numargs, vector) &&
6012 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6014 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6015 goto done;
6018 r = ERROR_SUCCESS;
6019 if (wait)
6021 /* wait for at most 30 seconds for the service to be up and running */
6022 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6023 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6025 TRACE("failed to query service status (%u)\n", GetLastError());
6026 goto done;
6028 start_time = GetTickCount64();
6029 while (status.dwCurrentState == SERVICE_START_PENDING)
6031 if (GetTickCount64() - start_time > 30000) break;
6032 Sleep(1000);
6033 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6034 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6036 TRACE("failed to query service status (%u)\n", GetLastError());
6037 goto done;
6040 if (status.dwCurrentState != SERVICE_RUNNING)
6042 WARN("service failed to start %u\n", status.dwCurrentState);
6043 r = ERROR_FUNCTION_FAILED;
6047 done:
6048 uirow = MSI_CreateRecord( 2 );
6049 MSI_RecordSetStringW( uirow, 1, display_name );
6050 MSI_RecordSetStringW( uirow, 2, name );
6051 msi_ui_actiondata( package, szStartServices, uirow );
6052 msiobj_release( &uirow->hdr );
6054 if (service) CloseServiceHandle(service);
6055 if (scm) CloseServiceHandle(scm);
6057 msi_free(name);
6058 msi_free(args);
6059 msi_free(vector);
6060 msi_free(display_name);
6061 return r;
6064 static UINT ACTION_StartServices( MSIPACKAGE *package )
6066 static const WCHAR query[] = {
6067 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6068 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6069 MSIQUERY *view;
6070 UINT rc;
6072 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6073 if (rc != ERROR_SUCCESS)
6074 return ERROR_SUCCESS;
6076 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6077 msiobj_release(&view->hdr);
6078 return rc;
6081 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6083 DWORD i, needed, count;
6084 ENUM_SERVICE_STATUSW *dependencies;
6085 SERVICE_STATUS ss;
6086 SC_HANDLE depserv;
6087 BOOL stopped, ret = FALSE;
6089 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6090 0, &needed, &count))
6091 return TRUE;
6093 if (GetLastError() != ERROR_MORE_DATA)
6094 return FALSE;
6096 dependencies = msi_alloc(needed);
6097 if (!dependencies)
6098 return FALSE;
6100 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6101 needed, &needed, &count))
6102 goto done;
6104 for (i = 0; i < count; i++)
6106 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6107 SERVICE_STOP | SERVICE_QUERY_STATUS);
6108 if (!depserv)
6109 goto done;
6111 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6112 CloseServiceHandle(depserv);
6113 if (!stopped)
6114 goto done;
6117 ret = TRUE;
6119 done:
6120 msi_free(dependencies);
6121 return ret;
6124 static UINT stop_service( LPCWSTR name )
6126 SC_HANDLE scm = NULL, service = NULL;
6127 SERVICE_STATUS status;
6128 SERVICE_STATUS_PROCESS ssp;
6129 DWORD needed;
6131 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6132 if (!scm)
6134 WARN("Failed to open the SCM: %d\n", GetLastError());
6135 goto done;
6138 service = OpenServiceW(scm, name,
6139 SERVICE_STOP |
6140 SERVICE_QUERY_STATUS |
6141 SERVICE_ENUMERATE_DEPENDENTS);
6142 if (!service)
6144 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6145 goto done;
6148 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6149 sizeof(SERVICE_STATUS_PROCESS), &needed))
6151 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6152 goto done;
6155 if (ssp.dwCurrentState == SERVICE_STOPPED)
6156 goto done;
6158 stop_service_dependents(scm, service);
6160 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6161 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6163 done:
6164 if (service) CloseServiceHandle(service);
6165 if (scm) CloseServiceHandle(scm);
6167 return ERROR_SUCCESS;
6170 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6172 MSIPACKAGE *package = param;
6173 MSICOMPONENT *comp;
6174 MSIRECORD *uirow;
6175 LPCWSTR component;
6176 WCHAR *name, *display_name = NULL;
6177 DWORD event, len;
6178 SC_HANDLE scm;
6180 component = MSI_RecordGetString( rec, 6 );
6181 comp = msi_get_loaded_component( package, component );
6182 if (!comp)
6183 return ERROR_SUCCESS;
6185 event = MSI_RecordGetInteger( rec, 3 );
6186 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6188 comp->Action = msi_get_component_action( package, comp );
6189 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6190 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6192 TRACE("not stopping %s\n", debugstr_w(name));
6193 msi_free( name );
6194 return ERROR_SUCCESS;
6197 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6198 if (!scm)
6200 ERR("Failed to open the service control manager\n");
6201 goto done;
6204 len = 0;
6205 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6206 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6208 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6209 GetServiceDisplayNameW( scm, name, display_name, &len );
6211 CloseServiceHandle( scm );
6213 stop_service( name );
6215 done:
6216 uirow = MSI_CreateRecord( 2 );
6217 MSI_RecordSetStringW( uirow, 1, display_name );
6218 MSI_RecordSetStringW( uirow, 2, name );
6219 msi_ui_actiondata( package, szStopServices, uirow );
6220 msiobj_release( &uirow->hdr );
6222 msi_free( name );
6223 msi_free( display_name );
6224 return ERROR_SUCCESS;
6227 static UINT ACTION_StopServices( MSIPACKAGE *package )
6229 static const WCHAR query[] = {
6230 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6231 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6232 MSIQUERY *view;
6233 UINT rc;
6235 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6236 if (rc != ERROR_SUCCESS)
6237 return ERROR_SUCCESS;
6239 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6240 msiobj_release(&view->hdr);
6241 return rc;
6244 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6246 MSIPACKAGE *package = param;
6247 MSICOMPONENT *comp;
6248 MSIRECORD *uirow;
6249 LPWSTR name = NULL, display_name = NULL;
6250 DWORD event, len;
6251 SC_HANDLE scm = NULL, service = NULL;
6253 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6254 if (!comp)
6255 return ERROR_SUCCESS;
6257 event = MSI_RecordGetInteger( rec, 3 );
6258 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6260 comp->Action = msi_get_component_action( package, comp );
6261 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6262 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6264 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6265 msi_free( name );
6266 return ERROR_SUCCESS;
6268 stop_service( name );
6270 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6271 if (!scm)
6273 WARN("Failed to open the SCM: %d\n", GetLastError());
6274 goto done;
6277 len = 0;
6278 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6279 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6281 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6282 GetServiceDisplayNameW( scm, name, display_name, &len );
6285 service = OpenServiceW( scm, name, DELETE );
6286 if (!service)
6288 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6289 goto done;
6292 if (!DeleteService( service ))
6293 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6295 done:
6296 uirow = MSI_CreateRecord( 2 );
6297 MSI_RecordSetStringW( uirow, 1, display_name );
6298 MSI_RecordSetStringW( uirow, 2, name );
6299 msi_ui_actiondata( package, szDeleteServices, uirow );
6300 msiobj_release( &uirow->hdr );
6302 if (service) CloseServiceHandle( service );
6303 if (scm) CloseServiceHandle( scm );
6304 msi_free( name );
6305 msi_free( display_name );
6307 return ERROR_SUCCESS;
6310 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6312 static const WCHAR query[] = {
6313 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6314 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6315 MSIQUERY *view;
6316 UINT rc;
6318 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6319 if (rc != ERROR_SUCCESS)
6320 return ERROR_SUCCESS;
6322 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6323 msiobj_release( &view->hdr );
6324 return rc;
6327 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6329 MSIPACKAGE *package = param;
6330 LPWSTR driver, driver_path, ptr;
6331 WCHAR outpath[MAX_PATH];
6332 MSIFILE *driver_file = NULL, *setup_file = NULL;
6333 MSICOMPONENT *comp;
6334 MSIRECORD *uirow;
6335 LPCWSTR desc, file_key, component;
6336 DWORD len, usage;
6337 UINT r = ERROR_SUCCESS;
6339 static const WCHAR driver_fmt[] = {
6340 'D','r','i','v','e','r','=','%','s',0};
6341 static const WCHAR setup_fmt[] = {
6342 'S','e','t','u','p','=','%','s',0};
6343 static const WCHAR usage_fmt[] = {
6344 'F','i','l','e','U','s','a','g','e','=','1',0};
6346 component = MSI_RecordGetString( rec, 2 );
6347 comp = msi_get_loaded_component( package, component );
6348 if (!comp)
6349 return ERROR_SUCCESS;
6351 comp->Action = msi_get_component_action( package, comp );
6352 if (comp->Action != INSTALLSTATE_LOCAL)
6354 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6355 return ERROR_SUCCESS;
6357 desc = MSI_RecordGetString(rec, 3);
6359 file_key = MSI_RecordGetString( rec, 4 );
6360 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6362 file_key = MSI_RecordGetString( rec, 5 );
6363 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6365 if (!driver_file)
6367 ERR("ODBC Driver entry not found!\n");
6368 return ERROR_FUNCTION_FAILED;
6371 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6372 if (setup_file)
6373 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6374 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6376 driver = msi_alloc(len * sizeof(WCHAR));
6377 if (!driver)
6378 return ERROR_OUTOFMEMORY;
6380 ptr = driver;
6381 lstrcpyW(ptr, desc);
6382 ptr += lstrlenW(ptr) + 1;
6384 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6385 ptr += len + 1;
6387 if (setup_file)
6389 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6390 ptr += len + 1;
6393 lstrcpyW(ptr, usage_fmt);
6394 ptr += lstrlenW(ptr) + 1;
6395 *ptr = '\0';
6397 if (!driver_file->TargetPath)
6399 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6400 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6402 driver_path = strdupW(driver_file->TargetPath);
6403 ptr = strrchrW(driver_path, '\\');
6404 if (ptr) *ptr = '\0';
6406 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6407 NULL, ODBC_INSTALL_COMPLETE, &usage))
6409 ERR("Failed to install SQL driver!\n");
6410 r = ERROR_FUNCTION_FAILED;
6413 uirow = MSI_CreateRecord( 5 );
6414 MSI_RecordSetStringW( uirow, 1, desc );
6415 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6416 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6417 msi_ui_actiondata( package, szInstallODBC, uirow );
6418 msiobj_release( &uirow->hdr );
6420 msi_free(driver);
6421 msi_free(driver_path);
6423 return r;
6426 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6428 MSIPACKAGE *package = param;
6429 LPWSTR translator, translator_path, ptr;
6430 WCHAR outpath[MAX_PATH];
6431 MSIFILE *translator_file = NULL, *setup_file = NULL;
6432 MSICOMPONENT *comp;
6433 MSIRECORD *uirow;
6434 LPCWSTR desc, file_key, component;
6435 DWORD len, usage;
6436 UINT r = ERROR_SUCCESS;
6438 static const WCHAR translator_fmt[] = {
6439 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6440 static const WCHAR setup_fmt[] = {
6441 'S','e','t','u','p','=','%','s',0};
6443 component = MSI_RecordGetString( rec, 2 );
6444 comp = msi_get_loaded_component( package, component );
6445 if (!comp)
6446 return ERROR_SUCCESS;
6448 comp->Action = msi_get_component_action( package, comp );
6449 if (comp->Action != INSTALLSTATE_LOCAL)
6451 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6452 return ERROR_SUCCESS;
6454 desc = MSI_RecordGetString(rec, 3);
6456 file_key = MSI_RecordGetString( rec, 4 );
6457 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6459 file_key = MSI_RecordGetString( rec, 5 );
6460 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6462 if (!translator_file)
6464 ERR("ODBC Translator entry not found!\n");
6465 return ERROR_FUNCTION_FAILED;
6468 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6469 if (setup_file)
6470 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6472 translator = msi_alloc(len * sizeof(WCHAR));
6473 if (!translator)
6474 return ERROR_OUTOFMEMORY;
6476 ptr = translator;
6477 lstrcpyW(ptr, desc);
6478 ptr += lstrlenW(ptr) + 1;
6480 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6481 ptr += len + 1;
6483 if (setup_file)
6485 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6486 ptr += len + 1;
6488 *ptr = '\0';
6490 translator_path = strdupW(translator_file->TargetPath);
6491 ptr = strrchrW(translator_path, '\\');
6492 if (ptr) *ptr = '\0';
6494 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6495 NULL, ODBC_INSTALL_COMPLETE, &usage))
6497 ERR("Failed to install SQL translator!\n");
6498 r = ERROR_FUNCTION_FAILED;
6501 uirow = MSI_CreateRecord( 5 );
6502 MSI_RecordSetStringW( uirow, 1, desc );
6503 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6504 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6505 msi_ui_actiondata( package, szInstallODBC, uirow );
6506 msiobj_release( &uirow->hdr );
6508 msi_free(translator);
6509 msi_free(translator_path);
6511 return r;
6514 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6516 MSIPACKAGE *package = param;
6517 MSICOMPONENT *comp;
6518 LPWSTR attrs;
6519 LPCWSTR desc, driver, component;
6520 WORD request = ODBC_ADD_SYS_DSN;
6521 INT registration;
6522 DWORD len;
6523 UINT r = ERROR_SUCCESS;
6524 MSIRECORD *uirow;
6526 static const WCHAR attrs_fmt[] = {
6527 'D','S','N','=','%','s',0 };
6529 component = MSI_RecordGetString( rec, 2 );
6530 comp = msi_get_loaded_component( package, component );
6531 if (!comp)
6532 return ERROR_SUCCESS;
6534 comp->Action = msi_get_component_action( package, comp );
6535 if (comp->Action != INSTALLSTATE_LOCAL)
6537 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6538 return ERROR_SUCCESS;
6541 desc = MSI_RecordGetString(rec, 3);
6542 driver = MSI_RecordGetString(rec, 4);
6543 registration = MSI_RecordGetInteger(rec, 5);
6545 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6546 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6548 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6549 attrs = msi_alloc(len * sizeof(WCHAR));
6550 if (!attrs)
6551 return ERROR_OUTOFMEMORY;
6553 len = sprintfW(attrs, attrs_fmt, desc);
6554 attrs[len + 1] = 0;
6556 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6558 ERR("Failed to install SQL data source!\n");
6559 r = ERROR_FUNCTION_FAILED;
6562 uirow = MSI_CreateRecord( 5 );
6563 MSI_RecordSetStringW( uirow, 1, desc );
6564 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6565 MSI_RecordSetInteger( uirow, 3, request );
6566 msi_ui_actiondata( package, szInstallODBC, uirow );
6567 msiobj_release( &uirow->hdr );
6569 msi_free(attrs);
6571 return r;
6574 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6576 static const WCHAR driver_query[] = {
6577 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6578 'O','D','B','C','D','r','i','v','e','r',0};
6579 static const WCHAR translator_query[] = {
6580 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6581 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6582 static const WCHAR source_query[] = {
6583 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6584 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6585 MSIQUERY *view;
6586 UINT rc;
6588 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6589 if (rc == ERROR_SUCCESS)
6591 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6592 msiobj_release(&view->hdr);
6593 if (rc != ERROR_SUCCESS)
6594 return rc;
6596 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6597 if (rc == ERROR_SUCCESS)
6599 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6600 msiobj_release(&view->hdr);
6601 if (rc != ERROR_SUCCESS)
6602 return rc;
6604 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6605 if (rc == ERROR_SUCCESS)
6607 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6608 msiobj_release(&view->hdr);
6609 if (rc != ERROR_SUCCESS)
6610 return rc;
6612 return ERROR_SUCCESS;
6615 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6617 MSIPACKAGE *package = param;
6618 MSICOMPONENT *comp;
6619 MSIRECORD *uirow;
6620 DWORD usage;
6621 LPCWSTR desc, component;
6623 component = MSI_RecordGetString( rec, 2 );
6624 comp = msi_get_loaded_component( package, component );
6625 if (!comp)
6626 return ERROR_SUCCESS;
6628 comp->Action = msi_get_component_action( package, comp );
6629 if (comp->Action != INSTALLSTATE_ABSENT)
6631 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6632 return ERROR_SUCCESS;
6635 desc = MSI_RecordGetString( rec, 3 );
6636 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6638 WARN("Failed to remove ODBC driver\n");
6640 else if (!usage)
6642 FIXME("Usage count reached 0\n");
6645 uirow = MSI_CreateRecord( 2 );
6646 MSI_RecordSetStringW( uirow, 1, desc );
6647 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6648 msi_ui_actiondata( package, szRemoveODBC, uirow );
6649 msiobj_release( &uirow->hdr );
6651 return ERROR_SUCCESS;
6654 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6656 MSIPACKAGE *package = param;
6657 MSICOMPONENT *comp;
6658 MSIRECORD *uirow;
6659 DWORD usage;
6660 LPCWSTR desc, component;
6662 component = MSI_RecordGetString( rec, 2 );
6663 comp = msi_get_loaded_component( package, component );
6664 if (!comp)
6665 return ERROR_SUCCESS;
6667 comp->Action = msi_get_component_action( package, comp );
6668 if (comp->Action != INSTALLSTATE_ABSENT)
6670 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6671 return ERROR_SUCCESS;
6674 desc = MSI_RecordGetString( rec, 3 );
6675 if (!SQLRemoveTranslatorW( desc, &usage ))
6677 WARN("Failed to remove ODBC translator\n");
6679 else if (!usage)
6681 FIXME("Usage count reached 0\n");
6684 uirow = MSI_CreateRecord( 2 );
6685 MSI_RecordSetStringW( uirow, 1, desc );
6686 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6687 msi_ui_actiondata( package, szRemoveODBC, uirow );
6688 msiobj_release( &uirow->hdr );
6690 return ERROR_SUCCESS;
6693 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6695 MSIPACKAGE *package = param;
6696 MSICOMPONENT *comp;
6697 MSIRECORD *uirow;
6698 LPWSTR attrs;
6699 LPCWSTR desc, driver, component;
6700 WORD request = ODBC_REMOVE_SYS_DSN;
6701 INT registration;
6702 DWORD len;
6704 static const WCHAR attrs_fmt[] = {
6705 'D','S','N','=','%','s',0 };
6707 component = MSI_RecordGetString( rec, 2 );
6708 comp = msi_get_loaded_component( package, component );
6709 if (!comp)
6710 return ERROR_SUCCESS;
6712 comp->Action = msi_get_component_action( package, comp );
6713 if (comp->Action != INSTALLSTATE_ABSENT)
6715 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6716 return ERROR_SUCCESS;
6719 desc = MSI_RecordGetString( rec, 3 );
6720 driver = MSI_RecordGetString( rec, 4 );
6721 registration = MSI_RecordGetInteger( rec, 5 );
6723 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6724 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6726 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6727 attrs = msi_alloc( len * sizeof(WCHAR) );
6728 if (!attrs)
6729 return ERROR_OUTOFMEMORY;
6731 FIXME("Use ODBCSourceAttribute table\n");
6733 len = sprintfW( attrs, attrs_fmt, desc );
6734 attrs[len + 1] = 0;
6736 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6738 WARN("Failed to remove ODBC data source\n");
6740 msi_free( attrs );
6742 uirow = MSI_CreateRecord( 3 );
6743 MSI_RecordSetStringW( uirow, 1, desc );
6744 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6745 MSI_RecordSetInteger( uirow, 3, request );
6746 msi_ui_actiondata( package, szRemoveODBC, uirow );
6747 msiobj_release( &uirow->hdr );
6749 return ERROR_SUCCESS;
6752 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6754 static const WCHAR driver_query[] = {
6755 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6756 'O','D','B','C','D','r','i','v','e','r',0};
6757 static const WCHAR translator_query[] = {
6758 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6759 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6760 static const WCHAR source_query[] = {
6761 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6762 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6763 MSIQUERY *view;
6764 UINT rc;
6766 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6767 if (rc == ERROR_SUCCESS)
6769 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6770 msiobj_release( &view->hdr );
6771 if (rc != ERROR_SUCCESS)
6772 return rc;
6774 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6775 if (rc == ERROR_SUCCESS)
6777 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6778 msiobj_release( &view->hdr );
6779 if (rc != ERROR_SUCCESS)
6780 return rc;
6782 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6783 if (rc == ERROR_SUCCESS)
6785 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6786 msiobj_release( &view->hdr );
6787 if (rc != ERROR_SUCCESS)
6788 return rc;
6790 return ERROR_SUCCESS;
6793 #define ENV_ACT_SETALWAYS 0x1
6794 #define ENV_ACT_SETABSENT 0x2
6795 #define ENV_ACT_REMOVE 0x4
6796 #define ENV_ACT_REMOVEMATCH 0x8
6798 #define ENV_MOD_MACHINE 0x20000000
6799 #define ENV_MOD_APPEND 0x40000000
6800 #define ENV_MOD_PREFIX 0x80000000
6801 #define ENV_MOD_MASK 0xC0000000
6803 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6805 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6807 LPCWSTR cptr = *name;
6809 static const WCHAR prefix[] = {'[','~',']',0};
6810 static const int prefix_len = 3;
6812 *flags = 0;
6813 while (*cptr)
6815 if (*cptr == '=')
6816 *flags |= ENV_ACT_SETALWAYS;
6817 else if (*cptr == '+')
6818 *flags |= ENV_ACT_SETABSENT;
6819 else if (*cptr == '-')
6820 *flags |= ENV_ACT_REMOVE;
6821 else if (*cptr == '!')
6822 *flags |= ENV_ACT_REMOVEMATCH;
6823 else if (*cptr == '*')
6824 *flags |= ENV_MOD_MACHINE;
6825 else
6826 break;
6828 cptr++;
6829 (*name)++;
6832 if (!*cptr)
6834 ERR("Missing environment variable\n");
6835 return ERROR_FUNCTION_FAILED;
6838 if (*value)
6840 LPCWSTR ptr = *value;
6841 if (!strncmpW(ptr, prefix, prefix_len))
6843 if (ptr[prefix_len] == szSemiColon[0])
6845 *flags |= ENV_MOD_APPEND;
6846 *value += lstrlenW(prefix);
6848 else
6850 *value = NULL;
6853 else if (lstrlenW(*value) >= prefix_len)
6855 ptr += lstrlenW(ptr) - prefix_len;
6856 if (!strcmpW( ptr, prefix ))
6858 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6860 *flags |= ENV_MOD_PREFIX;
6861 /* the "[~]" will be removed by deformat_string */;
6863 else
6865 *value = NULL;
6871 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6872 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6873 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6874 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6876 ERR("Invalid flags: %08x\n", *flags);
6877 return ERROR_FUNCTION_FAILED;
6880 if (!*flags)
6881 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6883 return ERROR_SUCCESS;
6886 static UINT open_env_key( DWORD flags, HKEY *key )
6888 static const WCHAR user_env[] =
6889 {'E','n','v','i','r','o','n','m','e','n','t',0};
6890 static const WCHAR machine_env[] =
6891 {'S','y','s','t','e','m','\\',
6892 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6893 'C','o','n','t','r','o','l','\\',
6894 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6895 'E','n','v','i','r','o','n','m','e','n','t',0};
6896 const WCHAR *env;
6897 HKEY root;
6898 LONG res;
6900 if (flags & ENV_MOD_MACHINE)
6902 env = machine_env;
6903 root = HKEY_LOCAL_MACHINE;
6905 else
6907 env = user_env;
6908 root = HKEY_CURRENT_USER;
6911 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6912 if (res != ERROR_SUCCESS)
6914 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6915 return ERROR_FUNCTION_FAILED;
6918 return ERROR_SUCCESS;
6921 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6923 MSIPACKAGE *package = param;
6924 LPCWSTR name, value, component;
6925 WCHAR *data = NULL, *newval = NULL, *deformatted = NULL, *p, *q;
6926 DWORD flags, type, size, len, len_value = 0;
6927 UINT res;
6928 HKEY env = NULL;
6929 MSICOMPONENT *comp;
6930 MSIRECORD *uirow;
6931 int action = 0, found = 0;
6933 component = MSI_RecordGetString(rec, 4);
6934 comp = msi_get_loaded_component(package, component);
6935 if (!comp)
6936 return ERROR_SUCCESS;
6938 comp->Action = msi_get_component_action( package, comp );
6939 if (comp->Action != INSTALLSTATE_LOCAL)
6941 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6942 return ERROR_SUCCESS;
6944 name = MSI_RecordGetString(rec, 2);
6945 value = MSI_RecordGetString(rec, 3);
6947 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6949 res = env_parse_flags(&name, &value, &flags);
6950 if (res != ERROR_SUCCESS || !value)
6951 goto done;
6953 if (value && !deformat_string(package, value, &deformatted))
6955 res = ERROR_OUTOFMEMORY;
6956 goto done;
6959 if ((value = deformatted))
6961 if (flags & ENV_MOD_PREFIX)
6963 p = strrchrW( value, ';' );
6964 len_value = p - value;
6966 else if (flags & ENV_MOD_APPEND)
6968 value = strchrW( value, ';' ) + 1;
6969 len_value = strlenW( value );
6971 else len_value = strlenW( value );
6974 res = open_env_key( flags, &env );
6975 if (res != ERROR_SUCCESS)
6976 goto done;
6978 if (flags & ENV_MOD_MACHINE)
6979 action |= 0x20000000;
6981 size = 0;
6982 type = REG_SZ;
6983 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6984 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6985 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6986 goto done;
6988 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6990 action = 0x2;
6992 /* Nothing to do. */
6993 if (!value)
6995 res = ERROR_SUCCESS;
6996 goto done;
6998 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6999 newval = strdupW(value);
7000 if (!newval)
7002 res = ERROR_OUTOFMEMORY;
7003 goto done;
7006 else
7008 action = 0x1;
7010 /* Contrary to MSDN, +-variable to [~];path works */
7011 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7013 res = ERROR_SUCCESS;
7014 goto done;
7017 if (!(p = q = data = msi_alloc( size )))
7019 msi_free(deformatted);
7020 RegCloseKey(env);
7021 return ERROR_OUTOFMEMORY;
7024 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)data, &size );
7025 if (res != ERROR_SUCCESS)
7026 goto done;
7028 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7030 action = 0x4;
7031 res = RegDeleteValueW(env, name);
7032 if (res != ERROR_SUCCESS)
7033 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7034 goto done;
7037 for (;;)
7039 while (*q && *q != ';') q++;
7040 len = q - p;
7041 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ) &&
7042 (!p[len] || p[len] == ';'))
7044 found = 1;
7045 break;
7047 if (!*q) break;
7048 p = ++q;
7051 if (found)
7053 TRACE("string already set\n");
7054 goto done;
7057 size = (len_value + 1 + strlenW( data ) + 1) * sizeof(WCHAR);
7058 if (!(p = newval = msi_alloc( size )))
7060 res = ERROR_OUTOFMEMORY;
7061 goto done;
7064 if (flags & ENV_MOD_PREFIX)
7066 memcpy( newval, value, len_value * sizeof(WCHAR) );
7067 newval[len_value] = ';';
7068 p = newval + len_value + 1;
7069 action |= 0x80000000;
7072 strcpyW( p, data );
7074 if (flags & ENV_MOD_APPEND)
7076 p += strlenW( data );
7077 *p++ = ';';
7078 memcpy( p, value, (len_value + 1) * sizeof(WCHAR) );
7079 action |= 0x40000000;
7082 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7083 res = RegSetValueExW( env, name, 0, type, (BYTE *)newval, size );
7084 if (res)
7086 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7089 done:
7090 uirow = MSI_CreateRecord( 3 );
7091 MSI_RecordSetStringW( uirow, 1, name );
7092 MSI_RecordSetStringW( uirow, 2, newval );
7093 MSI_RecordSetInteger( uirow, 3, action );
7094 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7095 msiobj_release( &uirow->hdr );
7097 if (env) RegCloseKey(env);
7098 msi_free(deformatted);
7099 msi_free(data);
7100 msi_free(newval);
7101 return res;
7104 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7106 static const WCHAR query[] = {
7107 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7108 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7109 MSIQUERY *view;
7110 UINT rc;
7112 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7113 if (rc != ERROR_SUCCESS)
7114 return ERROR_SUCCESS;
7116 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7117 msiobj_release(&view->hdr);
7118 return rc;
7121 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7123 MSIPACKAGE *package = param;
7124 LPCWSTR name, value, component;
7125 WCHAR *p, *q, *deformatted = NULL, *new_value = NULL;
7126 DWORD flags, type, size, len, len_value = 0, len_new_value;
7127 HKEY env;
7128 MSICOMPONENT *comp;
7129 MSIRECORD *uirow;
7130 int action = 0;
7131 LONG res;
7132 UINT r;
7134 component = MSI_RecordGetString( rec, 4 );
7135 comp = msi_get_loaded_component( package, component );
7136 if (!comp)
7137 return ERROR_SUCCESS;
7139 comp->Action = msi_get_component_action( package, comp );
7140 if (comp->Action != INSTALLSTATE_ABSENT)
7142 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7143 return ERROR_SUCCESS;
7145 name = MSI_RecordGetString( rec, 2 );
7146 value = MSI_RecordGetString( rec, 3 );
7148 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7150 r = env_parse_flags( &name, &value, &flags );
7151 if (r != ERROR_SUCCESS)
7152 return r;
7154 if (!(flags & ENV_ACT_REMOVE))
7156 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7157 return ERROR_SUCCESS;
7160 if (value && !deformat_string( package, value, &deformatted ))
7161 return ERROR_OUTOFMEMORY;
7163 if ((value = deformatted))
7165 if (flags & ENV_MOD_PREFIX)
7167 p = strchrW( value, ';' );
7168 len_value = p - value;
7170 else if (flags & ENV_MOD_APPEND)
7172 value = strchrW( value, ';' ) + 1;
7173 len_value = strlenW( value );
7175 else len_value = strlenW( value );
7178 r = open_env_key( flags, &env );
7179 if (r != ERROR_SUCCESS)
7181 r = ERROR_SUCCESS;
7182 goto done;
7185 if (flags & ENV_MOD_MACHINE)
7186 action |= 0x20000000;
7188 size = 0;
7189 type = REG_SZ;
7190 res = RegQueryValueExW( env, name, NULL, &type, NULL, &size );
7191 if (res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ))
7192 goto done;
7194 if (!(new_value = msi_alloc( size ))) goto done;
7196 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)new_value, &size );
7197 if (res != ERROR_SUCCESS)
7198 goto done;
7200 len_new_value = size / sizeof(WCHAR) - 1;
7201 p = q = new_value;
7202 for (;;)
7204 while (*q && *q != ';') q++;
7205 len = q - p;
7206 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ))
7208 if (*q == ';') q++;
7209 memmove( p, q, (len_new_value - (q - new_value) + 1) * sizeof(WCHAR) );
7210 break;
7212 if (!*q) break;
7213 p = ++q;
7216 if (!new_value[0] || !value)
7218 TRACE("removing %s\n", debugstr_w(name));
7219 res = RegDeleteValueW( env, name );
7220 if (res != ERROR_SUCCESS)
7221 WARN("failed to delete value %s (%d)\n", debugstr_w(name), res);
7223 else
7225 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(new_value));
7226 size = (strlenW( new_value ) + 1) * sizeof(WCHAR);
7227 res = RegSetValueExW( env, name, 0, type, (BYTE *)new_value, size );
7228 if (res != ERROR_SUCCESS)
7229 WARN("failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(new_value), res);
7232 done:
7233 uirow = MSI_CreateRecord( 3 );
7234 MSI_RecordSetStringW( uirow, 1, name );
7235 MSI_RecordSetStringW( uirow, 2, value );
7236 MSI_RecordSetInteger( uirow, 3, action );
7237 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7238 msiobj_release( &uirow->hdr );
7240 if (env) RegCloseKey( env );
7241 msi_free( deformatted );
7242 msi_free( new_value );
7243 return r;
7246 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7248 static const WCHAR query[] = {
7249 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7250 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7251 MSIQUERY *view;
7252 UINT rc;
7254 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7255 if (rc != ERROR_SUCCESS)
7256 return ERROR_SUCCESS;
7258 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7259 msiobj_release( &view->hdr );
7260 return rc;
7263 UINT msi_validate_product_id( MSIPACKAGE *package )
7265 LPWSTR key, template, id;
7266 UINT r = ERROR_SUCCESS;
7268 id = msi_dup_property( package->db, szProductID );
7269 if (id)
7271 msi_free( id );
7272 return ERROR_SUCCESS;
7274 template = msi_dup_property( package->db, szPIDTemplate );
7275 key = msi_dup_property( package->db, szPIDKEY );
7276 if (key && template)
7278 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7279 r = msi_set_property( package->db, szProductID, key, -1 );
7281 msi_free( template );
7282 msi_free( key );
7283 return r;
7286 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7288 return msi_validate_product_id( package );
7291 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7293 TRACE("\n");
7294 package->need_reboot_at_end = 1;
7295 return ERROR_SUCCESS;
7298 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7300 static const WCHAR szAvailableFreeReg[] =
7301 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7302 MSIRECORD *uirow;
7303 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7305 TRACE("%p %d kilobytes\n", package, space);
7307 uirow = MSI_CreateRecord( 1 );
7308 MSI_RecordSetInteger( uirow, 1, space );
7309 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7310 msiobj_release( &uirow->hdr );
7312 return ERROR_SUCCESS;
7315 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7317 TRACE("%p\n", package);
7319 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7320 return ERROR_SUCCESS;
7323 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7325 FIXME("%p\n", package);
7326 return ERROR_SUCCESS;
7329 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7331 static const WCHAR driver_query[] = {
7332 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7333 'O','D','B','C','D','r','i','v','e','r',0};
7334 static const WCHAR translator_query[] = {
7335 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7336 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7337 MSIQUERY *view;
7338 UINT r, count;
7340 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7341 if (r == ERROR_SUCCESS)
7343 count = 0;
7344 r = MSI_IterateRecords( view, &count, NULL, package );
7345 msiobj_release( &view->hdr );
7346 if (r != ERROR_SUCCESS)
7347 return r;
7348 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7350 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7351 if (r == ERROR_SUCCESS)
7353 count = 0;
7354 r = MSI_IterateRecords( view, &count, NULL, package );
7355 msiobj_release( &view->hdr );
7356 if (r != ERROR_SUCCESS)
7357 return r;
7358 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7360 return ERROR_SUCCESS;
7363 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7365 static const WCHAR fmtW[] =
7366 {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7367 MSIPACKAGE *package = param;
7368 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7369 int attrs = MSI_RecordGetInteger( rec, 5 );
7370 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7371 WCHAR *product, *features, *cmd;
7372 STARTUPINFOW si;
7373 PROCESS_INFORMATION info;
7374 BOOL ret;
7376 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7377 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7379 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7381 len += strlenW( product );
7382 if (features)
7383 len += strlenW( features );
7384 else
7385 len += sizeof(szAll) / sizeof(szAll[0]);
7387 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7389 msi_free( product );
7390 msi_free( features );
7391 return ERROR_OUTOFMEMORY;
7393 sprintfW( cmd, fmtW, product, features ? features : szAll );
7394 msi_free( product );
7395 msi_free( features );
7397 memset( &si, 0, sizeof(STARTUPINFOW) );
7398 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7399 msi_free( cmd );
7400 if (!ret) return GetLastError();
7401 CloseHandle( info.hThread );
7403 WaitForSingleObject( info.hProcess, INFINITE );
7404 CloseHandle( info.hProcess );
7405 return ERROR_SUCCESS;
7408 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7410 static const WCHAR query[] = {
7411 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7412 MSIQUERY *view;
7413 UINT r;
7415 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7416 if (r == ERROR_SUCCESS)
7418 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7419 msiobj_release( &view->hdr );
7420 if (r != ERROR_SUCCESS)
7421 return r;
7423 return ERROR_SUCCESS;
7426 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7428 MSIPACKAGE *package = param;
7429 int attributes = MSI_RecordGetInteger( rec, 5 );
7431 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7433 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7434 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7435 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7436 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7437 HKEY hkey;
7438 UINT r;
7440 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7442 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7443 if (r != ERROR_SUCCESS)
7444 return ERROR_SUCCESS;
7446 else
7448 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7449 if (r != ERROR_SUCCESS)
7450 return ERROR_SUCCESS;
7452 RegCloseKey( hkey );
7454 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7455 debugstr_w(upgrade_code), debugstr_w(version_min),
7456 debugstr_w(version_max), debugstr_w(language));
7458 return ERROR_SUCCESS;
7461 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7463 static const WCHAR query[] = {
7464 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7465 'U','p','g','r','a','d','e',0};
7466 MSIQUERY *view;
7467 UINT r;
7469 if (msi_get_property_int( package->db, szInstalled, 0 ))
7471 TRACE("product is installed, skipping action\n");
7472 return ERROR_SUCCESS;
7474 if (msi_get_property_int( package->db, szPreselected, 0 ))
7476 TRACE("Preselected property is set, not migrating feature states\n");
7477 return ERROR_SUCCESS;
7479 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7480 if (r == ERROR_SUCCESS)
7482 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7483 msiobj_release( &view->hdr );
7484 if (r != ERROR_SUCCESS)
7485 return r;
7487 return ERROR_SUCCESS;
7490 static void bind_image( const char *filename, const char *path )
7492 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7494 WARN("failed to bind image %u\n", GetLastError());
7498 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7500 UINT i;
7501 MSIFILE *file;
7502 MSIPACKAGE *package = param;
7503 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7504 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7505 char *filenameA, *pathA;
7506 WCHAR *pathW, **path_list;
7508 if (!(file = msi_get_loaded_file( package, key )))
7510 WARN("file %s not found\n", debugstr_w(key));
7511 return ERROR_SUCCESS;
7513 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7514 path_list = msi_split_string( paths, ';' );
7515 if (!path_list) bind_image( filenameA, NULL );
7516 else
7518 for (i = 0; path_list[i] && path_list[i][0]; i++)
7520 deformat_string( package, path_list[i], &pathW );
7521 if ((pathA = strdupWtoA( pathW )))
7523 bind_image( filenameA, pathA );
7524 msi_free( pathA );
7526 msi_free( pathW );
7529 msi_free( path_list );
7530 msi_free( filenameA );
7531 return ERROR_SUCCESS;
7534 static UINT ACTION_BindImage( MSIPACKAGE *package )
7536 static const WCHAR query[] = {
7537 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7538 'B','i','n','d','I','m','a','g','e',0};
7539 MSIQUERY *view;
7540 UINT r;
7542 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7543 if (r == ERROR_SUCCESS)
7545 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7546 msiobj_release( &view->hdr );
7547 if (r != ERROR_SUCCESS)
7548 return r;
7550 return ERROR_SUCCESS;
7553 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7555 static const WCHAR query[] = {
7556 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7557 MSIQUERY *view;
7558 DWORD count = 0;
7559 UINT r;
7561 r = MSI_OpenQuery( package->db, &view, query, table );
7562 if (r == ERROR_SUCCESS)
7564 r = MSI_IterateRecords(view, &count, NULL, package);
7565 msiobj_release(&view->hdr);
7566 if (r != ERROR_SUCCESS)
7567 return r;
7569 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7570 return ERROR_SUCCESS;
7573 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7575 static const WCHAR table[] = {
7576 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7577 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7580 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7582 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7583 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7586 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7588 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7589 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7592 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7594 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7595 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7598 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7600 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7601 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7604 static const struct
7606 const WCHAR *action;
7607 UINT (*handler)(MSIPACKAGE *);
7608 const WCHAR *action_rollback;
7610 StandardActions[] =
7612 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7613 { szAppSearch, ACTION_AppSearch, NULL },
7614 { szBindImage, ACTION_BindImage, NULL },
7615 { szCCPSearch, ACTION_CCPSearch, NULL },
7616 { szCostFinalize, ACTION_CostFinalize, NULL },
7617 { szCostInitialize, ACTION_CostInitialize, NULL },
7618 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7619 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7620 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7621 { szDisableRollback, ACTION_DisableRollback, NULL },
7622 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7623 { szExecuteAction, ACTION_ExecuteAction, NULL },
7624 { szFileCost, ACTION_FileCost, NULL },
7625 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7626 { szForceReboot, ACTION_ForceReboot, NULL },
7627 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7628 { szInstallExecute, ACTION_InstallExecute, NULL },
7629 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7630 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7631 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7632 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7633 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7634 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7635 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7636 { szInstallValidate, ACTION_InstallValidate, NULL },
7637 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7638 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7639 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7640 { szMoveFiles, ACTION_MoveFiles, NULL },
7641 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7642 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7643 { szPatchFiles, ACTION_PatchFiles, NULL },
7644 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7645 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7646 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7647 { szPublishProduct, ACTION_PublishProduct, NULL },
7648 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7649 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7650 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7651 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7652 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7653 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7654 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7655 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7656 { szRegisterUser, ACTION_RegisterUser, NULL },
7657 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7658 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7659 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7660 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7661 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7662 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7663 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7664 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7665 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7666 { szResolveSource, ACTION_ResolveSource, NULL },
7667 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7668 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7669 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7670 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7671 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7672 { szStartServices, ACTION_StartServices, szStopServices },
7673 { szStopServices, ACTION_StopServices, szStartServices },
7674 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7675 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7676 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7677 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7678 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7679 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7680 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7681 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7682 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7683 { szValidateProductID, ACTION_ValidateProductID, NULL },
7684 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7685 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7686 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7687 { NULL, NULL, NULL }
7690 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7692 BOOL ret = FALSE;
7693 UINT i;
7695 i = 0;
7696 while (StandardActions[i].action != NULL)
7698 if (!strcmpW( StandardActions[i].action, action ))
7700 ui_actionstart( package, action );
7701 if (StandardActions[i].handler)
7703 ui_actioninfo( package, action, TRUE, 0 );
7704 *rc = StandardActions[i].handler( package );
7705 ui_actioninfo( package, action, FALSE, *rc );
7707 if (StandardActions[i].action_rollback && !package->need_rollback)
7709 TRACE("scheduling rollback action\n");
7710 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7713 else
7715 FIXME("unhandled standard action %s\n", debugstr_w(action));
7716 *rc = ERROR_SUCCESS;
7718 ret = TRUE;
7719 break;
7721 i++;
7723 return ret;
7726 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7728 UINT rc = ERROR_SUCCESS;
7729 BOOL handled;
7731 TRACE("Performing action (%s)\n", debugstr_w(action));
7733 handled = ACTION_HandleStandardAction(package, action, &rc);
7735 if (!handled)
7736 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7738 if (!handled)
7740 WARN("unhandled msi action %s\n", debugstr_w(action));
7741 rc = ERROR_FUNCTION_NOT_CALLED;
7744 return rc;
7747 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7749 UINT rc = ERROR_SUCCESS;
7750 BOOL handled = FALSE;
7752 TRACE("Performing action (%s)\n", debugstr_w(action));
7754 package->action_progress_increment = 0;
7755 handled = ACTION_HandleStandardAction(package, action, &rc);
7757 if (!handled)
7758 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7760 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7761 handled = TRUE;
7763 if (!handled)
7765 WARN("unhandled msi action %s\n", debugstr_w(action));
7766 rc = ERROR_FUNCTION_NOT_CALLED;
7769 return rc;
7772 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7774 UINT rc = ERROR_SUCCESS;
7775 MSIRECORD *row;
7777 static const WCHAR query[] =
7778 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7779 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7780 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7781 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7782 static const WCHAR ui_query[] =
7783 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7784 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7785 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7786 ' ', '=',' ','%','i',0};
7788 if (needs_ui_sequence(package))
7789 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7790 else
7791 row = MSI_QueryGetRecord(package->db, query, seq);
7793 if (row)
7795 LPCWSTR action, cond;
7797 TRACE("Running the actions\n");
7799 /* check conditions */
7800 cond = MSI_RecordGetString(row, 2);
7802 /* this is a hack to skip errors in the condition code */
7803 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7805 msiobj_release(&row->hdr);
7806 return ERROR_SUCCESS;
7809 action = MSI_RecordGetString(row, 1);
7810 if (!action)
7812 ERR("failed to fetch action\n");
7813 msiobj_release(&row->hdr);
7814 return ERROR_FUNCTION_FAILED;
7817 if (needs_ui_sequence(package))
7818 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7819 else
7820 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7822 msiobj_release(&row->hdr);
7825 return rc;
7828 /****************************************************
7829 * TOP level entry points
7830 *****************************************************/
7832 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7833 LPCWSTR szCommandLine )
7835 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7836 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7837 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7838 WCHAR *reinstall, *remove, *patch, *productcode;
7839 BOOL ui_exists;
7840 UINT rc;
7842 msi_set_property( package->db, szAction, szInstall, -1 );
7844 package->script->InWhatSequence = SEQUENCE_INSTALL;
7846 if (szPackagePath)
7848 LPWSTR p, dir;
7849 LPCWSTR file;
7851 dir = strdupW(szPackagePath);
7852 p = strrchrW(dir, '\\');
7853 if (p)
7855 *(++p) = 0;
7856 file = szPackagePath + (p - dir);
7858 else
7860 msi_free(dir);
7861 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7862 GetCurrentDirectoryW(MAX_PATH, dir);
7863 lstrcatW(dir, szBackSlash);
7864 file = szPackagePath;
7867 msi_free( package->PackagePath );
7868 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7869 if (!package->PackagePath)
7871 msi_free(dir);
7872 return ERROR_OUTOFMEMORY;
7875 lstrcpyW(package->PackagePath, dir);
7876 lstrcatW(package->PackagePath, file);
7877 msi_free(dir);
7879 msi_set_sourcedir_props(package, FALSE);
7882 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7883 if (rc != ERROR_SUCCESS)
7884 return rc;
7886 msi_apply_transforms( package );
7887 msi_apply_patches( package );
7889 patch = msi_dup_property( package->db, szPatch );
7890 remove = msi_dup_property( package->db, szRemove );
7891 reinstall = msi_dup_property( package->db, szReinstall );
7892 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7894 TRACE("setting REINSTALL property to ALL\n");
7895 msi_set_property( package->db, szReinstall, szAll, -1 );
7896 package->full_reinstall = 1;
7899 msi_set_original_database_property( package->db, szPackagePath );
7900 msi_parse_command_line( package, szCommandLine, FALSE );
7901 msi_adjust_privilege_properties( package );
7902 msi_set_context( package );
7904 productcode = msi_dup_property( package->db, szProductCode );
7905 if (strcmpiW( productcode, package->ProductCode ))
7907 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
7908 msi_free( package->ProductCode );
7909 package->ProductCode = productcode;
7911 else msi_free( productcode );
7913 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7915 TRACE("disabling rollback\n");
7916 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7919 if (needs_ui_sequence( package))
7921 package->script->InWhatSequence |= SEQUENCE_UI;
7922 rc = ACTION_ProcessUISequence(package);
7923 ui_exists = ui_sequence_exists(package);
7924 if (rc == ERROR_SUCCESS || !ui_exists)
7926 package->script->InWhatSequence |= SEQUENCE_EXEC;
7927 rc = ACTION_ProcessExecSequence(package, ui_exists);
7930 else
7931 rc = ACTION_ProcessExecSequence(package, FALSE);
7933 /* process the ending type action */
7934 if (rc == ERROR_SUCCESS)
7935 ACTION_PerformActionSequence(package, -1);
7936 else if (rc == ERROR_INSTALL_USEREXIT)
7937 ACTION_PerformActionSequence(package, -2);
7938 else if (rc == ERROR_INSTALL_SUSPEND)
7939 ACTION_PerformActionSequence(package, -4);
7940 else /* failed */
7942 ACTION_PerformActionSequence(package, -3);
7943 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7945 package->need_rollback = TRUE;
7949 /* finish up running custom actions */
7950 ACTION_FinishCustomActions(package);
7952 if (package->need_rollback && !reinstall)
7954 WARN("installation failed, running rollback script\n");
7955 execute_script( package, SCRIPT_ROLLBACK );
7957 msi_free( reinstall );
7958 msi_free( remove );
7959 msi_free( patch );
7961 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7962 return ERROR_SUCCESS_REBOOT_REQUIRED;
7964 return rc;