configure: Add a check for sys/ucontext.h and include it where appropriate.
[wine.git] / dlls / msi / action.c
blob4c6d6af37f7c5bf9a6059870023e19c53149cd49
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "shlwapi.h"
39 #include "imagehlp.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
48 static const WCHAR szCreateFolders[] =
49 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
50 static const WCHAR szCostFinalize[] =
51 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
52 static const WCHAR szWriteRegistryValues[] =
53 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
54 static const WCHAR szFileCost[] =
55 {'F','i','l','e','C','o','s','t',0};
56 static const WCHAR szInstallInitialize[] =
57 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
58 static const WCHAR szInstallValidate[] =
59 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
60 static const WCHAR szLaunchConditions[] =
61 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
62 static const WCHAR szProcessComponents[] =
63 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
64 static const WCHAR szRegisterTypeLibraries[] =
65 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
66 static const WCHAR szCreateShortcuts[] =
67 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
68 static const WCHAR szPublishProduct[] =
69 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
70 static const WCHAR szWriteIniValues[] =
71 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
72 static const WCHAR szSelfRegModules[] =
73 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
74 static const WCHAR szPublishFeatures[] =
75 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
76 static const WCHAR szRegisterProduct[] =
77 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
78 static const WCHAR szInstallExecute[] =
79 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
80 static const WCHAR szInstallExecuteAgain[] =
81 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
82 static const WCHAR szInstallFinalize[] =
83 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
84 static const WCHAR szForceReboot[] =
85 {'F','o','r','c','e','R','e','b','o','o','t',0};
86 static const WCHAR szResolveSource[] =
87 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
88 static const WCHAR szAllocateRegistrySpace[] =
89 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
90 static const WCHAR szBindImage[] =
91 {'B','i','n','d','I','m','a','g','e',0};
92 static const WCHAR szDeleteServices[] =
93 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
94 static const WCHAR szDisableRollback[] =
95 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
96 static const WCHAR szExecuteAction[] =
97 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
98 static const WCHAR szInstallAdminPackage[] =
99 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
100 static const WCHAR szInstallSFPCatalogFile[] =
101 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
102 static const WCHAR szIsolateComponents[] =
103 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
104 static const WCHAR szMigrateFeatureStates[] =
105 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
106 static const WCHAR szMsiUnpublishAssemblies[] =
107 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
108 static const WCHAR szInstallODBC[] =
109 {'I','n','s','t','a','l','l','O','D','B','C',0};
110 static const WCHAR szInstallServices[] =
111 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
112 static const WCHAR szPublishComponents[] =
113 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
114 static const WCHAR szRegisterComPlus[] =
115 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
116 static const WCHAR szRegisterUser[] =
117 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
118 static const WCHAR szRemoveEnvironmentStrings[] =
119 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
120 static const WCHAR szRemoveExistingProducts[] =
121 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
122 static const WCHAR szRemoveFolders[] =
123 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
124 static const WCHAR szRemoveIniValues[] =
125 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
126 static const WCHAR szRemoveODBC[] =
127 {'R','e','m','o','v','e','O','D','B','C',0};
128 static const WCHAR szRemoveRegistryValues[] =
129 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
130 static const WCHAR szRemoveShortcuts[] =
131 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
132 static const WCHAR szRMCCPSearch[] =
133 {'R','M','C','C','P','S','e','a','r','c','h',0};
134 static const WCHAR szScheduleReboot[] =
135 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
136 static const WCHAR szSelfUnregModules[] =
137 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
138 static const WCHAR szSetODBCFolders[] =
139 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
140 static const WCHAR szStartServices[] =
141 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
142 static const WCHAR szStopServices[] =
143 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
144 static const WCHAR szUnpublishComponents[] =
145 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
146 static const WCHAR szUnpublishFeatures[] =
147 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
148 static const WCHAR szUnregisterComPlus[] =
149 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
150 static const WCHAR szUnregisterTypeLibraries[] =
151 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
152 static const WCHAR szValidateProductID[] =
153 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
154 static const WCHAR szWriteEnvironmentStrings[] =
155 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
157 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
159 static const WCHAR Query_t[] =
160 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
161 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
162 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
163 ' ','\'','%','s','\'',0};
164 MSIRECORD * row;
166 row = MSI_QueryGetRecord( package->db, Query_t, action );
167 if (!row)
168 return;
169 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
170 msiobj_release(&row->hdr);
173 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
174 UINT rc)
176 MSIRECORD * row;
177 static const WCHAR template_s[]=
178 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
179 '%','s', '.',0};
180 static const WCHAR template_e[]=
181 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
182 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
183 '%','i','.',0};
184 static const WCHAR format[] =
185 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
186 WCHAR message[1024];
187 WCHAR timet[0x100];
189 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
190 if (start)
191 sprintfW(message,template_s,timet,action);
192 else
193 sprintfW(message,template_e,timet,action,rc);
195 row = MSI_CreateRecord(1);
196 MSI_RecordSetStringW(row,1,message);
198 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
199 msiobj_release(&row->hdr);
202 enum parse_state
204 state_whitespace,
205 state_token,
206 state_quote
209 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
211 enum parse_state state = state_quote;
212 const WCHAR *p;
213 WCHAR *out = value;
214 BOOL ignore, in_quotes = FALSE;
215 int count = 0, len = 0;
217 for (p = str; *p; p++)
219 ignore = FALSE;
220 switch (state)
222 case state_whitespace:
223 switch (*p)
225 case ' ':
226 in_quotes = TRUE;
227 ignore = TRUE;
228 len++;
229 break;
230 case '"':
231 state = state_quote;
232 if (in_quotes && p[1] != '\"') count--;
233 else count++;
234 break;
235 default:
236 state = state_token;
237 in_quotes = TRUE;
238 len++;
239 break;
241 break;
243 case state_token:
244 switch (*p)
246 case '"':
247 state = state_quote;
248 if (in_quotes) count--;
249 else count++;
250 break;
251 case ' ':
252 state = state_whitespace;
253 if (!count) goto done;
254 in_quotes = TRUE;
255 len++;
256 break;
257 default:
258 if (!count) in_quotes = FALSE;
259 else in_quotes = TRUE;
260 len++;
261 break;
263 break;
265 case state_quote:
266 switch (*p)
268 case '"':
269 if (in_quotes && p[1] != '\"') count--;
270 else count++;
271 break;
272 case ' ':
273 state = state_whitespace;
274 if (!count || (count > 1 && !len)) goto done;
275 in_quotes = TRUE;
276 len++;
277 break;
278 default:
279 state = state_token;
280 if (!count) in_quotes = FALSE;
281 else in_quotes = TRUE;
282 len++;
283 break;
285 break;
287 default: break;
289 if (!ignore) *out++ = *p;
292 done:
293 if (!len) *value = 0;
294 else *out = 0;
296 *quotes = count;
297 return p - str;
300 static void remove_quotes( WCHAR *str )
302 WCHAR *p = str;
303 int len = strlenW( str );
305 while ((p = strchrW( p, '"' )))
307 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
308 p++;
312 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
313 BOOL preserve_case )
315 LPCWSTR ptr, ptr2;
316 int num_quotes;
317 DWORD len;
318 WCHAR *prop, *val;
319 UINT r;
321 if (!szCommandLine)
322 return ERROR_SUCCESS;
324 ptr = szCommandLine;
325 while (*ptr)
327 while (*ptr == ' ') ptr++;
328 if (!*ptr) break;
330 ptr2 = strchrW( ptr, '=' );
331 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
333 len = ptr2 - ptr;
334 if (!len) return ERROR_INVALID_COMMAND_LINE;
336 while (ptr[len - 1] == ' ') len--;
338 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
339 memcpy( prop, ptr, len * sizeof(WCHAR) );
340 prop[len] = 0;
341 if (!preserve_case) struprW( prop );
343 ptr2++;
344 while (*ptr2 == ' ') ptr2++;
346 num_quotes = 0;
347 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
348 len = parse_prop( ptr2, val, &num_quotes );
349 if (num_quotes % 2)
351 WARN("unbalanced quotes\n");
352 msi_free( val );
353 msi_free( prop );
354 return ERROR_INVALID_COMMAND_LINE;
356 remove_quotes( val );
357 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
359 r = msi_set_property( package->db, prop, val, -1 );
360 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
361 msi_reset_folders( package, TRUE );
363 msi_free( val );
364 msi_free( prop );
366 ptr = ptr2 + len;
369 return ERROR_SUCCESS;
372 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
374 LPCWSTR pc;
375 LPWSTR p, *ret = NULL;
376 UINT count = 0;
378 if (!str)
379 return ret;
381 /* count the number of substrings */
382 for ( pc = str, count = 0; pc; count++ )
384 pc = strchrW( pc, sep );
385 if (pc)
386 pc++;
389 /* allocate space for an array of substring pointers and the substrings */
390 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
391 (lstrlenW(str)+1) * sizeof(WCHAR) );
392 if (!ret)
393 return ret;
395 /* copy the string and set the pointers */
396 p = (LPWSTR) &ret[count+1];
397 lstrcpyW( p, str );
398 for( count = 0; (ret[count] = p); count++ )
400 p = strchrW( p, sep );
401 if (p)
402 *p++ = 0;
405 return ret;
408 static BOOL ui_sequence_exists( MSIPACKAGE *package )
410 static const WCHAR query [] = {
411 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
412 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
413 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',0};
414 MSIQUERY *view;
415 DWORD count = 0;
417 if (!(MSI_DatabaseOpenViewW( package->db, query, &view )))
419 MSI_IterateRecords( view, &count, NULL, package );
420 msiobj_release( &view->hdr );
422 return count != 0;
425 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
427 WCHAR *source, *check, *p, *db;
428 DWORD len;
430 if (!(db = msi_dup_property( package->db, szOriginalDatabase )))
431 return ERROR_OUTOFMEMORY;
433 if (!(p = strrchrW( db, '\\' )) && !(p = strrchrW( db, '/' )))
435 msi_free(db);
436 return ERROR_SUCCESS;
438 len = p - db + 2;
439 source = msi_alloc( len * sizeof(WCHAR) );
440 lstrcpynW( source, db, len );
441 msi_free( db );
443 check = msi_dup_property( package->db, szSourceDir );
444 if (!check || replace)
446 UINT r = msi_set_property( package->db, szSourceDir, source, -1 );
447 if (r == ERROR_SUCCESS)
448 msi_reset_folders( package, TRUE );
450 msi_free( check );
452 check = msi_dup_property( package->db, szSOURCEDIR );
453 if (!check || replace)
454 msi_set_property( package->db, szSOURCEDIR, source, -1 );
456 msi_free( check );
457 msi_free( source );
459 return ERROR_SUCCESS;
462 static BOOL needs_ui_sequence(MSIPACKAGE *package)
464 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
467 UINT msi_set_context(MSIPACKAGE *package)
469 UINT r = msi_locate_product( package->ProductCode, &package->Context );
470 if (r != ERROR_SUCCESS)
472 int num = msi_get_property_int( package->db, szAllUsers, 0 );
473 if (num == 1 || num == 2)
474 package->Context = MSIINSTALLCONTEXT_MACHINE;
475 else
476 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
478 return ERROR_SUCCESS;
481 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
483 UINT rc;
484 LPCWSTR cond, action;
485 MSIPACKAGE *package = param;
487 action = MSI_RecordGetString(row,1);
488 if (!action)
490 ERR("Error is retrieving action name\n");
491 return ERROR_FUNCTION_FAILED;
494 /* check conditions */
495 cond = MSI_RecordGetString(row,2);
497 /* this is a hack to skip errors in the condition code */
498 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
500 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
501 return ERROR_SUCCESS;
504 if (needs_ui_sequence(package))
505 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
506 else
507 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
509 msi_dialog_check_messages( NULL );
511 if (package->CurrentInstallState != ERROR_SUCCESS)
512 rc = package->CurrentInstallState;
514 if (rc == ERROR_FUNCTION_NOT_CALLED)
515 rc = ERROR_SUCCESS;
517 if (rc != ERROR_SUCCESS)
518 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
520 if (package->need_reboot_now)
522 TRACE("action %s asked for immediate reboot, suspending installation\n",
523 debugstr_w(action));
524 rc = ACTION_ForceReboot( package );
526 return rc;
529 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
531 static const WCHAR query[] = {
532 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
533 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
534 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
535 '`','S','e','q','u','e','n','c','e','`',0};
536 MSIQUERY *view;
537 UINT r;
539 TRACE("%p %s\n", package, debugstr_w(table));
541 r = MSI_OpenQuery( package->db, &view, query, table );
542 if (r == ERROR_SUCCESS)
544 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
545 msiobj_release(&view->hdr);
547 return r;
550 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
552 static const WCHAR query[] = {
553 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
554 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
555 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
556 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
557 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
558 static const WCHAR query_validate[] = {
559 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
560 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
561 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
562 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
563 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
564 MSIQUERY *view;
565 INT seq = 0;
566 UINT rc;
568 if (package->script->ExecuteSequenceRun)
570 TRACE("Execute Sequence already Run\n");
571 return ERROR_SUCCESS;
574 package->script->ExecuteSequenceRun = TRUE;
576 /* get the sequence number */
577 if (UIran)
579 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
580 if (!row) return ERROR_FUNCTION_FAILED;
581 seq = MSI_RecordGetInteger(row,1);
582 msiobj_release(&row->hdr);
584 rc = MSI_OpenQuery(package->db, &view, query, seq);
585 if (rc == ERROR_SUCCESS)
587 TRACE("Running the actions\n");
589 msi_set_property( package->db, szSourceDir, NULL, -1 );
590 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
591 msiobj_release(&view->hdr);
593 return rc;
596 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
598 static const WCHAR query[] = {
599 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
600 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
601 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
602 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
603 MSIQUERY *view;
604 UINT rc;
606 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
607 if (rc == ERROR_SUCCESS)
609 TRACE("Running the actions\n");
610 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
611 msiobj_release(&view->hdr);
613 return rc;
616 /********************************************************
617 * ACTION helper functions and functions that perform the actions
618 *******************************************************/
619 static BOOL ACTION_HandleCustomAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc, UINT script )
621 BOOL ret=FALSE;
622 UINT arc;
624 arc = ACTION_CustomAction( package, action, script );
625 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
627 *rc = arc;
628 ret = TRUE;
630 return ret;
633 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
635 MSICOMPONENT *comp;
637 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
639 if (!strcmpW( Component, comp->Component )) return comp;
641 return NULL;
644 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
646 MSIFEATURE *feature;
648 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
650 if (!strcmpW( Feature, feature->Feature )) return feature;
652 return NULL;
655 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
657 MSIFILE *file;
659 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
661 if (!strcmpW( key, file->File )) return file;
663 return NULL;
666 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
668 MSIFOLDER *folder;
670 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
672 if (!strcmpW( dir, folder->Directory )) return folder;
674 return NULL;
678 * Recursively create all directories in the path.
679 * shamelessly stolen from setupapi/queue.c
681 BOOL msi_create_full_path( const WCHAR *path )
683 BOOL ret = TRUE;
684 WCHAR *new_path;
685 int len;
687 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
688 strcpyW( new_path, path );
690 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
691 new_path[len - 1] = 0;
693 while (!CreateDirectoryW( new_path, NULL ))
695 WCHAR *slash;
696 DWORD last_error = GetLastError();
697 if (last_error == ERROR_ALREADY_EXISTS) break;
698 if (last_error != ERROR_PATH_NOT_FOUND)
700 ret = FALSE;
701 break;
703 if (!(slash = strrchrW( new_path, '\\' )))
705 ret = FALSE;
706 break;
708 len = slash - new_path;
709 new_path[len] = 0;
710 if (!msi_create_full_path( new_path ))
712 ret = FALSE;
713 break;
715 new_path[len] = '\\';
717 msi_free( new_path );
718 return ret;
721 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
723 MSIRECORD *row;
725 row = MSI_CreateRecord( 4 );
726 MSI_RecordSetInteger( row, 1, a );
727 MSI_RecordSetInteger( row, 2, b );
728 MSI_RecordSetInteger( row, 3, c );
729 MSI_RecordSetInteger( row, 4, d );
730 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
731 msiobj_release( &row->hdr );
733 msi_dialog_check_messages( NULL );
736 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
738 static const WCHAR query[] =
739 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
740 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
741 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
742 WCHAR message[1024];
743 MSIRECORD *row = 0;
744 DWORD size;
746 if (!package->LastAction || strcmpW( package->LastAction, action ))
748 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
750 if (MSI_RecordIsNull( row, 3 ))
752 msiobj_release( &row->hdr );
753 return;
755 /* update the cached action format */
756 msi_free( package->ActionFormat );
757 package->ActionFormat = msi_dup_record_field( row, 3 );
758 msi_free( package->LastAction );
759 package->LastAction = strdupW( action );
760 msiobj_release( &row->hdr );
762 size = 1024;
763 MSI_RecordSetStringW( record, 0, package->ActionFormat );
764 MSI_FormatRecordW( package, record, message, &size );
765 row = MSI_CreateRecord( 1 );
766 MSI_RecordSetStringW( row, 1, message );
767 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
768 msiobj_release( &row->hdr );
771 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
773 if (!comp->Enabled)
775 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
776 return INSTALLSTATE_UNKNOWN;
778 if (package->need_rollback) return comp->Installed;
779 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
781 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
782 return INSTALLSTATE_UNKNOWN;
784 return comp->ActionRequest;
787 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
789 if (package->need_rollback) return feature->Installed;
790 return feature->ActionRequest;
793 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
795 MSIPACKAGE *package = param;
796 LPCWSTR dir, component, full_path;
797 MSIRECORD *uirow;
798 MSIFOLDER *folder;
799 MSICOMPONENT *comp;
801 component = MSI_RecordGetString(row, 2);
802 if (!component)
803 return ERROR_SUCCESS;
805 comp = msi_get_loaded_component(package, component);
806 if (!comp)
807 return ERROR_SUCCESS;
809 comp->Action = msi_get_component_action( package, comp );
810 if (comp->Action != INSTALLSTATE_LOCAL)
812 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
813 return ERROR_SUCCESS;
816 dir = MSI_RecordGetString(row,1);
817 if (!dir)
819 ERR("Unable to get folder id\n");
820 return ERROR_SUCCESS;
823 uirow = MSI_CreateRecord(1);
824 MSI_RecordSetStringW(uirow, 1, dir);
825 msi_ui_actiondata(package, szCreateFolders, uirow);
826 msiobj_release(&uirow->hdr);
828 full_path = msi_get_target_folder( package, dir );
829 if (!full_path)
831 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
832 return ERROR_SUCCESS;
834 TRACE("folder is %s\n", debugstr_w(full_path));
836 folder = msi_get_loaded_folder( package, dir );
837 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
838 folder->State = FOLDER_STATE_CREATED;
839 return ERROR_SUCCESS;
842 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
844 static const WCHAR query[] = {
845 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
846 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
847 MSIQUERY *view;
848 UINT rc;
850 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
851 if (rc != ERROR_SUCCESS)
852 return ERROR_SUCCESS;
854 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
855 msiobj_release(&view->hdr);
856 return rc;
859 static void remove_persistent_folder( MSIFOLDER *folder )
861 FolderList *fl;
863 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
865 remove_persistent_folder( fl->folder );
867 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
869 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
873 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
875 MSIPACKAGE *package = param;
876 LPCWSTR dir, component, full_path;
877 MSIRECORD *uirow;
878 MSIFOLDER *folder;
879 MSICOMPONENT *comp;
881 component = MSI_RecordGetString(row, 2);
882 if (!component)
883 return ERROR_SUCCESS;
885 comp = msi_get_loaded_component(package, component);
886 if (!comp)
887 return ERROR_SUCCESS;
889 comp->Action = msi_get_component_action( package, comp );
890 if (comp->Action != INSTALLSTATE_ABSENT)
892 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
893 return ERROR_SUCCESS;
896 dir = MSI_RecordGetString( row, 1 );
897 if (!dir)
899 ERR("Unable to get folder id\n");
900 return ERROR_SUCCESS;
903 full_path = msi_get_target_folder( package, dir );
904 if (!full_path)
906 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
907 return ERROR_SUCCESS;
909 TRACE("folder is %s\n", debugstr_w(full_path));
911 uirow = MSI_CreateRecord( 1 );
912 MSI_RecordSetStringW( uirow, 1, dir );
913 msi_ui_actiondata( package, szRemoveFolders, uirow );
914 msiobj_release( &uirow->hdr );
916 folder = msi_get_loaded_folder( package, dir );
917 remove_persistent_folder( folder );
918 return ERROR_SUCCESS;
921 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
923 static const WCHAR query[] = {
924 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
925 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
926 MSIQUERY *view;
927 UINT rc;
929 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
930 if (rc != ERROR_SUCCESS)
931 return ERROR_SUCCESS;
933 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
934 msiobj_release( &view->hdr );
935 return rc;
938 static UINT load_component( MSIRECORD *row, LPVOID param )
940 MSIPACKAGE *package = param;
941 MSICOMPONENT *comp;
943 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
944 if (!comp)
945 return ERROR_FUNCTION_FAILED;
947 list_add_tail( &package->components, &comp->entry );
949 /* fill in the data */
950 comp->Component = msi_dup_record_field( row, 1 );
952 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
954 comp->ComponentId = msi_dup_record_field( row, 2 );
955 comp->Directory = msi_dup_record_field( row, 3 );
956 comp->Attributes = MSI_RecordGetInteger(row,4);
957 comp->Condition = msi_dup_record_field( row, 5 );
958 comp->KeyPath = msi_dup_record_field( row, 6 );
960 comp->Installed = INSTALLSTATE_UNKNOWN;
961 comp->Action = INSTALLSTATE_UNKNOWN;
962 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
964 comp->assembly = msi_load_assembly( package, comp );
965 return ERROR_SUCCESS;
968 UINT msi_load_all_components( MSIPACKAGE *package )
970 static const WCHAR query[] = {
971 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
972 '`','C','o','m','p','o','n','e','n','t','`',0};
973 MSIQUERY *view;
974 UINT r;
976 if (!list_empty(&package->components))
977 return ERROR_SUCCESS;
979 r = MSI_DatabaseOpenViewW( package->db, query, &view );
980 if (r != ERROR_SUCCESS)
981 return r;
983 if (!msi_init_assembly_caches( package ))
985 ERR("can't initialize assembly caches\n");
986 msiobj_release( &view->hdr );
987 return ERROR_FUNCTION_FAILED;
990 r = MSI_IterateRecords(view, NULL, load_component, package);
991 msiobj_release(&view->hdr);
992 return r;
995 typedef struct {
996 MSIPACKAGE *package;
997 MSIFEATURE *feature;
998 } _ilfs;
1000 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1002 ComponentList *cl;
1004 cl = msi_alloc( sizeof (*cl) );
1005 if ( !cl )
1006 return ERROR_NOT_ENOUGH_MEMORY;
1007 cl->component = comp;
1008 list_add_tail( &feature->Components, &cl->entry );
1010 return ERROR_SUCCESS;
1013 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1015 FeatureList *fl;
1017 fl = msi_alloc( sizeof(*fl) );
1018 if ( !fl )
1019 return ERROR_NOT_ENOUGH_MEMORY;
1020 fl->feature = child;
1021 list_add_tail( &parent->Children, &fl->entry );
1023 return ERROR_SUCCESS;
1026 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1028 _ilfs* ilfs = param;
1029 LPCWSTR component;
1030 MSICOMPONENT *comp;
1032 component = MSI_RecordGetString(row,1);
1034 /* check to see if the component is already loaded */
1035 comp = msi_get_loaded_component( ilfs->package, component );
1036 if (!comp)
1038 WARN("ignoring unknown component %s\n", debugstr_w(component));
1039 return ERROR_SUCCESS;
1041 add_feature_component( ilfs->feature, comp );
1042 comp->Enabled = TRUE;
1044 return ERROR_SUCCESS;
1047 static UINT load_feature(MSIRECORD * row, LPVOID param)
1049 static const WCHAR query[] = {
1050 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1051 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1052 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1053 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1054 MSIPACKAGE *package = param;
1055 MSIFEATURE *feature;
1056 MSIQUERY *view;
1057 _ilfs ilfs;
1058 UINT rc;
1060 /* fill in the data */
1062 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1063 if (!feature)
1064 return ERROR_NOT_ENOUGH_MEMORY;
1066 list_init( &feature->Children );
1067 list_init( &feature->Components );
1069 feature->Feature = msi_dup_record_field( row, 1 );
1071 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1073 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1074 feature->Title = msi_dup_record_field( row, 3 );
1075 feature->Description = msi_dup_record_field( row, 4 );
1077 if (!MSI_RecordIsNull(row,5))
1078 feature->Display = MSI_RecordGetInteger(row,5);
1080 feature->Level= MSI_RecordGetInteger(row,6);
1081 feature->Directory = msi_dup_record_field( row, 7 );
1082 feature->Attributes = MSI_RecordGetInteger(row,8);
1084 feature->Installed = INSTALLSTATE_UNKNOWN;
1085 feature->Action = INSTALLSTATE_UNKNOWN;
1086 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1088 list_add_tail( &package->features, &feature->entry );
1090 /* load feature components */
1092 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1093 if (rc != ERROR_SUCCESS)
1094 return ERROR_SUCCESS;
1096 ilfs.package = package;
1097 ilfs.feature = feature;
1099 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1100 msiobj_release(&view->hdr);
1101 return rc;
1104 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1106 MSIPACKAGE *package = param;
1107 MSIFEATURE *parent, *child;
1109 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1110 if (!child)
1111 return ERROR_FUNCTION_FAILED;
1113 if (!child->Feature_Parent)
1114 return ERROR_SUCCESS;
1116 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1117 if (!parent)
1118 return ERROR_FUNCTION_FAILED;
1120 add_feature_child( parent, child );
1121 return ERROR_SUCCESS;
1124 UINT msi_load_all_features( MSIPACKAGE *package )
1126 static const WCHAR query[] = {
1127 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1128 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1129 '`','D','i','s','p','l','a','y','`',0};
1130 MSIQUERY *view;
1131 UINT r;
1133 if (!list_empty(&package->features))
1134 return ERROR_SUCCESS;
1136 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1137 if (r != ERROR_SUCCESS)
1138 return r;
1140 r = MSI_IterateRecords( view, NULL, load_feature, package );
1141 if (r != ERROR_SUCCESS)
1143 msiobj_release( &view->hdr );
1144 return r;
1146 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1147 msiobj_release( &view->hdr );
1148 return r;
1151 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1153 if (!p)
1154 return p;
1155 p = strchrW(p, ch);
1156 if (!p)
1157 return p;
1158 *p = 0;
1159 return p+1;
1162 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1164 static const WCHAR query[] = {
1165 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1166 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1167 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1168 MSIQUERY *view = NULL;
1169 MSIRECORD *row = NULL;
1170 UINT r;
1172 TRACE("%s\n", debugstr_w(file->File));
1174 r = MSI_OpenQuery(package->db, &view, query, file->File);
1175 if (r != ERROR_SUCCESS)
1176 goto done;
1178 r = MSI_ViewExecute(view, NULL);
1179 if (r != ERROR_SUCCESS)
1180 goto done;
1182 r = MSI_ViewFetch(view, &row);
1183 if (r != ERROR_SUCCESS)
1184 goto done;
1186 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1187 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1188 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1189 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1190 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1192 done:
1193 if (view) msiobj_release(&view->hdr);
1194 if (row) msiobj_release(&row->hdr);
1195 return r;
1198 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1200 MSIRECORD *row;
1201 static const WCHAR query[] = {
1202 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1203 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1204 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1206 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1207 if (!row)
1209 WARN("query failed\n");
1210 return ERROR_FUNCTION_FAILED;
1213 file->disk_id = MSI_RecordGetInteger( row, 1 );
1214 msiobj_release( &row->hdr );
1215 return ERROR_SUCCESS;
1218 static UINT load_file(MSIRECORD *row, LPVOID param)
1220 MSIPACKAGE* package = param;
1221 LPCWSTR component;
1222 MSIFILE *file;
1224 /* fill in the data */
1226 file = msi_alloc_zero( sizeof (MSIFILE) );
1227 if (!file)
1228 return ERROR_NOT_ENOUGH_MEMORY;
1230 file->File = msi_dup_record_field( row, 1 );
1232 component = MSI_RecordGetString( row, 2 );
1233 file->Component = msi_get_loaded_component( package, component );
1235 if (!file->Component)
1237 WARN("Component not found: %s\n", debugstr_w(component));
1238 msi_free(file->File);
1239 msi_free(file);
1240 return ERROR_SUCCESS;
1243 file->FileName = msi_dup_record_field( row, 3 );
1244 msi_reduce_to_long_filename( file->FileName );
1246 file->ShortName = msi_dup_record_field( row, 3 );
1247 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1249 file->FileSize = MSI_RecordGetInteger( row, 4 );
1250 file->Version = msi_dup_record_field( row, 5 );
1251 file->Language = msi_dup_record_field( row, 6 );
1252 file->Attributes = MSI_RecordGetInteger( row, 7 );
1253 file->Sequence = MSI_RecordGetInteger( row, 8 );
1255 file->state = msifs_invalid;
1257 /* if the compressed bits are not set in the file attributes,
1258 * then read the information from the package word count property
1260 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1262 file->IsCompressed = FALSE;
1264 else if (file->Attributes &
1265 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1267 file->IsCompressed = TRUE;
1269 else if (file->Attributes & msidbFileAttributesNoncompressed)
1271 file->IsCompressed = FALSE;
1273 else
1275 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1278 load_file_hash(package, file);
1279 load_file_disk_id(package, file);
1281 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1283 list_add_tail( &package->files, &file->entry );
1285 return ERROR_SUCCESS;
1288 static UINT load_all_files(MSIPACKAGE *package)
1290 static const WCHAR query[] = {
1291 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1292 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1293 '`','S','e','q','u','e','n','c','e','`', 0};
1294 MSIQUERY *view;
1295 UINT rc;
1297 if (!list_empty(&package->files))
1298 return ERROR_SUCCESS;
1300 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1301 if (rc != ERROR_SUCCESS)
1302 return ERROR_SUCCESS;
1304 rc = MSI_IterateRecords(view, NULL, load_file, package);
1305 msiobj_release(&view->hdr);
1306 return rc;
1309 static UINT load_media( MSIRECORD *row, LPVOID param )
1311 MSIPACKAGE *package = param;
1312 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1313 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1315 /* FIXME: load external cabinets and directory sources too */
1316 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1317 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1318 return ERROR_SUCCESS;
1321 static UINT load_all_media( MSIPACKAGE *package )
1323 static const WCHAR query[] = {
1324 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1325 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1326 '`','D','i','s','k','I','d','`',0};
1327 MSIQUERY *view;
1328 UINT r;
1330 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1331 if (r != ERROR_SUCCESS)
1332 return ERROR_SUCCESS;
1334 r = MSI_IterateRecords( view, NULL, load_media, package );
1335 msiobj_release( &view->hdr );
1336 return r;
1339 static UINT load_patch(MSIRECORD *row, LPVOID param)
1341 MSIPACKAGE *package = param;
1342 MSIFILEPATCH *patch;
1343 LPWSTR file_key;
1345 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1346 if (!patch)
1347 return ERROR_NOT_ENOUGH_MEMORY;
1349 file_key = msi_dup_record_field( row, 1 );
1350 patch->File = msi_get_loaded_file( package, file_key );
1351 msi_free(file_key);
1353 if( !patch->File )
1355 ERR("Failed to find target for patch in File table\n");
1356 msi_free(patch);
1357 return ERROR_FUNCTION_FAILED;
1360 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1362 /* FIXME: The database should be properly transformed */
1363 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1365 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1366 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1367 patch->IsApplied = FALSE;
1369 /* FIXME:
1370 * Header field - for patch validation.
1371 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1374 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1376 list_add_tail( &package->filepatches, &patch->entry );
1378 return ERROR_SUCCESS;
1381 static UINT load_all_patches(MSIPACKAGE *package)
1383 static const WCHAR query[] = {
1384 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1385 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1386 '`','S','e','q','u','e','n','c','e','`',0};
1387 MSIQUERY *view;
1388 UINT rc;
1390 if (!list_empty(&package->filepatches))
1391 return ERROR_SUCCESS;
1393 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1394 if (rc != ERROR_SUCCESS)
1395 return ERROR_SUCCESS;
1397 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1398 msiobj_release(&view->hdr);
1399 return rc;
1402 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1404 static const WCHAR query[] = {
1405 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1406 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1407 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1408 MSIQUERY *view;
1410 folder->persistent = FALSE;
1411 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1413 if (!MSI_ViewExecute( view, NULL ))
1415 MSIRECORD *rec;
1416 if (!MSI_ViewFetch( view, &rec ))
1418 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1419 folder->persistent = TRUE;
1420 msiobj_release( &rec->hdr );
1423 msiobj_release( &view->hdr );
1425 return ERROR_SUCCESS;
1428 static UINT load_folder( MSIRECORD *row, LPVOID param )
1430 MSIPACKAGE *package = param;
1431 static WCHAR szEmpty[] = { 0 };
1432 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1433 MSIFOLDER *folder;
1435 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1436 list_init( &folder->children );
1437 folder->Directory = msi_dup_record_field( row, 1 );
1438 folder->Parent = msi_dup_record_field( row, 2 );
1439 p = msi_dup_record_field(row, 3);
1441 TRACE("%s\n", debugstr_w(folder->Directory));
1443 /* split src and target dir */
1444 tgt_short = p;
1445 src_short = folder_split_path( p, ':' );
1447 /* split the long and short paths */
1448 tgt_long = folder_split_path( tgt_short, '|' );
1449 src_long = folder_split_path( src_short, '|' );
1451 /* check for no-op dirs */
1452 if (tgt_short && !strcmpW( szDot, tgt_short ))
1453 tgt_short = szEmpty;
1454 if (src_short && !strcmpW( szDot, src_short ))
1455 src_short = szEmpty;
1457 if (!tgt_long)
1458 tgt_long = tgt_short;
1460 if (!src_short) {
1461 src_short = tgt_short;
1462 src_long = tgt_long;
1465 if (!src_long)
1466 src_long = src_short;
1468 /* FIXME: use the target short path too */
1469 folder->TargetDefault = strdupW(tgt_long);
1470 folder->SourceShortPath = strdupW(src_short);
1471 folder->SourceLongPath = strdupW(src_long);
1472 msi_free(p);
1474 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1475 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1476 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1478 load_folder_persistence( package, folder );
1480 list_add_tail( &package->folders, &folder->entry );
1481 return ERROR_SUCCESS;
1484 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1486 FolderList *fl;
1488 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1489 fl->folder = child;
1490 list_add_tail( &parent->children, &fl->entry );
1491 return ERROR_SUCCESS;
1494 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1496 MSIPACKAGE *package = param;
1497 MSIFOLDER *parent, *child;
1499 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1500 return ERROR_FUNCTION_FAILED;
1502 if (!child->Parent) return ERROR_SUCCESS;
1504 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1505 return ERROR_FUNCTION_FAILED;
1507 return add_folder_child( parent, child );
1510 static UINT load_all_folders( MSIPACKAGE *package )
1512 static const WCHAR query[] = {
1513 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1514 '`','D','i','r','e','c','t','o','r','y','`',0};
1515 MSIQUERY *view;
1516 UINT r;
1518 if (!list_empty(&package->folders))
1519 return ERROR_SUCCESS;
1521 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1522 if (r != ERROR_SUCCESS)
1523 return r;
1525 r = MSI_IterateRecords( view, NULL, load_folder, package );
1526 if (r != ERROR_SUCCESS)
1528 msiobj_release( &view->hdr );
1529 return r;
1531 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1532 msiobj_release( &view->hdr );
1533 return r;
1536 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1538 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1539 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1541 load_all_folders( package );
1542 msi_load_all_components( package );
1543 msi_load_all_features( package );
1544 load_all_files( package );
1545 load_all_patches( package );
1546 load_all_media( package );
1548 return ERROR_SUCCESS;
1551 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1553 const WCHAR *action = package->script->Actions[script][index];
1554 ui_actionstart( package, action );
1555 TRACE("executing %s\n", debugstr_w(action));
1556 return ACTION_PerformAction( package, action, script );
1559 static UINT execute_script( MSIPACKAGE *package, UINT script )
1561 UINT i, rc = ERROR_SUCCESS;
1563 TRACE("executing script %u\n", script);
1565 if (!package->script)
1567 ERR("no script!\n");
1568 return ERROR_FUNCTION_FAILED;
1570 if (script == SCRIPT_ROLLBACK)
1572 for (i = package->script->ActionCount[script]; i > 0; i--)
1574 rc = execute_script_action( package, script, i - 1 );
1575 if (rc != ERROR_SUCCESS) break;
1578 else
1580 for (i = 0; i < package->script->ActionCount[script]; i++)
1582 rc = execute_script_action( package, script, i );
1583 if (rc != ERROR_SUCCESS) break;
1586 msi_free_action_script(package, script);
1587 return rc;
1590 static UINT ACTION_FileCost(MSIPACKAGE *package)
1592 return ERROR_SUCCESS;
1595 static void get_client_counts( MSIPACKAGE *package )
1597 MSICOMPONENT *comp;
1598 HKEY hkey;
1600 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1602 if (!comp->ComponentId) continue;
1604 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1605 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1607 comp->num_clients = 0;
1608 continue;
1610 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1611 NULL, NULL, NULL, NULL );
1612 RegCloseKey( hkey );
1616 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1618 MSICOMPONENT *comp;
1619 UINT r;
1621 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1623 if (!comp->ComponentId) continue;
1625 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1626 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1627 &comp->Installed );
1628 if (r == ERROR_SUCCESS) continue;
1630 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1631 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1632 &comp->Installed );
1633 if (r == ERROR_SUCCESS) continue;
1635 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1636 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1637 &comp->Installed );
1638 if (r == ERROR_SUCCESS) continue;
1640 comp->Installed = INSTALLSTATE_ABSENT;
1644 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1646 MSIFEATURE *feature;
1648 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1650 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1652 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1653 feature->Installed = INSTALLSTATE_ABSENT;
1654 else
1655 feature->Installed = state;
1659 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1661 return (feature->Level > 0 && feature->Level <= level);
1664 static BOOL process_state_property(MSIPACKAGE* package, int level,
1665 LPCWSTR property, INSTALLSTATE state)
1667 LPWSTR override;
1668 MSIFEATURE *feature;
1670 override = msi_dup_property( package->db, property );
1671 if (!override)
1672 return FALSE;
1674 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1676 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1677 continue;
1679 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1681 if (!strcmpiW( override, szAll ))
1683 if (feature->Installed != state)
1685 feature->Action = state;
1686 feature->ActionRequest = state;
1689 else
1691 LPWSTR ptr = override;
1692 LPWSTR ptr2 = strchrW(override,',');
1694 while (ptr)
1696 int len = ptr2 - ptr;
1698 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1699 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1701 if (feature->Installed != state)
1703 feature->Action = state;
1704 feature->ActionRequest = state;
1706 break;
1708 if (ptr2)
1710 ptr=ptr2+1;
1711 ptr2 = strchrW(ptr,',');
1713 else
1714 break;
1718 msi_free(override);
1719 return TRUE;
1722 static BOOL process_overrides( MSIPACKAGE *package, int level )
1724 static const WCHAR szAddLocal[] =
1725 {'A','D','D','L','O','C','A','L',0};
1726 static const WCHAR szAddSource[] =
1727 {'A','D','D','S','O','U','R','C','E',0};
1728 static const WCHAR szAdvertise[] =
1729 {'A','D','V','E','R','T','I','S','E',0};
1730 BOOL ret = FALSE;
1732 /* all these activation/deactivation things happen in order and things
1733 * later on the list override things earlier on the list.
1735 * 0 INSTALLLEVEL processing
1736 * 1 ADDLOCAL
1737 * 2 REMOVE
1738 * 3 ADDSOURCE
1739 * 4 ADDDEFAULT
1740 * 5 REINSTALL
1741 * 6 ADVERTISE
1742 * 7 COMPADDLOCAL
1743 * 8 COMPADDSOURCE
1744 * 9 FILEADDLOCAL
1745 * 10 FILEADDSOURCE
1746 * 11 FILEADDDEFAULT
1748 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1749 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1750 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1751 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1752 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1754 if (ret && !package->full_reinstall)
1755 msi_set_property( package->db, szPreselected, szOne, -1 );
1757 return ret;
1760 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1762 int level;
1763 MSICOMPONENT* component;
1764 MSIFEATURE *feature;
1766 TRACE("Checking Install Level\n");
1768 level = msi_get_property_int(package->db, szInstallLevel, 1);
1770 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1772 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1774 if (!is_feature_selected( feature, level )) continue;
1776 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1778 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1780 feature->Action = INSTALLSTATE_SOURCE;
1781 feature->ActionRequest = INSTALLSTATE_SOURCE;
1783 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1785 feature->Action = INSTALLSTATE_ADVERTISED;
1786 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1788 else
1790 feature->Action = INSTALLSTATE_LOCAL;
1791 feature->ActionRequest = INSTALLSTATE_LOCAL;
1795 /* disable child features of unselected parent or follow parent */
1796 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1798 FeatureList *fl;
1800 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1802 if (!is_feature_selected( feature, level ))
1804 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1805 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1807 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1809 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1810 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1811 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1812 fl->feature->Action = feature->Action;
1813 fl->feature->ActionRequest = feature->ActionRequest;
1818 else /* preselected */
1820 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1822 if (!is_feature_selected( feature, level )) continue;
1824 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1826 if (feature->Installed == INSTALLSTATE_ABSENT)
1828 feature->Action = INSTALLSTATE_UNKNOWN;
1829 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1831 else
1833 feature->Action = feature->Installed;
1834 feature->ActionRequest = feature->Installed;
1838 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1840 FeatureList *fl;
1842 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1844 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1845 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1847 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1848 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1849 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1850 fl->feature->Action = feature->Action;
1851 fl->feature->ActionRequest = feature->ActionRequest;
1857 /* now we want to set component state based based on feature state */
1858 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1860 ComponentList *cl;
1862 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1863 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1864 feature->ActionRequest, feature->Action);
1866 if (!is_feature_selected( feature, level )) continue;
1868 /* features with components that have compressed files are made local */
1869 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1871 if (cl->component->ForceLocalState &&
1872 feature->ActionRequest == INSTALLSTATE_SOURCE)
1874 feature->Action = INSTALLSTATE_LOCAL;
1875 feature->ActionRequest = INSTALLSTATE_LOCAL;
1876 break;
1880 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1882 component = cl->component;
1884 switch (feature->ActionRequest)
1886 case INSTALLSTATE_ABSENT:
1887 component->anyAbsent = 1;
1888 break;
1889 case INSTALLSTATE_ADVERTISED:
1890 component->hasAdvertisedFeature = 1;
1891 break;
1892 case INSTALLSTATE_SOURCE:
1893 component->hasSourceFeature = 1;
1894 break;
1895 case INSTALLSTATE_LOCAL:
1896 component->hasLocalFeature = 1;
1897 break;
1898 case INSTALLSTATE_DEFAULT:
1899 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1900 component->hasAdvertisedFeature = 1;
1901 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1902 component->hasSourceFeature = 1;
1903 else
1904 component->hasLocalFeature = 1;
1905 break;
1906 default:
1907 break;
1912 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1914 /* check if it's local or source */
1915 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1916 (component->hasLocalFeature || component->hasSourceFeature))
1918 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1919 !component->ForceLocalState)
1921 component->Action = INSTALLSTATE_SOURCE;
1922 component->ActionRequest = INSTALLSTATE_SOURCE;
1924 else
1926 component->Action = INSTALLSTATE_LOCAL;
1927 component->ActionRequest = INSTALLSTATE_LOCAL;
1929 continue;
1932 /* if any feature is local, the component must be local too */
1933 if (component->hasLocalFeature)
1935 component->Action = INSTALLSTATE_LOCAL;
1936 component->ActionRequest = INSTALLSTATE_LOCAL;
1937 continue;
1939 if (component->hasSourceFeature)
1941 component->Action = INSTALLSTATE_SOURCE;
1942 component->ActionRequest = INSTALLSTATE_SOURCE;
1943 continue;
1945 if (component->hasAdvertisedFeature)
1947 component->Action = INSTALLSTATE_ADVERTISED;
1948 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1949 continue;
1951 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1952 if (component->anyAbsent && component->ComponentId)
1954 component->Action = INSTALLSTATE_ABSENT;
1955 component->ActionRequest = INSTALLSTATE_ABSENT;
1959 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1961 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1963 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1964 component->Action = INSTALLSTATE_LOCAL;
1965 component->ActionRequest = INSTALLSTATE_LOCAL;
1968 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1969 component->Installed == INSTALLSTATE_SOURCE &&
1970 component->hasSourceFeature)
1972 component->Action = INSTALLSTATE_UNKNOWN;
1973 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1976 TRACE("component %s (installed %d request %d action %d)\n",
1977 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1979 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1980 component->num_clients++;
1981 else if (component->Action == INSTALLSTATE_ABSENT)
1982 component->num_clients--;
1985 return ERROR_SUCCESS;
1988 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1990 MSIPACKAGE *package = param;
1991 LPCWSTR name;
1992 MSIFEATURE *feature;
1994 name = MSI_RecordGetString( row, 1 );
1996 feature = msi_get_loaded_feature( package, name );
1997 if (!feature)
1998 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1999 else
2001 LPCWSTR Condition;
2002 Condition = MSI_RecordGetString(row,3);
2004 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2006 int level = MSI_RecordGetInteger(row,2);
2007 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2008 feature->Level = level;
2011 return ERROR_SUCCESS;
2014 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2016 static const WCHAR name[] = {'\\',0};
2017 VS_FIXEDFILEINFO *ptr, *ret;
2018 LPVOID version;
2019 DWORD versize, handle;
2020 UINT sz;
2022 versize = GetFileVersionInfoSizeW( filename, &handle );
2023 if (!versize)
2024 return NULL;
2026 version = msi_alloc( versize );
2027 if (!version)
2028 return NULL;
2030 GetFileVersionInfoW( filename, 0, versize, version );
2032 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2034 msi_free( version );
2035 return NULL;
2038 ret = msi_alloc( sz );
2039 memcpy( ret, ptr, sz );
2041 msi_free( version );
2042 return ret;
2045 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2047 DWORD ms, ls;
2049 msi_parse_version_string( version, &ms, &ls );
2051 if (fi->dwFileVersionMS > ms) return 1;
2052 else if (fi->dwFileVersionMS < ms) return -1;
2053 else if (fi->dwFileVersionLS > ls) return 1;
2054 else if (fi->dwFileVersionLS < ls) return -1;
2055 return 0;
2058 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2060 DWORD ms1, ms2;
2062 msi_parse_version_string( ver1, &ms1, NULL );
2063 msi_parse_version_string( ver2, &ms2, NULL );
2065 if (ms1 > ms2) return 1;
2066 else if (ms1 < ms2) return -1;
2067 return 0;
2070 DWORD msi_get_disk_file_size( LPCWSTR filename )
2072 HANDLE file;
2073 DWORD size;
2075 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2076 if (file == INVALID_HANDLE_VALUE)
2077 return INVALID_FILE_SIZE;
2079 size = GetFileSize( file, NULL );
2080 TRACE("size is %u\n", size);
2081 CloseHandle( file );
2082 return size;
2085 BOOL msi_file_hash_matches( MSIFILE *file )
2087 UINT r;
2088 MSIFILEHASHINFO hash;
2090 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2091 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2092 if (r != ERROR_SUCCESS)
2093 return FALSE;
2095 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2098 static WCHAR *get_temp_dir( void )
2100 static UINT id;
2101 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2103 GetTempPathW( MAX_PATH, tmp );
2104 for (;;)
2106 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2107 if (CreateDirectoryW( dir, NULL )) break;
2109 return strdupW( dir );
2113 * msi_build_directory_name()
2115 * This function is to save messing round with directory names
2116 * It handles adding backslashes between path segments,
2117 * and can add \ at the end of the directory name if told to.
2119 * It takes a variable number of arguments.
2120 * It always allocates a new string for the result, so make sure
2121 * to free the return value when finished with it.
2123 * The first arg is the number of path segments that follow.
2124 * The arguments following count are a list of path segments.
2125 * A path segment may be NULL.
2127 * Path segments will be added with a \ separating them.
2128 * A \ will not be added after the last segment, however if the
2129 * last segment is NULL, then the last character will be a \
2131 WCHAR *msi_build_directory_name( DWORD count, ... )
2133 DWORD sz = 1, i;
2134 WCHAR *dir;
2135 va_list va;
2137 va_start( va, count );
2138 for (i = 0; i < count; i++)
2140 const WCHAR *str = va_arg( va, const WCHAR * );
2141 if (str) sz += strlenW( str ) + 1;
2143 va_end( va );
2145 dir = msi_alloc( sz * sizeof(WCHAR) );
2146 dir[0] = 0;
2148 va_start( va, count );
2149 for (i = 0; i < count; i++)
2151 const WCHAR *str = va_arg( va, const WCHAR * );
2152 if (!str) continue;
2153 strcatW( dir, str );
2154 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2156 va_end( va );
2157 return dir;
2160 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2162 MSIASSEMBLY *assembly = file->Component->assembly;
2164 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2166 msi_free( file->TargetPath );
2167 if (assembly && !assembly->application)
2169 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2170 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2171 msi_track_tempfile( package, file->TargetPath );
2173 else
2175 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2176 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2179 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2182 static UINT calculate_file_cost( MSIPACKAGE *package )
2184 VS_FIXEDFILEINFO *file_version;
2185 WCHAR *font_version;
2186 MSIFILE *file;
2188 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2190 MSICOMPONENT *comp = file->Component;
2191 DWORD file_size;
2193 if (!comp->Enabled) continue;
2195 if (file->IsCompressed)
2196 comp->ForceLocalState = TRUE;
2198 set_target_path( package, file );
2200 if ((comp->assembly && !comp->assembly->installed) ||
2201 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2203 comp->Cost += file->FileSize;
2204 continue;
2206 file_size = msi_get_disk_file_size( file->TargetPath );
2208 if (file->Version)
2210 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2212 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2214 comp->Cost += file->FileSize - file_size;
2216 msi_free( file_version );
2217 continue;
2219 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2221 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2223 comp->Cost += file->FileSize - file_size;
2225 msi_free( font_version );
2226 continue;
2229 if (file_size != file->FileSize)
2231 comp->Cost += file->FileSize - file_size;
2234 return ERROR_SUCCESS;
2237 WCHAR *msi_normalize_path( const WCHAR *in )
2239 const WCHAR *p = in;
2240 WCHAR *q, *ret;
2241 int n, len = strlenW( in ) + 2;
2243 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2245 len = 0;
2246 while (1)
2248 /* copy until the end of the string or a space */
2249 while (*p != ' ' && (*q = *p))
2251 p++, len++;
2252 /* reduce many backslashes to one */
2253 if (*p != '\\' || *q != '\\')
2254 q++;
2257 /* quit at the end of the string */
2258 if (!*p)
2259 break;
2261 /* count the number of spaces */
2262 n = 0;
2263 while (p[n] == ' ')
2264 n++;
2266 /* if it's leading or trailing space, skip it */
2267 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2268 p += n;
2269 else /* copy n spaces */
2270 while (n && (*q++ = *p++)) n--;
2272 while (q - ret > 0 && q[-1] == ' ') q--;
2273 if (q - ret > 0 && q[-1] != '\\')
2275 q[0] = '\\';
2276 q[1] = 0;
2278 return ret;
2281 static WCHAR *get_install_location( MSIPACKAGE *package )
2283 HKEY hkey;
2284 WCHAR *path;
2286 if (!package->ProductCode) return NULL;
2287 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2288 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2290 msi_free( path );
2291 path = NULL;
2293 RegCloseKey( hkey );
2294 return path;
2297 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2299 FolderList *fl;
2300 MSIFOLDER *folder, *parent, *child;
2301 WCHAR *path, *normalized_path;
2303 TRACE("resolving %s\n", debugstr_w(name));
2305 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2307 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2309 if (!(path = get_install_location( package )) &&
2310 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2312 path = msi_dup_property( package->db, szRootDrive );
2315 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2317 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2319 parent = msi_get_loaded_folder( package, folder->Parent );
2320 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2322 else
2323 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2325 normalized_path = msi_normalize_path( path );
2326 msi_free( path );
2327 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2329 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2330 msi_free( normalized_path );
2331 return;
2333 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2334 msi_free( folder->ResolvedTarget );
2335 folder->ResolvedTarget = normalized_path;
2337 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2339 child = fl->folder;
2340 msi_resolve_target_folder( package, child->Directory, load_prop );
2342 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2345 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2347 static const WCHAR query[] =
2348 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2349 '`','C','o','n','d','i','t','i','o','n','`',0};
2350 static const WCHAR szOutOfDiskSpace[] =
2351 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2352 static const WCHAR szPrimaryFolder[] =
2353 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2354 static const WCHAR szPrimaryVolumePath[] =
2355 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2356 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2357 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2358 'A','v','a','i','l','a','b','l','e',0};
2359 MSICOMPONENT *comp;
2360 MSIQUERY *view;
2361 WCHAR *level, *primary_key, *primary_folder;
2362 UINT rc;
2364 TRACE("Building directory properties\n");
2365 msi_resolve_target_folder( package, szTargetDir, TRUE );
2367 TRACE("Evaluating component conditions\n");
2368 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2370 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2372 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2373 comp->Enabled = FALSE;
2375 else
2376 comp->Enabled = TRUE;
2378 get_client_counts( package );
2380 /* read components states from the registry */
2381 ACTION_GetComponentInstallStates(package);
2382 ACTION_GetFeatureInstallStates(package);
2384 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2386 TRACE("Evaluating feature conditions\n");
2388 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2389 if (rc == ERROR_SUCCESS)
2391 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2392 msiobj_release( &view->hdr );
2393 if (rc != ERROR_SUCCESS)
2394 return rc;
2398 TRACE("Calculating file cost\n");
2399 calculate_file_cost( package );
2401 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2402 /* set default run level if not set */
2403 level = msi_dup_property( package->db, szInstallLevel );
2404 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2405 msi_free(level);
2407 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2409 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2411 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2412 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2414 ULARGE_INTEGER free;
2416 primary_folder[2] = 0;
2417 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2419 static const WCHAR fmtW[] = {'%','l','u',0};
2420 WCHAR buf[21];
2422 sprintfW( buf, fmtW, free.QuadPart / 512 );
2423 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2425 toupperW( primary_folder[0] );
2426 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2428 msi_free( primary_folder );
2430 msi_free( primary_key );
2433 /* FIXME: check volume disk space */
2434 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2436 return MSI_SetFeatureStates(package);
2439 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD *type, DWORD *size )
2441 BYTE *data = NULL;
2443 if (!value)
2445 *size = sizeof(WCHAR);
2446 *type = REG_SZ;
2447 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2448 return data;
2450 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2452 if (value[1]=='x')
2454 LPWSTR ptr;
2455 CHAR byte[5];
2456 LPWSTR deformated = NULL;
2457 int count;
2459 deformat_string(package, &value[2], &deformated);
2461 /* binary value type */
2462 ptr = deformated;
2463 *type = REG_BINARY;
2464 if (strlenW(ptr)%2)
2465 *size = (strlenW(ptr)/2)+1;
2466 else
2467 *size = strlenW(ptr)/2;
2469 data = msi_alloc(*size);
2471 byte[0] = '0';
2472 byte[1] = 'x';
2473 byte[4] = 0;
2474 count = 0;
2475 /* if uneven pad with a zero in front */
2476 if (strlenW(ptr)%2)
2478 byte[2]= '0';
2479 byte[3]= *ptr;
2480 ptr++;
2481 data[count] = (BYTE)strtol(byte,NULL,0);
2482 count ++;
2483 TRACE("Uneven byte count\n");
2485 while (*ptr)
2487 byte[2]= *ptr;
2488 ptr++;
2489 byte[3]= *ptr;
2490 ptr++;
2491 data[count] = (BYTE)strtol(byte,NULL,0);
2492 count ++;
2494 msi_free(deformated);
2496 TRACE("Data %i bytes(%i)\n",*size,count);
2498 else
2500 LPWSTR deformated;
2501 LPWSTR p;
2502 DWORD d = 0;
2503 deformat_string(package, &value[1], &deformated);
2505 *type=REG_DWORD;
2506 *size = sizeof(DWORD);
2507 data = msi_alloc(*size);
2508 p = deformated;
2509 if (*p == '-')
2510 p++;
2511 while (*p)
2513 if ( (*p < '0') || (*p > '9') )
2514 break;
2515 d *= 10;
2516 d += (*p - '0');
2517 p++;
2519 if (deformated[0] == '-')
2520 d = -d;
2521 *(LPDWORD)data = d;
2522 TRACE("DWORD %i\n",*(LPDWORD)data);
2524 msi_free(deformated);
2527 else
2529 const WCHAR *ptr = value;
2530 DWORD len;
2532 *type = REG_SZ;
2533 if (value[0] == '#')
2535 ptr++;
2536 if (value[1] == '%')
2538 ptr++;
2539 *type = REG_EXPAND_SZ;
2542 len = deformat_string( package, ptr, (WCHAR **)&data );
2543 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2544 *size = (len + 1) * sizeof(WCHAR);
2546 return data;
2549 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2551 const WCHAR *ret;
2553 switch (root)
2555 case -1:
2556 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2558 *root_key = HKEY_LOCAL_MACHINE;
2559 ret = szHLM;
2561 else
2563 *root_key = HKEY_CURRENT_USER;
2564 ret = szHCU;
2566 break;
2567 case 0:
2568 *root_key = HKEY_CLASSES_ROOT;
2569 ret = szHCR;
2570 break;
2571 case 1:
2572 *root_key = HKEY_CURRENT_USER;
2573 ret = szHCU;
2574 break;
2575 case 2:
2576 *root_key = HKEY_LOCAL_MACHINE;
2577 ret = szHLM;
2578 break;
2579 case 3:
2580 *root_key = HKEY_USERS;
2581 ret = szHU;
2582 break;
2583 default:
2584 ERR("Unknown root %i\n", root);
2585 return NULL;
2588 return ret;
2591 static WCHAR *get_keypath( MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2593 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2594 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2596 if ((is_64bit || is_wow64) &&
2597 !(comp->Attributes & msidbComponentAttributes64bit) &&
2598 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2600 UINT size;
2601 WCHAR *path_32node;
2603 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2604 if (!(path_32node = msi_alloc( size ))) return NULL;
2606 memcpy( path_32node, path, len * sizeof(WCHAR) );
2607 strcpyW( path_32node + len, szWow6432Node );
2608 strcatW( path_32node, szBackSlash );
2609 strcatW( path_32node, path + len );
2610 return path_32node;
2612 return strdupW( path );
2615 static HKEY open_key( HKEY root, const WCHAR *path, BOOL create )
2617 REGSAM access = KEY_ALL_ACCESS;
2618 WCHAR *subkey, *p, *q;
2619 HKEY hkey, ret = NULL;
2620 LONG res;
2622 if (is_wow64) access |= KEY_WOW64_64KEY;
2624 if (!(subkey = strdupW( path ))) return NULL;
2625 p = subkey;
2626 if ((q = strchrW( p, '\\' ))) *q = 0;
2627 if (create)
2628 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2629 else
2630 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2631 if (res)
2633 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2634 msi_free( subkey );
2635 return NULL;
2637 if (q && q[1])
2639 ret = open_key( hkey, q + 1, create );
2640 RegCloseKey( hkey );
2642 else ret = hkey;
2643 msi_free( subkey );
2644 return ret;
2647 static BOOL is_special_entry( const WCHAR *name )
2649 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2652 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2654 const WCHAR *p = str;
2655 WCHAR **ret;
2656 int i = 0;
2658 *count = 0;
2659 if (!str) return NULL;
2660 while ((p - str) < len)
2662 p += strlenW( p ) + 1;
2663 (*count)++;
2665 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2666 p = str;
2667 while ((p - str) < len)
2669 if (!(ret[i] = strdupW( p )))
2671 for (; i >= 0; i--) msi_free( ret[i] );
2672 msi_free( ret );
2673 return NULL;
2675 p += strlenW( p ) + 1;
2676 i++;
2678 return ret;
2681 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2682 WCHAR **right, DWORD right_count, DWORD *size )
2684 WCHAR *ret, *p;
2685 unsigned int i;
2687 *size = sizeof(WCHAR);
2688 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2689 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2691 if (!(ret = p = msi_alloc( *size ))) return NULL;
2693 for (i = 0; i < left_count; i++)
2695 strcpyW( p, left[i] );
2696 p += strlenW( p ) + 1;
2698 for (i = 0; i < right_count; i++)
2700 strcpyW( p, right[i] );
2701 p += strlenW( p ) + 1;
2703 *p = 0;
2704 return ret;
2707 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2708 WCHAR **new, DWORD new_count )
2710 DWORD ret = old_count;
2711 unsigned int i, j, k;
2713 for (i = 0; i < new_count; i++)
2715 for (j = 0; j < old_count; j++)
2717 if (old[j] && !strcmpW( new[i], old[j] ))
2719 msi_free( old[j] );
2720 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2721 old[k] = NULL;
2722 ret--;
2726 return ret;
2729 enum join_op
2731 JOIN_OP_APPEND,
2732 JOIN_OP_PREPEND,
2733 JOIN_OP_REPLACE
2736 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2737 WCHAR **new, DWORD new_count, DWORD *size )
2739 switch (op)
2741 case JOIN_OP_APPEND:
2742 old_count = remove_duplicate_values( old, old_count, new, new_count );
2743 return flatten_multi_string_values( old, old_count, new, new_count, size );
2745 case JOIN_OP_PREPEND:
2746 old_count = remove_duplicate_values( old, old_count, new, new_count );
2747 return flatten_multi_string_values( new, new_count, old, old_count, size );
2749 case JOIN_OP_REPLACE:
2750 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2752 default:
2753 ERR("unhandled join op %u\n", op);
2754 return NULL;
2758 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2759 BYTE *new_value, DWORD new_size, DWORD *size )
2761 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2762 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2763 enum join_op op = JOIN_OP_REPLACE;
2764 WCHAR **old = NULL, **new = NULL;
2765 BYTE *ret;
2767 if (new_size / sizeof(WCHAR) - 1 > 1)
2769 new_ptr = (const WCHAR *)new_value;
2770 new_len = new_size / sizeof(WCHAR) - 1;
2772 if (!new_ptr[0] && new_ptr[new_len - 1])
2774 op = JOIN_OP_APPEND;
2775 new_len--;
2776 new_ptr++;
2778 else if (new_ptr[0] && !new_ptr[new_len - 1])
2780 op = JOIN_OP_PREPEND;
2781 new_len--;
2783 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2785 op = JOIN_OP_REPLACE;
2786 new_len -= 2;
2787 new_ptr++;
2789 new = split_multi_string_values( new_ptr, new_len, &new_count );
2791 if (old_size / sizeof(WCHAR) - 1 > 1)
2793 old_ptr = (const WCHAR *)old_value;
2794 old_len = old_size / sizeof(WCHAR) - 1;
2795 old = split_multi_string_values( old_ptr, old_len, &old_count );
2797 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2798 for (i = 0; i < old_count; i++) msi_free( old[i] );
2799 for (i = 0; i < new_count; i++) msi_free( new[i] );
2800 msi_free( old );
2801 msi_free( new );
2802 return ret;
2805 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2807 BYTE *ret;
2808 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2809 if (!(ret = msi_alloc( *size ))) return NULL;
2810 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2811 return ret;
2814 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2816 MSIPACKAGE *package = param;
2817 BYTE *new_value, *old_value = NULL;
2818 HKEY root_key, hkey;
2819 DWORD type, old_type, new_size, old_size = 0;
2820 LPWSTR deformated, uikey, keypath;
2821 const WCHAR *szRoot, *component, *name, *key, *str;
2822 MSICOMPONENT *comp;
2823 MSIRECORD * uirow;
2824 INT root;
2825 BOOL check_first = FALSE;
2826 int len;
2828 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2830 component = MSI_RecordGetString(row, 6);
2831 comp = msi_get_loaded_component(package,component);
2832 if (!comp)
2833 return ERROR_SUCCESS;
2835 comp->Action = msi_get_component_action( package, comp );
2836 if (comp->Action != INSTALLSTATE_LOCAL)
2838 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2839 return ERROR_SUCCESS;
2842 name = MSI_RecordGetString(row, 4);
2843 if( MSI_RecordIsNull(row,5) && name )
2845 /* null values can have special meanings */
2846 if (name[0]=='-' && name[1] == 0)
2847 return ERROR_SUCCESS;
2848 if ((name[0] == '+' || name[0] == '*') && !name[1])
2849 check_first = TRUE;
2852 root = MSI_RecordGetInteger(row,2);
2853 key = MSI_RecordGetString(row, 3);
2855 szRoot = get_root_key( package, root, &root_key );
2856 if (!szRoot)
2857 return ERROR_SUCCESS;
2859 deformat_string(package, key , &deformated);
2860 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2861 strcpyW(uikey,szRoot);
2862 strcatW(uikey,deformated);
2864 keypath = get_keypath( comp, root_key, deformated );
2865 msi_free( deformated );
2866 if (!(hkey = open_key( root_key, keypath, TRUE )))
2868 ERR("Could not create key %s\n", debugstr_w(keypath));
2869 msi_free(uikey);
2870 msi_free(keypath);
2871 return ERROR_FUNCTION_FAILED;
2873 str = msi_record_get_string( row, 5, &len );
2874 if (str && len > strlenW( str ))
2876 type = REG_MULTI_SZ;
2877 new_size = (len + 1) * sizeof(WCHAR);
2878 new_value = (BYTE *)msi_strdupW( str, len );
2880 else new_value = parse_value( package, str, &type, &new_size );
2881 deformat_string(package, name, &deformated);
2883 if (!is_special_entry( name ))
2885 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2886 if (type == REG_MULTI_SZ)
2888 BYTE *new;
2889 if (old_value && old_type != REG_MULTI_SZ)
2891 msi_free( old_value );
2892 old_value = NULL;
2893 old_size = 0;
2895 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2896 msi_free( new_value );
2897 new_value = new;
2899 if (!check_first)
2901 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2902 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2904 else if (!old_value)
2906 if (deformated || new_size)
2908 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2909 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2912 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2914 RegCloseKey(hkey);
2916 uirow = MSI_CreateRecord(3);
2917 MSI_RecordSetStringW(uirow,2,deformated);
2918 MSI_RecordSetStringW(uirow,1,uikey);
2919 if (type == REG_SZ || type == REG_EXPAND_SZ)
2920 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2921 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2922 msiobj_release( &uirow->hdr );
2924 msi_free(new_value);
2925 msi_free(old_value);
2926 msi_free(deformated);
2927 msi_free(uikey);
2928 msi_free(keypath);
2930 return ERROR_SUCCESS;
2933 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2935 static const WCHAR query[] = {
2936 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2937 '`','R','e','g','i','s','t','r','y','`',0};
2938 MSIQUERY *view;
2939 UINT rc;
2941 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2942 if (rc != ERROR_SUCCESS)
2943 return ERROR_SUCCESS;
2945 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2946 msiobj_release(&view->hdr);
2947 return rc;
2950 static void delete_key( HKEY root, const WCHAR *path )
2952 REGSAM access = 0;
2953 WCHAR *subkey, *p;
2954 HKEY hkey;
2955 LONG res;
2957 if (is_wow64) access |= KEY_WOW64_64KEY;
2959 if (!(subkey = strdupW( path ))) return;
2960 for (;;)
2962 if ((p = strrchrW( subkey, '\\' ))) *p = 0;
2963 hkey = open_key( root, subkey, FALSE );
2964 if (!hkey) break;
2965 if (p && p[1])
2966 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2967 else
2968 res = RegDeleteKeyExW( root, subkey, access, 0 );
2969 if (res)
2971 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
2972 break;
2974 if (p && p[1]) RegCloseKey( hkey );
2975 else break;
2977 msi_free( subkey );
2980 static void delete_value( HKEY root, const WCHAR *path, const WCHAR *value )
2982 LONG res;
2983 HKEY hkey;
2984 DWORD num_subkeys, num_values;
2986 if ((hkey = open_key( root, path, FALSE )))
2988 if ((res = RegDeleteValueW( hkey, value )))
2989 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2991 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2992 NULL, NULL, NULL, NULL );
2993 RegCloseKey( hkey );
2994 if (!res && !num_subkeys && !num_values)
2996 TRACE("removing empty key %s\n", debugstr_w(path));
2997 delete_key( root, path );
3002 static void delete_tree( HKEY root, const WCHAR *path )
3004 LONG res;
3005 HKEY hkey;
3007 if (!(hkey = open_key( root, path, FALSE ))) return;
3008 res = RegDeleteTreeW( hkey, NULL );
3009 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3010 delete_key( root, path );
3011 RegCloseKey( hkey );
3014 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3016 MSIPACKAGE *package = param;
3017 LPCWSTR component, name, key_str, root_key_str;
3018 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3019 MSICOMPONENT *comp;
3020 MSIRECORD *uirow;
3021 BOOL delete_key = FALSE;
3022 HKEY hkey_root;
3023 UINT size;
3024 INT root;
3026 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3028 component = MSI_RecordGetString( row, 6 );
3029 comp = msi_get_loaded_component( package, component );
3030 if (!comp)
3031 return ERROR_SUCCESS;
3033 comp->Action = msi_get_component_action( package, comp );
3034 if (comp->Action != INSTALLSTATE_ABSENT)
3036 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3037 return ERROR_SUCCESS;
3040 name = MSI_RecordGetString( row, 4 );
3041 if (MSI_RecordIsNull( row, 5 ) && name )
3043 if (name[0] == '+' && !name[1])
3044 return ERROR_SUCCESS;
3045 if ((name[0] == '-' || name[0] == '*') && !name[1])
3047 delete_key = TRUE;
3048 name = NULL;
3052 root = MSI_RecordGetInteger( row, 2 );
3053 key_str = MSI_RecordGetString( row, 3 );
3055 root_key_str = get_root_key( package, root, &hkey_root );
3056 if (!root_key_str)
3057 return ERROR_SUCCESS;
3059 deformat_string( package, key_str, &deformated_key );
3060 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3061 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3062 strcpyW( ui_key_str, root_key_str );
3063 strcatW( ui_key_str, deformated_key );
3065 deformat_string( package, name, &deformated_name );
3067 keypath = get_keypath( comp, hkey_root, deformated_key );
3068 msi_free( deformated_key );
3069 if (delete_key) delete_tree( hkey_root, keypath );
3070 else delete_value( hkey_root, keypath, deformated_name );
3071 msi_free( keypath );
3073 uirow = MSI_CreateRecord( 2 );
3074 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3075 MSI_RecordSetStringW( uirow, 2, deformated_name );
3076 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3077 msiobj_release( &uirow->hdr );
3079 msi_free( ui_key_str );
3080 msi_free( deformated_name );
3081 return ERROR_SUCCESS;
3084 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3086 MSIPACKAGE *package = param;
3087 LPCWSTR component, name, key_str, root_key_str;
3088 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3089 MSICOMPONENT *comp;
3090 MSIRECORD *uirow;
3091 BOOL delete_key = FALSE;
3092 HKEY hkey_root;
3093 UINT size;
3094 INT root;
3096 component = MSI_RecordGetString( row, 5 );
3097 comp = msi_get_loaded_component( package, component );
3098 if (!comp)
3099 return ERROR_SUCCESS;
3101 comp->Action = msi_get_component_action( package, comp );
3102 if (comp->Action != INSTALLSTATE_LOCAL)
3104 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3105 return ERROR_SUCCESS;
3108 if ((name = MSI_RecordGetString( row, 4 )))
3110 if (name[0] == '-' && !name[1])
3112 delete_key = TRUE;
3113 name = NULL;
3117 root = MSI_RecordGetInteger( row, 2 );
3118 key_str = MSI_RecordGetString( row, 3 );
3120 root_key_str = get_root_key( package, root, &hkey_root );
3121 if (!root_key_str)
3122 return ERROR_SUCCESS;
3124 deformat_string( package, key_str, &deformated_key );
3125 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3126 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3127 strcpyW( ui_key_str, root_key_str );
3128 strcatW( ui_key_str, deformated_key );
3130 deformat_string( package, name, &deformated_name );
3132 keypath = get_keypath( comp, hkey_root, deformated_key );
3133 msi_free( deformated_key );
3134 if (delete_key) delete_tree( hkey_root, keypath );
3135 else delete_value( hkey_root, keypath, deformated_name );
3136 msi_free( keypath );
3138 uirow = MSI_CreateRecord( 2 );
3139 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3140 MSI_RecordSetStringW( uirow, 2, deformated_name );
3141 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3142 msiobj_release( &uirow->hdr );
3144 msi_free( ui_key_str );
3145 msi_free( deformated_name );
3146 return ERROR_SUCCESS;
3149 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3151 static const WCHAR registry_query[] = {
3152 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3153 '`','R','e','g','i','s','t','r','y','`',0};
3154 static const WCHAR remove_registry_query[] = {
3155 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3156 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3157 MSIQUERY *view;
3158 UINT rc;
3160 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3161 if (rc == ERROR_SUCCESS)
3163 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3164 msiobj_release( &view->hdr );
3165 if (rc != ERROR_SUCCESS)
3166 return rc;
3168 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3169 if (rc == ERROR_SUCCESS)
3171 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3172 msiobj_release( &view->hdr );
3173 if (rc != ERROR_SUCCESS)
3174 return rc;
3176 return ERROR_SUCCESS;
3179 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3181 return ERROR_SUCCESS;
3185 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3187 static const WCHAR query[]= {
3188 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3189 '`','R','e','g','i','s','t','r','y','`',0};
3190 MSICOMPONENT *comp;
3191 DWORD total = 0, count = 0;
3192 MSIQUERY *view;
3193 MSIFEATURE *feature;
3194 MSIFILE *file;
3195 UINT rc;
3197 TRACE("InstallValidate\n");
3199 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3200 if (rc == ERROR_SUCCESS)
3202 rc = MSI_IterateRecords( view, &count, NULL, package );
3203 msiobj_release( &view->hdr );
3204 if (rc != ERROR_SUCCESS)
3205 return rc;
3206 total += count * REG_PROGRESS_VALUE;
3208 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3209 total += COMPONENT_PROGRESS_VALUE;
3211 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3212 total += file->FileSize;
3214 msi_ui_progress( package, 0, total, 0, 0 );
3216 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3218 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3219 debugstr_w(feature->Feature), feature->Installed,
3220 feature->ActionRequest, feature->Action);
3222 return ERROR_SUCCESS;
3225 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3227 MSIPACKAGE* package = param;
3228 LPCWSTR cond = NULL;
3229 LPCWSTR message = NULL;
3230 UINT r;
3232 static const WCHAR title[]=
3233 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3235 cond = MSI_RecordGetString(row,1);
3237 r = MSI_EvaluateConditionW(package,cond);
3238 if (r == MSICONDITION_FALSE)
3240 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3242 LPWSTR deformated;
3243 message = MSI_RecordGetString(row,2);
3244 deformat_string(package,message,&deformated);
3245 MessageBoxW(NULL,deformated,title,MB_OK);
3246 msi_free(deformated);
3249 return ERROR_INSTALL_FAILURE;
3252 return ERROR_SUCCESS;
3255 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3257 static const WCHAR query[] = {
3258 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3259 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3260 MSIQUERY *view;
3261 UINT rc;
3263 TRACE("Checking launch conditions\n");
3265 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3266 if (rc != ERROR_SUCCESS)
3267 return ERROR_SUCCESS;
3269 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3270 msiobj_release(&view->hdr);
3271 return rc;
3274 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3277 if (!cmp->KeyPath)
3278 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3280 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3282 static const WCHAR query[] = {
3283 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3284 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3285 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3286 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3287 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3288 MSIRECORD *row;
3289 UINT root, len;
3290 LPWSTR deformated, buffer, deformated_name;
3291 LPCWSTR key, name;
3293 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3294 if (!row)
3295 return NULL;
3297 root = MSI_RecordGetInteger(row,2);
3298 key = MSI_RecordGetString(row, 3);
3299 name = MSI_RecordGetString(row, 4);
3300 deformat_string(package, key , &deformated);
3301 deformat_string(package, name, &deformated_name);
3303 len = strlenW(deformated) + 6;
3304 if (deformated_name)
3305 len+=strlenW(deformated_name);
3307 buffer = msi_alloc( len *sizeof(WCHAR));
3309 if (deformated_name)
3310 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3311 else
3312 sprintfW(buffer,fmt,root,deformated);
3314 msi_free(deformated);
3315 msi_free(deformated_name);
3316 msiobj_release(&row->hdr);
3318 return buffer;
3320 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3322 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3323 return NULL;
3325 else
3327 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3329 if (file)
3330 return strdupW( file->TargetPath );
3332 return NULL;
3335 static HKEY openSharedDLLsKey(void)
3337 HKEY hkey=0;
3338 static const WCHAR path[] =
3339 {'S','o','f','t','w','a','r','e','\\',
3340 'M','i','c','r','o','s','o','f','t','\\',
3341 'W','i','n','d','o','w','s','\\',
3342 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3343 'S','h','a','r','e','d','D','L','L','s',0};
3345 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3346 return hkey;
3349 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3351 HKEY hkey;
3352 DWORD count=0;
3353 DWORD type;
3354 DWORD sz = sizeof(count);
3355 DWORD rc;
3357 hkey = openSharedDLLsKey();
3358 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3359 if (rc != ERROR_SUCCESS)
3360 count = 0;
3361 RegCloseKey(hkey);
3362 return count;
3365 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3367 HKEY hkey;
3369 hkey = openSharedDLLsKey();
3370 if (count > 0)
3371 msi_reg_set_val_dword( hkey, path, count );
3372 else
3373 RegDeleteValueW(hkey,path);
3374 RegCloseKey(hkey);
3375 return count;
3378 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3380 MSIFEATURE *feature;
3381 INT count = 0;
3382 BOOL write = FALSE;
3384 /* only refcount DLLs */
3385 if (comp->KeyPath == NULL ||
3386 comp->assembly ||
3387 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3388 comp->Attributes & msidbComponentAttributesODBCDataSource)
3389 write = FALSE;
3390 else
3392 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3393 write = (count > 0);
3395 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3396 write = TRUE;
3399 /* increment counts */
3400 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3402 ComponentList *cl;
3404 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3405 continue;
3407 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3409 if ( cl->component == comp )
3410 count++;
3414 /* decrement counts */
3415 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3417 ComponentList *cl;
3419 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3420 continue;
3422 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3424 if ( cl->component == comp )
3425 count--;
3429 /* ref count all the files in the component */
3430 if (write)
3432 MSIFILE *file;
3434 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3436 if (file->Component == comp)
3437 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3441 /* add a count for permanent */
3442 if (comp->Attributes & msidbComponentAttributesPermanent)
3443 count ++;
3445 comp->RefCount = count;
3447 if (write)
3448 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3451 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3453 if (comp->assembly)
3455 const WCHAR prefixW[] = {'<','\\',0};
3456 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3457 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3459 if (keypath)
3461 strcpyW( keypath, prefixW );
3462 strcatW( keypath, comp->assembly->display_name );
3464 return keypath;
3466 return resolve_keypath( package, comp );
3469 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3471 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3472 UINT rc;
3473 MSICOMPONENT *comp;
3474 HKEY hkey;
3476 TRACE("\n");
3478 squash_guid(package->ProductCode,squished_pc);
3479 msi_set_sourcedir_props(package, FALSE);
3481 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3483 MSIRECORD *uirow;
3484 INSTALLSTATE action;
3486 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3487 if (!comp->ComponentId)
3488 continue;
3490 squash_guid( comp->ComponentId, squished_cc );
3491 msi_free( comp->FullKeypath );
3492 comp->FullKeypath = build_full_keypath( package, comp );
3494 ACTION_RefCountComponent( package, comp );
3496 if (package->need_rollback) action = comp->Installed;
3497 else action = comp->ActionRequest;
3499 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3500 debugstr_w(comp->Component), debugstr_w(squished_cc),
3501 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3503 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3505 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3506 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3507 else
3508 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3510 if (rc != ERROR_SUCCESS)
3511 continue;
3513 if (comp->Attributes & msidbComponentAttributesPermanent)
3515 static const WCHAR szPermKey[] =
3516 { '0','0','0','0','0','0','0','0','0','0','0','0',
3517 '0','0','0','0','0','0','0','0','0','0','0','0',
3518 '0','0','0','0','0','0','0','0',0 };
3520 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3522 if (action == INSTALLSTATE_LOCAL)
3523 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3524 else
3526 MSIFILE *file;
3527 MSIRECORD *row;
3528 LPWSTR ptr, ptr2;
3529 WCHAR source[MAX_PATH];
3530 WCHAR base[MAX_PATH];
3531 LPWSTR sourcepath;
3533 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3534 static const WCHAR query[] = {
3535 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3536 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3537 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3538 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3539 '`','D','i','s','k','I','d','`',0};
3541 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3542 continue;
3544 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3545 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3546 ptr2 = strrchrW(source, '\\') + 1;
3547 msiobj_release(&row->hdr);
3549 lstrcpyW(base, package->PackagePath);
3550 ptr = strrchrW(base, '\\');
3551 *(ptr + 1) = '\0';
3553 sourcepath = msi_resolve_file_source(package, file);
3554 ptr = sourcepath + lstrlenW(base);
3555 lstrcpyW(ptr2, ptr);
3556 msi_free(sourcepath);
3558 msi_reg_set_val_str(hkey, squished_pc, source);
3560 RegCloseKey(hkey);
3562 else if (action == INSTALLSTATE_ABSENT)
3564 if (comp->num_clients <= 0)
3566 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3567 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3568 else
3569 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3571 if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
3573 else
3575 LONG res;
3577 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3578 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE );
3579 else
3580 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );
3582 if (rc != ERROR_SUCCESS)
3584 WARN( "failed to open component key %u\n", rc );
3585 continue;
3587 res = RegDeleteValueW( hkey, squished_pc );
3588 RegCloseKey(hkey);
3589 if (res) WARN( "failed to delete component value %d\n", res );
3593 /* UI stuff */
3594 uirow = MSI_CreateRecord(3);
3595 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3596 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3597 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3598 msi_ui_actiondata( package, szProcessComponents, uirow );
3599 msiobj_release( &uirow->hdr );
3601 return ERROR_SUCCESS;
3604 typedef struct {
3605 CLSID clsid;
3606 LPWSTR source;
3608 LPWSTR path;
3609 ITypeLib *ptLib;
3610 } typelib_struct;
3612 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3613 LPWSTR lpszName, LONG_PTR lParam)
3615 TLIBATTR *attr;
3616 typelib_struct *tl_struct = (typelib_struct*) lParam;
3617 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3618 int sz;
3619 HRESULT res;
3621 if (!IS_INTRESOURCE(lpszName))
3623 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3624 return TRUE;
3627 sz = strlenW(tl_struct->source)+4;
3628 sz *= sizeof(WCHAR);
3630 if ((INT_PTR)lpszName == 1)
3631 tl_struct->path = strdupW(tl_struct->source);
3632 else
3634 tl_struct->path = msi_alloc(sz);
3635 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3638 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3639 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3640 if (FAILED(res))
3642 msi_free(tl_struct->path);
3643 tl_struct->path = NULL;
3645 return TRUE;
3648 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3649 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3651 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3652 return FALSE;
3655 msi_free(tl_struct->path);
3656 tl_struct->path = NULL;
3658 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3659 ITypeLib_Release(tl_struct->ptLib);
3661 return TRUE;
3664 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3666 MSIPACKAGE* package = param;
3667 LPCWSTR component;
3668 MSICOMPONENT *comp;
3669 MSIFILE *file;
3670 typelib_struct tl_struct;
3671 ITypeLib *tlib;
3672 HMODULE module;
3673 HRESULT hr;
3675 component = MSI_RecordGetString(row,3);
3676 comp = msi_get_loaded_component(package,component);
3677 if (!comp)
3678 return ERROR_SUCCESS;
3680 comp->Action = msi_get_component_action( package, comp );
3681 if (comp->Action != INSTALLSTATE_LOCAL)
3683 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3684 return ERROR_SUCCESS;
3687 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3689 TRACE("component has no key path\n");
3690 return ERROR_SUCCESS;
3692 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3694 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3695 if (module)
3697 LPCWSTR guid;
3698 guid = MSI_RecordGetString(row,1);
3699 CLSIDFromString( guid, &tl_struct.clsid);
3700 tl_struct.source = strdupW( file->TargetPath );
3701 tl_struct.path = NULL;
3703 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3704 (LONG_PTR)&tl_struct);
3706 if (tl_struct.path)
3708 LPCWSTR helpid, help_path = NULL;
3709 HRESULT res;
3711 helpid = MSI_RecordGetString(row,6);
3713 if (helpid) help_path = msi_get_target_folder( package, helpid );
3714 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3716 if (FAILED(res))
3717 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3718 else
3719 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3721 ITypeLib_Release(tl_struct.ptLib);
3722 msi_free(tl_struct.path);
3724 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3726 FreeLibrary(module);
3727 msi_free(tl_struct.source);
3729 else
3731 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3732 if (FAILED(hr))
3734 ERR("Failed to load type library: %08x\n", hr);
3735 return ERROR_INSTALL_FAILURE;
3738 ITypeLib_Release(tlib);
3741 return ERROR_SUCCESS;
3744 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3746 static const WCHAR query[] = {
3747 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3748 '`','T','y','p','e','L','i','b','`',0};
3749 MSIQUERY *view;
3750 UINT rc;
3752 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3753 if (rc != ERROR_SUCCESS)
3754 return ERROR_SUCCESS;
3756 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3757 msiobj_release(&view->hdr);
3758 return rc;
3761 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3763 MSIPACKAGE *package = param;
3764 LPCWSTR component, guid;
3765 MSICOMPONENT *comp;
3766 GUID libid;
3767 UINT version;
3768 LCID language;
3769 SYSKIND syskind;
3770 HRESULT hr;
3772 component = MSI_RecordGetString( row, 3 );
3773 comp = msi_get_loaded_component( package, component );
3774 if (!comp)
3775 return ERROR_SUCCESS;
3777 comp->Action = msi_get_component_action( package, comp );
3778 if (comp->Action != INSTALLSTATE_ABSENT)
3780 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3781 return ERROR_SUCCESS;
3783 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3785 guid = MSI_RecordGetString( row, 1 );
3786 CLSIDFromString( guid, &libid );
3787 version = MSI_RecordGetInteger( row, 4 );
3788 language = MSI_RecordGetInteger( row, 2 );
3790 #ifdef _WIN64
3791 syskind = SYS_WIN64;
3792 #else
3793 syskind = SYS_WIN32;
3794 #endif
3796 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3797 if (FAILED(hr))
3799 WARN("Failed to unregister typelib: %08x\n", hr);
3802 return ERROR_SUCCESS;
3805 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3807 static const WCHAR query[] = {
3808 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3809 '`','T','y','p','e','L','i','b','`',0};
3810 MSIQUERY *view;
3811 UINT rc;
3813 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3814 if (rc != ERROR_SUCCESS)
3815 return ERROR_SUCCESS;
3817 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3818 msiobj_release( &view->hdr );
3819 return rc;
3822 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3824 static const WCHAR szlnk[] = {'.','l','n','k',0};
3825 LPCWSTR directory, extension, link_folder;
3826 LPWSTR link_file, filename;
3828 directory = MSI_RecordGetString( row, 2 );
3829 link_folder = msi_get_target_folder( package, directory );
3830 if (!link_folder)
3832 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3833 return NULL;
3835 /* may be needed because of a bug somewhere else */
3836 msi_create_full_path( link_folder );
3838 filename = msi_dup_record_field( row, 3 );
3839 msi_reduce_to_long_filename( filename );
3841 extension = strrchrW( filename, '.' );
3842 if (!extension || strcmpiW( extension, szlnk ))
3844 int len = strlenW( filename );
3845 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3846 memcpy( filename + len, szlnk, sizeof(szlnk) );
3848 link_file = msi_build_directory_name( 2, link_folder, filename );
3849 msi_free( filename );
3851 return link_file;
3854 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3856 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3857 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3858 WCHAR *folder, *dest, *path;
3860 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3861 folder = msi_dup_property( package->db, szWindowsFolder );
3862 else
3864 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3865 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3866 msi_free( appdata );
3868 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3869 msi_create_full_path( dest );
3870 path = msi_build_directory_name( 2, dest, icon_name );
3871 msi_free( folder );
3872 msi_free( dest );
3873 return path;
3876 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3878 MSIPACKAGE *package = param;
3879 LPWSTR link_file, deformated, path;
3880 LPCWSTR component, target;
3881 MSICOMPONENT *comp;
3882 IShellLinkW *sl = NULL;
3883 IPersistFile *pf = NULL;
3884 HRESULT res;
3886 component = MSI_RecordGetString(row, 4);
3887 comp = msi_get_loaded_component(package, component);
3888 if (!comp)
3889 return ERROR_SUCCESS;
3891 comp->Action = msi_get_component_action( package, comp );
3892 if (comp->Action != INSTALLSTATE_LOCAL)
3894 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3895 return ERROR_SUCCESS;
3897 msi_ui_actiondata( package, szCreateShortcuts, row );
3899 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3900 &IID_IShellLinkW, (LPVOID *) &sl );
3902 if (FAILED( res ))
3904 ERR("CLSID_ShellLink not available\n");
3905 goto err;
3908 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3909 if (FAILED( res ))
3911 ERR("QueryInterface(IID_IPersistFile) failed\n");
3912 goto err;
3915 target = MSI_RecordGetString(row, 5);
3916 if (strchrW(target, '['))
3918 deformat_string( package, target, &path );
3919 TRACE("target path is %s\n", debugstr_w(path));
3920 IShellLinkW_SetPath( sl, path );
3921 msi_free( path );
3923 else
3925 FIXME("poorly handled shortcut format, advertised shortcut\n");
3926 IShellLinkW_SetPath(sl,comp->FullKeypath);
3929 if (!MSI_RecordIsNull(row,6))
3931 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3932 deformat_string(package, arguments, &deformated);
3933 IShellLinkW_SetArguments(sl,deformated);
3934 msi_free(deformated);
3937 if (!MSI_RecordIsNull(row,7))
3939 LPCWSTR description = MSI_RecordGetString(row, 7);
3940 IShellLinkW_SetDescription(sl, description);
3943 if (!MSI_RecordIsNull(row,8))
3944 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3946 if (!MSI_RecordIsNull(row,9))
3948 INT index;
3949 LPCWSTR icon = MSI_RecordGetString(row, 9);
3951 path = msi_build_icon_path(package, icon);
3952 index = MSI_RecordGetInteger(row,10);
3954 /* no value means 0 */
3955 if (index == MSI_NULL_INTEGER)
3956 index = 0;
3958 IShellLinkW_SetIconLocation(sl, path, index);
3959 msi_free(path);
3962 if (!MSI_RecordIsNull(row,11))
3963 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3965 if (!MSI_RecordIsNull(row,12))
3967 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3968 full_path = msi_get_target_folder( package, wkdir );
3969 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3971 link_file = get_link_file(package, row);
3973 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3974 IPersistFile_Save(pf, link_file, FALSE);
3975 msi_free(link_file);
3977 err:
3978 if (pf)
3979 IPersistFile_Release( pf );
3980 if (sl)
3981 IShellLinkW_Release( sl );
3983 return ERROR_SUCCESS;
3986 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3988 static const WCHAR query[] = {
3989 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3990 '`','S','h','o','r','t','c','u','t','`',0};
3991 MSIQUERY *view;
3992 HRESULT res;
3993 UINT rc;
3995 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3996 if (rc != ERROR_SUCCESS)
3997 return ERROR_SUCCESS;
3999 res = CoInitialize( NULL );
4001 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4002 msiobj_release(&view->hdr);
4004 if (SUCCEEDED(res)) CoUninitialize();
4005 return rc;
4008 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4010 MSIPACKAGE *package = param;
4011 LPWSTR link_file;
4012 LPCWSTR component;
4013 MSICOMPONENT *comp;
4015 component = MSI_RecordGetString( row, 4 );
4016 comp = msi_get_loaded_component( package, component );
4017 if (!comp)
4018 return ERROR_SUCCESS;
4020 comp->Action = msi_get_component_action( package, comp );
4021 if (comp->Action != INSTALLSTATE_ABSENT)
4023 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4024 return ERROR_SUCCESS;
4026 msi_ui_actiondata( package, szRemoveShortcuts, row );
4028 link_file = get_link_file( package, row );
4030 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4031 if (!DeleteFileW( link_file ))
4033 WARN("Failed to remove shortcut file %u\n", GetLastError());
4035 msi_free( link_file );
4037 return ERROR_SUCCESS;
4040 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4042 static const WCHAR query[] = {
4043 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4044 '`','S','h','o','r','t','c','u','t','`',0};
4045 MSIQUERY *view;
4046 UINT rc;
4048 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4049 if (rc != ERROR_SUCCESS)
4050 return ERROR_SUCCESS;
4052 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4053 msiobj_release( &view->hdr );
4054 return rc;
4057 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4059 MSIPACKAGE* package = param;
4060 HANDLE the_file;
4061 LPWSTR FilePath;
4062 LPCWSTR FileName;
4063 CHAR buffer[1024];
4064 DWORD sz;
4065 UINT rc;
4067 FileName = MSI_RecordGetString(row,1);
4068 if (!FileName)
4070 ERR("Unable to get FileName\n");
4071 return ERROR_SUCCESS;
4074 FilePath = msi_build_icon_path(package, FileName);
4076 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4078 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4079 FILE_ATTRIBUTE_NORMAL, NULL);
4081 if (the_file == INVALID_HANDLE_VALUE)
4083 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4084 msi_free(FilePath);
4085 return ERROR_SUCCESS;
4090 DWORD write;
4091 sz = 1024;
4092 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4093 if (rc != ERROR_SUCCESS)
4095 ERR("Failed to get stream\n");
4096 CloseHandle(the_file);
4097 DeleteFileW(FilePath);
4098 break;
4100 WriteFile(the_file,buffer,sz,&write,NULL);
4101 } while (sz == 1024);
4103 msi_free(FilePath);
4104 CloseHandle(the_file);
4106 return ERROR_SUCCESS;
4109 static UINT msi_publish_icons(MSIPACKAGE *package)
4111 static const WCHAR query[]= {
4112 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4113 '`','I','c','o','n','`',0};
4114 MSIQUERY *view;
4115 UINT r;
4117 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4118 if (r == ERROR_SUCCESS)
4120 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4121 msiobj_release(&view->hdr);
4122 if (r != ERROR_SUCCESS)
4123 return r;
4125 return ERROR_SUCCESS;
4128 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4130 UINT r;
4131 HKEY source;
4132 LPWSTR buffer;
4133 MSIMEDIADISK *disk;
4134 MSISOURCELISTINFO *info;
4136 r = RegCreateKeyW(hkey, szSourceList, &source);
4137 if (r != ERROR_SUCCESS)
4138 return r;
4140 RegCloseKey(source);
4142 buffer = strrchrW(package->PackagePath, '\\') + 1;
4143 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4144 package->Context, MSICODE_PRODUCT,
4145 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4146 if (r != ERROR_SUCCESS)
4147 return r;
4149 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4150 package->Context, MSICODE_PRODUCT,
4151 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4152 if (r != ERROR_SUCCESS)
4153 return r;
4155 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4156 package->Context, MSICODE_PRODUCT,
4157 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4158 if (r != ERROR_SUCCESS)
4159 return r;
4161 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4163 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4164 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4165 info->options, info->value);
4166 else
4167 MsiSourceListSetInfoW(package->ProductCode, NULL,
4168 info->context, info->options,
4169 info->property, info->value);
4172 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4174 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4175 disk->context, disk->options,
4176 disk->disk_id, disk->volume_label, disk->disk_prompt);
4179 return ERROR_SUCCESS;
4182 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4184 MSIHANDLE hdb, suminfo;
4185 WCHAR guids[MAX_PATH];
4186 WCHAR packcode[SQUISH_GUID_SIZE];
4187 LPWSTR buffer;
4188 LPWSTR ptr;
4189 DWORD langid;
4190 DWORD size;
4191 UINT r;
4193 static const WCHAR szARPProductIcon[] =
4194 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4195 static const WCHAR szAssignment[] =
4196 {'A','s','s','i','g','n','m','e','n','t',0};
4197 static const WCHAR szAdvertiseFlags[] =
4198 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4199 static const WCHAR szClients[] =
4200 {'C','l','i','e','n','t','s',0};
4201 static const WCHAR szColon[] = {':',0};
4203 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4204 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4205 msi_free(buffer);
4207 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4208 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4210 /* FIXME */
4211 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4213 buffer = msi_dup_property(package->db, szARPProductIcon);
4214 if (buffer)
4216 LPWSTR path = msi_build_icon_path(package, buffer);
4217 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4218 msi_free(path);
4219 msi_free(buffer);
4222 buffer = msi_dup_property(package->db, szProductVersion);
4223 if (buffer)
4225 DWORD verdword = msi_version_str_to_dword(buffer);
4226 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4227 msi_free(buffer);
4230 msi_reg_set_val_dword(hkey, szAssignment, 0);
4231 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4232 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4233 msi_reg_set_val_str(hkey, szClients, szColon);
4235 hdb = alloc_msihandle(&package->db->hdr);
4236 if (!hdb)
4237 return ERROR_NOT_ENOUGH_MEMORY;
4239 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4240 MsiCloseHandle(hdb);
4241 if (r != ERROR_SUCCESS)
4242 goto done;
4244 size = MAX_PATH;
4245 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4246 NULL, guids, &size);
4247 if (r != ERROR_SUCCESS)
4248 goto done;
4250 ptr = strchrW(guids, ';');
4251 if (ptr) *ptr = 0;
4252 squash_guid(guids, packcode);
4253 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4255 done:
4256 MsiCloseHandle(suminfo);
4257 return ERROR_SUCCESS;
4260 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4262 UINT r;
4263 HKEY hkey;
4264 LPWSTR upgrade;
4265 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4267 upgrade = msi_dup_property(package->db, szUpgradeCode);
4268 if (!upgrade)
4269 return ERROR_SUCCESS;
4271 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4272 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4273 else
4274 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4276 if (r != ERROR_SUCCESS)
4278 WARN("failed to open upgrade code key\n");
4279 msi_free(upgrade);
4280 return ERROR_SUCCESS;
4282 squash_guid(package->ProductCode, squashed_pc);
4283 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4284 RegCloseKey(hkey);
4285 msi_free(upgrade);
4286 return ERROR_SUCCESS;
4289 static BOOL msi_check_publish(MSIPACKAGE *package)
4291 MSIFEATURE *feature;
4293 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4295 feature->Action = msi_get_feature_action( package, feature );
4296 if (feature->Action == INSTALLSTATE_LOCAL)
4297 return TRUE;
4300 return FALSE;
4303 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4305 MSIFEATURE *feature;
4307 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4309 feature->Action = msi_get_feature_action( package, feature );
4310 if (feature->Action != INSTALLSTATE_ABSENT)
4311 return FALSE;
4314 return TRUE;
4317 static UINT msi_publish_patches( MSIPACKAGE *package )
4319 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4320 WCHAR patch_squashed[GUID_SIZE];
4321 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4322 LONG res;
4323 MSIPATCHINFO *patch;
4324 UINT r;
4325 WCHAR *p, *all_patches = NULL;
4326 DWORD len = 0;
4328 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4329 if (r != ERROR_SUCCESS)
4330 return ERROR_FUNCTION_FAILED;
4332 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4333 if (res != ERROR_SUCCESS)
4335 r = ERROR_FUNCTION_FAILED;
4336 goto done;
4339 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4340 if (r != ERROR_SUCCESS)
4341 goto done;
4343 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4345 squash_guid( patch->patchcode, patch_squashed );
4346 len += strlenW( patch_squashed ) + 1;
4349 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4350 if (!all_patches)
4351 goto done;
4353 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4355 HKEY patch_key;
4357 squash_guid( patch->patchcode, p );
4358 p += strlenW( p ) + 1;
4360 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4361 (const BYTE *)patch->transforms,
4362 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4363 if (res != ERROR_SUCCESS)
4364 goto done;
4366 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4367 if (r != ERROR_SUCCESS)
4368 goto done;
4370 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4371 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4372 RegCloseKey( patch_key );
4373 if (res != ERROR_SUCCESS)
4374 goto done;
4376 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4378 res = GetLastError();
4379 ERR("Unable to copy patch package %d\n", res);
4380 goto done;
4382 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4383 if (res != ERROR_SUCCESS)
4384 goto done;
4386 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4387 RegCloseKey( patch_key );
4388 if (res != ERROR_SUCCESS)
4389 goto done;
4392 all_patches[len] = 0;
4393 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4394 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4395 if (res != ERROR_SUCCESS)
4396 goto done;
4398 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4399 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4400 if (res != ERROR_SUCCESS)
4401 r = ERROR_FUNCTION_FAILED;
4403 done:
4404 RegCloseKey( product_patches_key );
4405 RegCloseKey( patches_key );
4406 RegCloseKey( product_key );
4407 msi_free( all_patches );
4408 return r;
4411 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4413 UINT rc;
4414 HKEY hukey = NULL, hudkey = NULL;
4415 MSIRECORD *uirow;
4417 if (!list_empty(&package->patches))
4419 rc = msi_publish_patches(package);
4420 if (rc != ERROR_SUCCESS)
4421 goto end;
4424 /* FIXME: also need to publish if the product is in advertise mode */
4425 if (!msi_check_publish(package))
4426 return ERROR_SUCCESS;
4428 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4429 &hukey, TRUE);
4430 if (rc != ERROR_SUCCESS)
4431 goto end;
4433 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4434 NULL, &hudkey, TRUE);
4435 if (rc != ERROR_SUCCESS)
4436 goto end;
4438 rc = msi_publish_upgrade_code(package);
4439 if (rc != ERROR_SUCCESS)
4440 goto end;
4442 rc = msi_publish_product_properties(package, hukey);
4443 if (rc != ERROR_SUCCESS)
4444 goto end;
4446 rc = msi_publish_sourcelist(package, hukey);
4447 if (rc != ERROR_SUCCESS)
4448 goto end;
4450 rc = msi_publish_icons(package);
4452 end:
4453 uirow = MSI_CreateRecord( 1 );
4454 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4455 msi_ui_actiondata( package, szPublishProduct, uirow );
4456 msiobj_release( &uirow->hdr );
4458 RegCloseKey(hukey);
4459 RegCloseKey(hudkey);
4460 return rc;
4463 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4465 WCHAR *filename, *ptr, *folder, *ret;
4466 const WCHAR *dirprop;
4468 filename = msi_dup_record_field( row, 2 );
4469 if (filename && (ptr = strchrW( filename, '|' )))
4470 ptr++;
4471 else
4472 ptr = filename;
4474 dirprop = MSI_RecordGetString( row, 3 );
4475 if (dirprop)
4477 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4478 if (!folder) folder = msi_dup_property( package->db, dirprop );
4480 else
4481 folder = msi_dup_property( package->db, szWindowsFolder );
4483 if (!folder)
4485 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4486 msi_free( filename );
4487 return NULL;
4490 ret = msi_build_directory_name( 2, folder, ptr );
4492 msi_free( filename );
4493 msi_free( folder );
4494 return ret;
4497 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4499 MSIPACKAGE *package = param;
4500 LPCWSTR component, section, key, value, identifier;
4501 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4502 MSIRECORD * uirow;
4503 INT action;
4504 MSICOMPONENT *comp;
4506 component = MSI_RecordGetString(row, 8);
4507 comp = msi_get_loaded_component(package,component);
4508 if (!comp)
4509 return ERROR_SUCCESS;
4511 comp->Action = msi_get_component_action( package, comp );
4512 if (comp->Action != INSTALLSTATE_LOCAL)
4514 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4515 return ERROR_SUCCESS;
4518 identifier = MSI_RecordGetString(row,1);
4519 section = MSI_RecordGetString(row,4);
4520 key = MSI_RecordGetString(row,5);
4521 value = MSI_RecordGetString(row,6);
4522 action = MSI_RecordGetInteger(row,7);
4524 deformat_string(package,section,&deformated_section);
4525 deformat_string(package,key,&deformated_key);
4526 deformat_string(package,value,&deformated_value);
4528 fullname = get_ini_file_name(package, row);
4530 if (action == 0)
4532 TRACE("Adding value %s to section %s in %s\n",
4533 debugstr_w(deformated_key), debugstr_w(deformated_section),
4534 debugstr_w(fullname));
4535 WritePrivateProfileStringW(deformated_section, deformated_key,
4536 deformated_value, fullname);
4538 else if (action == 1)
4540 WCHAR returned[10];
4541 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4542 returned, 10, fullname);
4543 if (returned[0] == 0)
4545 TRACE("Adding value %s to section %s in %s\n",
4546 debugstr_w(deformated_key), debugstr_w(deformated_section),
4547 debugstr_w(fullname));
4549 WritePrivateProfileStringW(deformated_section, deformated_key,
4550 deformated_value, fullname);
4553 else if (action == 3)
4554 FIXME("Append to existing section not yet implemented\n");
4556 uirow = MSI_CreateRecord(4);
4557 MSI_RecordSetStringW(uirow,1,identifier);
4558 MSI_RecordSetStringW(uirow,2,deformated_section);
4559 MSI_RecordSetStringW(uirow,3,deformated_key);
4560 MSI_RecordSetStringW(uirow,4,deformated_value);
4561 msi_ui_actiondata( package, szWriteIniValues, uirow );
4562 msiobj_release( &uirow->hdr );
4564 msi_free(fullname);
4565 msi_free(deformated_key);
4566 msi_free(deformated_value);
4567 msi_free(deformated_section);
4568 return ERROR_SUCCESS;
4571 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4573 static const WCHAR query[] = {
4574 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4575 '`','I','n','i','F','i','l','e','`',0};
4576 MSIQUERY *view;
4577 UINT rc;
4579 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4580 if (rc != ERROR_SUCCESS)
4581 return ERROR_SUCCESS;
4583 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4584 msiobj_release(&view->hdr);
4585 return rc;
4588 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4590 MSIPACKAGE *package = param;
4591 LPCWSTR component, section, key, value, identifier;
4592 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4593 MSICOMPONENT *comp;
4594 MSIRECORD *uirow;
4595 INT action;
4597 component = MSI_RecordGetString( row, 8 );
4598 comp = msi_get_loaded_component( package, component );
4599 if (!comp)
4600 return ERROR_SUCCESS;
4602 comp->Action = msi_get_component_action( package, comp );
4603 if (comp->Action != INSTALLSTATE_ABSENT)
4605 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4606 return ERROR_SUCCESS;
4609 identifier = MSI_RecordGetString( row, 1 );
4610 section = MSI_RecordGetString( row, 4 );
4611 key = MSI_RecordGetString( row, 5 );
4612 value = MSI_RecordGetString( row, 6 );
4613 action = MSI_RecordGetInteger( row, 7 );
4615 deformat_string( package, section, &deformated_section );
4616 deformat_string( package, key, &deformated_key );
4617 deformat_string( package, value, &deformated_value );
4619 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4621 filename = get_ini_file_name( package, row );
4623 TRACE("Removing key %s from section %s in %s\n",
4624 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4626 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4628 WARN("Unable to remove key %u\n", GetLastError());
4630 msi_free( filename );
4632 else
4633 FIXME("Unsupported action %d\n", action);
4636 uirow = MSI_CreateRecord( 4 );
4637 MSI_RecordSetStringW( uirow, 1, identifier );
4638 MSI_RecordSetStringW( uirow, 2, deformated_section );
4639 MSI_RecordSetStringW( uirow, 3, deformated_key );
4640 MSI_RecordSetStringW( uirow, 4, deformated_value );
4641 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4642 msiobj_release( &uirow->hdr );
4644 msi_free( deformated_key );
4645 msi_free( deformated_value );
4646 msi_free( deformated_section );
4647 return ERROR_SUCCESS;
4650 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4652 MSIPACKAGE *package = param;
4653 LPCWSTR component, section, key, value, identifier;
4654 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4655 MSICOMPONENT *comp;
4656 MSIRECORD *uirow;
4657 INT action;
4659 component = MSI_RecordGetString( row, 8 );
4660 comp = msi_get_loaded_component( package, component );
4661 if (!comp)
4662 return ERROR_SUCCESS;
4664 comp->Action = msi_get_component_action( package, comp );
4665 if (comp->Action != INSTALLSTATE_LOCAL)
4667 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4668 return ERROR_SUCCESS;
4671 identifier = MSI_RecordGetString( row, 1 );
4672 section = MSI_RecordGetString( row, 4 );
4673 key = MSI_RecordGetString( row, 5 );
4674 value = MSI_RecordGetString( row, 6 );
4675 action = MSI_RecordGetInteger( row, 7 );
4677 deformat_string( package, section, &deformated_section );
4678 deformat_string( package, key, &deformated_key );
4679 deformat_string( package, value, &deformated_value );
4681 if (action == msidbIniFileActionRemoveLine)
4683 filename = get_ini_file_name( package, row );
4685 TRACE("Removing key %s from section %s in %s\n",
4686 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4688 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4690 WARN("Unable to remove key %u\n", GetLastError());
4692 msi_free( filename );
4694 else
4695 FIXME("Unsupported action %d\n", action);
4697 uirow = MSI_CreateRecord( 4 );
4698 MSI_RecordSetStringW( uirow, 1, identifier );
4699 MSI_RecordSetStringW( uirow, 2, deformated_section );
4700 MSI_RecordSetStringW( uirow, 3, deformated_key );
4701 MSI_RecordSetStringW( uirow, 4, deformated_value );
4702 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4703 msiobj_release( &uirow->hdr );
4705 msi_free( deformated_key );
4706 msi_free( deformated_value );
4707 msi_free( deformated_section );
4708 return ERROR_SUCCESS;
4711 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4713 static const WCHAR query[] = {
4714 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4715 '`','I','n','i','F','i','l','e','`',0};
4716 static const WCHAR remove_query[] = {
4717 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4718 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4719 MSIQUERY *view;
4720 UINT rc;
4722 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4723 if (rc == ERROR_SUCCESS)
4725 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4726 msiobj_release( &view->hdr );
4727 if (rc != ERROR_SUCCESS)
4728 return rc;
4730 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4731 if (rc == ERROR_SUCCESS)
4733 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4734 msiobj_release( &view->hdr );
4735 if (rc != ERROR_SUCCESS)
4736 return rc;
4738 return ERROR_SUCCESS;
4741 static void register_dll( const WCHAR *dll, BOOL unregister )
4743 static const WCHAR regW[] =
4744 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4745 static const WCHAR unregW[] =
4746 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4747 PROCESS_INFORMATION pi;
4748 STARTUPINFOW si;
4749 WCHAR *cmd;
4751 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4753 if (unregister) sprintfW( cmd, unregW, dll );
4754 else sprintfW( cmd, regW, dll );
4756 memset( &si, 0, sizeof(STARTUPINFOW) );
4757 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4759 CloseHandle( pi.hThread );
4760 msi_dialog_check_messages( pi.hProcess );
4761 CloseHandle( pi.hProcess );
4763 msi_free( cmd );
4766 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4768 MSIPACKAGE *package = param;
4769 LPCWSTR filename;
4770 MSIFILE *file;
4771 MSIRECORD *uirow;
4773 filename = MSI_RecordGetString( row, 1 );
4774 file = msi_get_loaded_file( package, filename );
4775 if (!file)
4777 WARN("unable to find file %s\n", debugstr_w(filename));
4778 return ERROR_SUCCESS;
4780 file->Component->Action = msi_get_component_action( package, file->Component );
4781 if (file->Component->Action != INSTALLSTATE_LOCAL)
4783 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4784 return ERROR_SUCCESS;
4787 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4788 register_dll( file->TargetPath, FALSE );
4790 uirow = MSI_CreateRecord( 2 );
4791 MSI_RecordSetStringW( uirow, 1, file->File );
4792 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4793 msi_ui_actiondata( package, szSelfRegModules, uirow );
4794 msiobj_release( &uirow->hdr );
4796 return ERROR_SUCCESS;
4799 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4801 static const WCHAR query[] = {
4802 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4803 '`','S','e','l','f','R','e','g','`',0};
4804 MSIQUERY *view;
4805 UINT rc;
4807 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4808 if (rc != ERROR_SUCCESS)
4809 return ERROR_SUCCESS;
4811 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4812 msiobj_release(&view->hdr);
4813 return rc;
4816 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4818 MSIPACKAGE *package = param;
4819 LPCWSTR filename;
4820 MSIFILE *file;
4821 MSIRECORD *uirow;
4823 filename = MSI_RecordGetString( row, 1 );
4824 file = msi_get_loaded_file( package, filename );
4825 if (!file)
4827 WARN("unable to find file %s\n", debugstr_w(filename));
4828 return ERROR_SUCCESS;
4830 file->Component->Action = msi_get_component_action( package, file->Component );
4831 if (file->Component->Action != INSTALLSTATE_ABSENT)
4833 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4834 return ERROR_SUCCESS;
4837 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4838 register_dll( file->TargetPath, TRUE );
4840 uirow = MSI_CreateRecord( 2 );
4841 MSI_RecordSetStringW( uirow, 1, file->File );
4842 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4843 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4844 msiobj_release( &uirow->hdr );
4846 return ERROR_SUCCESS;
4849 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4851 static const WCHAR query[] = {
4852 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4853 '`','S','e','l','f','R','e','g','`',0};
4854 MSIQUERY *view;
4855 UINT rc;
4857 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4858 if (rc != ERROR_SUCCESS)
4859 return ERROR_SUCCESS;
4861 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4862 msiobj_release( &view->hdr );
4863 return rc;
4866 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4868 MSIFEATURE *feature;
4869 UINT rc;
4870 HKEY hkey = NULL, userdata = NULL;
4872 if (!msi_check_publish(package))
4873 return ERROR_SUCCESS;
4875 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4876 &hkey, TRUE);
4877 if (rc != ERROR_SUCCESS)
4878 goto end;
4880 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4881 &userdata, TRUE);
4882 if (rc != ERROR_SUCCESS)
4883 goto end;
4885 /* here the guids are base 85 encoded */
4886 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4888 ComponentList *cl;
4889 LPWSTR data = NULL;
4890 GUID clsid;
4891 INT size;
4892 BOOL absent = FALSE;
4893 MSIRECORD *uirow;
4895 if (feature->Action != INSTALLSTATE_LOCAL &&
4896 feature->Action != INSTALLSTATE_SOURCE &&
4897 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4899 size = 1;
4900 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4902 size += 21;
4904 if (feature->Feature_Parent)
4905 size += strlenW( feature->Feature_Parent )+2;
4907 data = msi_alloc(size * sizeof(WCHAR));
4909 data[0] = 0;
4910 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4912 MSICOMPONENT* component = cl->component;
4913 WCHAR buf[21];
4915 buf[0] = 0;
4916 if (component->ComponentId)
4918 TRACE("From %s\n",debugstr_w(component->ComponentId));
4919 CLSIDFromString(component->ComponentId, &clsid);
4920 encode_base85_guid(&clsid,buf);
4921 TRACE("to %s\n",debugstr_w(buf));
4922 strcatW(data,buf);
4926 if (feature->Feature_Parent)
4928 static const WCHAR sep[] = {'\2',0};
4929 strcatW(data,sep);
4930 strcatW(data,feature->Feature_Parent);
4933 msi_reg_set_val_str( userdata, feature->Feature, data );
4934 msi_free(data);
4936 size = 0;
4937 if (feature->Feature_Parent)
4938 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4939 if (!absent)
4941 size += sizeof(WCHAR);
4942 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4943 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4945 else
4947 size += 2*sizeof(WCHAR);
4948 data = msi_alloc(size);
4949 data[0] = 0x6;
4950 data[1] = 0;
4951 if (feature->Feature_Parent)
4952 strcpyW( &data[1], feature->Feature_Parent );
4953 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4954 (LPBYTE)data,size);
4955 msi_free(data);
4958 /* the UI chunk */
4959 uirow = MSI_CreateRecord( 1 );
4960 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4961 msi_ui_actiondata( package, szPublishFeatures, uirow );
4962 msiobj_release( &uirow->hdr );
4963 /* FIXME: call msi_ui_progress? */
4966 end:
4967 RegCloseKey(hkey);
4968 RegCloseKey(userdata);
4969 return rc;
4972 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4974 UINT r;
4975 HKEY hkey;
4976 MSIRECORD *uirow;
4978 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4980 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4981 &hkey, FALSE);
4982 if (r == ERROR_SUCCESS)
4984 RegDeleteValueW(hkey, feature->Feature);
4985 RegCloseKey(hkey);
4988 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4989 &hkey, FALSE);
4990 if (r == ERROR_SUCCESS)
4992 RegDeleteValueW(hkey, feature->Feature);
4993 RegCloseKey(hkey);
4996 uirow = MSI_CreateRecord( 1 );
4997 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4998 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4999 msiobj_release( &uirow->hdr );
5001 return ERROR_SUCCESS;
5004 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5006 MSIFEATURE *feature;
5008 if (!msi_check_unpublish(package))
5009 return ERROR_SUCCESS;
5011 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5013 msi_unpublish_feature(package, feature);
5016 return ERROR_SUCCESS;
5019 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5021 SYSTEMTIME systime;
5022 DWORD size, langid;
5023 WCHAR date[9], *val, *buffer;
5024 const WCHAR *prop, *key;
5026 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5027 static const WCHAR modpath_fmt[] =
5028 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5029 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5030 static const WCHAR szModifyPath[] =
5031 {'M','o','d','i','f','y','P','a','t','h',0};
5032 static const WCHAR szUninstallString[] =
5033 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5034 static const WCHAR szEstimatedSize[] =
5035 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5036 static const WCHAR szDisplayVersion[] =
5037 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5038 static const WCHAR szInstallSource[] =
5039 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5040 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5041 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5042 static const WCHAR szAuthorizedCDFPrefix[] =
5043 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5044 static const WCHAR szARPCONTACT[] =
5045 {'A','R','P','C','O','N','T','A','C','T',0};
5046 static const WCHAR szContact[] =
5047 {'C','o','n','t','a','c','t',0};
5048 static const WCHAR szARPCOMMENTS[] =
5049 {'A','R','P','C','O','M','M','E','N','T','S',0};
5050 static const WCHAR szComments[] =
5051 {'C','o','m','m','e','n','t','s',0};
5052 static const WCHAR szProductName[] =
5053 {'P','r','o','d','u','c','t','N','a','m','e',0};
5054 static const WCHAR szDisplayName[] =
5055 {'D','i','s','p','l','a','y','N','a','m','e',0};
5056 static const WCHAR szARPHELPLINK[] =
5057 {'A','R','P','H','E','L','P','L','I','N','K',0};
5058 static const WCHAR szHelpLink[] =
5059 {'H','e','l','p','L','i','n','k',0};
5060 static const WCHAR szARPHELPTELEPHONE[] =
5061 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5062 static const WCHAR szHelpTelephone[] =
5063 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5064 static const WCHAR szARPINSTALLLOCATION[] =
5065 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5066 static const WCHAR szManufacturer[] =
5067 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5068 static const WCHAR szPublisher[] =
5069 {'P','u','b','l','i','s','h','e','r',0};
5070 static const WCHAR szARPREADME[] =
5071 {'A','R','P','R','E','A','D','M','E',0};
5072 static const WCHAR szReadme[] =
5073 {'R','e','a','d','M','e',0};
5074 static const WCHAR szARPSIZE[] =
5075 {'A','R','P','S','I','Z','E',0};
5076 static const WCHAR szSize[] =
5077 {'S','i','z','e',0};
5078 static const WCHAR szARPURLINFOABOUT[] =
5079 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5080 static const WCHAR szURLInfoAbout[] =
5081 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5082 static const WCHAR szARPURLUPDATEINFO[] =
5083 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5084 static const WCHAR szURLUpdateInfo[] =
5085 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5086 static const WCHAR szARPSYSTEMCOMPONENT[] =
5087 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5088 static const WCHAR szSystemComponent[] =
5089 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5091 static const WCHAR *propval[] = {
5092 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5093 szARPCONTACT, szContact,
5094 szARPCOMMENTS, szComments,
5095 szProductName, szDisplayName,
5096 szARPHELPLINK, szHelpLink,
5097 szARPHELPTELEPHONE, szHelpTelephone,
5098 szARPINSTALLLOCATION, szInstallLocation,
5099 szSourceDir, szInstallSource,
5100 szManufacturer, szPublisher,
5101 szARPREADME, szReadme,
5102 szARPSIZE, szSize,
5103 szARPURLINFOABOUT, szURLInfoAbout,
5104 szARPURLUPDATEINFO, szURLUpdateInfo,
5105 NULL
5107 const WCHAR **p = propval;
5109 while (*p)
5111 prop = *p++;
5112 key = *p++;
5113 val = msi_dup_property(package->db, prop);
5114 msi_reg_set_val_str(hkey, key, val);
5115 msi_free(val);
5118 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5119 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5121 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5123 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5124 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5125 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5126 msi_free(buffer);
5128 /* FIXME: Write real Estimated Size when we have it */
5129 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5131 GetLocalTime(&systime);
5132 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5133 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5135 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5136 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5138 buffer = msi_dup_property(package->db, szProductVersion);
5139 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5140 if (buffer)
5142 DWORD verdword = msi_version_str_to_dword(buffer);
5144 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5145 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5146 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5147 msi_free(buffer);
5150 return ERROR_SUCCESS;
5153 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5155 WCHAR squashed_pc[SQUISH_GUID_SIZE];
5156 MSIRECORD *uirow;
5157 LPWSTR upgrade_code;
5158 HKEY hkey, props, upgrade_key;
5159 UINT rc;
5161 /* FIXME: also need to publish if the product is in advertise mode */
5162 if (!msi_check_publish(package))
5163 return ERROR_SUCCESS;
5165 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5166 if (rc != ERROR_SUCCESS)
5167 return rc;
5169 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5170 if (rc != ERROR_SUCCESS)
5171 goto done;
5173 rc = msi_publish_install_properties(package, hkey);
5174 if (rc != ERROR_SUCCESS)
5175 goto done;
5177 rc = msi_publish_install_properties(package, props);
5178 if (rc != ERROR_SUCCESS)
5179 goto done;
5181 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5182 if (upgrade_code)
5184 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5185 if (rc == ERROR_SUCCESS)
5187 squash_guid( package->ProductCode, squashed_pc );
5188 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5189 RegCloseKey( upgrade_key );
5191 msi_free( upgrade_code );
5193 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5194 package->delete_on_close = FALSE;
5196 done:
5197 uirow = MSI_CreateRecord( 1 );
5198 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5199 msi_ui_actiondata( package, szRegisterProduct, uirow );
5200 msiobj_release( &uirow->hdr );
5202 RegCloseKey(hkey);
5203 return ERROR_SUCCESS;
5206 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5208 return execute_script(package, SCRIPT_INSTALL);
5211 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5213 MSIPACKAGE *package = param;
5214 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5215 WCHAR *p, *icon_path;
5217 if (!icon) return ERROR_SUCCESS;
5218 if ((icon_path = msi_build_icon_path( package, icon )))
5220 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5221 DeleteFileW( icon_path );
5222 if ((p = strrchrW( icon_path, '\\' )))
5224 *p = 0;
5225 RemoveDirectoryW( icon_path );
5227 msi_free( icon_path );
5229 return ERROR_SUCCESS;
5232 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5234 static const WCHAR query[]= {
5235 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5236 MSIQUERY *view;
5237 UINT r;
5239 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5240 if (r == ERROR_SUCCESS)
5242 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5243 msiobj_release( &view->hdr );
5244 if (r != ERROR_SUCCESS)
5245 return r;
5247 return ERROR_SUCCESS;
5250 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
5252 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
5253 WCHAR *upgrade, **features;
5254 BOOL full_uninstall = TRUE;
5255 MSIFEATURE *feature;
5256 MSIPATCHINFO *patch;
5257 UINT i;
5259 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5261 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
5263 features = msi_split_string( remove, ',' );
5264 for (i = 0; features && features[i]; i++)
5266 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
5268 msi_free(features);
5270 if (!full_uninstall)
5271 return ERROR_SUCCESS;
5273 MSIREG_DeleteProductKey(package->ProductCode);
5274 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5275 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5277 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5278 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5279 MSIREG_DeleteUserProductKey(package->ProductCode);
5280 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5282 upgrade = msi_dup_property(package->db, szUpgradeCode);
5283 if (upgrade)
5285 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5286 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5287 msi_free(upgrade);
5290 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5292 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5293 if (!strcmpW( package->ProductCode, patch->products ))
5295 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5296 patch->delete_on_close = TRUE;
5298 /* FIXME: remove local patch package if this is the last product */
5300 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5301 package->delete_on_close = TRUE;
5303 msi_unpublish_icons( package );
5304 return ERROR_SUCCESS;
5307 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5309 UINT rc;
5310 WCHAR *remove;
5312 /* first do the same as an InstallExecute */
5313 rc = ACTION_InstallExecute(package);
5314 if (rc != ERROR_SUCCESS)
5315 return rc;
5317 /* then handle commit actions */
5318 rc = execute_script(package, SCRIPT_COMMIT);
5319 if (rc != ERROR_SUCCESS)
5320 return rc;
5322 remove = msi_dup_property(package->db, szRemove);
5323 rc = msi_unpublish_product(package, remove);
5324 msi_free(remove);
5325 return rc;
5328 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5330 static const WCHAR RunOnce[] = {
5331 'S','o','f','t','w','a','r','e','\\',
5332 'M','i','c','r','o','s','o','f','t','\\',
5333 'W','i','n','d','o','w','s','\\',
5334 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5335 'R','u','n','O','n','c','e',0};
5336 static const WCHAR InstallRunOnce[] = {
5337 'S','o','f','t','w','a','r','e','\\',
5338 'M','i','c','r','o','s','o','f','t','\\',
5339 'W','i','n','d','o','w','s','\\',
5340 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5341 'I','n','s','t','a','l','l','e','r','\\',
5342 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5344 static const WCHAR msiexec_fmt[] = {
5345 '%','s',
5346 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5347 '\"','%','s','\"',0};
5348 static const WCHAR install_fmt[] = {
5349 '/','I',' ','\"','%','s','\"',' ',
5350 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5351 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5352 WCHAR buffer[256], sysdir[MAX_PATH];
5353 HKEY hkey;
5354 WCHAR squished_pc[100];
5356 squash_guid(package->ProductCode,squished_pc);
5358 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5359 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5360 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5361 squished_pc);
5363 msi_reg_set_val_str( hkey, squished_pc, buffer );
5364 RegCloseKey(hkey);
5366 TRACE("Reboot command %s\n",debugstr_w(buffer));
5368 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5369 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5371 msi_reg_set_val_str( hkey, squished_pc, buffer );
5372 RegCloseKey(hkey);
5374 return ERROR_INSTALL_SUSPEND;
5377 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5379 static const WCHAR query[] =
5380 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5381 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5382 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5383 MSIRECORD *rec, *row;
5384 DWORD i, size = 0;
5385 va_list va;
5386 const WCHAR *str;
5387 WCHAR *data;
5389 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5391 rec = MSI_CreateRecord( count + 2 );
5392 str = MSI_RecordGetString( row, 1 );
5393 MSI_RecordSetStringW( rec, 0, str );
5394 msiobj_release( &row->hdr );
5395 MSI_RecordSetInteger( rec, 1, error );
5397 va_start( va, count );
5398 for (i = 0; i < count; i++)
5400 str = va_arg( va, const WCHAR *);
5401 MSI_RecordSetStringW( rec, i + 2, str );
5403 va_end( va );
5405 MSI_FormatRecordW( package, rec, NULL, &size );
5406 size++;
5407 data = msi_alloc( size * sizeof(WCHAR) );
5408 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5409 else data[0] = 0;
5410 msiobj_release( &rec->hdr );
5411 return data;
5414 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5416 DWORD attrib;
5417 UINT rc;
5420 * We are currently doing what should be done here in the top level Install
5421 * however for Administrative and uninstalls this step will be needed
5423 if (!package->PackagePath)
5424 return ERROR_SUCCESS;
5426 msi_set_sourcedir_props(package, TRUE);
5428 attrib = GetFileAttributesW(package->db->path);
5429 if (attrib == INVALID_FILE_ATTRIBUTES)
5431 LPWSTR prompt, msg;
5432 DWORD size = 0;
5434 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5435 package->Context, MSICODE_PRODUCT,
5436 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5437 if (rc == ERROR_MORE_DATA)
5439 prompt = msi_alloc(size * sizeof(WCHAR));
5440 MsiSourceListGetInfoW(package->ProductCode, NULL,
5441 package->Context, MSICODE_PRODUCT,
5442 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5444 else
5445 prompt = strdupW(package->db->path);
5447 msg = msi_build_error_string(package, 1302, 1, prompt);
5448 msi_free(prompt);
5449 while(attrib == INVALID_FILE_ATTRIBUTES)
5451 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5452 if (rc == IDCANCEL)
5454 msi_free(msg);
5455 return ERROR_INSTALL_USEREXIT;
5457 attrib = GetFileAttributesW(package->db->path);
5459 msi_free(msg);
5460 rc = ERROR_SUCCESS;
5462 else
5463 return ERROR_SUCCESS;
5465 return rc;
5468 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5470 HKEY hkey = 0;
5471 LPWSTR buffer, productid = NULL;
5472 UINT i, rc = ERROR_SUCCESS;
5473 MSIRECORD *uirow;
5475 static const WCHAR szPropKeys[][80] =
5477 {'P','r','o','d','u','c','t','I','D',0},
5478 {'U','S','E','R','N','A','M','E',0},
5479 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5480 {0},
5483 static const WCHAR szRegKeys[][80] =
5485 {'P','r','o','d','u','c','t','I','D',0},
5486 {'R','e','g','O','w','n','e','r',0},
5487 {'R','e','g','C','o','m','p','a','n','y',0},
5488 {0},
5491 if (msi_check_unpublish(package))
5493 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5494 goto end;
5497 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5498 if (!productid)
5499 goto end;
5501 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5502 NULL, &hkey, TRUE);
5503 if (rc != ERROR_SUCCESS)
5504 goto end;
5506 for( i = 0; szPropKeys[i][0]; i++ )
5508 buffer = msi_dup_property( package->db, szPropKeys[i] );
5509 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5510 msi_free( buffer );
5513 end:
5514 uirow = MSI_CreateRecord( 1 );
5515 MSI_RecordSetStringW( uirow, 1, productid );
5516 msi_ui_actiondata( package, szRegisterUser, uirow );
5517 msiobj_release( &uirow->hdr );
5519 msi_free(productid);
5520 RegCloseKey(hkey);
5521 return rc;
5525 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5527 UINT rc;
5529 package->script->InWhatSequence |= SEQUENCE_EXEC;
5530 rc = ACTION_ProcessExecSequence(package,FALSE);
5531 return rc;
5534 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5536 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5537 WCHAR productid_85[21], component_85[21], *ret;
5538 GUID clsid;
5539 DWORD sz;
5541 /* > is used if there is a component GUID and < if not. */
5543 productid_85[0] = 0;
5544 component_85[0] = 0;
5545 CLSIDFromString( package->ProductCode, &clsid );
5547 encode_base85_guid( &clsid, productid_85 );
5548 if (component)
5550 CLSIDFromString( component->ComponentId, &clsid );
5551 encode_base85_guid( &clsid, component_85 );
5554 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5555 debugstr_w(component_85));
5557 sz = 20 + strlenW( feature ) + 20 + 3;
5558 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5559 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5560 return ret;
5563 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5565 MSIPACKAGE *package = param;
5566 LPCWSTR compgroupid, component, feature, qualifier, text;
5567 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5568 HKEY hkey = NULL;
5569 UINT rc;
5570 MSICOMPONENT *comp;
5571 MSIFEATURE *feat;
5572 DWORD sz;
5573 MSIRECORD *uirow;
5574 int len;
5576 feature = MSI_RecordGetString(rec, 5);
5577 feat = msi_get_loaded_feature(package, feature);
5578 if (!feat)
5579 return ERROR_SUCCESS;
5581 feat->Action = msi_get_feature_action( package, feat );
5582 if (feat->Action != INSTALLSTATE_LOCAL &&
5583 feat->Action != INSTALLSTATE_SOURCE &&
5584 feat->Action != INSTALLSTATE_ADVERTISED)
5586 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5587 return ERROR_SUCCESS;
5590 component = MSI_RecordGetString(rec, 3);
5591 comp = msi_get_loaded_component(package, component);
5592 if (!comp)
5593 return ERROR_SUCCESS;
5595 compgroupid = MSI_RecordGetString(rec,1);
5596 qualifier = MSI_RecordGetString(rec,2);
5598 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5599 if (rc != ERROR_SUCCESS)
5600 goto end;
5602 advertise = msi_create_component_advertise_string( package, comp, feature );
5603 text = MSI_RecordGetString( rec, 4 );
5604 if (text)
5606 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5607 strcpyW( p, advertise );
5608 strcatW( p, text );
5609 msi_free( advertise );
5610 advertise = p;
5612 existing = msi_reg_get_val_str( hkey, qualifier );
5614 sz = strlenW( advertise ) + 1;
5615 if (existing)
5617 for (p = existing; *p; p += len)
5619 len = strlenW( p ) + 1;
5620 if (strcmpW( advertise, p )) sz += len;
5623 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5625 rc = ERROR_OUTOFMEMORY;
5626 goto end;
5628 q = output;
5629 if (existing)
5631 for (p = existing; *p; p += len)
5633 len = strlenW( p ) + 1;
5634 if (strcmpW( advertise, p ))
5636 memcpy( q, p, len * sizeof(WCHAR) );
5637 q += len;
5641 strcpyW( q, advertise );
5642 q[strlenW( q ) + 1] = 0;
5644 msi_reg_set_val_multi_str( hkey, qualifier, output );
5646 end:
5647 RegCloseKey(hkey);
5648 msi_free( output );
5649 msi_free( advertise );
5650 msi_free( existing );
5652 /* the UI chunk */
5653 uirow = MSI_CreateRecord( 2 );
5654 MSI_RecordSetStringW( uirow, 1, compgroupid );
5655 MSI_RecordSetStringW( uirow, 2, qualifier);
5656 msi_ui_actiondata( package, szPublishComponents, uirow );
5657 msiobj_release( &uirow->hdr );
5658 /* FIXME: call ui_progress? */
5660 return rc;
5664 * At present I am ignorning the advertised components part of this and only
5665 * focusing on the qualified component sets
5667 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5669 static const WCHAR query[] = {
5670 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5671 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5672 MSIQUERY *view;
5673 UINT rc;
5675 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5676 if (rc != ERROR_SUCCESS)
5677 return ERROR_SUCCESS;
5679 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5680 msiobj_release(&view->hdr);
5681 return rc;
5684 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5686 static const WCHAR szInstallerComponents[] = {
5687 'S','o','f','t','w','a','r','e','\\',
5688 'M','i','c','r','o','s','o','f','t','\\',
5689 'I','n','s','t','a','l','l','e','r','\\',
5690 'C','o','m','p','o','n','e','n','t','s','\\',0};
5692 MSIPACKAGE *package = param;
5693 LPCWSTR compgroupid, component, feature, qualifier;
5694 MSICOMPONENT *comp;
5695 MSIFEATURE *feat;
5696 MSIRECORD *uirow;
5697 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5698 LONG res;
5700 feature = MSI_RecordGetString( rec, 5 );
5701 feat = msi_get_loaded_feature( package, feature );
5702 if (!feat)
5703 return ERROR_SUCCESS;
5705 feat->Action = msi_get_feature_action( package, feat );
5706 if (feat->Action != INSTALLSTATE_ABSENT)
5708 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5709 return ERROR_SUCCESS;
5712 component = MSI_RecordGetString( rec, 3 );
5713 comp = msi_get_loaded_component( package, component );
5714 if (!comp)
5715 return ERROR_SUCCESS;
5717 compgroupid = MSI_RecordGetString( rec, 1 );
5718 qualifier = MSI_RecordGetString( rec, 2 );
5720 squash_guid( compgroupid, squashed );
5721 strcpyW( keypath, szInstallerComponents );
5722 strcatW( keypath, squashed );
5724 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5725 if (res != ERROR_SUCCESS)
5727 WARN("Unable to delete component key %d\n", res);
5730 uirow = MSI_CreateRecord( 2 );
5731 MSI_RecordSetStringW( uirow, 1, compgroupid );
5732 MSI_RecordSetStringW( uirow, 2, qualifier );
5733 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5734 msiobj_release( &uirow->hdr );
5736 return ERROR_SUCCESS;
5739 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5741 static const WCHAR query[] = {
5742 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5743 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5744 MSIQUERY *view;
5745 UINT rc;
5747 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5748 if (rc != ERROR_SUCCESS)
5749 return ERROR_SUCCESS;
5751 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5752 msiobj_release( &view->hdr );
5753 return rc;
5756 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5758 static const WCHAR query[] =
5759 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5760 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5761 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5762 MSIPACKAGE *package = param;
5763 MSICOMPONENT *component;
5764 MSIRECORD *row;
5765 MSIFILE *file;
5766 SC_HANDLE hscm = NULL, service = NULL;
5767 LPCWSTR comp, key;
5768 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5769 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5770 DWORD serv_type, start_type, err_control;
5771 SERVICE_DESCRIPTIONW sd = {NULL};
5772 UINT ret = ERROR_SUCCESS;
5774 comp = MSI_RecordGetString( rec, 12 );
5775 component = msi_get_loaded_component( package, comp );
5776 if (!component)
5778 WARN("service component not found\n");
5779 goto done;
5781 component->Action = msi_get_component_action( package, component );
5782 if (component->Action != INSTALLSTATE_LOCAL)
5784 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5785 goto done;
5787 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5788 if (!hscm)
5790 ERR("Failed to open the SC Manager!\n");
5791 goto done;
5794 start_type = MSI_RecordGetInteger(rec, 5);
5795 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5796 goto done;
5798 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5799 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5800 serv_type = MSI_RecordGetInteger(rec, 4);
5801 err_control = MSI_RecordGetInteger(rec, 6);
5802 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5803 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5804 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5805 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5806 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5807 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5809 /* fetch the service path */
5810 row = MSI_QueryGetRecord(package->db, query, comp);
5811 if (!row)
5813 ERR("Query failed\n");
5814 goto done;
5816 if (!(key = MSI_RecordGetString(row, 6)))
5818 msiobj_release(&row->hdr);
5819 goto done;
5821 file = msi_get_loaded_file(package, key);
5822 msiobj_release(&row->hdr);
5823 if (!file)
5825 ERR("Failed to load the service file\n");
5826 goto done;
5829 if (!args || !args[0]) image_path = file->TargetPath;
5830 else
5832 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5833 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5835 ret = ERROR_OUTOFMEMORY;
5836 goto done;
5839 strcpyW(image_path, file->TargetPath);
5840 strcatW(image_path, szSpace);
5841 strcatW(image_path, args);
5843 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5844 start_type, err_control, image_path, load_order,
5845 NULL, depends, serv_name, pass);
5847 if (!service)
5849 if (GetLastError() != ERROR_SERVICE_EXISTS)
5850 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5852 else if (sd.lpDescription)
5854 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5855 WARN("failed to set service description %u\n", GetLastError());
5858 if (image_path != file->TargetPath) msi_free(image_path);
5859 done:
5860 CloseServiceHandle(service);
5861 CloseServiceHandle(hscm);
5862 msi_free(name);
5863 msi_free(disp);
5864 msi_free(sd.lpDescription);
5865 msi_free(load_order);
5866 msi_free(serv_name);
5867 msi_free(pass);
5868 msi_free(depends);
5869 msi_free(args);
5871 return ret;
5874 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5876 static const WCHAR query[] = {
5877 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5878 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5879 MSIQUERY *view;
5880 UINT rc;
5882 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5883 if (rc != ERROR_SUCCESS)
5884 return ERROR_SUCCESS;
5886 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5887 msiobj_release(&view->hdr);
5888 return rc;
5891 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5892 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5894 LPCWSTR *vector, *temp_vector;
5895 LPWSTR p, q;
5896 DWORD sep_len;
5898 static const WCHAR separator[] = {'[','~',']',0};
5900 *numargs = 0;
5901 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5903 if (!args)
5904 return NULL;
5906 vector = msi_alloc(sizeof(LPWSTR));
5907 if (!vector)
5908 return NULL;
5910 p = args;
5913 (*numargs)++;
5914 vector[*numargs - 1] = p;
5916 if ((q = strstrW(p, separator)))
5918 *q = '\0';
5920 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5921 if (!temp_vector)
5923 msi_free(vector);
5924 return NULL;
5926 vector = temp_vector;
5928 p = q + sep_len;
5930 } while (q);
5932 return vector;
5935 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5937 MSIPACKAGE *package = param;
5938 MSICOMPONENT *comp;
5939 MSIRECORD *uirow;
5940 SC_HANDLE scm = NULL, service = NULL;
5941 LPCWSTR component, *vector = NULL;
5942 LPWSTR name, args, display_name = NULL;
5943 DWORD event, numargs, len, wait, dummy;
5944 UINT r = ERROR_FUNCTION_FAILED;
5945 SERVICE_STATUS_PROCESS status;
5946 ULONGLONG start_time;
5948 component = MSI_RecordGetString(rec, 6);
5949 comp = msi_get_loaded_component(package, component);
5950 if (!comp)
5951 return ERROR_SUCCESS;
5953 comp->Action = msi_get_component_action( package, comp );
5954 if (comp->Action != INSTALLSTATE_LOCAL)
5956 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5957 return ERROR_SUCCESS;
5960 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5961 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5962 event = MSI_RecordGetInteger(rec, 3);
5963 wait = MSI_RecordGetInteger(rec, 5);
5965 if (!(event & msidbServiceControlEventStart))
5967 r = ERROR_SUCCESS;
5968 goto done;
5971 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5972 if (!scm)
5974 ERR("Failed to open the service control manager\n");
5975 goto done;
5978 len = 0;
5979 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5980 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5982 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5983 GetServiceDisplayNameW( scm, name, display_name, &len );
5986 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5987 if (!service)
5989 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5990 goto done;
5993 vector = msi_service_args_to_vector(args, &numargs);
5995 if (!StartServiceW(service, numargs, vector) &&
5996 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5998 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5999 goto done;
6002 r = ERROR_SUCCESS;
6003 if (wait)
6005 /* wait for at most 30 seconds for the service to be up and running */
6006 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6007 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6009 TRACE("failed to query service status (%u)\n", GetLastError());
6010 goto done;
6012 start_time = GetTickCount64();
6013 while (status.dwCurrentState == SERVICE_START_PENDING)
6015 if (GetTickCount64() - start_time > 30000) break;
6016 Sleep(1000);
6017 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6018 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6020 TRACE("failed to query service status (%u)\n", GetLastError());
6021 goto done;
6024 if (status.dwCurrentState != SERVICE_RUNNING)
6026 WARN("service failed to start %u\n", status.dwCurrentState);
6027 r = ERROR_FUNCTION_FAILED;
6031 done:
6032 uirow = MSI_CreateRecord( 2 );
6033 MSI_RecordSetStringW( uirow, 1, display_name );
6034 MSI_RecordSetStringW( uirow, 2, name );
6035 msi_ui_actiondata( package, szStartServices, uirow );
6036 msiobj_release( &uirow->hdr );
6038 CloseServiceHandle(service);
6039 CloseServiceHandle(scm);
6041 msi_free(name);
6042 msi_free(args);
6043 msi_free(vector);
6044 msi_free(display_name);
6045 return r;
6048 static UINT ACTION_StartServices( MSIPACKAGE *package )
6050 static const WCHAR query[] = {
6051 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6052 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6053 MSIQUERY *view;
6054 UINT rc;
6056 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6057 if (rc != ERROR_SUCCESS)
6058 return ERROR_SUCCESS;
6060 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6061 msiobj_release(&view->hdr);
6062 return rc;
6065 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6067 DWORD i, needed, count;
6068 ENUM_SERVICE_STATUSW *dependencies;
6069 SERVICE_STATUS ss;
6070 SC_HANDLE depserv;
6071 BOOL stopped, ret = FALSE;
6073 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6074 0, &needed, &count))
6075 return TRUE;
6077 if (GetLastError() != ERROR_MORE_DATA)
6078 return FALSE;
6080 dependencies = msi_alloc(needed);
6081 if (!dependencies)
6082 return FALSE;
6084 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6085 needed, &needed, &count))
6086 goto done;
6088 for (i = 0; i < count; i++)
6090 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6091 SERVICE_STOP | SERVICE_QUERY_STATUS);
6092 if (!depserv)
6093 goto done;
6095 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6096 CloseServiceHandle(depserv);
6097 if (!stopped)
6098 goto done;
6101 ret = TRUE;
6103 done:
6104 msi_free(dependencies);
6105 return ret;
6108 static UINT stop_service( LPCWSTR name )
6110 SC_HANDLE scm = NULL, service = NULL;
6111 SERVICE_STATUS status;
6112 SERVICE_STATUS_PROCESS ssp;
6113 DWORD needed;
6115 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6116 if (!scm)
6118 WARN("Failed to open the SCM: %d\n", GetLastError());
6119 goto done;
6122 service = OpenServiceW(scm, name,
6123 SERVICE_STOP |
6124 SERVICE_QUERY_STATUS |
6125 SERVICE_ENUMERATE_DEPENDENTS);
6126 if (!service)
6128 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6129 goto done;
6132 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6133 sizeof(SERVICE_STATUS_PROCESS), &needed))
6135 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6136 goto done;
6139 if (ssp.dwCurrentState == SERVICE_STOPPED)
6140 goto done;
6142 stop_service_dependents(scm, service);
6144 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6145 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6147 done:
6148 CloseServiceHandle(service);
6149 CloseServiceHandle(scm);
6151 return ERROR_SUCCESS;
6154 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6156 MSIPACKAGE *package = param;
6157 MSICOMPONENT *comp;
6158 MSIRECORD *uirow;
6159 LPCWSTR component;
6160 LPWSTR name = NULL, display_name = NULL;
6161 DWORD event, len;
6162 SC_HANDLE scm;
6164 event = MSI_RecordGetInteger( rec, 3 );
6165 if (!(event & msidbServiceControlEventStop))
6166 return ERROR_SUCCESS;
6168 component = MSI_RecordGetString( rec, 6 );
6169 comp = msi_get_loaded_component( package, component );
6170 if (!comp)
6171 return ERROR_SUCCESS;
6173 comp->Action = msi_get_component_action( package, comp );
6174 if (comp->Action != INSTALLSTATE_ABSENT)
6176 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6177 return ERROR_SUCCESS;
6180 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6181 if (!scm)
6183 ERR("Failed to open the service control manager\n");
6184 goto done;
6187 len = 0;
6188 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6189 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6191 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6192 GetServiceDisplayNameW( scm, name, display_name, &len );
6194 CloseServiceHandle( scm );
6196 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6197 stop_service( name );
6199 done:
6200 uirow = MSI_CreateRecord( 2 );
6201 MSI_RecordSetStringW( uirow, 1, display_name );
6202 MSI_RecordSetStringW( uirow, 2, name );
6203 msi_ui_actiondata( package, szStopServices, uirow );
6204 msiobj_release( &uirow->hdr );
6206 msi_free( name );
6207 msi_free( display_name );
6208 return ERROR_SUCCESS;
6211 static UINT ACTION_StopServices( MSIPACKAGE *package )
6213 static const WCHAR query[] = {
6214 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6215 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6216 MSIQUERY *view;
6217 UINT rc;
6219 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6220 if (rc != ERROR_SUCCESS)
6221 return ERROR_SUCCESS;
6223 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6224 msiobj_release(&view->hdr);
6225 return rc;
6228 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6230 MSIPACKAGE *package = param;
6231 MSICOMPONENT *comp;
6232 MSIRECORD *uirow;
6233 LPWSTR name = NULL, display_name = NULL;
6234 DWORD event, len;
6235 SC_HANDLE scm = NULL, service = NULL;
6237 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6238 if (!comp)
6239 return ERROR_SUCCESS;
6241 event = MSI_RecordGetInteger( rec, 3 );
6242 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6244 comp->Action = msi_get_component_action( package, comp );
6245 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6246 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6248 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6249 msi_free( name );
6250 return ERROR_SUCCESS;
6252 stop_service( name );
6254 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6255 if (!scm)
6257 WARN("Failed to open the SCM: %d\n", GetLastError());
6258 goto done;
6261 len = 0;
6262 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6263 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6265 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6266 GetServiceDisplayNameW( scm, name, display_name, &len );
6269 service = OpenServiceW( scm, name, DELETE );
6270 if (!service)
6272 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6273 goto done;
6276 if (!DeleteService( service ))
6277 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6279 done:
6280 uirow = MSI_CreateRecord( 2 );
6281 MSI_RecordSetStringW( uirow, 1, display_name );
6282 MSI_RecordSetStringW( uirow, 2, name );
6283 msi_ui_actiondata( package, szDeleteServices, uirow );
6284 msiobj_release( &uirow->hdr );
6286 CloseServiceHandle( service );
6287 CloseServiceHandle( scm );
6288 msi_free( name );
6289 msi_free( display_name );
6291 return ERROR_SUCCESS;
6294 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6296 static const WCHAR query[] = {
6297 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6298 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6299 MSIQUERY *view;
6300 UINT rc;
6302 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6303 if (rc != ERROR_SUCCESS)
6304 return ERROR_SUCCESS;
6306 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6307 msiobj_release( &view->hdr );
6308 return rc;
6311 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6313 MSIPACKAGE *package = param;
6314 LPWSTR driver, driver_path, ptr;
6315 WCHAR outpath[MAX_PATH];
6316 MSIFILE *driver_file = NULL, *setup_file = NULL;
6317 MSICOMPONENT *comp;
6318 MSIRECORD *uirow;
6319 LPCWSTR desc, file_key, component;
6320 DWORD len, usage;
6321 UINT r = ERROR_SUCCESS;
6323 static const WCHAR driver_fmt[] = {
6324 'D','r','i','v','e','r','=','%','s',0};
6325 static const WCHAR setup_fmt[] = {
6326 'S','e','t','u','p','=','%','s',0};
6327 static const WCHAR usage_fmt[] = {
6328 'F','i','l','e','U','s','a','g','e','=','1',0};
6330 component = MSI_RecordGetString( rec, 2 );
6331 comp = msi_get_loaded_component( package, component );
6332 if (!comp)
6333 return ERROR_SUCCESS;
6335 comp->Action = msi_get_component_action( package, comp );
6336 if (comp->Action != INSTALLSTATE_LOCAL)
6338 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6339 return ERROR_SUCCESS;
6341 desc = MSI_RecordGetString(rec, 3);
6343 file_key = MSI_RecordGetString( rec, 4 );
6344 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6346 file_key = MSI_RecordGetString( rec, 5 );
6347 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6349 if (!driver_file)
6351 ERR("ODBC Driver entry not found!\n");
6352 return ERROR_FUNCTION_FAILED;
6355 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6356 if (setup_file)
6357 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6358 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6360 driver = msi_alloc(len * sizeof(WCHAR));
6361 if (!driver)
6362 return ERROR_OUTOFMEMORY;
6364 ptr = driver;
6365 lstrcpyW(ptr, desc);
6366 ptr += lstrlenW(ptr) + 1;
6368 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6369 ptr += len + 1;
6371 if (setup_file)
6373 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6374 ptr += len + 1;
6377 lstrcpyW(ptr, usage_fmt);
6378 ptr += lstrlenW(ptr) + 1;
6379 *ptr = '\0';
6381 if (!driver_file->TargetPath)
6383 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6384 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6386 driver_path = strdupW(driver_file->TargetPath);
6387 ptr = strrchrW(driver_path, '\\');
6388 if (ptr) *ptr = '\0';
6390 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6391 NULL, ODBC_INSTALL_COMPLETE, &usage))
6393 ERR("Failed to install SQL driver!\n");
6394 r = ERROR_FUNCTION_FAILED;
6397 uirow = MSI_CreateRecord( 5 );
6398 MSI_RecordSetStringW( uirow, 1, desc );
6399 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6400 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6401 msi_ui_actiondata( package, szInstallODBC, uirow );
6402 msiobj_release( &uirow->hdr );
6404 msi_free(driver);
6405 msi_free(driver_path);
6407 return r;
6410 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6412 MSIPACKAGE *package = param;
6413 LPWSTR translator, translator_path, ptr;
6414 WCHAR outpath[MAX_PATH];
6415 MSIFILE *translator_file = NULL, *setup_file = NULL;
6416 MSICOMPONENT *comp;
6417 MSIRECORD *uirow;
6418 LPCWSTR desc, file_key, component;
6419 DWORD len, usage;
6420 UINT r = ERROR_SUCCESS;
6422 static const WCHAR translator_fmt[] = {
6423 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6424 static const WCHAR setup_fmt[] = {
6425 'S','e','t','u','p','=','%','s',0};
6427 component = MSI_RecordGetString( rec, 2 );
6428 comp = msi_get_loaded_component( package, component );
6429 if (!comp)
6430 return ERROR_SUCCESS;
6432 comp->Action = msi_get_component_action( package, comp );
6433 if (comp->Action != INSTALLSTATE_LOCAL)
6435 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6436 return ERROR_SUCCESS;
6438 desc = MSI_RecordGetString(rec, 3);
6440 file_key = MSI_RecordGetString( rec, 4 );
6441 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6443 file_key = MSI_RecordGetString( rec, 5 );
6444 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6446 if (!translator_file)
6448 ERR("ODBC Translator entry not found!\n");
6449 return ERROR_FUNCTION_FAILED;
6452 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6453 if (setup_file)
6454 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6456 translator = msi_alloc(len * sizeof(WCHAR));
6457 if (!translator)
6458 return ERROR_OUTOFMEMORY;
6460 ptr = translator;
6461 lstrcpyW(ptr, desc);
6462 ptr += lstrlenW(ptr) + 1;
6464 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6465 ptr += len + 1;
6467 if (setup_file)
6469 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6470 ptr += len + 1;
6472 *ptr = '\0';
6474 translator_path = strdupW(translator_file->TargetPath);
6475 ptr = strrchrW(translator_path, '\\');
6476 if (ptr) *ptr = '\0';
6478 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6479 NULL, ODBC_INSTALL_COMPLETE, &usage))
6481 ERR("Failed to install SQL translator!\n");
6482 r = ERROR_FUNCTION_FAILED;
6485 uirow = MSI_CreateRecord( 5 );
6486 MSI_RecordSetStringW( uirow, 1, desc );
6487 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6488 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6489 msi_ui_actiondata( package, szInstallODBC, uirow );
6490 msiobj_release( &uirow->hdr );
6492 msi_free(translator);
6493 msi_free(translator_path);
6495 return r;
6498 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6500 MSIPACKAGE *package = param;
6501 MSICOMPONENT *comp;
6502 LPWSTR attrs;
6503 LPCWSTR desc, driver, component;
6504 WORD request = ODBC_ADD_SYS_DSN;
6505 INT registration;
6506 DWORD len;
6507 UINT r = ERROR_SUCCESS;
6508 MSIRECORD *uirow;
6510 static const WCHAR attrs_fmt[] = {
6511 'D','S','N','=','%','s',0 };
6513 component = MSI_RecordGetString( rec, 2 );
6514 comp = msi_get_loaded_component( package, component );
6515 if (!comp)
6516 return ERROR_SUCCESS;
6518 comp->Action = msi_get_component_action( package, comp );
6519 if (comp->Action != INSTALLSTATE_LOCAL)
6521 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6522 return ERROR_SUCCESS;
6525 desc = MSI_RecordGetString(rec, 3);
6526 driver = MSI_RecordGetString(rec, 4);
6527 registration = MSI_RecordGetInteger(rec, 5);
6529 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6530 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6532 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6533 attrs = msi_alloc(len * sizeof(WCHAR));
6534 if (!attrs)
6535 return ERROR_OUTOFMEMORY;
6537 len = sprintfW(attrs, attrs_fmt, desc);
6538 attrs[len + 1] = 0;
6540 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6542 ERR("Failed to install SQL data source!\n");
6543 r = ERROR_FUNCTION_FAILED;
6546 uirow = MSI_CreateRecord( 5 );
6547 MSI_RecordSetStringW( uirow, 1, desc );
6548 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6549 MSI_RecordSetInteger( uirow, 3, request );
6550 msi_ui_actiondata( package, szInstallODBC, uirow );
6551 msiobj_release( &uirow->hdr );
6553 msi_free(attrs);
6555 return r;
6558 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6560 static const WCHAR driver_query[] = {
6561 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6562 'O','D','B','C','D','r','i','v','e','r',0};
6563 static const WCHAR translator_query[] = {
6564 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6565 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6566 static const WCHAR source_query[] = {
6567 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6568 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6569 MSIQUERY *view;
6570 UINT rc;
6572 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6573 if (rc == ERROR_SUCCESS)
6575 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6576 msiobj_release(&view->hdr);
6577 if (rc != ERROR_SUCCESS)
6578 return rc;
6580 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6581 if (rc == ERROR_SUCCESS)
6583 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6584 msiobj_release(&view->hdr);
6585 if (rc != ERROR_SUCCESS)
6586 return rc;
6588 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6589 if (rc == ERROR_SUCCESS)
6591 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6592 msiobj_release(&view->hdr);
6593 if (rc != ERROR_SUCCESS)
6594 return rc;
6596 return ERROR_SUCCESS;
6599 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6601 MSIPACKAGE *package = param;
6602 MSICOMPONENT *comp;
6603 MSIRECORD *uirow;
6604 DWORD usage;
6605 LPCWSTR desc, component;
6607 component = MSI_RecordGetString( rec, 2 );
6608 comp = msi_get_loaded_component( package, component );
6609 if (!comp)
6610 return ERROR_SUCCESS;
6612 comp->Action = msi_get_component_action( package, comp );
6613 if (comp->Action != INSTALLSTATE_ABSENT)
6615 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6616 return ERROR_SUCCESS;
6619 desc = MSI_RecordGetString( rec, 3 );
6620 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6622 WARN("Failed to remove ODBC driver\n");
6624 else if (!usage)
6626 FIXME("Usage count reached 0\n");
6629 uirow = MSI_CreateRecord( 2 );
6630 MSI_RecordSetStringW( uirow, 1, desc );
6631 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6632 msi_ui_actiondata( package, szRemoveODBC, uirow );
6633 msiobj_release( &uirow->hdr );
6635 return ERROR_SUCCESS;
6638 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6640 MSIPACKAGE *package = param;
6641 MSICOMPONENT *comp;
6642 MSIRECORD *uirow;
6643 DWORD usage;
6644 LPCWSTR desc, component;
6646 component = MSI_RecordGetString( rec, 2 );
6647 comp = msi_get_loaded_component( package, component );
6648 if (!comp)
6649 return ERROR_SUCCESS;
6651 comp->Action = msi_get_component_action( package, comp );
6652 if (comp->Action != INSTALLSTATE_ABSENT)
6654 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6655 return ERROR_SUCCESS;
6658 desc = MSI_RecordGetString( rec, 3 );
6659 if (!SQLRemoveTranslatorW( desc, &usage ))
6661 WARN("Failed to remove ODBC translator\n");
6663 else if (!usage)
6665 FIXME("Usage count reached 0\n");
6668 uirow = MSI_CreateRecord( 2 );
6669 MSI_RecordSetStringW( uirow, 1, desc );
6670 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6671 msi_ui_actiondata( package, szRemoveODBC, uirow );
6672 msiobj_release( &uirow->hdr );
6674 return ERROR_SUCCESS;
6677 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6679 MSIPACKAGE *package = param;
6680 MSICOMPONENT *comp;
6681 MSIRECORD *uirow;
6682 LPWSTR attrs;
6683 LPCWSTR desc, driver, component;
6684 WORD request = ODBC_REMOVE_SYS_DSN;
6685 INT registration;
6686 DWORD len;
6688 static const WCHAR attrs_fmt[] = {
6689 'D','S','N','=','%','s',0 };
6691 component = MSI_RecordGetString( rec, 2 );
6692 comp = msi_get_loaded_component( package, component );
6693 if (!comp)
6694 return ERROR_SUCCESS;
6696 comp->Action = msi_get_component_action( package, comp );
6697 if (comp->Action != INSTALLSTATE_ABSENT)
6699 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6700 return ERROR_SUCCESS;
6703 desc = MSI_RecordGetString( rec, 3 );
6704 driver = MSI_RecordGetString( rec, 4 );
6705 registration = MSI_RecordGetInteger( rec, 5 );
6707 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6708 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6710 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6711 attrs = msi_alloc( len * sizeof(WCHAR) );
6712 if (!attrs)
6713 return ERROR_OUTOFMEMORY;
6715 FIXME("Use ODBCSourceAttribute table\n");
6717 len = sprintfW( attrs, attrs_fmt, desc );
6718 attrs[len + 1] = 0;
6720 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6722 WARN("Failed to remove ODBC data source\n");
6724 msi_free( attrs );
6726 uirow = MSI_CreateRecord( 3 );
6727 MSI_RecordSetStringW( uirow, 1, desc );
6728 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6729 MSI_RecordSetInteger( uirow, 3, request );
6730 msi_ui_actiondata( package, szRemoveODBC, uirow );
6731 msiobj_release( &uirow->hdr );
6733 return ERROR_SUCCESS;
6736 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6738 static const WCHAR driver_query[] = {
6739 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6740 'O','D','B','C','D','r','i','v','e','r',0};
6741 static const WCHAR translator_query[] = {
6742 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6743 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6744 static const WCHAR source_query[] = {
6745 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6746 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6747 MSIQUERY *view;
6748 UINT rc;
6750 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6751 if (rc == ERROR_SUCCESS)
6753 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6754 msiobj_release( &view->hdr );
6755 if (rc != ERROR_SUCCESS)
6756 return rc;
6758 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6759 if (rc == ERROR_SUCCESS)
6761 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6762 msiobj_release( &view->hdr );
6763 if (rc != ERROR_SUCCESS)
6764 return rc;
6766 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6767 if (rc == ERROR_SUCCESS)
6769 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6770 msiobj_release( &view->hdr );
6771 if (rc != ERROR_SUCCESS)
6772 return rc;
6774 return ERROR_SUCCESS;
6777 #define ENV_ACT_SETALWAYS 0x1
6778 #define ENV_ACT_SETABSENT 0x2
6779 #define ENV_ACT_REMOVE 0x4
6780 #define ENV_ACT_REMOVEMATCH 0x8
6782 #define ENV_MOD_MACHINE 0x20000000
6783 #define ENV_MOD_APPEND 0x40000000
6784 #define ENV_MOD_PREFIX 0x80000000
6785 #define ENV_MOD_MASK 0xC0000000
6787 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6789 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6791 LPCWSTR cptr = *name;
6793 static const WCHAR prefix[] = {'[','~',']',0};
6794 static const int prefix_len = 3;
6796 *flags = 0;
6797 while (*cptr)
6799 if (*cptr == '=')
6800 *flags |= ENV_ACT_SETALWAYS;
6801 else if (*cptr == '+')
6802 *flags |= ENV_ACT_SETABSENT;
6803 else if (*cptr == '-')
6804 *flags |= ENV_ACT_REMOVE;
6805 else if (*cptr == '!')
6806 *flags |= ENV_ACT_REMOVEMATCH;
6807 else if (*cptr == '*')
6808 *flags |= ENV_MOD_MACHINE;
6809 else
6810 break;
6812 cptr++;
6813 (*name)++;
6816 if (!*cptr)
6818 ERR("Missing environment variable\n");
6819 return ERROR_FUNCTION_FAILED;
6822 if (*value)
6824 LPCWSTR ptr = *value;
6825 if (!strncmpW(ptr, prefix, prefix_len))
6827 if (ptr[prefix_len] == szSemiColon[0])
6829 *flags |= ENV_MOD_APPEND;
6830 *value += lstrlenW(prefix);
6832 else
6834 *value = NULL;
6837 else if (lstrlenW(*value) >= prefix_len)
6839 ptr += lstrlenW(ptr) - prefix_len;
6840 if (!strcmpW( ptr, prefix ))
6842 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6844 *flags |= ENV_MOD_PREFIX;
6845 /* the "[~]" will be removed by deformat_string */;
6847 else
6849 *value = NULL;
6855 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6856 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6857 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6858 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6860 ERR("Invalid flags: %08x\n", *flags);
6861 return ERROR_FUNCTION_FAILED;
6864 if (!*flags)
6865 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6867 return ERROR_SUCCESS;
6870 static UINT open_env_key( DWORD flags, HKEY *key )
6872 static const WCHAR user_env[] =
6873 {'E','n','v','i','r','o','n','m','e','n','t',0};
6874 static const WCHAR machine_env[] =
6875 {'S','y','s','t','e','m','\\',
6876 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6877 'C','o','n','t','r','o','l','\\',
6878 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6879 'E','n','v','i','r','o','n','m','e','n','t',0};
6880 const WCHAR *env;
6881 HKEY root;
6882 LONG res;
6884 if (flags & ENV_MOD_MACHINE)
6886 env = machine_env;
6887 root = HKEY_LOCAL_MACHINE;
6889 else
6891 env = user_env;
6892 root = HKEY_CURRENT_USER;
6895 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6896 if (res != ERROR_SUCCESS)
6898 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6899 return ERROR_FUNCTION_FAILED;
6902 return ERROR_SUCCESS;
6905 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6907 MSIPACKAGE *package = param;
6908 LPCWSTR name, value, component;
6909 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6910 DWORD flags, type, size;
6911 UINT res;
6912 HKEY env = NULL;
6913 MSICOMPONENT *comp;
6914 MSIRECORD *uirow;
6915 int action = 0;
6917 component = MSI_RecordGetString(rec, 4);
6918 comp = msi_get_loaded_component(package, component);
6919 if (!comp)
6920 return ERROR_SUCCESS;
6922 comp->Action = msi_get_component_action( package, comp );
6923 if (comp->Action != INSTALLSTATE_LOCAL)
6925 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6926 return ERROR_SUCCESS;
6928 name = MSI_RecordGetString(rec, 2);
6929 value = MSI_RecordGetString(rec, 3);
6931 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6933 res = env_parse_flags(&name, &value, &flags);
6934 if (res != ERROR_SUCCESS || !value)
6935 goto done;
6937 if (value && !deformat_string(package, value, &deformatted))
6939 res = ERROR_OUTOFMEMORY;
6940 goto done;
6943 value = deformatted;
6945 res = open_env_key( flags, &env );
6946 if (res != ERROR_SUCCESS)
6947 goto done;
6949 if (flags & ENV_MOD_MACHINE)
6950 action |= 0x20000000;
6952 size = 0;
6953 type = REG_SZ;
6954 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6955 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6956 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6957 goto done;
6959 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6961 action = 0x2;
6963 /* Nothing to do. */
6964 if (!value)
6966 res = ERROR_SUCCESS;
6967 goto done;
6970 /* If we are appending but the string was empty, strip ; */
6971 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6973 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6974 newval = strdupW(value);
6975 if (!newval)
6977 res = ERROR_OUTOFMEMORY;
6978 goto done;
6981 else
6983 action = 0x1;
6985 /* Contrary to MSDN, +-variable to [~];path works */
6986 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6988 res = ERROR_SUCCESS;
6989 goto done;
6992 data = msi_alloc(size);
6993 if (!data)
6995 RegCloseKey(env);
6996 return ERROR_OUTOFMEMORY;
6999 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
7000 if (res != ERROR_SUCCESS)
7001 goto done;
7003 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7005 action = 0x4;
7006 res = RegDeleteValueW(env, name);
7007 if (res != ERROR_SUCCESS)
7008 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7009 goto done;
7012 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
7013 if (flags & ENV_MOD_MASK)
7015 DWORD mod_size;
7016 int multiplier = 0;
7017 if (flags & ENV_MOD_APPEND) multiplier++;
7018 if (flags & ENV_MOD_PREFIX) multiplier++;
7019 mod_size = lstrlenW(value) * multiplier;
7020 size += mod_size * sizeof(WCHAR);
7023 newval = msi_alloc(size);
7024 ptr = newval;
7025 if (!newval)
7027 res = ERROR_OUTOFMEMORY;
7028 goto done;
7031 if (flags & ENV_MOD_PREFIX)
7033 lstrcpyW(newval, value);
7034 ptr = newval + lstrlenW(value);
7035 action |= 0x80000000;
7038 lstrcpyW(ptr, data);
7040 if (flags & ENV_MOD_APPEND)
7042 lstrcatW(newval, value);
7043 action |= 0x40000000;
7046 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7047 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
7048 if (res)
7050 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7053 done:
7054 uirow = MSI_CreateRecord( 3 );
7055 MSI_RecordSetStringW( uirow, 1, name );
7056 MSI_RecordSetStringW( uirow, 2, newval );
7057 MSI_RecordSetInteger( uirow, 3, action );
7058 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7059 msiobj_release( &uirow->hdr );
7061 if (env) RegCloseKey(env);
7062 msi_free(deformatted);
7063 msi_free(data);
7064 msi_free(newval);
7065 return res;
7068 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7070 static const WCHAR query[] = {
7071 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7072 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7073 MSIQUERY *view;
7074 UINT rc;
7076 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7077 if (rc != ERROR_SUCCESS)
7078 return ERROR_SUCCESS;
7080 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7081 msiobj_release(&view->hdr);
7082 return rc;
7085 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7087 MSIPACKAGE *package = param;
7088 LPCWSTR name, value, component;
7089 LPWSTR deformatted = NULL;
7090 DWORD flags;
7091 HKEY env;
7092 MSICOMPONENT *comp;
7093 MSIRECORD *uirow;
7094 int action = 0;
7095 LONG res;
7096 UINT r;
7098 component = MSI_RecordGetString( rec, 4 );
7099 comp = msi_get_loaded_component( package, component );
7100 if (!comp)
7101 return ERROR_SUCCESS;
7103 comp->Action = msi_get_component_action( package, comp );
7104 if (comp->Action != INSTALLSTATE_ABSENT)
7106 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7107 return ERROR_SUCCESS;
7109 name = MSI_RecordGetString( rec, 2 );
7110 value = MSI_RecordGetString( rec, 3 );
7112 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7114 r = env_parse_flags( &name, &value, &flags );
7115 if (r != ERROR_SUCCESS)
7116 return r;
7118 if (!(flags & ENV_ACT_REMOVE))
7120 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7121 return ERROR_SUCCESS;
7124 if (value && !deformat_string( package, value, &deformatted ))
7125 return ERROR_OUTOFMEMORY;
7127 value = deformatted;
7129 r = open_env_key( flags, &env );
7130 if (r != ERROR_SUCCESS)
7132 r = ERROR_SUCCESS;
7133 goto done;
7136 if (flags & ENV_MOD_MACHINE)
7137 action |= 0x20000000;
7139 TRACE("Removing %s\n", debugstr_w(name));
7141 res = RegDeleteValueW( env, name );
7142 if (res != ERROR_SUCCESS)
7144 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
7145 r = ERROR_SUCCESS;
7148 done:
7149 uirow = MSI_CreateRecord( 3 );
7150 MSI_RecordSetStringW( uirow, 1, name );
7151 MSI_RecordSetStringW( uirow, 2, value );
7152 MSI_RecordSetInteger( uirow, 3, action );
7153 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7154 msiobj_release( &uirow->hdr );
7156 if (env) RegCloseKey( env );
7157 msi_free( deformatted );
7158 return r;
7161 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7163 static const WCHAR query[] = {
7164 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7165 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7166 MSIQUERY *view;
7167 UINT rc;
7169 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7170 if (rc != ERROR_SUCCESS)
7171 return ERROR_SUCCESS;
7173 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7174 msiobj_release( &view->hdr );
7175 return rc;
7178 UINT msi_validate_product_id( MSIPACKAGE *package )
7180 LPWSTR key, template, id;
7181 UINT r = ERROR_SUCCESS;
7183 id = msi_dup_property( package->db, szProductID );
7184 if (id)
7186 msi_free( id );
7187 return ERROR_SUCCESS;
7189 template = msi_dup_property( package->db, szPIDTemplate );
7190 key = msi_dup_property( package->db, szPIDKEY );
7191 if (key && template)
7193 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7194 r = msi_set_property( package->db, szProductID, key, -1 );
7196 msi_free( template );
7197 msi_free( key );
7198 return r;
7201 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7203 return msi_validate_product_id( package );
7206 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7208 TRACE("\n");
7209 package->need_reboot_at_end = 1;
7210 return ERROR_SUCCESS;
7213 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7215 static const WCHAR szAvailableFreeReg[] =
7216 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7217 MSIRECORD *uirow;
7218 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7220 TRACE("%p %d kilobytes\n", package, space);
7222 uirow = MSI_CreateRecord( 1 );
7223 MSI_RecordSetInteger( uirow, 1, space );
7224 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7225 msiobj_release( &uirow->hdr );
7227 return ERROR_SUCCESS;
7230 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7232 TRACE("%p\n", package);
7234 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7235 return ERROR_SUCCESS;
7238 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7240 FIXME("%p\n", package);
7241 return ERROR_SUCCESS;
7244 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7246 static const WCHAR driver_query[] = {
7247 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7248 'O','D','B','C','D','r','i','v','e','r',0};
7249 static const WCHAR translator_query[] = {
7250 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7251 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7252 MSIQUERY *view;
7253 UINT r, count;
7255 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7256 if (r == ERROR_SUCCESS)
7258 count = 0;
7259 r = MSI_IterateRecords( view, &count, NULL, package );
7260 msiobj_release( &view->hdr );
7261 if (r != ERROR_SUCCESS)
7262 return r;
7263 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7265 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7266 if (r == ERROR_SUCCESS)
7268 count = 0;
7269 r = MSI_IterateRecords( view, &count, NULL, package );
7270 msiobj_release( &view->hdr );
7271 if (r != ERROR_SUCCESS)
7272 return r;
7273 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7275 return ERROR_SUCCESS;
7278 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7280 static const WCHAR fmtW[] =
7281 {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7282 MSIPACKAGE *package = param;
7283 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7284 int attrs = MSI_RecordGetInteger( rec, 5 );
7285 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7286 WCHAR *product, *features, *cmd;
7287 STARTUPINFOW si;
7288 PROCESS_INFORMATION info;
7289 BOOL ret;
7291 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7292 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7294 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7296 len += strlenW( product );
7297 if (features)
7298 len += strlenW( features );
7299 else
7300 len += sizeof(szAll) / sizeof(szAll[0]);
7302 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7304 msi_free( product );
7305 msi_free( features );
7306 return ERROR_OUTOFMEMORY;
7308 sprintfW( cmd, fmtW, product, features ? features : szAll );
7309 msi_free( product );
7310 msi_free( features );
7312 memset( &si, 0, sizeof(STARTUPINFOW) );
7313 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7314 msi_free( cmd );
7315 if (!ret) return GetLastError();
7316 CloseHandle( info.hThread );
7318 WaitForSingleObject( info.hProcess, INFINITE );
7319 CloseHandle( info.hProcess );
7320 return ERROR_SUCCESS;
7323 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7325 static const WCHAR query[] = {
7326 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7327 MSIQUERY *view;
7328 UINT r;
7330 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7331 if (r == ERROR_SUCCESS)
7333 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7334 msiobj_release( &view->hdr );
7335 if (r != ERROR_SUCCESS)
7336 return r;
7338 return ERROR_SUCCESS;
7341 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7343 MSIPACKAGE *package = param;
7344 int attributes = MSI_RecordGetInteger( rec, 5 );
7346 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7348 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7349 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7350 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7351 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7352 HKEY hkey;
7353 UINT r;
7355 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7357 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7358 if (r != ERROR_SUCCESS)
7359 return ERROR_SUCCESS;
7361 else
7363 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7364 if (r != ERROR_SUCCESS)
7365 return ERROR_SUCCESS;
7367 RegCloseKey( hkey );
7369 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7370 debugstr_w(upgrade_code), debugstr_w(version_min),
7371 debugstr_w(version_max), debugstr_w(language));
7373 return ERROR_SUCCESS;
7376 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7378 static const WCHAR query[] = {
7379 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7380 'U','p','g','r','a','d','e',0};
7381 MSIQUERY *view;
7382 UINT r;
7384 if (msi_get_property_int( package->db, szInstalled, 0 ))
7386 TRACE("product is installed, skipping action\n");
7387 return ERROR_SUCCESS;
7389 if (msi_get_property_int( package->db, szPreselected, 0 ))
7391 TRACE("Preselected property is set, not migrating feature states\n");
7392 return ERROR_SUCCESS;
7394 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7395 if (r == ERROR_SUCCESS)
7397 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7398 msiobj_release( &view->hdr );
7399 if (r != ERROR_SUCCESS)
7400 return r;
7402 return ERROR_SUCCESS;
7405 static void bind_image( const char *filename, const char *path )
7407 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7409 WARN("failed to bind image %u\n", GetLastError());
7413 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7415 UINT i;
7416 MSIFILE *file;
7417 MSIPACKAGE *package = param;
7418 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7419 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7420 char *filenameA, *pathA;
7421 WCHAR *pathW, **path_list;
7423 if (!(file = msi_get_loaded_file( package, key )))
7425 WARN("file %s not found\n", debugstr_w(key));
7426 return ERROR_SUCCESS;
7428 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7429 path_list = msi_split_string( paths, ';' );
7430 if (!path_list) bind_image( filenameA, NULL );
7431 else
7433 for (i = 0; path_list[i] && path_list[i][0]; i++)
7435 deformat_string( package, path_list[i], &pathW );
7436 if ((pathA = strdupWtoA( pathW )))
7438 bind_image( filenameA, pathA );
7439 msi_free( pathA );
7441 msi_free( pathW );
7444 msi_free( path_list );
7445 msi_free( filenameA );
7446 return ERROR_SUCCESS;
7449 static UINT ACTION_BindImage( MSIPACKAGE *package )
7451 static const WCHAR query[] = {
7452 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7453 'B','i','n','d','I','m','a','g','e',0};
7454 MSIQUERY *view;
7455 UINT r;
7457 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7458 if (r == ERROR_SUCCESS)
7460 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7461 msiobj_release( &view->hdr );
7462 if (r != ERROR_SUCCESS)
7463 return r;
7465 return ERROR_SUCCESS;
7468 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7470 static const WCHAR query[] = {
7471 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7472 MSIQUERY *view;
7473 DWORD count = 0;
7474 UINT r;
7476 r = MSI_OpenQuery( package->db, &view, query, table );
7477 if (r == ERROR_SUCCESS)
7479 r = MSI_IterateRecords(view, &count, NULL, package);
7480 msiobj_release(&view->hdr);
7481 if (r != ERROR_SUCCESS)
7482 return r;
7484 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7485 return ERROR_SUCCESS;
7488 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7490 static const WCHAR table[] = {
7491 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7492 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7495 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7497 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7498 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7501 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7503 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7504 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7507 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7509 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7510 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7513 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7515 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7516 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7519 static const struct
7521 const WCHAR *action;
7522 UINT (*handler)(MSIPACKAGE *);
7523 const WCHAR *action_rollback;
7525 StandardActions[] =
7527 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7528 { szAppSearch, ACTION_AppSearch, NULL },
7529 { szBindImage, ACTION_BindImage, NULL },
7530 { szCCPSearch, ACTION_CCPSearch, NULL },
7531 { szCostFinalize, ACTION_CostFinalize, NULL },
7532 { szCostInitialize, ACTION_CostInitialize, NULL },
7533 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7534 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7535 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7536 { szDisableRollback, ACTION_DisableRollback, NULL },
7537 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7538 { szExecuteAction, ACTION_ExecuteAction, NULL },
7539 { szFileCost, ACTION_FileCost, NULL },
7540 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7541 { szForceReboot, ACTION_ForceReboot, NULL },
7542 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7543 { szInstallExecute, ACTION_InstallExecute, NULL },
7544 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7545 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7546 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7547 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7548 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7549 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7550 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7551 { szInstallValidate, ACTION_InstallValidate, NULL },
7552 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7553 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7554 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7555 { szMoveFiles, ACTION_MoveFiles, NULL },
7556 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7557 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7558 { szPatchFiles, ACTION_PatchFiles, NULL },
7559 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7560 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7561 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7562 { szPublishProduct, ACTION_PublishProduct, NULL },
7563 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7564 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7565 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7566 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7567 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7568 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7569 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7570 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7571 { szRegisterUser, ACTION_RegisterUser, NULL },
7572 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7573 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7574 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7575 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7576 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7577 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7578 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7579 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7580 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7581 { szResolveSource, ACTION_ResolveSource, NULL },
7582 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7583 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7584 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7585 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7586 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7587 { szStartServices, ACTION_StartServices, szStopServices },
7588 { szStopServices, ACTION_StopServices, szStartServices },
7589 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7590 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7591 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7592 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7593 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7594 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7595 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7596 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7597 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7598 { szValidateProductID, ACTION_ValidateProductID, NULL },
7599 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7600 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7601 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7602 { NULL, NULL, NULL }
7605 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7607 BOOL ret = FALSE;
7608 UINT i;
7610 i = 0;
7611 while (StandardActions[i].action != NULL)
7613 if (!strcmpW( StandardActions[i].action, action ))
7615 ui_actionstart( package, action );
7616 if (StandardActions[i].handler)
7618 ui_actioninfo( package, action, TRUE, 0 );
7619 *rc = StandardActions[i].handler( package );
7620 ui_actioninfo( package, action, FALSE, *rc );
7622 if (StandardActions[i].action_rollback && !package->need_rollback)
7624 TRACE("scheduling rollback action\n");
7625 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7628 else
7630 FIXME("unhandled standard action %s\n", debugstr_w(action));
7631 *rc = ERROR_SUCCESS;
7633 ret = TRUE;
7634 break;
7636 i++;
7638 return ret;
7641 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7643 UINT rc = ERROR_SUCCESS;
7644 BOOL handled;
7646 TRACE("Performing action (%s)\n", debugstr_w(action));
7648 handled = ACTION_HandleStandardAction(package, action, &rc);
7650 if (!handled)
7651 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7653 if (!handled)
7655 WARN("unhandled msi action %s\n", debugstr_w(action));
7656 rc = ERROR_FUNCTION_NOT_CALLED;
7659 return rc;
7662 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7664 UINT rc = ERROR_SUCCESS;
7665 BOOL handled = FALSE;
7667 TRACE("Performing action (%s)\n", debugstr_w(action));
7669 package->action_progress_increment = 0;
7670 handled = ACTION_HandleStandardAction(package, action, &rc);
7672 if (!handled)
7673 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7675 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7676 handled = TRUE;
7678 if (!handled)
7680 WARN("unhandled msi action %s\n", debugstr_w(action));
7681 rc = ERROR_FUNCTION_NOT_CALLED;
7684 return rc;
7687 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7689 UINT rc = ERROR_SUCCESS;
7690 MSIRECORD *row;
7692 static const WCHAR query[] =
7693 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7694 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7695 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7696 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7697 static const WCHAR ui_query[] =
7698 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7699 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7700 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7701 ' ', '=',' ','%','i',0};
7703 if (needs_ui_sequence(package))
7704 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7705 else
7706 row = MSI_QueryGetRecord(package->db, query, seq);
7708 if (row)
7710 LPCWSTR action, cond;
7712 TRACE("Running the actions\n");
7714 /* check conditions */
7715 cond = MSI_RecordGetString(row, 2);
7717 /* this is a hack to skip errors in the condition code */
7718 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7720 msiobj_release(&row->hdr);
7721 return ERROR_SUCCESS;
7724 action = MSI_RecordGetString(row, 1);
7725 if (!action)
7727 ERR("failed to fetch action\n");
7728 msiobj_release(&row->hdr);
7729 return ERROR_FUNCTION_FAILED;
7732 if (needs_ui_sequence(package))
7733 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7734 else
7735 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7737 msiobj_release(&row->hdr);
7740 return rc;
7743 /****************************************************
7744 * TOP level entry points
7745 *****************************************************/
7747 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7748 LPCWSTR szCommandLine )
7750 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7751 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7752 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7753 WCHAR *reinstall, *remove, *patch;
7754 BOOL ui_exists;
7755 UINT rc;
7757 msi_set_property( package->db, szAction, szInstall, -1 );
7759 package->script->InWhatSequence = SEQUENCE_INSTALL;
7761 if (szPackagePath)
7763 LPWSTR p, dir;
7764 LPCWSTR file;
7766 dir = strdupW(szPackagePath);
7767 p = strrchrW(dir, '\\');
7768 if (p)
7770 *(++p) = 0;
7771 file = szPackagePath + (p - dir);
7773 else
7775 msi_free(dir);
7776 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7777 GetCurrentDirectoryW(MAX_PATH, dir);
7778 lstrcatW(dir, szBackSlash);
7779 file = szPackagePath;
7782 msi_free( package->PackagePath );
7783 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7784 if (!package->PackagePath)
7786 msi_free(dir);
7787 return ERROR_OUTOFMEMORY;
7790 lstrcpyW(package->PackagePath, dir);
7791 lstrcatW(package->PackagePath, file);
7792 msi_free(dir);
7794 msi_set_sourcedir_props(package, FALSE);
7797 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7798 if (rc != ERROR_SUCCESS)
7799 return rc;
7801 msi_apply_transforms( package );
7802 msi_apply_patches( package );
7804 patch = msi_dup_property( package->db, szPatch );
7805 remove = msi_dup_property( package->db, szRemove );
7806 reinstall = msi_dup_property( package->db, szReinstall );
7807 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7809 TRACE("setting REINSTALL property to ALL\n");
7810 msi_set_property( package->db, szReinstall, szAll, -1 );
7811 package->full_reinstall = 1;
7814 /* properties may have been added by a transform */
7815 msi_clone_properties( package );
7816 msi_set_original_database_property( package->db, szPackagePath );
7818 msi_parse_command_line( package, szCommandLine, FALSE );
7819 msi_adjust_privilege_properties( package );
7820 msi_set_context( package );
7822 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7824 TRACE("disabling rollback\n");
7825 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7828 if (needs_ui_sequence( package))
7830 package->script->InWhatSequence |= SEQUENCE_UI;
7831 rc = ACTION_ProcessUISequence(package);
7832 ui_exists = ui_sequence_exists(package);
7833 if (rc == ERROR_SUCCESS || !ui_exists)
7835 package->script->InWhatSequence |= SEQUENCE_EXEC;
7836 rc = ACTION_ProcessExecSequence(package, ui_exists);
7839 else
7840 rc = ACTION_ProcessExecSequence(package, FALSE);
7842 /* process the ending type action */
7843 if (rc == ERROR_SUCCESS)
7844 ACTION_PerformActionSequence(package, -1);
7845 else if (rc == ERROR_INSTALL_USEREXIT)
7846 ACTION_PerformActionSequence(package, -2);
7847 else if (rc == ERROR_INSTALL_SUSPEND)
7848 ACTION_PerformActionSequence(package, -4);
7849 else /* failed */
7851 ACTION_PerformActionSequence(package, -3);
7852 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7854 package->need_rollback = TRUE;
7858 /* finish up running custom actions */
7859 ACTION_FinishCustomActions(package);
7861 if (package->need_rollback && !reinstall)
7863 WARN("installation failed, running rollback script\n");
7864 execute_script( package, SCRIPT_ROLLBACK );
7866 msi_free( reinstall );
7867 msi_free( remove );
7868 msi_free( patch );
7870 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7871 return ERROR_SUCCESS_REBOOT_REQUIRED;
7873 return rc;