windowscodecs: Add support for 8bpp grayscale TIFF with 8bpp alpha.
[wine.git] / dlls / msi / action.c
blob88bb1bbc6c003f7551a1c89c448b819a844630b0
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "shlwapi.h"
39 #include "imagehlp.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
48 static const WCHAR szCreateFolders[] =
49 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
50 static const WCHAR szCostFinalize[] =
51 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
52 static const WCHAR szWriteRegistryValues[] =
53 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
54 static const WCHAR szFileCost[] =
55 {'F','i','l','e','C','o','s','t',0};
56 static const WCHAR szInstallInitialize[] =
57 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
58 static const WCHAR szInstallValidate[] =
59 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
60 static const WCHAR szLaunchConditions[] =
61 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
62 static const WCHAR szProcessComponents[] =
63 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
64 static const WCHAR szRegisterTypeLibraries[] =
65 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
66 static const WCHAR szCreateShortcuts[] =
67 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
68 static const WCHAR szPublishProduct[] =
69 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
70 static const WCHAR szWriteIniValues[] =
71 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
72 static const WCHAR szSelfRegModules[] =
73 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
74 static const WCHAR szPublishFeatures[] =
75 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
76 static const WCHAR szRegisterProduct[] =
77 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
78 static const WCHAR szInstallExecute[] =
79 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
80 static const WCHAR szInstallExecuteAgain[] =
81 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
82 static const WCHAR szInstallFinalize[] =
83 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
84 static const WCHAR szForceReboot[] =
85 {'F','o','r','c','e','R','e','b','o','o','t',0};
86 static const WCHAR szResolveSource[] =
87 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
88 static const WCHAR szAllocateRegistrySpace[] =
89 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
90 static const WCHAR szBindImage[] =
91 {'B','i','n','d','I','m','a','g','e',0};
92 static const WCHAR szDeleteServices[] =
93 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
94 static const WCHAR szDisableRollback[] =
95 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
96 static const WCHAR szExecuteAction[] =
97 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
98 static const WCHAR szInstallAdminPackage[] =
99 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
100 static const WCHAR szInstallSFPCatalogFile[] =
101 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
102 static const WCHAR szIsolateComponents[] =
103 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
104 static const WCHAR szMigrateFeatureStates[] =
105 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
106 static const WCHAR szMsiUnpublishAssemblies[] =
107 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
108 static const WCHAR szInstallODBC[] =
109 {'I','n','s','t','a','l','l','O','D','B','C',0};
110 static const WCHAR szInstallServices[] =
111 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
112 static const WCHAR szPublishComponents[] =
113 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
114 static const WCHAR szRegisterComPlus[] =
115 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
116 static const WCHAR szRegisterUser[] =
117 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
118 static const WCHAR szRemoveEnvironmentStrings[] =
119 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
120 static const WCHAR szRemoveExistingProducts[] =
121 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
122 static const WCHAR szRemoveFolders[] =
123 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
124 static const WCHAR szRemoveIniValues[] =
125 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
126 static const WCHAR szRemoveODBC[] =
127 {'R','e','m','o','v','e','O','D','B','C',0};
128 static const WCHAR szRemoveRegistryValues[] =
129 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
130 static const WCHAR szRemoveShortcuts[] =
131 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
132 static const WCHAR szRMCCPSearch[] =
133 {'R','M','C','C','P','S','e','a','r','c','h',0};
134 static const WCHAR szScheduleReboot[] =
135 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
136 static const WCHAR szSelfUnregModules[] =
137 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
138 static const WCHAR szSetODBCFolders[] =
139 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
140 static const WCHAR szStartServices[] =
141 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
142 static const WCHAR szStopServices[] =
143 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
144 static const WCHAR szUnpublishComponents[] =
145 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
146 static const WCHAR szUnpublishFeatures[] =
147 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
148 static const WCHAR szUnregisterComPlus[] =
149 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
150 static const WCHAR szUnregisterTypeLibraries[] =
151 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
152 static const WCHAR szValidateProductID[] =
153 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
154 static const WCHAR szWriteEnvironmentStrings[] =
155 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
157 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
159 static const WCHAR Query_t[] =
160 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
161 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
162 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
163 ' ','\'','%','s','\'',0};
164 MSIRECORD * row;
166 row = MSI_QueryGetRecord( package->db, Query_t, action );
167 if (!row)
168 return;
169 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
170 msiobj_release(&row->hdr);
173 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
174 UINT rc)
176 MSIRECORD * row;
177 static const WCHAR template_s[]=
178 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
179 '%','s', '.',0};
180 static const WCHAR template_e[]=
181 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
182 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
183 '%','i','.',0};
184 static const WCHAR format[] =
185 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
186 WCHAR message[1024];
187 WCHAR timet[0x100];
189 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
190 if (start)
191 sprintfW(message,template_s,timet,action);
192 else
193 sprintfW(message,template_e,timet,action,rc);
195 row = MSI_CreateRecord(1);
196 MSI_RecordSetStringW(row,1,message);
198 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
199 msiobj_release(&row->hdr);
202 enum parse_state
204 state_whitespace,
205 state_token,
206 state_quote
209 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
211 enum parse_state state = state_quote;
212 const WCHAR *p;
213 WCHAR *out = value;
214 BOOL ignore, in_quotes = FALSE;
215 int count = 0, len = 0;
217 for (p = str; *p; p++)
219 ignore = FALSE;
220 switch (state)
222 case state_whitespace:
223 switch (*p)
225 case ' ':
226 in_quotes = TRUE;
227 ignore = TRUE;
228 len++;
229 break;
230 case '"':
231 state = state_quote;
232 if (in_quotes && p[1] != '\"') count--;
233 else count++;
234 break;
235 default:
236 state = state_token;
237 in_quotes = TRUE;
238 len++;
239 break;
241 break;
243 case state_token:
244 switch (*p)
246 case '"':
247 state = state_quote;
248 if (in_quotes) count--;
249 else count++;
250 break;
251 case ' ':
252 state = state_whitespace;
253 if (!count) goto done;
254 in_quotes = TRUE;
255 len++;
256 break;
257 default:
258 if (!count) in_quotes = FALSE;
259 else in_quotes = TRUE;
260 len++;
261 break;
263 break;
265 case state_quote:
266 switch (*p)
268 case '"':
269 if (in_quotes && p[1] != '\"') count--;
270 else count++;
271 break;
272 case ' ':
273 state = state_whitespace;
274 if (!count || (count > 1 && !len)) goto done;
275 in_quotes = TRUE;
276 len++;
277 break;
278 default:
279 state = state_token;
280 if (!count) in_quotes = FALSE;
281 else in_quotes = TRUE;
282 len++;
283 break;
285 break;
287 default: break;
289 if (!ignore) *out++ = *p;
292 done:
293 if (!len) *value = 0;
294 else *out = 0;
296 *quotes = count;
297 return p - str;
300 static void remove_quotes( WCHAR *str )
302 WCHAR *p = str;
303 int len = strlenW( str );
305 while ((p = strchrW( p, '"' )))
307 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
308 p++;
312 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
313 BOOL preserve_case )
315 LPCWSTR ptr, ptr2;
316 int num_quotes;
317 DWORD len;
318 WCHAR *prop, *val;
319 UINT r;
321 if (!szCommandLine)
322 return ERROR_SUCCESS;
324 ptr = szCommandLine;
325 while (*ptr)
327 while (*ptr == ' ') ptr++;
328 if (!*ptr) break;
330 ptr2 = strchrW( ptr, '=' );
331 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
333 len = ptr2 - ptr;
334 if (!len) return ERROR_INVALID_COMMAND_LINE;
336 while (ptr[len - 1] == ' ') len--;
338 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
339 memcpy( prop, ptr, len * sizeof(WCHAR) );
340 prop[len] = 0;
341 if (!preserve_case) struprW( prop );
343 ptr2++;
344 while (*ptr2 == ' ') ptr2++;
346 num_quotes = 0;
347 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
348 len = parse_prop( ptr2, val, &num_quotes );
349 if (num_quotes % 2)
351 WARN("unbalanced quotes\n");
352 msi_free( val );
353 msi_free( prop );
354 return ERROR_INVALID_COMMAND_LINE;
356 remove_quotes( val );
357 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
359 r = msi_set_property( package->db, prop, val, -1 );
360 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
361 msi_reset_folders( package, TRUE );
363 msi_free( val );
364 msi_free( prop );
366 ptr = ptr2 + len;
369 return ERROR_SUCCESS;
372 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
374 LPCWSTR pc;
375 LPWSTR p, *ret = NULL;
376 UINT count = 0;
378 if (!str)
379 return ret;
381 /* count the number of substrings */
382 for ( pc = str, count = 0; pc; count++ )
384 pc = strchrW( pc, sep );
385 if (pc)
386 pc++;
389 /* allocate space for an array of substring pointers and the substrings */
390 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
391 (lstrlenW(str)+1) * sizeof(WCHAR) );
392 if (!ret)
393 return ret;
395 /* copy the string and set the pointers */
396 p = (LPWSTR) &ret[count+1];
397 lstrcpyW( p, str );
398 for( count = 0; (ret[count] = p); count++ )
400 p = strchrW( p, sep );
401 if (p)
402 *p++ = 0;
405 return ret;
408 static BOOL ui_sequence_exists( MSIPACKAGE *package )
410 static const WCHAR query [] = {
411 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
412 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
413 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',0};
414 MSIQUERY *view;
415 DWORD count = 0;
417 if (!(MSI_DatabaseOpenViewW( package->db, query, &view )))
419 MSI_IterateRecords( view, &count, NULL, package );
420 msiobj_release( &view->hdr );
422 return count != 0;
425 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
427 WCHAR *source, *check, *p, *db;
428 DWORD len;
430 if (!(db = msi_dup_property( package->db, szOriginalDatabase )))
431 return ERROR_OUTOFMEMORY;
433 if (!(p = strrchrW( db, '\\' )) && !(p = strrchrW( db, '/' )))
435 msi_free(db);
436 return ERROR_SUCCESS;
438 len = p - db + 2;
439 source = msi_alloc( len * sizeof(WCHAR) );
440 lstrcpynW( source, db, len );
441 msi_free( db );
443 check = msi_dup_property( package->db, szSourceDir );
444 if (!check || replace)
446 UINT r = msi_set_property( package->db, szSourceDir, source, -1 );
447 if (r == ERROR_SUCCESS)
448 msi_reset_folders( package, TRUE );
450 msi_free( check );
452 check = msi_dup_property( package->db, szSOURCEDIR );
453 if (!check || replace)
454 msi_set_property( package->db, szSOURCEDIR, source, -1 );
456 msi_free( check );
457 msi_free( source );
459 return ERROR_SUCCESS;
462 static BOOL needs_ui_sequence(MSIPACKAGE *package)
464 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
467 UINT msi_set_context(MSIPACKAGE *package)
469 UINT r = msi_locate_product( package->ProductCode, &package->Context );
470 if (r != ERROR_SUCCESS)
472 int num = msi_get_property_int( package->db, szAllUsers, 0 );
473 if (num == 1 || num == 2)
474 package->Context = MSIINSTALLCONTEXT_MACHINE;
475 else
476 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
478 return ERROR_SUCCESS;
481 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
483 UINT rc;
484 LPCWSTR cond, action;
485 MSIPACKAGE *package = param;
487 action = MSI_RecordGetString(row,1);
488 if (!action)
490 ERR("Error is retrieving action name\n");
491 return ERROR_FUNCTION_FAILED;
494 /* check conditions */
495 cond = MSI_RecordGetString(row,2);
497 /* this is a hack to skip errors in the condition code */
498 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
500 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
501 return ERROR_SUCCESS;
504 if (needs_ui_sequence(package))
505 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
506 else
507 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
509 msi_dialog_check_messages( NULL );
511 if (package->CurrentInstallState != ERROR_SUCCESS)
512 rc = package->CurrentInstallState;
514 if (rc == ERROR_FUNCTION_NOT_CALLED)
515 rc = ERROR_SUCCESS;
517 if (rc != ERROR_SUCCESS)
518 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
520 if (package->need_reboot_now)
522 TRACE("action %s asked for immediate reboot, suspending installation\n",
523 debugstr_w(action));
524 rc = ACTION_ForceReboot( package );
526 return rc;
529 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
531 static const WCHAR query[] = {
532 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
533 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
534 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
535 '`','S','e','q','u','e','n','c','e','`',0};
536 MSIQUERY *view;
537 UINT r;
539 TRACE("%p %s\n", package, debugstr_w(table));
541 r = MSI_OpenQuery( package->db, &view, query, table );
542 if (r == ERROR_SUCCESS)
544 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
545 msiobj_release(&view->hdr);
547 return r;
550 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
552 static const WCHAR query[] = {
553 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
554 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
555 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
556 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
557 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
558 static const WCHAR query_validate[] = {
559 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
560 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
561 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
562 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
563 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
564 MSIQUERY *view;
565 INT seq = 0;
566 UINT rc;
568 if (package->script->ExecuteSequenceRun)
570 TRACE("Execute Sequence already Run\n");
571 return ERROR_SUCCESS;
574 package->script->ExecuteSequenceRun = TRUE;
576 /* get the sequence number */
577 if (UIran)
579 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
580 if (!row) return ERROR_FUNCTION_FAILED;
581 seq = MSI_RecordGetInteger(row,1);
582 msiobj_release(&row->hdr);
584 rc = MSI_OpenQuery(package->db, &view, query, seq);
585 if (rc == ERROR_SUCCESS)
587 TRACE("Running the actions\n");
589 msi_set_property( package->db, szSourceDir, NULL, -1 );
590 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
591 msiobj_release(&view->hdr);
593 return rc;
596 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
598 static const WCHAR query[] = {
599 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
600 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
601 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
602 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
603 MSIQUERY *view;
604 UINT rc;
606 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
607 if (rc == ERROR_SUCCESS)
609 TRACE("Running the actions\n");
610 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
611 msiobj_release(&view->hdr);
613 return rc;
616 /********************************************************
617 * ACTION helper functions and functions that perform the actions
618 *******************************************************/
619 static BOOL ACTION_HandleCustomAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc, UINT script )
621 BOOL ret=FALSE;
622 UINT arc;
624 arc = ACTION_CustomAction( package, action, script );
625 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
627 *rc = arc;
628 ret = TRUE;
630 return ret;
633 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
635 MSICOMPONENT *comp;
637 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
639 if (!strcmpW( Component, comp->Component )) return comp;
641 return NULL;
644 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
646 MSIFEATURE *feature;
648 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
650 if (!strcmpW( Feature, feature->Feature )) return feature;
652 return NULL;
655 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
657 MSIFILE *file;
659 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
661 if (!strcmpW( key, file->File )) return file;
663 return NULL;
666 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
668 MSIFOLDER *folder;
670 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
672 if (!strcmpW( dir, folder->Directory )) return folder;
674 return NULL;
678 * Recursively create all directories in the path.
679 * shamelessly stolen from setupapi/queue.c
681 BOOL msi_create_full_path( const WCHAR *path )
683 BOOL ret = TRUE;
684 WCHAR *new_path;
685 int len;
687 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
688 strcpyW( new_path, path );
690 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
691 new_path[len - 1] = 0;
693 while (!CreateDirectoryW( new_path, NULL ))
695 WCHAR *slash;
696 DWORD last_error = GetLastError();
697 if (last_error == ERROR_ALREADY_EXISTS) break;
698 if (last_error != ERROR_PATH_NOT_FOUND)
700 ret = FALSE;
701 break;
703 if (!(slash = strrchrW( new_path, '\\' )))
705 ret = FALSE;
706 break;
708 len = slash - new_path;
709 new_path[len] = 0;
710 if (!msi_create_full_path( new_path ))
712 ret = FALSE;
713 break;
715 new_path[len] = '\\';
717 msi_free( new_path );
718 return ret;
721 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
723 MSIRECORD *row;
725 row = MSI_CreateRecord( 4 );
726 MSI_RecordSetInteger( row, 1, a );
727 MSI_RecordSetInteger( row, 2, b );
728 MSI_RecordSetInteger( row, 3, c );
729 MSI_RecordSetInteger( row, 4, d );
730 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
731 msiobj_release( &row->hdr );
733 msi_dialog_check_messages( NULL );
736 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
738 static const WCHAR query[] =
739 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
740 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
741 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
742 WCHAR message[1024];
743 MSIRECORD *row = 0;
744 DWORD size;
746 if (!package->LastAction || strcmpW( package->LastAction, action ))
748 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
750 if (MSI_RecordIsNull( row, 3 ))
752 msiobj_release( &row->hdr );
753 return;
755 /* update the cached action format */
756 msi_free( package->ActionFormat );
757 package->ActionFormat = msi_dup_record_field( row, 3 );
758 msi_free( package->LastAction );
759 package->LastAction = strdupW( action );
760 msiobj_release( &row->hdr );
762 size = 1024;
763 MSI_RecordSetStringW( record, 0, package->ActionFormat );
764 MSI_FormatRecordW( package, record, message, &size );
765 row = MSI_CreateRecord( 1 );
766 MSI_RecordSetStringW( row, 1, message );
767 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
768 msiobj_release( &row->hdr );
771 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
773 if (!comp->Enabled)
775 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
776 return INSTALLSTATE_UNKNOWN;
778 if (package->need_rollback) return comp->Installed;
779 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
781 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
782 return INSTALLSTATE_UNKNOWN;
784 return comp->ActionRequest;
787 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
789 if (package->need_rollback) return feature->Installed;
790 return feature->ActionRequest;
793 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
795 MSIPACKAGE *package = param;
796 LPCWSTR dir, component, full_path;
797 MSIRECORD *uirow;
798 MSIFOLDER *folder;
799 MSICOMPONENT *comp;
801 component = MSI_RecordGetString(row, 2);
802 if (!component)
803 return ERROR_SUCCESS;
805 comp = msi_get_loaded_component(package, component);
806 if (!comp)
807 return ERROR_SUCCESS;
809 comp->Action = msi_get_component_action( package, comp );
810 if (comp->Action != INSTALLSTATE_LOCAL)
812 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
813 return ERROR_SUCCESS;
816 dir = MSI_RecordGetString(row,1);
817 if (!dir)
819 ERR("Unable to get folder id\n");
820 return ERROR_SUCCESS;
823 uirow = MSI_CreateRecord(1);
824 MSI_RecordSetStringW(uirow, 1, dir);
825 msi_ui_actiondata(package, szCreateFolders, uirow);
826 msiobj_release(&uirow->hdr);
828 full_path = msi_get_target_folder( package, dir );
829 if (!full_path)
831 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
832 return ERROR_SUCCESS;
834 TRACE("folder is %s\n", debugstr_w(full_path));
836 folder = msi_get_loaded_folder( package, dir );
837 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
838 folder->State = FOLDER_STATE_CREATED;
839 return ERROR_SUCCESS;
842 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
844 static const WCHAR query[] = {
845 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
846 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
847 MSIQUERY *view;
848 UINT rc;
850 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
851 if (rc != ERROR_SUCCESS)
852 return ERROR_SUCCESS;
854 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
855 msiobj_release(&view->hdr);
856 return rc;
859 static void remove_persistent_folder( MSIFOLDER *folder )
861 FolderList *fl;
863 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
865 remove_persistent_folder( fl->folder );
867 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
869 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
873 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
875 MSIPACKAGE *package = param;
876 LPCWSTR dir, component, full_path;
877 MSIRECORD *uirow;
878 MSIFOLDER *folder;
879 MSICOMPONENT *comp;
881 component = MSI_RecordGetString(row, 2);
882 if (!component)
883 return ERROR_SUCCESS;
885 comp = msi_get_loaded_component(package, component);
886 if (!comp)
887 return ERROR_SUCCESS;
889 comp->Action = msi_get_component_action( package, comp );
890 if (comp->Action != INSTALLSTATE_ABSENT)
892 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
893 return ERROR_SUCCESS;
896 dir = MSI_RecordGetString( row, 1 );
897 if (!dir)
899 ERR("Unable to get folder id\n");
900 return ERROR_SUCCESS;
903 full_path = msi_get_target_folder( package, dir );
904 if (!full_path)
906 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
907 return ERROR_SUCCESS;
909 TRACE("folder is %s\n", debugstr_w(full_path));
911 uirow = MSI_CreateRecord( 1 );
912 MSI_RecordSetStringW( uirow, 1, dir );
913 msi_ui_actiondata( package, szRemoveFolders, uirow );
914 msiobj_release( &uirow->hdr );
916 folder = msi_get_loaded_folder( package, dir );
917 remove_persistent_folder( folder );
918 return ERROR_SUCCESS;
921 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
923 static const WCHAR query[] = {
924 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
925 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
926 MSIQUERY *view;
927 UINT rc;
929 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
930 if (rc != ERROR_SUCCESS)
931 return ERROR_SUCCESS;
933 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
934 msiobj_release( &view->hdr );
935 return rc;
938 static UINT load_component( MSIRECORD *row, LPVOID param )
940 MSIPACKAGE *package = param;
941 MSICOMPONENT *comp;
943 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
944 if (!comp)
945 return ERROR_FUNCTION_FAILED;
947 list_add_tail( &package->components, &comp->entry );
949 /* fill in the data */
950 comp->Component = msi_dup_record_field( row, 1 );
952 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
954 comp->ComponentId = msi_dup_record_field( row, 2 );
955 comp->Directory = msi_dup_record_field( row, 3 );
956 comp->Attributes = MSI_RecordGetInteger(row,4);
957 comp->Condition = msi_dup_record_field( row, 5 );
958 comp->KeyPath = msi_dup_record_field( row, 6 );
960 comp->Installed = INSTALLSTATE_UNKNOWN;
961 comp->Action = INSTALLSTATE_UNKNOWN;
962 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
964 comp->assembly = msi_load_assembly( package, comp );
965 return ERROR_SUCCESS;
968 UINT msi_load_all_components( MSIPACKAGE *package )
970 static const WCHAR query[] = {
971 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
972 '`','C','o','m','p','o','n','e','n','t','`',0};
973 MSIQUERY *view;
974 UINT r;
976 if (!list_empty(&package->components))
977 return ERROR_SUCCESS;
979 r = MSI_DatabaseOpenViewW( package->db, query, &view );
980 if (r != ERROR_SUCCESS)
981 return r;
983 if (!msi_init_assembly_caches( package ))
985 ERR("can't initialize assembly caches\n");
986 msiobj_release( &view->hdr );
987 return ERROR_FUNCTION_FAILED;
990 r = MSI_IterateRecords(view, NULL, load_component, package);
991 msiobj_release(&view->hdr);
992 return r;
995 typedef struct {
996 MSIPACKAGE *package;
997 MSIFEATURE *feature;
998 } _ilfs;
1000 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1002 ComponentList *cl;
1004 cl = msi_alloc( sizeof (*cl) );
1005 if ( !cl )
1006 return ERROR_NOT_ENOUGH_MEMORY;
1007 cl->component = comp;
1008 list_add_tail( &feature->Components, &cl->entry );
1010 return ERROR_SUCCESS;
1013 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1015 FeatureList *fl;
1017 fl = msi_alloc( sizeof(*fl) );
1018 if ( !fl )
1019 return ERROR_NOT_ENOUGH_MEMORY;
1020 fl->feature = child;
1021 list_add_tail( &parent->Children, &fl->entry );
1023 return ERROR_SUCCESS;
1026 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1028 _ilfs* ilfs = param;
1029 LPCWSTR component;
1030 MSICOMPONENT *comp;
1032 component = MSI_RecordGetString(row,1);
1034 /* check to see if the component is already loaded */
1035 comp = msi_get_loaded_component( ilfs->package, component );
1036 if (!comp)
1038 WARN("ignoring unknown component %s\n", debugstr_w(component));
1039 return ERROR_SUCCESS;
1041 add_feature_component( ilfs->feature, comp );
1042 comp->Enabled = TRUE;
1044 return ERROR_SUCCESS;
1047 static UINT load_feature(MSIRECORD * row, LPVOID param)
1049 static const WCHAR query[] = {
1050 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1051 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1052 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1053 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1054 MSIPACKAGE *package = param;
1055 MSIFEATURE *feature;
1056 MSIQUERY *view;
1057 _ilfs ilfs;
1058 UINT rc;
1060 /* fill in the data */
1062 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1063 if (!feature)
1064 return ERROR_NOT_ENOUGH_MEMORY;
1066 list_init( &feature->Children );
1067 list_init( &feature->Components );
1069 feature->Feature = msi_dup_record_field( row, 1 );
1071 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1073 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1074 feature->Title = msi_dup_record_field( row, 3 );
1075 feature->Description = msi_dup_record_field( row, 4 );
1077 if (!MSI_RecordIsNull(row,5))
1078 feature->Display = MSI_RecordGetInteger(row,5);
1080 feature->Level= MSI_RecordGetInteger(row,6);
1081 feature->Directory = msi_dup_record_field( row, 7 );
1082 feature->Attributes = MSI_RecordGetInteger(row,8);
1084 feature->Installed = INSTALLSTATE_UNKNOWN;
1085 feature->Action = INSTALLSTATE_UNKNOWN;
1086 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1088 list_add_tail( &package->features, &feature->entry );
1090 /* load feature components */
1092 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1093 if (rc != ERROR_SUCCESS)
1094 return ERROR_SUCCESS;
1096 ilfs.package = package;
1097 ilfs.feature = feature;
1099 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1100 msiobj_release(&view->hdr);
1101 return rc;
1104 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1106 MSIPACKAGE *package = param;
1107 MSIFEATURE *parent, *child;
1109 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1110 if (!child)
1111 return ERROR_FUNCTION_FAILED;
1113 if (!child->Feature_Parent)
1114 return ERROR_SUCCESS;
1116 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1117 if (!parent)
1118 return ERROR_FUNCTION_FAILED;
1120 add_feature_child( parent, child );
1121 return ERROR_SUCCESS;
1124 UINT msi_load_all_features( MSIPACKAGE *package )
1126 static const WCHAR query[] = {
1127 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1128 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1129 '`','D','i','s','p','l','a','y','`',0};
1130 MSIQUERY *view;
1131 UINT r;
1133 if (!list_empty(&package->features))
1134 return ERROR_SUCCESS;
1136 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1137 if (r != ERROR_SUCCESS)
1138 return r;
1140 r = MSI_IterateRecords( view, NULL, load_feature, package );
1141 if (r != ERROR_SUCCESS)
1143 msiobj_release( &view->hdr );
1144 return r;
1146 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1147 msiobj_release( &view->hdr );
1148 return r;
1151 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1153 if (!p)
1154 return p;
1155 p = strchrW(p, ch);
1156 if (!p)
1157 return p;
1158 *p = 0;
1159 return p+1;
1162 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1164 static const WCHAR query[] = {
1165 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1166 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1167 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1168 MSIQUERY *view = NULL;
1169 MSIRECORD *row = NULL;
1170 UINT r;
1172 TRACE("%s\n", debugstr_w(file->File));
1174 r = MSI_OpenQuery(package->db, &view, query, file->File);
1175 if (r != ERROR_SUCCESS)
1176 goto done;
1178 r = MSI_ViewExecute(view, NULL);
1179 if (r != ERROR_SUCCESS)
1180 goto done;
1182 r = MSI_ViewFetch(view, &row);
1183 if (r != ERROR_SUCCESS)
1184 goto done;
1186 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1187 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1188 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1189 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1190 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1192 done:
1193 if (view) msiobj_release(&view->hdr);
1194 if (row) msiobj_release(&row->hdr);
1195 return r;
1198 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1200 MSIRECORD *row;
1201 static const WCHAR query[] = {
1202 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1203 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1204 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1206 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1207 if (!row)
1209 WARN("query failed\n");
1210 return ERROR_FUNCTION_FAILED;
1213 file->disk_id = MSI_RecordGetInteger( row, 1 );
1214 msiobj_release( &row->hdr );
1215 return ERROR_SUCCESS;
1218 static UINT load_file(MSIRECORD *row, LPVOID param)
1220 MSIPACKAGE* package = param;
1221 LPCWSTR component;
1222 MSIFILE *file;
1224 /* fill in the data */
1226 file = msi_alloc_zero( sizeof (MSIFILE) );
1227 if (!file)
1228 return ERROR_NOT_ENOUGH_MEMORY;
1230 file->File = msi_dup_record_field( row, 1 );
1232 component = MSI_RecordGetString( row, 2 );
1233 file->Component = msi_get_loaded_component( package, component );
1235 if (!file->Component)
1237 WARN("Component not found: %s\n", debugstr_w(component));
1238 msi_free(file->File);
1239 msi_free(file);
1240 return ERROR_SUCCESS;
1243 file->FileName = msi_dup_record_field( row, 3 );
1244 msi_reduce_to_long_filename( file->FileName );
1246 file->ShortName = msi_dup_record_field( row, 3 );
1247 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1249 file->FileSize = MSI_RecordGetInteger( row, 4 );
1250 file->Version = msi_dup_record_field( row, 5 );
1251 file->Language = msi_dup_record_field( row, 6 );
1252 file->Attributes = MSI_RecordGetInteger( row, 7 );
1253 file->Sequence = MSI_RecordGetInteger( row, 8 );
1255 file->state = msifs_invalid;
1257 /* if the compressed bits are not set in the file attributes,
1258 * then read the information from the package word count property
1260 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1262 file->IsCompressed = FALSE;
1264 else if (file->Attributes &
1265 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1267 file->IsCompressed = TRUE;
1269 else if (file->Attributes & msidbFileAttributesNoncompressed)
1271 file->IsCompressed = FALSE;
1273 else
1275 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1278 load_file_hash(package, file);
1279 load_file_disk_id(package, file);
1281 TRACE("File loaded (file %s sequence %u)\n", debugstr_w(file->File), file->Sequence);
1283 list_add_tail( &package->files, &file->entry );
1285 return ERROR_SUCCESS;
1288 static UINT load_all_files(MSIPACKAGE *package)
1290 static const WCHAR query[] = {
1291 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1292 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1293 '`','S','e','q','u','e','n','c','e','`', 0};
1294 MSIQUERY *view;
1295 UINT rc;
1297 if (!list_empty(&package->files))
1298 return ERROR_SUCCESS;
1300 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1301 if (rc != ERROR_SUCCESS)
1302 return ERROR_SUCCESS;
1304 rc = MSI_IterateRecords(view, NULL, load_file, package);
1305 msiobj_release(&view->hdr);
1306 return rc;
1309 static UINT load_media( MSIRECORD *row, LPVOID param )
1311 MSIPACKAGE *package = param;
1312 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1313 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1315 /* FIXME: load external cabinets and directory sources too */
1316 if (!cabinet || cabinet[0] != '#' || disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
1317 return ERROR_SUCCESS;
1319 return msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1322 static UINT load_all_media( MSIPACKAGE *package )
1324 static const WCHAR query[] = {
1325 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1326 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1327 '`','D','i','s','k','I','d','`',0};
1328 MSIQUERY *view;
1329 UINT r;
1331 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1332 if (r != ERROR_SUCCESS)
1333 return ERROR_SUCCESS;
1335 r = MSI_IterateRecords( view, NULL, load_media, package );
1336 msiobj_release( &view->hdr );
1337 return r;
1340 static UINT load_patch_disk_id( MSIPACKAGE *package, MSIFILEPATCH *patch )
1342 static const WCHAR query[] =
1343 {'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1344 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1345 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','u',0};
1346 MSIRECORD *rec;
1348 if (!(rec = MSI_QueryGetRecord( package->db, query, patch->Sequence )))
1350 WARN("query failed\n");
1351 return ERROR_FUNCTION_FAILED;
1354 patch->disk_id = MSI_RecordGetInteger( rec, 1 );
1355 msiobj_release( &rec->hdr );
1356 return ERROR_SUCCESS;
1359 static UINT load_patch(MSIRECORD *row, LPVOID param)
1361 MSIPACKAGE *package = param;
1362 MSIFILEPATCH *patch;
1363 const WCHAR *file_key;
1365 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1366 if (!patch)
1367 return ERROR_NOT_ENOUGH_MEMORY;
1369 file_key = MSI_RecordGetString( row, 1 );
1370 patch->File = msi_get_loaded_file( package, file_key );
1371 if (!patch->File)
1373 ERR("Failed to find target for patch in File table\n");
1374 msi_free(patch);
1375 return ERROR_FUNCTION_FAILED;
1378 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1379 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1380 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1382 /* FIXME:
1383 * Header field - for patch validation.
1384 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1387 load_patch_disk_id( package, patch );
1389 TRACE("Patch loaded (file %s sequence %u)\n", debugstr_w(patch->File->File), patch->Sequence);
1391 list_add_tail( &package->filepatches, &patch->entry );
1393 return ERROR_SUCCESS;
1396 static UINT load_all_patches(MSIPACKAGE *package)
1398 static const WCHAR query[] = {
1399 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1400 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1401 '`','S','e','q','u','e','n','c','e','`',0};
1402 MSIQUERY *view;
1403 UINT rc;
1405 if (!list_empty(&package->filepatches))
1406 return ERROR_SUCCESS;
1408 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1409 if (rc != ERROR_SUCCESS)
1410 return ERROR_SUCCESS;
1412 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1413 msiobj_release(&view->hdr);
1414 return rc;
1417 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1419 static const WCHAR query[] = {
1420 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1421 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1422 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1423 MSIQUERY *view;
1425 folder->persistent = FALSE;
1426 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1428 if (!MSI_ViewExecute( view, NULL ))
1430 MSIRECORD *rec;
1431 if (!MSI_ViewFetch( view, &rec ))
1433 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1434 folder->persistent = TRUE;
1435 msiobj_release( &rec->hdr );
1438 msiobj_release( &view->hdr );
1440 return ERROR_SUCCESS;
1443 static UINT load_folder( MSIRECORD *row, LPVOID param )
1445 MSIPACKAGE *package = param;
1446 static WCHAR szEmpty[] = { 0 };
1447 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1448 MSIFOLDER *folder;
1450 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1451 list_init( &folder->children );
1452 folder->Directory = msi_dup_record_field( row, 1 );
1453 folder->Parent = msi_dup_record_field( row, 2 );
1454 p = msi_dup_record_field(row, 3);
1456 TRACE("%s\n", debugstr_w(folder->Directory));
1458 /* split src and target dir */
1459 tgt_short = p;
1460 src_short = folder_split_path( p, ':' );
1462 /* split the long and short paths */
1463 tgt_long = folder_split_path( tgt_short, '|' );
1464 src_long = folder_split_path( src_short, '|' );
1466 /* check for no-op dirs */
1467 if (tgt_short && !strcmpW( szDot, tgt_short ))
1468 tgt_short = szEmpty;
1469 if (src_short && !strcmpW( szDot, src_short ))
1470 src_short = szEmpty;
1472 if (!tgt_long)
1473 tgt_long = tgt_short;
1475 if (!src_short) {
1476 src_short = tgt_short;
1477 src_long = tgt_long;
1480 if (!src_long)
1481 src_long = src_short;
1483 /* FIXME: use the target short path too */
1484 folder->TargetDefault = strdupW(tgt_long);
1485 folder->SourceShortPath = strdupW(src_short);
1486 folder->SourceLongPath = strdupW(src_long);
1487 msi_free(p);
1489 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1490 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1491 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1493 load_folder_persistence( package, folder );
1495 list_add_tail( &package->folders, &folder->entry );
1496 return ERROR_SUCCESS;
1499 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1501 FolderList *fl;
1503 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1504 fl->folder = child;
1505 list_add_tail( &parent->children, &fl->entry );
1506 return ERROR_SUCCESS;
1509 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1511 MSIPACKAGE *package = param;
1512 MSIFOLDER *parent, *child;
1514 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1515 return ERROR_FUNCTION_FAILED;
1517 if (!child->Parent) return ERROR_SUCCESS;
1519 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1520 return ERROR_FUNCTION_FAILED;
1522 return add_folder_child( parent, child );
1525 static UINT load_all_folders( MSIPACKAGE *package )
1527 static const WCHAR query[] = {
1528 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1529 '`','D','i','r','e','c','t','o','r','y','`',0};
1530 MSIQUERY *view;
1531 UINT r;
1533 if (!list_empty(&package->folders))
1534 return ERROR_SUCCESS;
1536 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1537 if (r != ERROR_SUCCESS)
1538 return r;
1540 r = MSI_IterateRecords( view, NULL, load_folder, package );
1541 if (r != ERROR_SUCCESS)
1543 msiobj_release( &view->hdr );
1544 return r;
1546 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1547 msiobj_release( &view->hdr );
1548 return r;
1551 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1553 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1554 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1556 load_all_folders( package );
1557 msi_load_all_components( package );
1558 msi_load_all_features( package );
1559 load_all_files( package );
1560 load_all_patches( package );
1561 load_all_media( package );
1563 return ERROR_SUCCESS;
1566 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1568 const WCHAR *action = package->script->Actions[script][index];
1569 ui_actionstart( package, action );
1570 TRACE("executing %s\n", debugstr_w(action));
1571 return ACTION_PerformAction( package, action, script );
1574 static UINT execute_script( MSIPACKAGE *package, UINT script )
1576 UINT i, rc = ERROR_SUCCESS;
1578 TRACE("executing script %u\n", script);
1580 if (!package->script)
1582 ERR("no script!\n");
1583 return ERROR_FUNCTION_FAILED;
1585 if (script == SCRIPT_ROLLBACK)
1587 for (i = package->script->ActionCount[script]; i > 0; i--)
1589 rc = execute_script_action( package, script, i - 1 );
1590 if (rc != ERROR_SUCCESS) break;
1593 else
1595 for (i = 0; i < package->script->ActionCount[script]; i++)
1597 rc = execute_script_action( package, script, i );
1598 if (rc != ERROR_SUCCESS) break;
1601 msi_free_action_script(package, script);
1602 return rc;
1605 static UINT ACTION_FileCost(MSIPACKAGE *package)
1607 return ERROR_SUCCESS;
1610 static void get_client_counts( MSIPACKAGE *package )
1612 MSICOMPONENT *comp;
1613 HKEY hkey;
1615 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1617 if (!comp->ComponentId) continue;
1619 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1620 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1622 comp->num_clients = 0;
1623 continue;
1625 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1626 NULL, NULL, NULL, NULL );
1627 RegCloseKey( hkey );
1631 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1633 MSICOMPONENT *comp;
1634 UINT r;
1636 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1638 if (!comp->ComponentId) continue;
1640 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1641 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1642 &comp->Installed );
1643 if (r == ERROR_SUCCESS) continue;
1645 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1646 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1647 &comp->Installed );
1648 if (r == ERROR_SUCCESS) continue;
1650 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1651 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1652 &comp->Installed );
1653 if (r == ERROR_SUCCESS) continue;
1655 comp->Installed = INSTALLSTATE_ABSENT;
1659 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1661 MSIFEATURE *feature;
1663 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1665 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1667 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1668 feature->Installed = INSTALLSTATE_ABSENT;
1669 else
1670 feature->Installed = state;
1674 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1676 return (feature->Level > 0 && feature->Level <= level);
1679 static BOOL process_state_property(MSIPACKAGE* package, int level,
1680 LPCWSTR property, INSTALLSTATE state)
1682 LPWSTR override;
1683 MSIFEATURE *feature;
1684 BOOL remove = !strcmpW(property, szRemove);
1685 BOOL reinstall = !strcmpW(property, szReinstall);
1687 override = msi_dup_property( package->db, property );
1688 if (!override)
1689 return FALSE;
1691 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1693 if (feature->Level <= 0)
1694 continue;
1696 if (reinstall)
1697 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1698 else if (remove)
1699 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1701 if (!strcmpiW( override, szAll ))
1703 feature->Action = state;
1704 feature->ActionRequest = state;
1706 else
1708 LPWSTR ptr = override;
1709 LPWSTR ptr2 = strchrW(override,',');
1711 while (ptr)
1713 int len = ptr2 - ptr;
1715 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1716 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1718 feature->Action = state;
1719 feature->ActionRequest = state;
1720 break;
1722 if (ptr2)
1724 ptr=ptr2+1;
1725 ptr2 = strchrW(ptr,',');
1727 else
1728 break;
1732 msi_free(override);
1733 return TRUE;
1736 static BOOL process_overrides( MSIPACKAGE *package, int level )
1738 static const WCHAR szAddLocal[] =
1739 {'A','D','D','L','O','C','A','L',0};
1740 static const WCHAR szAddSource[] =
1741 {'A','D','D','S','O','U','R','C','E',0};
1742 static const WCHAR szAdvertise[] =
1743 {'A','D','V','E','R','T','I','S','E',0};
1744 BOOL ret = FALSE;
1746 /* all these activation/deactivation things happen in order and things
1747 * later on the list override things earlier on the list.
1749 * 0 INSTALLLEVEL processing
1750 * 1 ADDLOCAL
1751 * 2 REMOVE
1752 * 3 ADDSOURCE
1753 * 4 ADDDEFAULT
1754 * 5 REINSTALL
1755 * 6 ADVERTISE
1756 * 7 COMPADDLOCAL
1757 * 8 COMPADDSOURCE
1758 * 9 FILEADDLOCAL
1759 * 10 FILEADDSOURCE
1760 * 11 FILEADDDEFAULT
1762 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1763 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1764 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1765 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1766 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1768 if (ret && !package->full_reinstall)
1769 msi_set_property( package->db, szPreselected, szOne, -1 );
1771 return ret;
1774 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1776 int level;
1777 MSICOMPONENT* component;
1778 MSIFEATURE *feature;
1780 TRACE("Checking Install Level\n");
1782 level = msi_get_property_int(package->db, szInstallLevel, 1);
1784 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1786 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1788 if (!is_feature_selected( feature, level )) continue;
1790 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1792 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1794 feature->Action = INSTALLSTATE_SOURCE;
1795 feature->ActionRequest = INSTALLSTATE_SOURCE;
1797 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1799 feature->Action = INSTALLSTATE_ADVERTISED;
1800 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1802 else
1804 feature->Action = INSTALLSTATE_LOCAL;
1805 feature->ActionRequest = INSTALLSTATE_LOCAL;
1809 /* disable child features of unselected parent or follow parent */
1810 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1812 FeatureList *fl;
1814 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1816 if (!is_feature_selected( feature, level ))
1818 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1819 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1821 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1823 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1824 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1825 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1826 fl->feature->Action = feature->Action;
1827 fl->feature->ActionRequest = feature->ActionRequest;
1832 else /* preselected */
1834 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1836 if (!is_feature_selected( feature, level )) continue;
1838 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1840 if (feature->Installed == INSTALLSTATE_ABSENT)
1842 feature->Action = INSTALLSTATE_UNKNOWN;
1843 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1845 else
1847 feature->Action = feature->Installed;
1848 feature->ActionRequest = feature->Installed;
1852 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1854 FeatureList *fl;
1856 if (!is_feature_selected( feature, level )) continue;
1858 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1860 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1861 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1863 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1864 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1865 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1866 fl->feature->Action = feature->Action;
1867 fl->feature->ActionRequest = feature->ActionRequest;
1873 /* now we want to set component state based based on feature state */
1874 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1876 ComponentList *cl;
1878 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1879 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1880 feature->ActionRequest, feature->Action);
1882 /* features with components that have compressed files are made local */
1883 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1885 if (cl->component->ForceLocalState &&
1886 feature->ActionRequest == INSTALLSTATE_SOURCE)
1888 feature->Action = INSTALLSTATE_LOCAL;
1889 feature->ActionRequest = INSTALLSTATE_LOCAL;
1890 break;
1894 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1896 component = cl->component;
1898 switch (feature->ActionRequest)
1900 case INSTALLSTATE_ABSENT:
1901 component->anyAbsent = 1;
1902 break;
1903 case INSTALLSTATE_ADVERTISED:
1904 component->hasAdvertisedFeature = 1;
1905 break;
1906 case INSTALLSTATE_SOURCE:
1907 component->hasSourceFeature = 1;
1908 break;
1909 case INSTALLSTATE_LOCAL:
1910 component->hasLocalFeature = 1;
1911 break;
1912 case INSTALLSTATE_DEFAULT:
1913 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1914 component->hasAdvertisedFeature = 1;
1915 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1916 component->hasSourceFeature = 1;
1917 else
1918 component->hasLocalFeature = 1;
1919 break;
1920 default:
1921 break;
1926 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1928 /* check if it's local or source */
1929 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1930 (component->hasLocalFeature || component->hasSourceFeature))
1932 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1933 !component->ForceLocalState)
1935 component->Action = INSTALLSTATE_SOURCE;
1936 component->ActionRequest = INSTALLSTATE_SOURCE;
1938 else
1940 component->Action = INSTALLSTATE_LOCAL;
1941 component->ActionRequest = INSTALLSTATE_LOCAL;
1943 continue;
1946 /* if any feature is local, the component must be local too */
1947 if (component->hasLocalFeature)
1949 component->Action = INSTALLSTATE_LOCAL;
1950 component->ActionRequest = INSTALLSTATE_LOCAL;
1951 continue;
1953 if (component->hasSourceFeature)
1955 component->Action = INSTALLSTATE_SOURCE;
1956 component->ActionRequest = INSTALLSTATE_SOURCE;
1957 continue;
1959 if (component->hasAdvertisedFeature)
1961 component->Action = INSTALLSTATE_ADVERTISED;
1962 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1963 continue;
1965 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1966 if (component->anyAbsent && component->ComponentId)
1968 component->Action = INSTALLSTATE_ABSENT;
1969 component->ActionRequest = INSTALLSTATE_ABSENT;
1973 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1975 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1977 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1978 component->Action = INSTALLSTATE_LOCAL;
1979 component->ActionRequest = INSTALLSTATE_LOCAL;
1982 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1983 component->Installed == INSTALLSTATE_SOURCE &&
1984 component->hasSourceFeature)
1986 component->Action = INSTALLSTATE_UNKNOWN;
1987 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1990 TRACE("component %s (installed %d request %d action %d)\n",
1991 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1993 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1994 component->num_clients++;
1995 else if (component->Action == INSTALLSTATE_ABSENT)
1996 component->num_clients--;
1999 return ERROR_SUCCESS;
2002 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2004 MSIPACKAGE *package = param;
2005 LPCWSTR name;
2006 MSIFEATURE *feature;
2008 name = MSI_RecordGetString( row, 1 );
2010 feature = msi_get_loaded_feature( package, name );
2011 if (!feature)
2012 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2013 else
2015 LPCWSTR Condition;
2016 Condition = MSI_RecordGetString(row,3);
2018 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2020 int level = MSI_RecordGetInteger(row,2);
2021 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2022 feature->Level = level;
2025 return ERROR_SUCCESS;
2028 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2030 static const WCHAR name[] = {'\\',0};
2031 VS_FIXEDFILEINFO *ptr, *ret;
2032 LPVOID version;
2033 DWORD versize, handle;
2034 UINT sz;
2036 versize = GetFileVersionInfoSizeW( filename, &handle );
2037 if (!versize)
2038 return NULL;
2040 version = msi_alloc( versize );
2041 if (!version)
2042 return NULL;
2044 GetFileVersionInfoW( filename, 0, versize, version );
2046 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2048 msi_free( version );
2049 return NULL;
2052 ret = msi_alloc( sz );
2053 memcpy( ret, ptr, sz );
2055 msi_free( version );
2056 return ret;
2059 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2061 DWORD ms, ls;
2063 msi_parse_version_string( version, &ms, &ls );
2065 if (fi->dwFileVersionMS > ms) return 1;
2066 else if (fi->dwFileVersionMS < ms) return -1;
2067 else if (fi->dwFileVersionLS > ls) return 1;
2068 else if (fi->dwFileVersionLS < ls) return -1;
2069 return 0;
2072 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2074 DWORD ms1, ms2;
2076 msi_parse_version_string( ver1, &ms1, NULL );
2077 msi_parse_version_string( ver2, &ms2, NULL );
2079 if (ms1 > ms2) return 1;
2080 else if (ms1 < ms2) return -1;
2081 return 0;
2084 DWORD msi_get_disk_file_size( LPCWSTR filename )
2086 HANDLE file;
2087 DWORD size;
2089 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2090 if (file == INVALID_HANDLE_VALUE)
2091 return INVALID_FILE_SIZE;
2093 size = GetFileSize( file, NULL );
2094 CloseHandle( file );
2095 return size;
2098 BOOL msi_file_hash_matches( MSIFILE *file )
2100 UINT r;
2101 MSIFILEHASHINFO hash;
2103 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2104 r = msi_get_filehash( file->TargetPath, &hash );
2105 if (r != ERROR_SUCCESS)
2106 return FALSE;
2108 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2111 static WCHAR *create_temp_dir( MSIDATABASE *db )
2113 static UINT id;
2114 WCHAR *ret;
2116 if (!db->tempfolder)
2118 WCHAR tmp[MAX_PATH];
2119 UINT len = sizeof(tmp)/sizeof(tmp[0]);
2121 if (msi_get_property( db, szTempFolder, tmp, &len ) ||
2122 GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY)
2124 GetTempPathW( MAX_PATH, tmp );
2126 if (!(db->tempfolder = strdupW( tmp ))) return NULL;
2129 if ((ret = msi_alloc( (strlenW( db->tempfolder ) + 20) * sizeof(WCHAR) )))
2131 for (;;)
2133 if (!GetTempFileNameW( db->tempfolder, szMsi, ++id, ret ))
2135 msi_free( ret );
2136 return NULL;
2138 if (CreateDirectoryW( ret, NULL )) break;
2142 return ret;
2146 * msi_build_directory_name()
2148 * This function is to save messing round with directory names
2149 * It handles adding backslashes between path segments,
2150 * and can add \ at the end of the directory name if told to.
2152 * It takes a variable number of arguments.
2153 * It always allocates a new string for the result, so make sure
2154 * to free the return value when finished with it.
2156 * The first arg is the number of path segments that follow.
2157 * The arguments following count are a list of path segments.
2158 * A path segment may be NULL.
2160 * Path segments will be added with a \ separating them.
2161 * A \ will not be added after the last segment, however if the
2162 * last segment is NULL, then the last character will be a \
2164 WCHAR *msi_build_directory_name( DWORD count, ... )
2166 DWORD sz = 1, i;
2167 WCHAR *dir;
2168 va_list va;
2170 va_start( va, count );
2171 for (i = 0; i < count; i++)
2173 const WCHAR *str = va_arg( va, const WCHAR * );
2174 if (str) sz += strlenW( str ) + 1;
2176 va_end( va );
2178 dir = msi_alloc( sz * sizeof(WCHAR) );
2179 dir[0] = 0;
2181 va_start( va, count );
2182 for (i = 0; i < count; i++)
2184 const WCHAR *str = va_arg( va, const WCHAR * );
2185 if (!str) continue;
2186 strcatW( dir, str );
2187 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2189 va_end( va );
2190 return dir;
2193 BOOL msi_is_global_assembly( MSICOMPONENT *comp )
2195 return comp->assembly && !comp->assembly->application;
2198 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2200 msi_free( file->TargetPath );
2201 if (msi_is_global_assembly( file->Component ))
2203 MSIASSEMBLY *assembly = file->Component->assembly;
2205 if (!assembly->tempdir) assembly->tempdir = create_temp_dir( package->db );
2206 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2208 else
2210 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2211 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2214 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2217 static UINT calculate_file_cost( MSIPACKAGE *package )
2219 VS_FIXEDFILEINFO *file_version;
2220 WCHAR *font_version;
2221 MSIFILE *file;
2223 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2225 MSICOMPONENT *comp = file->Component;
2226 DWORD file_size;
2228 if (!comp->Enabled) continue;
2230 if (file->IsCompressed)
2231 comp->ForceLocalState = TRUE;
2233 set_target_path( package, file );
2235 if ((comp->assembly && !comp->assembly->installed) ||
2236 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2238 comp->Cost += file->FileSize;
2239 continue;
2241 file_size = msi_get_disk_file_size( file->TargetPath );
2242 TRACE("%s (size %u)\n", debugstr_w(file->TargetPath), file_size);
2244 if (file->Version)
2246 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2248 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2250 comp->Cost += file->FileSize - file_size;
2252 msi_free( file_version );
2253 continue;
2255 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2257 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2259 comp->Cost += file->FileSize - file_size;
2261 msi_free( font_version );
2262 continue;
2265 if (file_size != file->FileSize)
2267 comp->Cost += file->FileSize - file_size;
2270 return ERROR_SUCCESS;
2273 WCHAR *msi_normalize_path( const WCHAR *in )
2275 const WCHAR *p = in;
2276 WCHAR *q, *ret;
2277 int n, len = strlenW( in ) + 2;
2279 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2281 len = 0;
2282 while (1)
2284 /* copy until the end of the string or a space */
2285 while (*p != ' ' && (*q = *p))
2287 p++, len++;
2288 /* reduce many backslashes to one */
2289 if (*p != '\\' || *q != '\\')
2290 q++;
2293 /* quit at the end of the string */
2294 if (!*p)
2295 break;
2297 /* count the number of spaces */
2298 n = 0;
2299 while (p[n] == ' ')
2300 n++;
2302 /* if it's leading or trailing space, skip it */
2303 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2304 p += n;
2305 else /* copy n spaces */
2306 while (n && (*q++ = *p++)) n--;
2308 while (q - ret > 0 && q[-1] == ' ') q--;
2309 if (q - ret > 0 && q[-1] != '\\')
2311 q[0] = '\\';
2312 q[1] = 0;
2314 return ret;
2317 static WCHAR *get_install_location( MSIPACKAGE *package )
2319 HKEY hkey;
2320 WCHAR *path;
2322 if (!package->ProductCode) return NULL;
2323 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2324 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2326 msi_free( path );
2327 path = NULL;
2329 RegCloseKey( hkey );
2330 return path;
2333 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2335 FolderList *fl;
2336 MSIFOLDER *folder, *parent, *child;
2337 WCHAR *path, *normalized_path;
2339 TRACE("resolving %s\n", debugstr_w(name));
2341 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2343 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2345 if (!(path = get_install_location( package )) &&
2346 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2348 path = msi_dup_property( package->db, szRootDrive );
2351 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2353 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2355 parent = msi_get_loaded_folder( package, folder->Parent );
2356 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2358 else
2359 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2361 normalized_path = msi_normalize_path( path );
2362 msi_free( path );
2363 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2365 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2366 msi_free( normalized_path );
2367 return;
2369 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2370 msi_free( folder->ResolvedTarget );
2371 folder->ResolvedTarget = normalized_path;
2373 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2375 child = fl->folder;
2376 msi_resolve_target_folder( package, child->Directory, load_prop );
2378 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2381 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2383 static const WCHAR query[] =
2384 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2385 '`','C','o','n','d','i','t','i','o','n','`',0};
2386 static const WCHAR szOutOfDiskSpace[] =
2387 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2388 static const WCHAR szPrimaryFolder[] =
2389 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2390 static const WCHAR szPrimaryVolumePath[] =
2391 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2392 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2393 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2394 'A','v','a','i','l','a','b','l','e',0};
2395 static const WCHAR szOutOfNoRbDiskSpace[] =
2396 {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0};
2397 MSICOMPONENT *comp;
2398 MSIQUERY *view;
2399 WCHAR *level, *primary_key, *primary_folder;
2400 UINT rc;
2402 TRACE("Building directory properties\n");
2403 msi_resolve_target_folder( package, szTargetDir, TRUE );
2405 TRACE("Evaluating component conditions\n");
2406 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2408 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2410 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2411 comp->Enabled = FALSE;
2413 else
2414 comp->Enabled = TRUE;
2416 get_client_counts( package );
2418 /* read components states from the registry */
2419 ACTION_GetComponentInstallStates(package);
2420 ACTION_GetFeatureInstallStates(package);
2422 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2424 TRACE("Evaluating feature conditions\n");
2426 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2427 if (rc == ERROR_SUCCESS)
2429 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2430 msiobj_release( &view->hdr );
2431 if (rc != ERROR_SUCCESS)
2432 return rc;
2436 TRACE("Calculating file cost\n");
2437 calculate_file_cost( package );
2439 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2440 /* set default run level if not set */
2441 level = msi_dup_property( package->db, szInstallLevel );
2442 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2443 msi_free(level);
2445 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2447 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2449 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2450 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2452 ULARGE_INTEGER free;
2454 primary_folder[2] = 0;
2455 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2457 static const WCHAR fmtW[] = {'%','l','u',0};
2458 WCHAR buf[21];
2460 sprintfW( buf, fmtW, free.QuadPart / 512 );
2461 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2463 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2465 msi_free( primary_folder );
2467 msi_free( primary_key );
2470 /* FIXME: check volume disk space */
2471 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2472 msi_set_property( package->db, szOutOfNoRbDiskSpace, szZero, -1 );
2474 return MSI_SetFeatureStates(package);
2477 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size )
2479 BYTE *data;
2481 if (!value)
2483 *size = sizeof(WCHAR);
2484 *type = REG_SZ;
2485 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2486 return data;
2488 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2490 if (value[1]=='x')
2492 LPWSTR ptr;
2493 CHAR byte[5];
2494 LPWSTR deformated = NULL;
2495 int count;
2497 deformat_string(package, &value[2], &deformated);
2499 /* binary value type */
2500 ptr = deformated;
2501 *type = REG_BINARY;
2502 if (strlenW(ptr)%2)
2503 *size = (strlenW(ptr)/2)+1;
2504 else
2505 *size = strlenW(ptr)/2;
2507 data = msi_alloc(*size);
2509 byte[0] = '0';
2510 byte[1] = 'x';
2511 byte[4] = 0;
2512 count = 0;
2513 /* if uneven pad with a zero in front */
2514 if (strlenW(ptr)%2)
2516 byte[2]= '0';
2517 byte[3]= *ptr;
2518 ptr++;
2519 data[count] = (BYTE)strtol(byte,NULL,0);
2520 count ++;
2521 TRACE("Uneven byte count\n");
2523 while (*ptr)
2525 byte[2]= *ptr;
2526 ptr++;
2527 byte[3]= *ptr;
2528 ptr++;
2529 data[count] = (BYTE)strtol(byte,NULL,0);
2530 count ++;
2532 msi_free(deformated);
2534 TRACE("Data %i bytes(%i)\n",*size,count);
2536 else
2538 LPWSTR deformated;
2539 LPWSTR p;
2540 DWORD d = 0;
2541 deformat_string(package, &value[1], &deformated);
2543 *type=REG_DWORD;
2544 *size = sizeof(DWORD);
2545 data = msi_alloc(*size);
2546 p = deformated;
2547 if (*p == '-')
2548 p++;
2549 while (*p)
2551 if ( (*p < '0') || (*p > '9') )
2552 break;
2553 d *= 10;
2554 d += (*p - '0');
2555 p++;
2557 if (deformated[0] == '-')
2558 d = -d;
2559 *(LPDWORD)data = d;
2560 TRACE("DWORD %i\n",*(LPDWORD)data);
2562 msi_free(deformated);
2565 else
2567 const WCHAR *ptr = value;
2569 *type = REG_SZ;
2570 if (value[0] == '#')
2572 ptr++; len--;
2573 if (value[1] == '%')
2575 ptr++; len--;
2576 *type = REG_EXPAND_SZ;
2579 data = (BYTE *)msi_strdupW( ptr, len );
2580 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2581 *size = (len + 1) * sizeof(WCHAR);
2583 return data;
2586 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2588 const WCHAR *ret;
2590 switch (root)
2592 case -1:
2593 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2595 *root_key = HKEY_LOCAL_MACHINE;
2596 ret = szHLM;
2598 else
2600 *root_key = HKEY_CURRENT_USER;
2601 ret = szHCU;
2603 break;
2604 case 0:
2605 *root_key = HKEY_CLASSES_ROOT;
2606 ret = szHCR;
2607 break;
2608 case 1:
2609 *root_key = HKEY_CURRENT_USER;
2610 ret = szHCU;
2611 break;
2612 case 2:
2613 *root_key = HKEY_LOCAL_MACHINE;
2614 ret = szHLM;
2615 break;
2616 case 3:
2617 *root_key = HKEY_USERS;
2618 ret = szHU;
2619 break;
2620 default:
2621 ERR("Unknown root %i\n", root);
2622 return NULL;
2625 return ret;
2628 static inline REGSAM get_registry_view( const MSICOMPONENT *comp )
2630 REGSAM view = 0;
2631 if (is_wow64 || is_64bit)
2632 view |= (comp->Attributes & msidbComponentAttributes64bit) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
2633 return view;
2636 static HKEY open_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, BOOL create )
2638 REGSAM access = KEY_ALL_ACCESS;
2639 WCHAR *subkey, *p, *q;
2640 HKEY hkey, ret = NULL;
2641 LONG res;
2643 access |= get_registry_view( comp );
2645 if (!(subkey = strdupW( path ))) return NULL;
2646 p = subkey;
2647 if ((q = strchrW( p, '\\' ))) *q = 0;
2648 if (create)
2649 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2650 else
2651 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2652 if (res)
2654 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2655 msi_free( subkey );
2656 return NULL;
2658 if (q && q[1])
2660 ret = open_key( comp, hkey, q + 1, create );
2661 RegCloseKey( hkey );
2663 else ret = hkey;
2664 msi_free( subkey );
2665 return ret;
2668 static BOOL is_special_entry( const WCHAR *name )
2670 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2673 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2675 const WCHAR *p = str;
2676 WCHAR **ret;
2677 int i = 0;
2679 *count = 0;
2680 if (!str) return NULL;
2681 while ((p - str) < len)
2683 p += strlenW( p ) + 1;
2684 (*count)++;
2686 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2687 p = str;
2688 while ((p - str) < len)
2690 if (!(ret[i] = strdupW( p )))
2692 for (; i >= 0; i--) msi_free( ret[i] );
2693 msi_free( ret );
2694 return NULL;
2696 p += strlenW( p ) + 1;
2697 i++;
2699 return ret;
2702 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2703 WCHAR **right, DWORD right_count, DWORD *size )
2705 WCHAR *ret, *p;
2706 unsigned int i;
2708 *size = sizeof(WCHAR);
2709 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2710 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2712 if (!(ret = p = msi_alloc( *size ))) return NULL;
2714 for (i = 0; i < left_count; i++)
2716 strcpyW( p, left[i] );
2717 p += strlenW( p ) + 1;
2719 for (i = 0; i < right_count; i++)
2721 strcpyW( p, right[i] );
2722 p += strlenW( p ) + 1;
2724 *p = 0;
2725 return ret;
2728 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2729 WCHAR **new, DWORD new_count )
2731 DWORD ret = old_count;
2732 unsigned int i, j, k;
2734 for (i = 0; i < new_count; i++)
2736 for (j = 0; j < old_count; j++)
2738 if (old[j] && !strcmpW( new[i], old[j] ))
2740 msi_free( old[j] );
2741 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2742 old[k] = NULL;
2743 ret--;
2747 return ret;
2750 enum join_op
2752 JOIN_OP_APPEND,
2753 JOIN_OP_PREPEND,
2754 JOIN_OP_REPLACE
2757 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2758 WCHAR **new, DWORD new_count, DWORD *size )
2760 switch (op)
2762 case JOIN_OP_APPEND:
2763 old_count = remove_duplicate_values( old, old_count, new, new_count );
2764 return flatten_multi_string_values( old, old_count, new, new_count, size );
2766 case JOIN_OP_PREPEND:
2767 old_count = remove_duplicate_values( old, old_count, new, new_count );
2768 return flatten_multi_string_values( new, new_count, old, old_count, size );
2770 case JOIN_OP_REPLACE:
2771 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2773 default:
2774 ERR("unhandled join op %u\n", op);
2775 return NULL;
2779 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2780 BYTE *new_value, DWORD new_size, DWORD *size )
2782 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2783 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2784 enum join_op op = JOIN_OP_REPLACE;
2785 WCHAR **old = NULL, **new = NULL;
2786 BYTE *ret;
2788 if (new_size / sizeof(WCHAR) - 1 > 1)
2790 new_ptr = (const WCHAR *)new_value;
2791 new_len = new_size / sizeof(WCHAR) - 1;
2793 if (!new_ptr[0] && new_ptr[new_len - 1])
2795 op = JOIN_OP_APPEND;
2796 new_len--;
2797 new_ptr++;
2799 else if (new_ptr[0] && !new_ptr[new_len - 1])
2801 op = JOIN_OP_PREPEND;
2802 new_len--;
2804 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2806 op = JOIN_OP_REPLACE;
2807 new_len -= 2;
2808 new_ptr++;
2810 new = split_multi_string_values( new_ptr, new_len, &new_count );
2812 if (old_size / sizeof(WCHAR) - 1 > 1)
2814 old_ptr = (const WCHAR *)old_value;
2815 old_len = old_size / sizeof(WCHAR) - 1;
2816 old = split_multi_string_values( old_ptr, old_len, &old_count );
2818 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2819 for (i = 0; i < old_count; i++) msi_free( old[i] );
2820 for (i = 0; i < new_count; i++) msi_free( new[i] );
2821 msi_free( old );
2822 msi_free( new );
2823 return ret;
2826 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2828 BYTE *ret;
2829 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2830 if (!(ret = msi_alloc( *size ))) return NULL;
2831 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2832 return ret;
2835 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2837 MSIPACKAGE *package = param;
2838 BYTE *new_value, *old_value = NULL;
2839 HKEY root_key, hkey;
2840 DWORD type, old_type, new_size, old_size = 0;
2841 LPWSTR deformated, uikey;
2842 const WCHAR *szRoot, *component, *name, *key, *str;
2843 MSICOMPONENT *comp;
2844 MSIRECORD * uirow;
2845 INT root;
2846 BOOL check_first = FALSE;
2847 int len;
2849 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2851 component = MSI_RecordGetString(row, 6);
2852 comp = msi_get_loaded_component(package,component);
2853 if (!comp)
2854 return ERROR_SUCCESS;
2856 comp->Action = msi_get_component_action( package, comp );
2857 if (comp->Action != INSTALLSTATE_LOCAL)
2859 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2860 return ERROR_SUCCESS;
2863 name = MSI_RecordGetString(row, 4);
2864 if( MSI_RecordIsNull(row,5) && name )
2866 /* null values can have special meanings */
2867 if (name[0]=='-' && name[1] == 0)
2868 return ERROR_SUCCESS;
2869 if ((name[0] == '+' || name[0] == '*') && !name[1])
2870 check_first = TRUE;
2873 root = MSI_RecordGetInteger(row,2);
2874 key = MSI_RecordGetString(row, 3);
2876 szRoot = get_root_key( package, root, &root_key );
2877 if (!szRoot)
2878 return ERROR_SUCCESS;
2880 deformat_string(package, key , &deformated);
2881 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2882 strcpyW(uikey,szRoot);
2883 strcatW(uikey,deformated);
2885 if (!(hkey = open_key( comp, root_key, deformated, TRUE )))
2887 ERR("Could not create key %s\n", debugstr_w(deformated));
2888 msi_free(uikey);
2889 msi_free(deformated);
2890 return ERROR_FUNCTION_FAILED;
2892 msi_free( deformated );
2893 str = msi_record_get_string( row, 5, NULL );
2894 len = deformat_string( package, str, &deformated );
2895 new_value = parse_value( package, deformated, len, &type, &new_size );
2897 msi_free( deformated );
2898 deformat_string(package, name, &deformated);
2900 if (!is_special_entry( name ))
2902 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2903 if (type == REG_MULTI_SZ)
2905 BYTE *new;
2906 if (old_value && old_type != REG_MULTI_SZ)
2908 msi_free( old_value );
2909 old_value = NULL;
2910 old_size = 0;
2912 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2913 msi_free( new_value );
2914 new_value = new;
2916 if (!check_first)
2918 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2919 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2921 else if (!old_value)
2923 if (deformated || new_size)
2925 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2926 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2929 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2931 RegCloseKey(hkey);
2933 uirow = MSI_CreateRecord(3);
2934 MSI_RecordSetStringW(uirow,2,deformated);
2935 MSI_RecordSetStringW(uirow,1,uikey);
2936 if (type == REG_SZ || type == REG_EXPAND_SZ)
2937 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2938 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2939 msiobj_release( &uirow->hdr );
2941 msi_free(new_value);
2942 msi_free(old_value);
2943 msi_free(deformated);
2944 msi_free(uikey);
2946 return ERROR_SUCCESS;
2949 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2951 static const WCHAR query[] = {
2952 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2953 '`','R','e','g','i','s','t','r','y','`',0};
2954 MSIQUERY *view;
2955 UINT rc;
2957 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2958 if (rc != ERROR_SUCCESS)
2959 return ERROR_SUCCESS;
2961 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2962 msiobj_release(&view->hdr);
2963 return rc;
2966 static void delete_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2968 REGSAM access = 0;
2969 WCHAR *subkey, *p;
2970 HKEY hkey;
2971 LONG res;
2973 access |= get_registry_view( comp );
2975 if (!(subkey = strdupW( path ))) return;
2976 for (;;)
2978 if ((p = strrchrW( subkey, '\\' ))) *p = 0;
2979 hkey = open_key( comp, root, subkey, FALSE );
2980 if (!hkey) break;
2981 if (p && p[1])
2982 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2983 else
2984 res = RegDeleteKeyExW( root, subkey, access, 0 );
2985 if (res)
2987 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
2988 break;
2990 if (p && p[1]) RegCloseKey( hkey );
2991 else break;
2993 msi_free( subkey );
2996 static void delete_value( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, const WCHAR *value )
2998 LONG res;
2999 HKEY hkey;
3000 DWORD num_subkeys, num_values;
3002 if ((hkey = open_key( comp, root, path, FALSE )))
3004 if ((res = RegDeleteValueW( hkey, value )))
3005 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
3007 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
3008 NULL, NULL, NULL, NULL );
3009 RegCloseKey( hkey );
3010 if (!res && !num_subkeys && !num_values)
3012 TRACE("removing empty key %s\n", debugstr_w(path));
3013 delete_key( comp, root, path );
3018 static void delete_tree( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
3020 LONG res;
3021 HKEY hkey;
3023 if (!(hkey = open_key( comp, root, path, FALSE ))) return;
3024 res = RegDeleteTreeW( hkey, NULL );
3025 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3026 delete_key( comp, root, path );
3027 RegCloseKey( hkey );
3030 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3032 MSIPACKAGE *package = param;
3033 LPCWSTR component, name, key_str, root_key_str;
3034 LPWSTR deformated_key, deformated_name, ui_key_str;
3035 MSICOMPONENT *comp;
3036 MSIRECORD *uirow;
3037 BOOL delete_key = FALSE;
3038 HKEY hkey_root;
3039 UINT size;
3040 INT root;
3042 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3044 component = MSI_RecordGetString( row, 6 );
3045 comp = msi_get_loaded_component( package, component );
3046 if (!comp)
3047 return ERROR_SUCCESS;
3049 comp->Action = msi_get_component_action( package, comp );
3050 if (comp->Action != INSTALLSTATE_ABSENT)
3052 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3053 return ERROR_SUCCESS;
3056 name = MSI_RecordGetString( row, 4 );
3057 if (MSI_RecordIsNull( row, 5 ) && name )
3059 if (name[0] == '+' && !name[1])
3060 return ERROR_SUCCESS;
3061 if ((name[0] == '-' || name[0] == '*') && !name[1])
3063 delete_key = TRUE;
3064 name = NULL;
3068 root = MSI_RecordGetInteger( row, 2 );
3069 key_str = MSI_RecordGetString( row, 3 );
3071 root_key_str = get_root_key( package, root, &hkey_root );
3072 if (!root_key_str)
3073 return ERROR_SUCCESS;
3075 deformat_string( package, key_str, &deformated_key );
3076 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3077 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3078 strcpyW( ui_key_str, root_key_str );
3079 strcatW( ui_key_str, deformated_key );
3081 deformat_string( package, name, &deformated_name );
3083 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3084 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3085 msi_free( deformated_key );
3087 uirow = MSI_CreateRecord( 2 );
3088 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3089 MSI_RecordSetStringW( uirow, 2, deformated_name );
3090 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3091 msiobj_release( &uirow->hdr );
3093 msi_free( ui_key_str );
3094 msi_free( deformated_name );
3095 return ERROR_SUCCESS;
3098 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3100 MSIPACKAGE *package = param;
3101 LPCWSTR component, name, key_str, root_key_str;
3102 LPWSTR deformated_key, deformated_name, ui_key_str;
3103 MSICOMPONENT *comp;
3104 MSIRECORD *uirow;
3105 BOOL delete_key = FALSE;
3106 HKEY hkey_root;
3107 UINT size;
3108 INT root;
3110 component = MSI_RecordGetString( row, 5 );
3111 comp = msi_get_loaded_component( package, component );
3112 if (!comp)
3113 return ERROR_SUCCESS;
3115 comp->Action = msi_get_component_action( package, comp );
3116 if (comp->Action != INSTALLSTATE_LOCAL)
3118 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3119 return ERROR_SUCCESS;
3122 if ((name = MSI_RecordGetString( row, 4 )))
3124 if (name[0] == '-' && !name[1])
3126 delete_key = TRUE;
3127 name = NULL;
3131 root = MSI_RecordGetInteger( row, 2 );
3132 key_str = MSI_RecordGetString( row, 3 );
3134 root_key_str = get_root_key( package, root, &hkey_root );
3135 if (!root_key_str)
3136 return ERROR_SUCCESS;
3138 deformat_string( package, key_str, &deformated_key );
3139 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3140 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3141 strcpyW( ui_key_str, root_key_str );
3142 strcatW( ui_key_str, deformated_key );
3144 deformat_string( package, name, &deformated_name );
3146 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3147 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3148 msi_free( deformated_key );
3150 uirow = MSI_CreateRecord( 2 );
3151 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3152 MSI_RecordSetStringW( uirow, 2, deformated_name );
3153 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3154 msiobj_release( &uirow->hdr );
3156 msi_free( ui_key_str );
3157 msi_free( deformated_name );
3158 return ERROR_SUCCESS;
3161 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3163 static const WCHAR registry_query[] = {
3164 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3165 '`','R','e','g','i','s','t','r','y','`',0};
3166 static const WCHAR remove_registry_query[] = {
3167 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3168 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3169 MSIQUERY *view;
3170 UINT rc;
3172 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3173 if (rc == ERROR_SUCCESS)
3175 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3176 msiobj_release( &view->hdr );
3177 if (rc != ERROR_SUCCESS)
3178 return rc;
3180 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3181 if (rc == ERROR_SUCCESS)
3183 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3184 msiobj_release( &view->hdr );
3185 if (rc != ERROR_SUCCESS)
3186 return rc;
3188 return ERROR_SUCCESS;
3191 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3193 return ERROR_SUCCESS;
3197 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3199 static const WCHAR query[]= {
3200 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3201 '`','R','e','g','i','s','t','r','y','`',0};
3202 MSICOMPONENT *comp;
3203 DWORD total = 0, count = 0;
3204 MSIQUERY *view;
3205 MSIFEATURE *feature;
3206 MSIFILE *file;
3207 UINT rc;
3209 TRACE("InstallValidate\n");
3211 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3212 if (rc == ERROR_SUCCESS)
3214 rc = MSI_IterateRecords( view, &count, NULL, package );
3215 msiobj_release( &view->hdr );
3216 if (rc != ERROR_SUCCESS)
3217 return rc;
3218 total += count * REG_PROGRESS_VALUE;
3220 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3221 total += COMPONENT_PROGRESS_VALUE;
3223 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3224 total += file->FileSize;
3226 msi_ui_progress( package, 0, total, 0, 0 );
3228 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3230 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3231 debugstr_w(feature->Feature), feature->Installed,
3232 feature->ActionRequest, feature->Action);
3234 return ERROR_SUCCESS;
3237 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3239 MSIPACKAGE* package = param;
3240 LPCWSTR cond = NULL;
3241 LPCWSTR message = NULL;
3242 UINT r;
3244 static const WCHAR title[]=
3245 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3247 cond = MSI_RecordGetString(row,1);
3249 r = MSI_EvaluateConditionW(package,cond);
3250 if (r == MSICONDITION_FALSE)
3252 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3254 LPWSTR deformated;
3255 message = MSI_RecordGetString(row,2);
3256 deformat_string(package,message,&deformated);
3257 MessageBoxW(NULL,deformated,title,MB_OK);
3258 msi_free(deformated);
3261 return ERROR_INSTALL_FAILURE;
3264 return ERROR_SUCCESS;
3267 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3269 static const WCHAR query[] = {
3270 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3271 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3272 MSIQUERY *view;
3273 UINT rc;
3275 TRACE("Checking launch conditions\n");
3277 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3278 if (rc != ERROR_SUCCESS)
3279 return ERROR_SUCCESS;
3281 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3282 msiobj_release(&view->hdr);
3283 return rc;
3286 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3289 if (!cmp->KeyPath)
3290 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3292 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3294 static const WCHAR query[] = {
3295 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3296 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3297 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3298 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3299 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3300 MSIRECORD *row;
3301 UINT root, len;
3302 LPWSTR deformated, buffer, deformated_name;
3303 LPCWSTR key, name;
3305 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3306 if (!row)
3307 return NULL;
3309 root = MSI_RecordGetInteger(row,2);
3310 key = MSI_RecordGetString(row, 3);
3311 name = MSI_RecordGetString(row, 4);
3312 deformat_string(package, key , &deformated);
3313 deformat_string(package, name, &deformated_name);
3315 len = strlenW(deformated) + 6;
3316 if (deformated_name)
3317 len+=strlenW(deformated_name);
3319 buffer = msi_alloc( len *sizeof(WCHAR));
3321 if (deformated_name)
3322 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3323 else
3324 sprintfW(buffer,fmt,root,deformated);
3326 msi_free(deformated);
3327 msi_free(deformated_name);
3328 msiobj_release(&row->hdr);
3330 return buffer;
3332 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3334 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3335 return NULL;
3337 else
3339 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3341 if (file)
3342 return strdupW( file->TargetPath );
3344 return NULL;
3347 static HKEY openSharedDLLsKey(void)
3349 HKEY hkey=0;
3350 static const WCHAR path[] =
3351 {'S','o','f','t','w','a','r','e','\\',
3352 'M','i','c','r','o','s','o','f','t','\\',
3353 'W','i','n','d','o','w','s','\\',
3354 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3355 'S','h','a','r','e','d','D','L','L','s',0};
3357 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3358 return hkey;
3361 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3363 HKEY hkey;
3364 DWORD count=0;
3365 DWORD type;
3366 DWORD sz = sizeof(count);
3367 DWORD rc;
3369 hkey = openSharedDLLsKey();
3370 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3371 if (rc != ERROR_SUCCESS)
3372 count = 0;
3373 RegCloseKey(hkey);
3374 return count;
3377 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3379 HKEY hkey;
3381 hkey = openSharedDLLsKey();
3382 if (count > 0)
3383 msi_reg_set_val_dword( hkey, path, count );
3384 else
3385 RegDeleteValueW(hkey,path);
3386 RegCloseKey(hkey);
3387 return count;
3390 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3392 MSIFEATURE *feature;
3393 INT count = 0;
3394 BOOL write = FALSE;
3396 /* only refcount DLLs */
3397 if (comp->KeyPath == NULL ||
3398 comp->assembly ||
3399 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3400 comp->Attributes & msidbComponentAttributesODBCDataSource)
3401 write = FALSE;
3402 else
3404 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3405 write = (count > 0);
3407 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3408 write = TRUE;
3411 /* increment counts */
3412 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3414 ComponentList *cl;
3416 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3417 continue;
3419 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3421 if ( cl->component == comp )
3422 count++;
3426 /* decrement counts */
3427 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3429 ComponentList *cl;
3431 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3432 continue;
3434 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3436 if ( cl->component == comp )
3437 count--;
3441 /* ref count all the files in the component */
3442 if (write)
3444 MSIFILE *file;
3446 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3448 if (file->Component == comp)
3449 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3453 /* add a count for permanent */
3454 if (comp->Attributes & msidbComponentAttributesPermanent)
3455 count ++;
3457 comp->RefCount = count;
3459 if (write)
3460 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3463 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3465 if (comp->assembly)
3467 const WCHAR prefixW[] = {'<','\\',0};
3468 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3469 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3471 if (keypath)
3473 strcpyW( keypath, prefixW );
3474 strcatW( keypath, comp->assembly->display_name );
3476 return keypath;
3478 return resolve_keypath( package, comp );
3481 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3483 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3484 UINT rc;
3485 MSICOMPONENT *comp;
3486 HKEY hkey;
3488 TRACE("\n");
3490 squash_guid(package->ProductCode,squished_pc);
3491 msi_set_sourcedir_props(package, FALSE);
3493 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3495 MSIRECORD *uirow;
3496 INSTALLSTATE action;
3498 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3499 if (!comp->ComponentId)
3500 continue;
3502 squash_guid( comp->ComponentId, squished_cc );
3503 msi_free( comp->FullKeypath );
3504 comp->FullKeypath = build_full_keypath( package, comp );
3506 ACTION_RefCountComponent( package, comp );
3508 if (package->need_rollback) action = comp->Installed;
3509 else action = comp->ActionRequest;
3511 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3512 debugstr_w(comp->Component), debugstr_w(squished_cc),
3513 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3515 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3517 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3518 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3519 else
3520 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3522 if (rc != ERROR_SUCCESS)
3523 continue;
3525 if (comp->Attributes & msidbComponentAttributesPermanent)
3527 static const WCHAR szPermKey[] =
3528 { '0','0','0','0','0','0','0','0','0','0','0','0',
3529 '0','0','0','0','0','0','0','0','0','0','0','0',
3530 '0','0','0','0','0','0','0','0',0 };
3532 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3534 if (action == INSTALLSTATE_LOCAL)
3535 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3536 else
3538 MSIFILE *file;
3539 MSIRECORD *row;
3540 LPWSTR ptr, ptr2;
3541 WCHAR source[MAX_PATH];
3542 WCHAR base[MAX_PATH];
3543 LPWSTR sourcepath;
3545 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3546 static const WCHAR query[] = {
3547 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3548 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3549 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3550 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3551 '`','D','i','s','k','I','d','`',0};
3553 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3554 continue;
3556 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3557 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3558 ptr2 = strrchrW(source, '\\') + 1;
3559 msiobj_release(&row->hdr);
3561 lstrcpyW(base, package->PackagePath);
3562 ptr = strrchrW(base, '\\');
3563 *(ptr + 1) = '\0';
3565 sourcepath = msi_resolve_file_source(package, file);
3566 ptr = sourcepath + lstrlenW(base);
3567 lstrcpyW(ptr2, ptr);
3568 msi_free(sourcepath);
3570 msi_reg_set_val_str(hkey, squished_pc, source);
3572 RegCloseKey(hkey);
3574 else if (action == INSTALLSTATE_ABSENT)
3576 if (comp->num_clients <= 0)
3578 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3579 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3580 else
3581 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3583 if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
3585 else
3587 LONG res;
3589 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3590 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE );
3591 else
3592 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );
3594 if (rc != ERROR_SUCCESS)
3596 WARN( "failed to open component key %u\n", rc );
3597 continue;
3599 res = RegDeleteValueW( hkey, squished_pc );
3600 RegCloseKey(hkey);
3601 if (res) WARN( "failed to delete component value %d\n", res );
3605 /* UI stuff */
3606 uirow = MSI_CreateRecord(3);
3607 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3608 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3609 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3610 msi_ui_actiondata( package, szProcessComponents, uirow );
3611 msiobj_release( &uirow->hdr );
3613 return ERROR_SUCCESS;
3616 typedef struct {
3617 CLSID clsid;
3618 LPWSTR source;
3620 LPWSTR path;
3621 ITypeLib *ptLib;
3622 } typelib_struct;
3624 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3625 LPWSTR lpszName, LONG_PTR lParam)
3627 TLIBATTR *attr;
3628 typelib_struct *tl_struct = (typelib_struct*) lParam;
3629 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3630 int sz;
3631 HRESULT res;
3633 if (!IS_INTRESOURCE(lpszName))
3635 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3636 return TRUE;
3639 sz = strlenW(tl_struct->source)+4;
3640 sz *= sizeof(WCHAR);
3642 if ((INT_PTR)lpszName == 1)
3643 tl_struct->path = strdupW(tl_struct->source);
3644 else
3646 tl_struct->path = msi_alloc(sz);
3647 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3650 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3651 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3652 if (FAILED(res))
3654 msi_free(tl_struct->path);
3655 tl_struct->path = NULL;
3657 return TRUE;
3660 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3661 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3663 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3664 return FALSE;
3667 msi_free(tl_struct->path);
3668 tl_struct->path = NULL;
3670 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3671 ITypeLib_Release(tl_struct->ptLib);
3673 return TRUE;
3676 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3678 MSIPACKAGE* package = param;
3679 LPCWSTR component;
3680 MSICOMPONENT *comp;
3681 MSIFILE *file;
3682 typelib_struct tl_struct;
3683 ITypeLib *tlib;
3684 HMODULE module;
3685 HRESULT hr;
3687 component = MSI_RecordGetString(row,3);
3688 comp = msi_get_loaded_component(package,component);
3689 if (!comp)
3690 return ERROR_SUCCESS;
3692 comp->Action = msi_get_component_action( package, comp );
3693 if (comp->Action != INSTALLSTATE_LOCAL)
3695 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3696 return ERROR_SUCCESS;
3699 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3701 TRACE("component has no key path\n");
3702 return ERROR_SUCCESS;
3704 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3706 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3707 if (module)
3709 LPCWSTR guid;
3710 guid = MSI_RecordGetString(row,1);
3711 CLSIDFromString( guid, &tl_struct.clsid);
3712 tl_struct.source = strdupW( file->TargetPath );
3713 tl_struct.path = NULL;
3715 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3716 (LONG_PTR)&tl_struct);
3718 if (tl_struct.path)
3720 LPCWSTR helpid, help_path = NULL;
3721 HRESULT res;
3723 helpid = MSI_RecordGetString(row,6);
3725 if (helpid) help_path = msi_get_target_folder( package, helpid );
3726 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3728 if (FAILED(res))
3729 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3730 else
3731 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3733 ITypeLib_Release(tl_struct.ptLib);
3734 msi_free(tl_struct.path);
3736 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3738 FreeLibrary(module);
3739 msi_free(tl_struct.source);
3741 else
3743 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3744 if (FAILED(hr))
3746 ERR("Failed to load type library: %08x\n", hr);
3747 return ERROR_INSTALL_FAILURE;
3750 ITypeLib_Release(tlib);
3753 return ERROR_SUCCESS;
3756 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3758 static const WCHAR query[] = {
3759 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3760 '`','T','y','p','e','L','i','b','`',0};
3761 MSIQUERY *view;
3762 UINT rc;
3764 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3765 if (rc != ERROR_SUCCESS)
3766 return ERROR_SUCCESS;
3768 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3769 msiobj_release(&view->hdr);
3770 return rc;
3773 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3775 MSIPACKAGE *package = param;
3776 LPCWSTR component, guid;
3777 MSICOMPONENT *comp;
3778 GUID libid;
3779 UINT version;
3780 LCID language;
3781 SYSKIND syskind;
3782 HRESULT hr;
3784 component = MSI_RecordGetString( row, 3 );
3785 comp = msi_get_loaded_component( package, component );
3786 if (!comp)
3787 return ERROR_SUCCESS;
3789 comp->Action = msi_get_component_action( package, comp );
3790 if (comp->Action != INSTALLSTATE_ABSENT)
3792 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3793 return ERROR_SUCCESS;
3795 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3797 guid = MSI_RecordGetString( row, 1 );
3798 CLSIDFromString( guid, &libid );
3799 version = MSI_RecordGetInteger( row, 4 );
3800 language = MSI_RecordGetInteger( row, 2 );
3802 #ifdef _WIN64
3803 syskind = SYS_WIN64;
3804 #else
3805 syskind = SYS_WIN32;
3806 #endif
3808 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3809 if (FAILED(hr))
3811 WARN("Failed to unregister typelib: %08x\n", hr);
3814 return ERROR_SUCCESS;
3817 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3819 static const WCHAR query[] = {
3820 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3821 '`','T','y','p','e','L','i','b','`',0};
3822 MSIQUERY *view;
3823 UINT rc;
3825 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3826 if (rc != ERROR_SUCCESS)
3827 return ERROR_SUCCESS;
3829 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3830 msiobj_release( &view->hdr );
3831 return rc;
3834 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3836 static const WCHAR szlnk[] = {'.','l','n','k',0};
3837 LPCWSTR directory, extension, link_folder;
3838 LPWSTR link_file, filename;
3840 directory = MSI_RecordGetString( row, 2 );
3841 link_folder = msi_get_target_folder( package, directory );
3842 if (!link_folder)
3844 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3845 return NULL;
3847 /* may be needed because of a bug somewhere else */
3848 msi_create_full_path( link_folder );
3850 filename = msi_dup_record_field( row, 3 );
3851 msi_reduce_to_long_filename( filename );
3853 extension = strrchrW( filename, '.' );
3854 if (!extension || strcmpiW( extension, szlnk ))
3856 int len = strlenW( filename );
3857 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3858 memcpy( filename + len, szlnk, sizeof(szlnk) );
3860 link_file = msi_build_directory_name( 2, link_folder, filename );
3861 msi_free( filename );
3863 return link_file;
3866 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3868 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3869 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3870 WCHAR *folder, *dest, *path;
3872 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3873 folder = msi_dup_property( package->db, szWindowsFolder );
3874 else
3876 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3877 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3878 msi_free( appdata );
3880 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3881 msi_create_full_path( dest );
3882 path = msi_build_directory_name( 2, dest, icon_name );
3883 msi_free( folder );
3884 msi_free( dest );
3885 return path;
3888 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3890 MSIPACKAGE *package = param;
3891 LPWSTR link_file, deformated, path;
3892 LPCWSTR component, target;
3893 MSICOMPONENT *comp;
3894 IShellLinkW *sl = NULL;
3895 IPersistFile *pf = NULL;
3896 HRESULT res;
3898 component = MSI_RecordGetString(row, 4);
3899 comp = msi_get_loaded_component(package, component);
3900 if (!comp)
3901 return ERROR_SUCCESS;
3903 comp->Action = msi_get_component_action( package, comp );
3904 if (comp->Action != INSTALLSTATE_LOCAL)
3906 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3907 return ERROR_SUCCESS;
3909 msi_ui_actiondata( package, szCreateShortcuts, row );
3911 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3912 &IID_IShellLinkW, (LPVOID *) &sl );
3914 if (FAILED( res ))
3916 ERR("CLSID_ShellLink not available\n");
3917 goto err;
3920 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3921 if (FAILED( res ))
3923 ERR("QueryInterface(IID_IPersistFile) failed\n");
3924 goto err;
3927 target = MSI_RecordGetString(row, 5);
3928 if (strchrW(target, '['))
3930 deformat_string( package, target, &path );
3931 TRACE("target path is %s\n", debugstr_w(path));
3932 IShellLinkW_SetPath( sl, path );
3933 msi_free( path );
3935 else
3937 FIXME("poorly handled shortcut format, advertised shortcut\n");
3938 path = resolve_keypath( package, comp );
3939 IShellLinkW_SetPath( sl, path );
3940 msi_free( path );
3943 if (!MSI_RecordIsNull(row,6))
3945 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3946 deformat_string(package, arguments, &deformated);
3947 IShellLinkW_SetArguments(sl,deformated);
3948 msi_free(deformated);
3951 if (!MSI_RecordIsNull(row,7))
3953 LPCWSTR description = MSI_RecordGetString(row, 7);
3954 IShellLinkW_SetDescription(sl, description);
3957 if (!MSI_RecordIsNull(row,8))
3958 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3960 if (!MSI_RecordIsNull(row,9))
3962 INT index;
3963 LPCWSTR icon = MSI_RecordGetString(row, 9);
3965 path = msi_build_icon_path(package, icon);
3966 index = MSI_RecordGetInteger(row,10);
3968 /* no value means 0 */
3969 if (index == MSI_NULL_INTEGER)
3970 index = 0;
3972 IShellLinkW_SetIconLocation(sl, path, index);
3973 msi_free(path);
3976 if (!MSI_RecordIsNull(row,11))
3977 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3979 if (!MSI_RecordIsNull(row,12))
3981 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3982 full_path = msi_get_target_folder( package, wkdir );
3983 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3985 link_file = get_link_file(package, row);
3987 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3988 IPersistFile_Save(pf, link_file, FALSE);
3989 msi_free(link_file);
3991 err:
3992 if (pf)
3993 IPersistFile_Release( pf );
3994 if (sl)
3995 IShellLinkW_Release( sl );
3997 return ERROR_SUCCESS;
4000 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
4002 static const WCHAR query[] = {
4003 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4004 '`','S','h','o','r','t','c','u','t','`',0};
4005 MSIQUERY *view;
4006 HRESULT res;
4007 UINT rc;
4009 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4010 if (rc != ERROR_SUCCESS)
4011 return ERROR_SUCCESS;
4013 res = CoInitialize( NULL );
4015 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4016 msiobj_release(&view->hdr);
4018 if (SUCCEEDED(res)) CoUninitialize();
4019 return rc;
4022 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4024 MSIPACKAGE *package = param;
4025 LPWSTR link_file;
4026 LPCWSTR component;
4027 MSICOMPONENT *comp;
4029 component = MSI_RecordGetString( row, 4 );
4030 comp = msi_get_loaded_component( package, component );
4031 if (!comp)
4032 return ERROR_SUCCESS;
4034 comp->Action = msi_get_component_action( package, comp );
4035 if (comp->Action != INSTALLSTATE_ABSENT)
4037 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4038 return ERROR_SUCCESS;
4040 msi_ui_actiondata( package, szRemoveShortcuts, row );
4042 link_file = get_link_file( package, row );
4044 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4045 if (!DeleteFileW( link_file ))
4047 WARN("Failed to remove shortcut file %u\n", GetLastError());
4049 msi_free( link_file );
4051 return ERROR_SUCCESS;
4054 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4056 static const WCHAR query[] = {
4057 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4058 '`','S','h','o','r','t','c','u','t','`',0};
4059 MSIQUERY *view;
4060 UINT rc;
4062 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4063 if (rc != ERROR_SUCCESS)
4064 return ERROR_SUCCESS;
4066 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4067 msiobj_release( &view->hdr );
4068 return rc;
4071 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4073 MSIPACKAGE* package = param;
4074 HANDLE the_file;
4075 LPWSTR FilePath;
4076 LPCWSTR FileName;
4077 CHAR buffer[1024];
4078 DWORD sz;
4079 UINT rc;
4081 FileName = MSI_RecordGetString(row,1);
4082 if (!FileName)
4084 ERR("Unable to get FileName\n");
4085 return ERROR_SUCCESS;
4088 FilePath = msi_build_icon_path(package, FileName);
4090 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4092 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4093 FILE_ATTRIBUTE_NORMAL, NULL);
4095 if (the_file == INVALID_HANDLE_VALUE)
4097 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4098 msi_free(FilePath);
4099 return ERROR_SUCCESS;
4104 DWORD write;
4105 sz = 1024;
4106 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4107 if (rc != ERROR_SUCCESS)
4109 ERR("Failed to get stream\n");
4110 CloseHandle(the_file);
4111 DeleteFileW(FilePath);
4112 break;
4114 WriteFile(the_file,buffer,sz,&write,NULL);
4115 } while (sz == 1024);
4117 msi_free(FilePath);
4118 CloseHandle(the_file);
4120 return ERROR_SUCCESS;
4123 static UINT msi_publish_icons(MSIPACKAGE *package)
4125 static const WCHAR query[]= {
4126 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4127 '`','I','c','o','n','`',0};
4128 MSIQUERY *view;
4129 UINT r;
4131 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4132 if (r == ERROR_SUCCESS)
4134 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4135 msiobj_release(&view->hdr);
4136 if (r != ERROR_SUCCESS)
4137 return r;
4139 return ERROR_SUCCESS;
4142 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4144 UINT r;
4145 HKEY source;
4146 LPWSTR buffer;
4147 MSIMEDIADISK *disk;
4148 MSISOURCELISTINFO *info;
4150 r = RegCreateKeyW(hkey, szSourceList, &source);
4151 if (r != ERROR_SUCCESS)
4152 return r;
4154 RegCloseKey(source);
4156 buffer = strrchrW(package->PackagePath, '\\') + 1;
4157 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4158 package->Context, MSICODE_PRODUCT,
4159 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4160 if (r != ERROR_SUCCESS)
4161 return r;
4163 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4164 package->Context, MSICODE_PRODUCT,
4165 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4166 if (r != ERROR_SUCCESS)
4167 return r;
4169 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4170 package->Context, MSICODE_PRODUCT,
4171 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4172 if (r != ERROR_SUCCESS)
4173 return r;
4175 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4177 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4178 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4179 info->options, info->value);
4180 else
4181 MsiSourceListSetInfoW(package->ProductCode, NULL,
4182 info->context, info->options,
4183 info->property, info->value);
4186 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4188 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4189 disk->context, disk->options,
4190 disk->disk_id, disk->volume_label, disk->disk_prompt);
4193 return ERROR_SUCCESS;
4196 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4198 MSIHANDLE hdb, suminfo;
4199 WCHAR guids[MAX_PATH];
4200 WCHAR packcode[SQUISH_GUID_SIZE];
4201 LPWSTR buffer;
4202 LPWSTR ptr;
4203 DWORD langid;
4204 DWORD size;
4205 UINT r;
4207 static const WCHAR szARPProductIcon[] =
4208 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4209 static const WCHAR szAssignment[] =
4210 {'A','s','s','i','g','n','m','e','n','t',0};
4211 static const WCHAR szAdvertiseFlags[] =
4212 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4213 static const WCHAR szClients[] =
4214 {'C','l','i','e','n','t','s',0};
4215 static const WCHAR szColon[] = {':',0};
4217 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4218 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4219 msi_free(buffer);
4221 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4222 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4224 /* FIXME */
4225 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4227 buffer = msi_dup_property(package->db, szARPProductIcon);
4228 if (buffer)
4230 LPWSTR path = msi_build_icon_path(package, buffer);
4231 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4232 msi_free(path);
4233 msi_free(buffer);
4236 buffer = msi_dup_property(package->db, szProductVersion);
4237 if (buffer)
4239 DWORD verdword = msi_version_str_to_dword(buffer);
4240 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4241 msi_free(buffer);
4244 msi_reg_set_val_dword(hkey, szAssignment, 0);
4245 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4246 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4247 msi_reg_set_val_str(hkey, szClients, szColon);
4249 hdb = alloc_msihandle(&package->db->hdr);
4250 if (!hdb)
4251 return ERROR_NOT_ENOUGH_MEMORY;
4253 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4254 MsiCloseHandle(hdb);
4255 if (r != ERROR_SUCCESS)
4256 goto done;
4258 size = MAX_PATH;
4259 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4260 NULL, guids, &size);
4261 if (r != ERROR_SUCCESS)
4262 goto done;
4264 ptr = strchrW(guids, ';');
4265 if (ptr) *ptr = 0;
4266 squash_guid(guids, packcode);
4267 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4269 done:
4270 MsiCloseHandle(suminfo);
4271 return ERROR_SUCCESS;
4274 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4276 UINT r;
4277 HKEY hkey;
4278 LPWSTR upgrade;
4279 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4281 upgrade = msi_dup_property(package->db, szUpgradeCode);
4282 if (!upgrade)
4283 return ERROR_SUCCESS;
4285 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4286 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4287 else
4288 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4290 if (r != ERROR_SUCCESS)
4292 WARN("failed to open upgrade code key\n");
4293 msi_free(upgrade);
4294 return ERROR_SUCCESS;
4296 squash_guid(package->ProductCode, squashed_pc);
4297 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4298 RegCloseKey(hkey);
4299 msi_free(upgrade);
4300 return ERROR_SUCCESS;
4303 static BOOL msi_check_publish(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_LOCAL)
4311 return TRUE;
4314 return FALSE;
4317 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4319 MSIFEATURE *feature;
4321 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4323 feature->Action = msi_get_feature_action( package, feature );
4324 if (feature->Action != INSTALLSTATE_ABSENT)
4325 return FALSE;
4328 return TRUE;
4331 static UINT msi_publish_patches( MSIPACKAGE *package )
4333 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4334 WCHAR patch_squashed[GUID_SIZE];
4335 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4336 LONG res;
4337 MSIPATCHINFO *patch;
4338 UINT r;
4339 WCHAR *p, *all_patches = NULL;
4340 DWORD len = 0;
4342 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4343 if (r != ERROR_SUCCESS)
4344 return ERROR_FUNCTION_FAILED;
4346 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4347 if (res != ERROR_SUCCESS)
4349 r = ERROR_FUNCTION_FAILED;
4350 goto done;
4353 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4354 if (r != ERROR_SUCCESS)
4355 goto done;
4357 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4359 squash_guid( patch->patchcode, patch_squashed );
4360 len += strlenW( patch_squashed ) + 1;
4363 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4364 if (!all_patches)
4365 goto done;
4367 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4369 HKEY patch_key;
4371 squash_guid( patch->patchcode, p );
4372 p += strlenW( p ) + 1;
4374 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4375 (const BYTE *)patch->transforms,
4376 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4377 if (res != ERROR_SUCCESS)
4378 goto done;
4380 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4381 if (r != ERROR_SUCCESS)
4382 goto done;
4384 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4385 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4386 RegCloseKey( patch_key );
4387 if (res != ERROR_SUCCESS)
4388 goto done;
4390 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4392 res = GetLastError();
4393 ERR("Unable to copy patch package %d\n", res);
4394 goto done;
4396 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4397 if (res != ERROR_SUCCESS)
4398 goto done;
4400 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4401 RegCloseKey( patch_key );
4402 if (res != ERROR_SUCCESS)
4403 goto done;
4406 all_patches[len] = 0;
4407 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4408 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4409 if (res != ERROR_SUCCESS)
4410 goto done;
4412 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4413 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4414 if (res != ERROR_SUCCESS)
4415 r = ERROR_FUNCTION_FAILED;
4417 done:
4418 RegCloseKey( product_patches_key );
4419 RegCloseKey( patches_key );
4420 RegCloseKey( product_key );
4421 msi_free( all_patches );
4422 return r;
4425 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4427 UINT rc;
4428 HKEY hukey = NULL, hudkey = NULL;
4429 MSIRECORD *uirow;
4431 if (!list_empty(&package->patches))
4433 rc = msi_publish_patches(package);
4434 if (rc != ERROR_SUCCESS)
4435 goto end;
4438 /* FIXME: also need to publish if the product is in advertise mode */
4439 if (!msi_check_publish(package))
4440 return ERROR_SUCCESS;
4442 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4443 &hukey, TRUE);
4444 if (rc != ERROR_SUCCESS)
4445 goto end;
4447 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4448 NULL, &hudkey, TRUE);
4449 if (rc != ERROR_SUCCESS)
4450 goto end;
4452 rc = msi_publish_upgrade_code(package);
4453 if (rc != ERROR_SUCCESS)
4454 goto end;
4456 rc = msi_publish_product_properties(package, hukey);
4457 if (rc != ERROR_SUCCESS)
4458 goto end;
4460 rc = msi_publish_sourcelist(package, hukey);
4461 if (rc != ERROR_SUCCESS)
4462 goto end;
4464 rc = msi_publish_icons(package);
4466 end:
4467 uirow = MSI_CreateRecord( 1 );
4468 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4469 msi_ui_actiondata( package, szPublishProduct, uirow );
4470 msiobj_release( &uirow->hdr );
4472 RegCloseKey(hukey);
4473 RegCloseKey(hudkey);
4474 return rc;
4477 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4479 WCHAR *filename, *ptr, *folder, *ret;
4480 const WCHAR *dirprop;
4482 filename = msi_dup_record_field( row, 2 );
4483 if (filename && (ptr = strchrW( filename, '|' )))
4484 ptr++;
4485 else
4486 ptr = filename;
4488 dirprop = MSI_RecordGetString( row, 3 );
4489 if (dirprop)
4491 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4492 if (!folder) folder = msi_dup_property( package->db, dirprop );
4494 else
4495 folder = msi_dup_property( package->db, szWindowsFolder );
4497 if (!folder)
4499 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4500 msi_free( filename );
4501 return NULL;
4504 ret = msi_build_directory_name( 2, folder, ptr );
4506 msi_free( filename );
4507 msi_free( folder );
4508 return ret;
4511 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4513 MSIPACKAGE *package = param;
4514 LPCWSTR component, section, key, value, identifier;
4515 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4516 MSIRECORD * uirow;
4517 INT action;
4518 MSICOMPONENT *comp;
4520 component = MSI_RecordGetString(row, 8);
4521 comp = msi_get_loaded_component(package,component);
4522 if (!comp)
4523 return ERROR_SUCCESS;
4525 comp->Action = msi_get_component_action( package, comp );
4526 if (comp->Action != INSTALLSTATE_LOCAL)
4528 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4529 return ERROR_SUCCESS;
4532 identifier = MSI_RecordGetString(row,1);
4533 section = MSI_RecordGetString(row,4);
4534 key = MSI_RecordGetString(row,5);
4535 value = MSI_RecordGetString(row,6);
4536 action = MSI_RecordGetInteger(row,7);
4538 deformat_string(package,section,&deformated_section);
4539 deformat_string(package,key,&deformated_key);
4540 deformat_string(package,value,&deformated_value);
4542 fullname = get_ini_file_name(package, row);
4544 if (action == 0)
4546 TRACE("Adding value %s to section %s in %s\n",
4547 debugstr_w(deformated_key), debugstr_w(deformated_section),
4548 debugstr_w(fullname));
4549 WritePrivateProfileStringW(deformated_section, deformated_key,
4550 deformated_value, fullname);
4552 else if (action == 1)
4554 WCHAR returned[10];
4555 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4556 returned, 10, fullname);
4557 if (returned[0] == 0)
4559 TRACE("Adding value %s to section %s in %s\n",
4560 debugstr_w(deformated_key), debugstr_w(deformated_section),
4561 debugstr_w(fullname));
4563 WritePrivateProfileStringW(deformated_section, deformated_key,
4564 deformated_value, fullname);
4567 else if (action == 3)
4568 FIXME("Append to existing section not yet implemented\n");
4570 uirow = MSI_CreateRecord(4);
4571 MSI_RecordSetStringW(uirow,1,identifier);
4572 MSI_RecordSetStringW(uirow,2,deformated_section);
4573 MSI_RecordSetStringW(uirow,3,deformated_key);
4574 MSI_RecordSetStringW(uirow,4,deformated_value);
4575 msi_ui_actiondata( package, szWriteIniValues, uirow );
4576 msiobj_release( &uirow->hdr );
4578 msi_free(fullname);
4579 msi_free(deformated_key);
4580 msi_free(deformated_value);
4581 msi_free(deformated_section);
4582 return ERROR_SUCCESS;
4585 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4587 static const WCHAR query[] = {
4588 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4589 '`','I','n','i','F','i','l','e','`',0};
4590 MSIQUERY *view;
4591 UINT rc;
4593 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4594 if (rc != ERROR_SUCCESS)
4595 return ERROR_SUCCESS;
4597 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4598 msiobj_release(&view->hdr);
4599 return rc;
4602 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4604 MSIPACKAGE *package = param;
4605 LPCWSTR component, section, key, value, identifier;
4606 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4607 MSICOMPONENT *comp;
4608 MSIRECORD *uirow;
4609 INT action;
4611 component = MSI_RecordGetString( row, 8 );
4612 comp = msi_get_loaded_component( package, component );
4613 if (!comp)
4614 return ERROR_SUCCESS;
4616 comp->Action = msi_get_component_action( package, comp );
4617 if (comp->Action != INSTALLSTATE_ABSENT)
4619 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4620 return ERROR_SUCCESS;
4623 identifier = MSI_RecordGetString( row, 1 );
4624 section = MSI_RecordGetString( row, 4 );
4625 key = MSI_RecordGetString( row, 5 );
4626 value = MSI_RecordGetString( row, 6 );
4627 action = MSI_RecordGetInteger( row, 7 );
4629 deformat_string( package, section, &deformated_section );
4630 deformat_string( package, key, &deformated_key );
4631 deformat_string( package, value, &deformated_value );
4633 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4635 filename = get_ini_file_name( package, row );
4637 TRACE("Removing key %s from section %s in %s\n",
4638 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4640 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4642 WARN("Unable to remove key %u\n", GetLastError());
4644 msi_free( filename );
4646 else
4647 FIXME("Unsupported action %d\n", action);
4650 uirow = MSI_CreateRecord( 4 );
4651 MSI_RecordSetStringW( uirow, 1, identifier );
4652 MSI_RecordSetStringW( uirow, 2, deformated_section );
4653 MSI_RecordSetStringW( uirow, 3, deformated_key );
4654 MSI_RecordSetStringW( uirow, 4, deformated_value );
4655 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4656 msiobj_release( &uirow->hdr );
4658 msi_free( deformated_key );
4659 msi_free( deformated_value );
4660 msi_free( deformated_section );
4661 return ERROR_SUCCESS;
4664 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4666 MSIPACKAGE *package = param;
4667 LPCWSTR component, section, key, value, identifier;
4668 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4669 MSICOMPONENT *comp;
4670 MSIRECORD *uirow;
4671 INT action;
4673 component = MSI_RecordGetString( row, 8 );
4674 comp = msi_get_loaded_component( package, component );
4675 if (!comp)
4676 return ERROR_SUCCESS;
4678 comp->Action = msi_get_component_action( package, comp );
4679 if (comp->Action != INSTALLSTATE_LOCAL)
4681 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4682 return ERROR_SUCCESS;
4685 identifier = MSI_RecordGetString( row, 1 );
4686 section = MSI_RecordGetString( row, 4 );
4687 key = MSI_RecordGetString( row, 5 );
4688 value = MSI_RecordGetString( row, 6 );
4689 action = MSI_RecordGetInteger( row, 7 );
4691 deformat_string( package, section, &deformated_section );
4692 deformat_string( package, key, &deformated_key );
4693 deformat_string( package, value, &deformated_value );
4695 if (action == msidbIniFileActionRemoveLine)
4697 filename = get_ini_file_name( package, row );
4699 TRACE("Removing key %s from section %s in %s\n",
4700 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4702 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4704 WARN("Unable to remove key %u\n", GetLastError());
4706 msi_free( filename );
4708 else
4709 FIXME("Unsupported action %d\n", action);
4711 uirow = MSI_CreateRecord( 4 );
4712 MSI_RecordSetStringW( uirow, 1, identifier );
4713 MSI_RecordSetStringW( uirow, 2, deformated_section );
4714 MSI_RecordSetStringW( uirow, 3, deformated_key );
4715 MSI_RecordSetStringW( uirow, 4, deformated_value );
4716 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4717 msiobj_release( &uirow->hdr );
4719 msi_free( deformated_key );
4720 msi_free( deformated_value );
4721 msi_free( deformated_section );
4722 return ERROR_SUCCESS;
4725 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4727 static const WCHAR query[] = {
4728 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4729 '`','I','n','i','F','i','l','e','`',0};
4730 static const WCHAR remove_query[] = {
4731 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4732 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4733 MSIQUERY *view;
4734 UINT rc;
4736 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4737 if (rc == ERROR_SUCCESS)
4739 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4740 msiobj_release( &view->hdr );
4741 if (rc != ERROR_SUCCESS)
4742 return rc;
4744 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4745 if (rc == ERROR_SUCCESS)
4747 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4748 msiobj_release( &view->hdr );
4749 if (rc != ERROR_SUCCESS)
4750 return rc;
4752 return ERROR_SUCCESS;
4755 static void register_dll( const WCHAR *dll, BOOL unregister )
4757 static const WCHAR regW[] =
4758 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4759 static const WCHAR unregW[] =
4760 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4761 PROCESS_INFORMATION pi;
4762 STARTUPINFOW si;
4763 WCHAR *cmd;
4765 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4767 if (unregister) sprintfW( cmd, unregW, dll );
4768 else sprintfW( cmd, regW, dll );
4770 memset( &si, 0, sizeof(STARTUPINFOW) );
4771 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4773 CloseHandle( pi.hThread );
4774 msi_dialog_check_messages( pi.hProcess );
4775 CloseHandle( pi.hProcess );
4777 msi_free( cmd );
4780 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4782 MSIPACKAGE *package = param;
4783 LPCWSTR filename;
4784 MSIFILE *file;
4785 MSIRECORD *uirow;
4787 filename = MSI_RecordGetString( row, 1 );
4788 file = msi_get_loaded_file( package, filename );
4789 if (!file)
4791 WARN("unable to find file %s\n", debugstr_w(filename));
4792 return ERROR_SUCCESS;
4794 file->Component->Action = msi_get_component_action( package, file->Component );
4795 if (file->Component->Action != INSTALLSTATE_LOCAL)
4797 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4798 return ERROR_SUCCESS;
4801 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4802 register_dll( file->TargetPath, FALSE );
4804 uirow = MSI_CreateRecord( 2 );
4805 MSI_RecordSetStringW( uirow, 1, file->File );
4806 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4807 msi_ui_actiondata( package, szSelfRegModules, uirow );
4808 msiobj_release( &uirow->hdr );
4810 return ERROR_SUCCESS;
4813 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4815 static const WCHAR query[] = {
4816 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4817 '`','S','e','l','f','R','e','g','`',0};
4818 MSIQUERY *view;
4819 UINT rc;
4821 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4822 if (rc != ERROR_SUCCESS)
4823 return ERROR_SUCCESS;
4825 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4826 msiobj_release(&view->hdr);
4827 return rc;
4830 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4832 MSIPACKAGE *package = param;
4833 LPCWSTR filename;
4834 MSIFILE *file;
4835 MSIRECORD *uirow;
4837 filename = MSI_RecordGetString( row, 1 );
4838 file = msi_get_loaded_file( package, filename );
4839 if (!file)
4841 WARN("unable to find file %s\n", debugstr_w(filename));
4842 return ERROR_SUCCESS;
4844 file->Component->Action = msi_get_component_action( package, file->Component );
4845 if (file->Component->Action != INSTALLSTATE_ABSENT)
4847 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4848 return ERROR_SUCCESS;
4851 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4852 register_dll( file->TargetPath, TRUE );
4854 uirow = MSI_CreateRecord( 2 );
4855 MSI_RecordSetStringW( uirow, 1, file->File );
4856 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4857 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4858 msiobj_release( &uirow->hdr );
4860 return ERROR_SUCCESS;
4863 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4865 static const WCHAR query[] = {
4866 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4867 '`','S','e','l','f','R','e','g','`',0};
4868 MSIQUERY *view;
4869 UINT rc;
4871 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4872 if (rc != ERROR_SUCCESS)
4873 return ERROR_SUCCESS;
4875 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4876 msiobj_release( &view->hdr );
4877 return rc;
4880 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4882 MSIFEATURE *feature;
4883 UINT rc;
4884 HKEY hkey = NULL, userdata = NULL;
4886 if (!msi_check_publish(package))
4887 return ERROR_SUCCESS;
4889 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4890 &hkey, TRUE);
4891 if (rc != ERROR_SUCCESS)
4892 goto end;
4894 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4895 &userdata, TRUE);
4896 if (rc != ERROR_SUCCESS)
4897 goto end;
4899 /* here the guids are base 85 encoded */
4900 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4902 ComponentList *cl;
4903 LPWSTR data = NULL;
4904 GUID clsid;
4905 INT size;
4906 BOOL absent = FALSE;
4907 MSIRECORD *uirow;
4909 if (feature->Level <= 0) continue;
4911 if (feature->Action != INSTALLSTATE_LOCAL &&
4912 feature->Action != INSTALLSTATE_SOURCE &&
4913 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4915 size = 1;
4916 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4918 size += 21;
4920 if (feature->Feature_Parent)
4921 size += strlenW( feature->Feature_Parent )+2;
4923 data = msi_alloc(size * sizeof(WCHAR));
4925 data[0] = 0;
4926 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4928 MSICOMPONENT* component = cl->component;
4929 WCHAR buf[21];
4931 buf[0] = 0;
4932 if (component->ComponentId)
4934 TRACE("From %s\n",debugstr_w(component->ComponentId));
4935 CLSIDFromString(component->ComponentId, &clsid);
4936 encode_base85_guid(&clsid,buf);
4937 TRACE("to %s\n",debugstr_w(buf));
4938 strcatW(data,buf);
4942 if (feature->Feature_Parent)
4944 static const WCHAR sep[] = {'\2',0};
4945 strcatW(data,sep);
4946 strcatW(data,feature->Feature_Parent);
4949 msi_reg_set_val_str( userdata, feature->Feature, data );
4950 msi_free(data);
4952 size = 0;
4953 if (feature->Feature_Parent)
4954 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4955 if (!absent)
4957 size += sizeof(WCHAR);
4958 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4959 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4961 else
4963 size += 2*sizeof(WCHAR);
4964 data = msi_alloc(size);
4965 data[0] = 0x6;
4966 data[1] = 0;
4967 if (feature->Feature_Parent)
4968 strcpyW( &data[1], feature->Feature_Parent );
4969 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4970 (LPBYTE)data,size);
4971 msi_free(data);
4974 /* the UI chunk */
4975 uirow = MSI_CreateRecord( 1 );
4976 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4977 msi_ui_actiondata( package, szPublishFeatures, uirow );
4978 msiobj_release( &uirow->hdr );
4979 /* FIXME: call msi_ui_progress? */
4982 end:
4983 RegCloseKey(hkey);
4984 RegCloseKey(userdata);
4985 return rc;
4988 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4990 UINT r;
4991 HKEY hkey;
4992 MSIRECORD *uirow;
4994 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4996 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4997 &hkey, FALSE);
4998 if (r == ERROR_SUCCESS)
5000 RegDeleteValueW(hkey, feature->Feature);
5001 RegCloseKey(hkey);
5004 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
5005 &hkey, FALSE);
5006 if (r == ERROR_SUCCESS)
5008 RegDeleteValueW(hkey, feature->Feature);
5009 RegCloseKey(hkey);
5012 uirow = MSI_CreateRecord( 1 );
5013 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5014 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
5015 msiobj_release( &uirow->hdr );
5017 return ERROR_SUCCESS;
5020 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5022 MSIFEATURE *feature;
5024 if (!msi_check_unpublish(package))
5025 return ERROR_SUCCESS;
5027 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5029 msi_unpublish_feature(package, feature);
5032 return ERROR_SUCCESS;
5035 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5037 SYSTEMTIME systime;
5038 DWORD size, langid;
5039 WCHAR date[9], *val, *buffer;
5040 const WCHAR *prop, *key;
5042 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5043 static const WCHAR modpath_fmt[] =
5044 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5045 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5046 static const WCHAR szModifyPath[] =
5047 {'M','o','d','i','f','y','P','a','t','h',0};
5048 static const WCHAR szUninstallString[] =
5049 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5050 static const WCHAR szEstimatedSize[] =
5051 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5052 static const WCHAR szDisplayVersion[] =
5053 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5054 static const WCHAR szInstallSource[] =
5055 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5056 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5057 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5058 static const WCHAR szAuthorizedCDFPrefix[] =
5059 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5060 static const WCHAR szARPCONTACT[] =
5061 {'A','R','P','C','O','N','T','A','C','T',0};
5062 static const WCHAR szContact[] =
5063 {'C','o','n','t','a','c','t',0};
5064 static const WCHAR szARPCOMMENTS[] =
5065 {'A','R','P','C','O','M','M','E','N','T','S',0};
5066 static const WCHAR szComments[] =
5067 {'C','o','m','m','e','n','t','s',0};
5068 static const WCHAR szProductName[] =
5069 {'P','r','o','d','u','c','t','N','a','m','e',0};
5070 static const WCHAR szDisplayName[] =
5071 {'D','i','s','p','l','a','y','N','a','m','e',0};
5072 static const WCHAR szARPHELPLINK[] =
5073 {'A','R','P','H','E','L','P','L','I','N','K',0};
5074 static const WCHAR szHelpLink[] =
5075 {'H','e','l','p','L','i','n','k',0};
5076 static const WCHAR szARPHELPTELEPHONE[] =
5077 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5078 static const WCHAR szHelpTelephone[] =
5079 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5080 static const WCHAR szARPINSTALLLOCATION[] =
5081 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5082 static const WCHAR szManufacturer[] =
5083 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5084 static const WCHAR szPublisher[] =
5085 {'P','u','b','l','i','s','h','e','r',0};
5086 static const WCHAR szARPREADME[] =
5087 {'A','R','P','R','E','A','D','M','E',0};
5088 static const WCHAR szReadme[] =
5089 {'R','e','a','d','M','e',0};
5090 static const WCHAR szARPSIZE[] =
5091 {'A','R','P','S','I','Z','E',0};
5092 static const WCHAR szSize[] =
5093 {'S','i','z','e',0};
5094 static const WCHAR szARPURLINFOABOUT[] =
5095 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5096 static const WCHAR szURLInfoAbout[] =
5097 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5098 static const WCHAR szARPURLUPDATEINFO[] =
5099 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5100 static const WCHAR szURLUpdateInfo[] =
5101 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5102 static const WCHAR szARPSYSTEMCOMPONENT[] =
5103 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5104 static const WCHAR szSystemComponent[] =
5105 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5107 static const WCHAR *propval[] = {
5108 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5109 szARPCONTACT, szContact,
5110 szARPCOMMENTS, szComments,
5111 szProductName, szDisplayName,
5112 szARPHELPLINK, szHelpLink,
5113 szARPHELPTELEPHONE, szHelpTelephone,
5114 szARPINSTALLLOCATION, szInstallLocation,
5115 szSourceDir, szInstallSource,
5116 szManufacturer, szPublisher,
5117 szARPREADME, szReadme,
5118 szARPSIZE, szSize,
5119 szARPURLINFOABOUT, szURLInfoAbout,
5120 szARPURLUPDATEINFO, szURLUpdateInfo,
5121 NULL
5123 const WCHAR **p = propval;
5125 while (*p)
5127 prop = *p++;
5128 key = *p++;
5129 val = msi_dup_property(package->db, prop);
5130 msi_reg_set_val_str(hkey, key, val);
5131 msi_free(val);
5134 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5135 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5137 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5139 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5140 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5141 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5142 msi_free(buffer);
5144 /* FIXME: Write real Estimated Size when we have it */
5145 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5147 GetLocalTime(&systime);
5148 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5149 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5151 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5152 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5154 buffer = msi_dup_property(package->db, szProductVersion);
5155 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5156 if (buffer)
5158 DWORD verdword = msi_version_str_to_dword(buffer);
5160 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5161 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5162 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5163 msi_free(buffer);
5166 return ERROR_SUCCESS;
5169 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5171 WCHAR squashed_pc[SQUISH_GUID_SIZE];
5172 MSIRECORD *uirow;
5173 LPWSTR upgrade_code;
5174 HKEY hkey, props, upgrade_key;
5175 UINT rc;
5177 /* FIXME: also need to publish if the product is in advertise mode */
5178 if (!msi_check_publish(package))
5179 return ERROR_SUCCESS;
5181 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5182 if (rc != ERROR_SUCCESS)
5183 return rc;
5185 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5186 if (rc != ERROR_SUCCESS)
5187 goto done;
5189 rc = msi_publish_install_properties(package, hkey);
5190 if (rc != ERROR_SUCCESS)
5191 goto done;
5193 rc = msi_publish_install_properties(package, props);
5194 if (rc != ERROR_SUCCESS)
5195 goto done;
5197 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5198 if (upgrade_code)
5200 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5201 if (rc == ERROR_SUCCESS)
5203 squash_guid( package->ProductCode, squashed_pc );
5204 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5205 RegCloseKey( upgrade_key );
5207 msi_free( upgrade_code );
5209 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5210 package->delete_on_close = FALSE;
5212 done:
5213 uirow = MSI_CreateRecord( 1 );
5214 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5215 msi_ui_actiondata( package, szRegisterProduct, uirow );
5216 msiobj_release( &uirow->hdr );
5218 RegCloseKey(hkey);
5219 return ERROR_SUCCESS;
5222 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5224 return execute_script(package, SCRIPT_INSTALL);
5227 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5229 MSIPACKAGE *package = param;
5230 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5231 WCHAR *p, *icon_path;
5233 if (!icon) return ERROR_SUCCESS;
5234 if ((icon_path = msi_build_icon_path( package, icon )))
5236 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5237 DeleteFileW( icon_path );
5238 if ((p = strrchrW( icon_path, '\\' )))
5240 *p = 0;
5241 RemoveDirectoryW( icon_path );
5243 msi_free( icon_path );
5245 return ERROR_SUCCESS;
5248 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5250 static const WCHAR query[]= {
5251 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5252 MSIQUERY *view;
5253 UINT r;
5255 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5256 if (r == ERROR_SUCCESS)
5258 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5259 msiobj_release( &view->hdr );
5260 if (r != ERROR_SUCCESS)
5261 return r;
5263 return ERROR_SUCCESS;
5266 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
5268 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
5269 WCHAR *upgrade, **features;
5270 BOOL full_uninstall = TRUE;
5271 MSIFEATURE *feature;
5272 MSIPATCHINFO *patch;
5273 UINT i;
5275 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5277 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
5279 features = msi_split_string( remove, ',' );
5280 for (i = 0; features && features[i]; i++)
5282 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
5284 msi_free(features);
5286 if (!full_uninstall)
5287 return ERROR_SUCCESS;
5289 MSIREG_DeleteProductKey(package->ProductCode);
5290 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5291 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5293 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5294 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5295 MSIREG_DeleteUserProductKey(package->ProductCode);
5296 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5298 upgrade = msi_dup_property(package->db, szUpgradeCode);
5299 if (upgrade)
5301 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5302 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5303 msi_free(upgrade);
5306 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5308 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5309 if (!strcmpW( package->ProductCode, patch->products ))
5311 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5312 patch->delete_on_close = TRUE;
5314 /* FIXME: remove local patch package if this is the last product */
5316 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5317 package->delete_on_close = TRUE;
5319 msi_unpublish_icons( package );
5320 return ERROR_SUCCESS;
5323 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5325 UINT rc;
5326 WCHAR *remove;
5328 /* first do the same as an InstallExecute */
5329 rc = execute_script(package, SCRIPT_INSTALL);
5330 if (rc != ERROR_SUCCESS)
5331 return rc;
5333 /* then handle commit actions */
5334 rc = execute_script(package, SCRIPT_COMMIT);
5335 if (rc != ERROR_SUCCESS)
5336 return rc;
5338 remove = msi_dup_property(package->db, szRemove);
5339 rc = msi_unpublish_product(package, remove);
5340 msi_free(remove);
5341 return rc;
5344 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5346 static const WCHAR RunOnce[] = {
5347 'S','o','f','t','w','a','r','e','\\',
5348 'M','i','c','r','o','s','o','f','t','\\',
5349 'W','i','n','d','o','w','s','\\',
5350 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5351 'R','u','n','O','n','c','e',0};
5352 static const WCHAR InstallRunOnce[] = {
5353 'S','o','f','t','w','a','r','e','\\',
5354 'M','i','c','r','o','s','o','f','t','\\',
5355 'W','i','n','d','o','w','s','\\',
5356 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5357 'I','n','s','t','a','l','l','e','r','\\',
5358 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5360 static const WCHAR msiexec_fmt[] = {
5361 '%','s',
5362 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5363 '\"','%','s','\"',0};
5364 static const WCHAR install_fmt[] = {
5365 '/','I',' ','\"','%','s','\"',' ',
5366 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5367 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5368 WCHAR buffer[256], sysdir[MAX_PATH];
5369 HKEY hkey;
5370 WCHAR squished_pc[100];
5372 squash_guid(package->ProductCode,squished_pc);
5374 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5375 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5376 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5377 squished_pc);
5379 msi_reg_set_val_str( hkey, squished_pc, buffer );
5380 RegCloseKey(hkey);
5382 TRACE("Reboot command %s\n",debugstr_w(buffer));
5384 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5385 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5387 msi_reg_set_val_str( hkey, squished_pc, buffer );
5388 RegCloseKey(hkey);
5390 return ERROR_INSTALL_SUSPEND;
5393 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5395 static const WCHAR query[] =
5396 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5397 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5398 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5399 MSIRECORD *rec, *row;
5400 DWORD i, size = 0;
5401 va_list va;
5402 const WCHAR *str;
5403 WCHAR *data;
5405 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5407 rec = MSI_CreateRecord( count + 2 );
5408 str = MSI_RecordGetString( row, 1 );
5409 MSI_RecordSetStringW( rec, 0, str );
5410 msiobj_release( &row->hdr );
5411 MSI_RecordSetInteger( rec, 1, error );
5413 va_start( va, count );
5414 for (i = 0; i < count; i++)
5416 str = va_arg( va, const WCHAR *);
5417 MSI_RecordSetStringW( rec, i + 2, str );
5419 va_end( va );
5421 MSI_FormatRecordW( package, rec, NULL, &size );
5422 size++;
5423 data = msi_alloc( size * sizeof(WCHAR) );
5424 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5425 else data[0] = 0;
5426 msiobj_release( &rec->hdr );
5427 return data;
5430 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5432 DWORD attrib;
5433 UINT rc;
5436 * We are currently doing what should be done here in the top level Install
5437 * however for Administrative and uninstalls this step will be needed
5439 if (!package->PackagePath)
5440 return ERROR_SUCCESS;
5442 msi_set_sourcedir_props(package, TRUE);
5444 attrib = GetFileAttributesW(package->db->path);
5445 if (attrib == INVALID_FILE_ATTRIBUTES)
5447 LPWSTR prompt, msg;
5448 DWORD size = 0;
5450 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5451 package->Context, MSICODE_PRODUCT,
5452 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5453 if (rc == ERROR_MORE_DATA)
5455 prompt = msi_alloc(size * sizeof(WCHAR));
5456 MsiSourceListGetInfoW(package->ProductCode, NULL,
5457 package->Context, MSICODE_PRODUCT,
5458 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5460 else
5461 prompt = strdupW(package->db->path);
5463 msg = msi_build_error_string(package, 1302, 1, prompt);
5464 msi_free(prompt);
5465 while(attrib == INVALID_FILE_ATTRIBUTES)
5467 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5468 if (rc == IDCANCEL)
5470 msi_free(msg);
5471 return ERROR_INSTALL_USEREXIT;
5473 attrib = GetFileAttributesW(package->db->path);
5475 msi_free(msg);
5476 rc = ERROR_SUCCESS;
5478 else
5479 return ERROR_SUCCESS;
5481 return rc;
5484 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5486 HKEY hkey = 0;
5487 LPWSTR buffer, productid = NULL;
5488 UINT i, rc = ERROR_SUCCESS;
5489 MSIRECORD *uirow;
5491 static const WCHAR szPropKeys[][80] =
5493 {'P','r','o','d','u','c','t','I','D',0},
5494 {'U','S','E','R','N','A','M','E',0},
5495 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5496 {0},
5499 static const WCHAR szRegKeys[][80] =
5501 {'P','r','o','d','u','c','t','I','D',0},
5502 {'R','e','g','O','w','n','e','r',0},
5503 {'R','e','g','C','o','m','p','a','n','y',0},
5504 {0},
5507 if (msi_check_unpublish(package))
5509 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5510 goto end;
5513 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5514 if (!productid)
5515 goto end;
5517 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5518 NULL, &hkey, TRUE);
5519 if (rc != ERROR_SUCCESS)
5520 goto end;
5522 for( i = 0; szPropKeys[i][0]; i++ )
5524 buffer = msi_dup_property( package->db, szPropKeys[i] );
5525 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5526 msi_free( buffer );
5529 end:
5530 uirow = MSI_CreateRecord( 1 );
5531 MSI_RecordSetStringW( uirow, 1, productid );
5532 msi_ui_actiondata( package, szRegisterUser, uirow );
5533 msiobj_release( &uirow->hdr );
5535 msi_free(productid);
5536 RegCloseKey(hkey);
5537 return rc;
5541 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5543 UINT rc;
5545 package->script->InWhatSequence |= SEQUENCE_EXEC;
5546 rc = ACTION_ProcessExecSequence(package,FALSE);
5547 return rc;
5550 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5552 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5553 WCHAR productid_85[21], component_85[21], *ret;
5554 GUID clsid;
5555 DWORD sz;
5557 /* > is used if there is a component GUID and < if not. */
5559 productid_85[0] = 0;
5560 component_85[0] = 0;
5561 CLSIDFromString( package->ProductCode, &clsid );
5563 encode_base85_guid( &clsid, productid_85 );
5564 if (component)
5566 CLSIDFromString( component->ComponentId, &clsid );
5567 encode_base85_guid( &clsid, component_85 );
5570 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5571 debugstr_w(component_85));
5573 sz = 20 + strlenW( feature ) + 20 + 3;
5574 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5575 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5576 return ret;
5579 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5581 MSIPACKAGE *package = param;
5582 LPCWSTR compgroupid, component, feature, qualifier, text;
5583 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5584 HKEY hkey = NULL;
5585 UINT rc;
5586 MSICOMPONENT *comp;
5587 MSIFEATURE *feat;
5588 DWORD sz;
5589 MSIRECORD *uirow;
5590 int len;
5592 feature = MSI_RecordGetString(rec, 5);
5593 feat = msi_get_loaded_feature(package, feature);
5594 if (!feat)
5595 return ERROR_SUCCESS;
5597 feat->Action = msi_get_feature_action( package, feat );
5598 if (feat->Action != INSTALLSTATE_LOCAL &&
5599 feat->Action != INSTALLSTATE_SOURCE &&
5600 feat->Action != INSTALLSTATE_ADVERTISED)
5602 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5603 return ERROR_SUCCESS;
5606 component = MSI_RecordGetString(rec, 3);
5607 comp = msi_get_loaded_component(package, component);
5608 if (!comp)
5609 return ERROR_SUCCESS;
5611 compgroupid = MSI_RecordGetString(rec,1);
5612 qualifier = MSI_RecordGetString(rec,2);
5614 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5615 if (rc != ERROR_SUCCESS)
5616 goto end;
5618 advertise = msi_create_component_advertise_string( package, comp, feature );
5619 text = MSI_RecordGetString( rec, 4 );
5620 if (text)
5622 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5623 strcpyW( p, advertise );
5624 strcatW( p, text );
5625 msi_free( advertise );
5626 advertise = p;
5628 existing = msi_reg_get_val_str( hkey, qualifier );
5630 sz = strlenW( advertise ) + 1;
5631 if (existing)
5633 for (p = existing; *p; p += len)
5635 len = strlenW( p ) + 1;
5636 if (strcmpW( advertise, p )) sz += len;
5639 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5641 rc = ERROR_OUTOFMEMORY;
5642 goto end;
5644 q = output;
5645 if (existing)
5647 for (p = existing; *p; p += len)
5649 len = strlenW( p ) + 1;
5650 if (strcmpW( advertise, p ))
5652 memcpy( q, p, len * sizeof(WCHAR) );
5653 q += len;
5657 strcpyW( q, advertise );
5658 q[strlenW( q ) + 1] = 0;
5660 msi_reg_set_val_multi_str( hkey, qualifier, output );
5662 end:
5663 RegCloseKey(hkey);
5664 msi_free( output );
5665 msi_free( advertise );
5666 msi_free( existing );
5668 /* the UI chunk */
5669 uirow = MSI_CreateRecord( 2 );
5670 MSI_RecordSetStringW( uirow, 1, compgroupid );
5671 MSI_RecordSetStringW( uirow, 2, qualifier);
5672 msi_ui_actiondata( package, szPublishComponents, uirow );
5673 msiobj_release( &uirow->hdr );
5674 /* FIXME: call ui_progress? */
5676 return rc;
5680 * At present I am ignorning the advertised components part of this and only
5681 * focusing on the qualified component sets
5683 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5685 static const WCHAR query[] = {
5686 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5687 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5688 MSIQUERY *view;
5689 UINT rc;
5691 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5692 if (rc != ERROR_SUCCESS)
5693 return ERROR_SUCCESS;
5695 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5696 msiobj_release(&view->hdr);
5697 return rc;
5700 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5702 static const WCHAR szInstallerComponents[] = {
5703 'S','o','f','t','w','a','r','e','\\',
5704 'M','i','c','r','o','s','o','f','t','\\',
5705 'I','n','s','t','a','l','l','e','r','\\',
5706 'C','o','m','p','o','n','e','n','t','s','\\',0};
5708 MSIPACKAGE *package = param;
5709 LPCWSTR compgroupid, component, feature, qualifier;
5710 MSICOMPONENT *comp;
5711 MSIFEATURE *feat;
5712 MSIRECORD *uirow;
5713 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5714 LONG res;
5716 feature = MSI_RecordGetString( rec, 5 );
5717 feat = msi_get_loaded_feature( package, feature );
5718 if (!feat)
5719 return ERROR_SUCCESS;
5721 feat->Action = msi_get_feature_action( package, feat );
5722 if (feat->Action != INSTALLSTATE_ABSENT)
5724 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5725 return ERROR_SUCCESS;
5728 component = MSI_RecordGetString( rec, 3 );
5729 comp = msi_get_loaded_component( package, component );
5730 if (!comp)
5731 return ERROR_SUCCESS;
5733 compgroupid = MSI_RecordGetString( rec, 1 );
5734 qualifier = MSI_RecordGetString( rec, 2 );
5736 squash_guid( compgroupid, squashed );
5737 strcpyW( keypath, szInstallerComponents );
5738 strcatW( keypath, squashed );
5740 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5741 if (res != ERROR_SUCCESS)
5743 WARN("Unable to delete component key %d\n", res);
5746 uirow = MSI_CreateRecord( 2 );
5747 MSI_RecordSetStringW( uirow, 1, compgroupid );
5748 MSI_RecordSetStringW( uirow, 2, qualifier );
5749 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5750 msiobj_release( &uirow->hdr );
5752 return ERROR_SUCCESS;
5755 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5757 static const WCHAR query[] = {
5758 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5759 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5760 MSIQUERY *view;
5761 UINT rc;
5763 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5764 if (rc != ERROR_SUCCESS)
5765 return ERROR_SUCCESS;
5767 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5768 msiobj_release( &view->hdr );
5769 return rc;
5772 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5774 static const WCHAR query[] =
5775 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5776 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5777 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5778 MSIPACKAGE *package = param;
5779 MSICOMPONENT *component;
5780 MSIRECORD *row;
5781 MSIFILE *file;
5782 SC_HANDLE hscm = NULL, service = NULL;
5783 LPCWSTR comp, key;
5784 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5785 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5786 DWORD serv_type, start_type, err_control;
5787 SERVICE_DESCRIPTIONW sd = {NULL};
5788 UINT ret = ERROR_SUCCESS;
5790 comp = MSI_RecordGetString( rec, 12 );
5791 component = msi_get_loaded_component( package, comp );
5792 if (!component)
5794 WARN("service component not found\n");
5795 goto done;
5797 component->Action = msi_get_component_action( package, component );
5798 if (component->Action != INSTALLSTATE_LOCAL)
5800 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5801 goto done;
5803 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5804 if (!hscm)
5806 ERR("Failed to open the SC Manager!\n");
5807 goto done;
5810 start_type = MSI_RecordGetInteger(rec, 5);
5811 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5812 goto done;
5814 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5815 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5816 serv_type = MSI_RecordGetInteger(rec, 4);
5817 err_control = MSI_RecordGetInteger(rec, 6);
5818 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5819 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5820 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5821 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5822 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5823 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5825 /* fetch the service path */
5826 row = MSI_QueryGetRecord(package->db, query, comp);
5827 if (!row)
5829 ERR("Query failed\n");
5830 goto done;
5832 if (!(key = MSI_RecordGetString(row, 6)))
5834 msiobj_release(&row->hdr);
5835 goto done;
5837 file = msi_get_loaded_file(package, key);
5838 msiobj_release(&row->hdr);
5839 if (!file)
5841 ERR("Failed to load the service file\n");
5842 goto done;
5845 if (!args || !args[0]) image_path = file->TargetPath;
5846 else
5848 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5849 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5851 ret = ERROR_OUTOFMEMORY;
5852 goto done;
5855 strcpyW(image_path, file->TargetPath);
5856 strcatW(image_path, szSpace);
5857 strcatW(image_path, args);
5859 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5860 start_type, err_control, image_path, load_order,
5861 NULL, depends, serv_name, pass);
5863 if (!service)
5865 if (GetLastError() != ERROR_SERVICE_EXISTS)
5866 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5868 else if (sd.lpDescription)
5870 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5871 WARN("failed to set service description %u\n", GetLastError());
5874 if (image_path != file->TargetPath) msi_free(image_path);
5875 done:
5876 CloseServiceHandle(service);
5877 CloseServiceHandle(hscm);
5878 msi_free(name);
5879 msi_free(disp);
5880 msi_free(sd.lpDescription);
5881 msi_free(load_order);
5882 msi_free(serv_name);
5883 msi_free(pass);
5884 msi_free(depends);
5885 msi_free(args);
5887 return ret;
5890 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5892 static const WCHAR query[] = {
5893 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5894 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5895 MSIQUERY *view;
5896 UINT rc;
5898 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5899 if (rc != ERROR_SUCCESS)
5900 return ERROR_SUCCESS;
5902 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5903 msiobj_release(&view->hdr);
5904 return rc;
5907 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5908 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5910 LPCWSTR *vector, *temp_vector;
5911 LPWSTR p, q;
5912 DWORD sep_len;
5914 static const WCHAR separator[] = {'[','~',']',0};
5916 *numargs = 0;
5917 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5919 if (!args)
5920 return NULL;
5922 vector = msi_alloc(sizeof(LPWSTR));
5923 if (!vector)
5924 return NULL;
5926 p = args;
5929 (*numargs)++;
5930 vector[*numargs - 1] = p;
5932 if ((q = strstrW(p, separator)))
5934 *q = '\0';
5936 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5937 if (!temp_vector)
5939 msi_free(vector);
5940 return NULL;
5942 vector = temp_vector;
5944 p = q + sep_len;
5946 } while (q);
5948 return vector;
5951 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5953 MSIPACKAGE *package = param;
5954 MSICOMPONENT *comp;
5955 MSIRECORD *uirow;
5956 SC_HANDLE scm = NULL, service = NULL;
5957 LPCWSTR component, *vector = NULL;
5958 LPWSTR name, args, display_name = NULL;
5959 DWORD event, numargs, len, wait, dummy;
5960 UINT r = ERROR_FUNCTION_FAILED;
5961 SERVICE_STATUS_PROCESS status;
5962 ULONGLONG start_time;
5964 component = MSI_RecordGetString(rec, 6);
5965 comp = msi_get_loaded_component(package, component);
5966 if (!comp)
5967 return ERROR_SUCCESS;
5969 event = MSI_RecordGetInteger( rec, 3 );
5970 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5972 comp->Action = msi_get_component_action( package, comp );
5973 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
5974 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
5976 TRACE("not starting %s\n", debugstr_w(name));
5977 msi_free( name );
5978 return ERROR_SUCCESS;
5981 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5982 wait = MSI_RecordGetInteger(rec, 5);
5984 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5985 if (!scm)
5987 ERR("Failed to open the service control manager\n");
5988 goto done;
5991 len = 0;
5992 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5993 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5995 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5996 GetServiceDisplayNameW( scm, name, display_name, &len );
5999 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
6000 if (!service)
6002 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6003 goto done;
6006 vector = msi_service_args_to_vector(args, &numargs);
6008 if (!StartServiceW(service, numargs, vector) &&
6009 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6011 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6012 goto done;
6015 r = ERROR_SUCCESS;
6016 if (wait)
6018 /* wait for at most 30 seconds for the service to be up and running */
6019 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6020 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6022 TRACE("failed to query service status (%u)\n", GetLastError());
6023 goto done;
6025 start_time = GetTickCount64();
6026 while (status.dwCurrentState == SERVICE_START_PENDING)
6028 if (GetTickCount64() - start_time > 30000) break;
6029 Sleep(1000);
6030 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6031 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6033 TRACE("failed to query service status (%u)\n", GetLastError());
6034 goto done;
6037 if (status.dwCurrentState != SERVICE_RUNNING)
6039 WARN("service failed to start %u\n", status.dwCurrentState);
6040 r = ERROR_FUNCTION_FAILED;
6044 done:
6045 uirow = MSI_CreateRecord( 2 );
6046 MSI_RecordSetStringW( uirow, 1, display_name );
6047 MSI_RecordSetStringW( uirow, 2, name );
6048 msi_ui_actiondata( package, szStartServices, uirow );
6049 msiobj_release( &uirow->hdr );
6051 CloseServiceHandle(service);
6052 CloseServiceHandle(scm);
6054 msi_free(name);
6055 msi_free(args);
6056 msi_free(vector);
6057 msi_free(display_name);
6058 return r;
6061 static UINT ACTION_StartServices( MSIPACKAGE *package )
6063 static const WCHAR query[] = {
6064 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6065 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6066 MSIQUERY *view;
6067 UINT rc;
6069 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6070 if (rc != ERROR_SUCCESS)
6071 return ERROR_SUCCESS;
6073 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6074 msiobj_release(&view->hdr);
6075 return rc;
6078 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6080 DWORD i, needed, count;
6081 ENUM_SERVICE_STATUSW *dependencies;
6082 SERVICE_STATUS ss;
6083 SC_HANDLE depserv;
6084 BOOL stopped, ret = FALSE;
6086 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6087 0, &needed, &count))
6088 return TRUE;
6090 if (GetLastError() != ERROR_MORE_DATA)
6091 return FALSE;
6093 dependencies = msi_alloc(needed);
6094 if (!dependencies)
6095 return FALSE;
6097 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6098 needed, &needed, &count))
6099 goto done;
6101 for (i = 0; i < count; i++)
6103 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6104 SERVICE_STOP | SERVICE_QUERY_STATUS);
6105 if (!depserv)
6106 goto done;
6108 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6109 CloseServiceHandle(depserv);
6110 if (!stopped)
6111 goto done;
6114 ret = TRUE;
6116 done:
6117 msi_free(dependencies);
6118 return ret;
6121 static UINT stop_service( LPCWSTR name )
6123 SC_HANDLE scm = NULL, service = NULL;
6124 SERVICE_STATUS status;
6125 SERVICE_STATUS_PROCESS ssp;
6126 DWORD needed;
6128 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6129 if (!scm)
6131 WARN("Failed to open the SCM: %d\n", GetLastError());
6132 goto done;
6135 service = OpenServiceW(scm, name,
6136 SERVICE_STOP |
6137 SERVICE_QUERY_STATUS |
6138 SERVICE_ENUMERATE_DEPENDENTS);
6139 if (!service)
6141 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6142 goto done;
6145 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6146 sizeof(SERVICE_STATUS_PROCESS), &needed))
6148 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6149 goto done;
6152 if (ssp.dwCurrentState == SERVICE_STOPPED)
6153 goto done;
6155 stop_service_dependents(scm, service);
6157 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6158 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6160 done:
6161 CloseServiceHandle(service);
6162 CloseServiceHandle(scm);
6164 return ERROR_SUCCESS;
6167 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6169 MSIPACKAGE *package = param;
6170 MSICOMPONENT *comp;
6171 MSIRECORD *uirow;
6172 LPCWSTR component;
6173 WCHAR *name, *display_name = NULL;
6174 DWORD event, len;
6175 SC_HANDLE scm;
6177 component = MSI_RecordGetString( rec, 6 );
6178 comp = msi_get_loaded_component( package, component );
6179 if (!comp)
6180 return ERROR_SUCCESS;
6182 event = MSI_RecordGetInteger( rec, 3 );
6183 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6185 comp->Action = msi_get_component_action( package, comp );
6186 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6187 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6189 TRACE("not stopping %s\n", debugstr_w(name));
6190 msi_free( name );
6191 return ERROR_SUCCESS;
6194 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6195 if (!scm)
6197 ERR("Failed to open the service control manager\n");
6198 goto done;
6201 len = 0;
6202 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6203 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6205 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6206 GetServiceDisplayNameW( scm, name, display_name, &len );
6208 CloseServiceHandle( scm );
6210 stop_service( name );
6212 done:
6213 uirow = MSI_CreateRecord( 2 );
6214 MSI_RecordSetStringW( uirow, 1, display_name );
6215 MSI_RecordSetStringW( uirow, 2, name );
6216 msi_ui_actiondata( package, szStopServices, uirow );
6217 msiobj_release( &uirow->hdr );
6219 msi_free( name );
6220 msi_free( display_name );
6221 return ERROR_SUCCESS;
6224 static UINT ACTION_StopServices( MSIPACKAGE *package )
6226 static const WCHAR query[] = {
6227 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6228 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6229 MSIQUERY *view;
6230 UINT rc;
6232 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6233 if (rc != ERROR_SUCCESS)
6234 return ERROR_SUCCESS;
6236 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6237 msiobj_release(&view->hdr);
6238 return rc;
6241 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6243 MSIPACKAGE *package = param;
6244 MSICOMPONENT *comp;
6245 MSIRECORD *uirow;
6246 LPWSTR name = NULL, display_name = NULL;
6247 DWORD event, len;
6248 SC_HANDLE scm = NULL, service = NULL;
6250 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6251 if (!comp)
6252 return ERROR_SUCCESS;
6254 event = MSI_RecordGetInteger( rec, 3 );
6255 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6257 comp->Action = msi_get_component_action( package, comp );
6258 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6259 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6261 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6262 msi_free( name );
6263 return ERROR_SUCCESS;
6265 stop_service( name );
6267 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6268 if (!scm)
6270 WARN("Failed to open the SCM: %d\n", GetLastError());
6271 goto done;
6274 len = 0;
6275 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6276 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6278 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6279 GetServiceDisplayNameW( scm, name, display_name, &len );
6282 service = OpenServiceW( scm, name, DELETE );
6283 if (!service)
6285 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6286 goto done;
6289 if (!DeleteService( service ))
6290 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6292 done:
6293 uirow = MSI_CreateRecord( 2 );
6294 MSI_RecordSetStringW( uirow, 1, display_name );
6295 MSI_RecordSetStringW( uirow, 2, name );
6296 msi_ui_actiondata( package, szDeleteServices, uirow );
6297 msiobj_release( &uirow->hdr );
6299 CloseServiceHandle( service );
6300 CloseServiceHandle( scm );
6301 msi_free( name );
6302 msi_free( display_name );
6304 return ERROR_SUCCESS;
6307 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6309 static const WCHAR query[] = {
6310 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6311 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6312 MSIQUERY *view;
6313 UINT rc;
6315 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6316 if (rc != ERROR_SUCCESS)
6317 return ERROR_SUCCESS;
6319 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6320 msiobj_release( &view->hdr );
6321 return rc;
6324 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6326 MSIPACKAGE *package = param;
6327 LPWSTR driver, driver_path, ptr;
6328 WCHAR outpath[MAX_PATH];
6329 MSIFILE *driver_file = NULL, *setup_file = NULL;
6330 MSICOMPONENT *comp;
6331 MSIRECORD *uirow;
6332 LPCWSTR desc, file_key, component;
6333 DWORD len, usage;
6334 UINT r = ERROR_SUCCESS;
6336 static const WCHAR driver_fmt[] = {
6337 'D','r','i','v','e','r','=','%','s',0};
6338 static const WCHAR setup_fmt[] = {
6339 'S','e','t','u','p','=','%','s',0};
6340 static const WCHAR usage_fmt[] = {
6341 'F','i','l','e','U','s','a','g','e','=','1',0};
6343 component = MSI_RecordGetString( rec, 2 );
6344 comp = msi_get_loaded_component( package, component );
6345 if (!comp)
6346 return ERROR_SUCCESS;
6348 comp->Action = msi_get_component_action( package, comp );
6349 if (comp->Action != INSTALLSTATE_LOCAL)
6351 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6352 return ERROR_SUCCESS;
6354 desc = MSI_RecordGetString(rec, 3);
6356 file_key = MSI_RecordGetString( rec, 4 );
6357 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6359 file_key = MSI_RecordGetString( rec, 5 );
6360 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6362 if (!driver_file)
6364 ERR("ODBC Driver entry not found!\n");
6365 return ERROR_FUNCTION_FAILED;
6368 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6369 if (setup_file)
6370 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6371 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6373 driver = msi_alloc(len * sizeof(WCHAR));
6374 if (!driver)
6375 return ERROR_OUTOFMEMORY;
6377 ptr = driver;
6378 lstrcpyW(ptr, desc);
6379 ptr += lstrlenW(ptr) + 1;
6381 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6382 ptr += len + 1;
6384 if (setup_file)
6386 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6387 ptr += len + 1;
6390 lstrcpyW(ptr, usage_fmt);
6391 ptr += lstrlenW(ptr) + 1;
6392 *ptr = '\0';
6394 if (!driver_file->TargetPath)
6396 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6397 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6399 driver_path = strdupW(driver_file->TargetPath);
6400 ptr = strrchrW(driver_path, '\\');
6401 if (ptr) *ptr = '\0';
6403 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6404 NULL, ODBC_INSTALL_COMPLETE, &usage))
6406 ERR("Failed to install SQL driver!\n");
6407 r = ERROR_FUNCTION_FAILED;
6410 uirow = MSI_CreateRecord( 5 );
6411 MSI_RecordSetStringW( uirow, 1, desc );
6412 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6413 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6414 msi_ui_actiondata( package, szInstallODBC, uirow );
6415 msiobj_release( &uirow->hdr );
6417 msi_free(driver);
6418 msi_free(driver_path);
6420 return r;
6423 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6425 MSIPACKAGE *package = param;
6426 LPWSTR translator, translator_path, ptr;
6427 WCHAR outpath[MAX_PATH];
6428 MSIFILE *translator_file = NULL, *setup_file = NULL;
6429 MSICOMPONENT *comp;
6430 MSIRECORD *uirow;
6431 LPCWSTR desc, file_key, component;
6432 DWORD len, usage;
6433 UINT r = ERROR_SUCCESS;
6435 static const WCHAR translator_fmt[] = {
6436 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6437 static const WCHAR setup_fmt[] = {
6438 'S','e','t','u','p','=','%','s',0};
6440 component = MSI_RecordGetString( rec, 2 );
6441 comp = msi_get_loaded_component( package, component );
6442 if (!comp)
6443 return ERROR_SUCCESS;
6445 comp->Action = msi_get_component_action( package, comp );
6446 if (comp->Action != INSTALLSTATE_LOCAL)
6448 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6449 return ERROR_SUCCESS;
6451 desc = MSI_RecordGetString(rec, 3);
6453 file_key = MSI_RecordGetString( rec, 4 );
6454 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6456 file_key = MSI_RecordGetString( rec, 5 );
6457 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6459 if (!translator_file)
6461 ERR("ODBC Translator entry not found!\n");
6462 return ERROR_FUNCTION_FAILED;
6465 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6466 if (setup_file)
6467 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6469 translator = msi_alloc(len * sizeof(WCHAR));
6470 if (!translator)
6471 return ERROR_OUTOFMEMORY;
6473 ptr = translator;
6474 lstrcpyW(ptr, desc);
6475 ptr += lstrlenW(ptr) + 1;
6477 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6478 ptr += len + 1;
6480 if (setup_file)
6482 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6483 ptr += len + 1;
6485 *ptr = '\0';
6487 translator_path = strdupW(translator_file->TargetPath);
6488 ptr = strrchrW(translator_path, '\\');
6489 if (ptr) *ptr = '\0';
6491 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6492 NULL, ODBC_INSTALL_COMPLETE, &usage))
6494 ERR("Failed to install SQL translator!\n");
6495 r = ERROR_FUNCTION_FAILED;
6498 uirow = MSI_CreateRecord( 5 );
6499 MSI_RecordSetStringW( uirow, 1, desc );
6500 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6501 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6502 msi_ui_actiondata( package, szInstallODBC, uirow );
6503 msiobj_release( &uirow->hdr );
6505 msi_free(translator);
6506 msi_free(translator_path);
6508 return r;
6511 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6513 MSIPACKAGE *package = param;
6514 MSICOMPONENT *comp;
6515 LPWSTR attrs;
6516 LPCWSTR desc, driver, component;
6517 WORD request = ODBC_ADD_SYS_DSN;
6518 INT registration;
6519 DWORD len;
6520 UINT r = ERROR_SUCCESS;
6521 MSIRECORD *uirow;
6523 static const WCHAR attrs_fmt[] = {
6524 'D','S','N','=','%','s',0 };
6526 component = MSI_RecordGetString( rec, 2 );
6527 comp = msi_get_loaded_component( package, component );
6528 if (!comp)
6529 return ERROR_SUCCESS;
6531 comp->Action = msi_get_component_action( package, comp );
6532 if (comp->Action != INSTALLSTATE_LOCAL)
6534 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6535 return ERROR_SUCCESS;
6538 desc = MSI_RecordGetString(rec, 3);
6539 driver = MSI_RecordGetString(rec, 4);
6540 registration = MSI_RecordGetInteger(rec, 5);
6542 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6543 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6545 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6546 attrs = msi_alloc(len * sizeof(WCHAR));
6547 if (!attrs)
6548 return ERROR_OUTOFMEMORY;
6550 len = sprintfW(attrs, attrs_fmt, desc);
6551 attrs[len + 1] = 0;
6553 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6555 ERR("Failed to install SQL data source!\n");
6556 r = ERROR_FUNCTION_FAILED;
6559 uirow = MSI_CreateRecord( 5 );
6560 MSI_RecordSetStringW( uirow, 1, desc );
6561 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6562 MSI_RecordSetInteger( uirow, 3, request );
6563 msi_ui_actiondata( package, szInstallODBC, uirow );
6564 msiobj_release( &uirow->hdr );
6566 msi_free(attrs);
6568 return r;
6571 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6573 static const WCHAR driver_query[] = {
6574 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6575 'O','D','B','C','D','r','i','v','e','r',0};
6576 static const WCHAR translator_query[] = {
6577 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6578 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6579 static const WCHAR source_query[] = {
6580 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6581 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6582 MSIQUERY *view;
6583 UINT rc;
6585 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6586 if (rc == ERROR_SUCCESS)
6588 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6589 msiobj_release(&view->hdr);
6590 if (rc != ERROR_SUCCESS)
6591 return rc;
6593 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6594 if (rc == ERROR_SUCCESS)
6596 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6597 msiobj_release(&view->hdr);
6598 if (rc != ERROR_SUCCESS)
6599 return rc;
6601 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6602 if (rc == ERROR_SUCCESS)
6604 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6605 msiobj_release(&view->hdr);
6606 if (rc != ERROR_SUCCESS)
6607 return rc;
6609 return ERROR_SUCCESS;
6612 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6614 MSIPACKAGE *package = param;
6615 MSICOMPONENT *comp;
6616 MSIRECORD *uirow;
6617 DWORD usage;
6618 LPCWSTR desc, component;
6620 component = MSI_RecordGetString( rec, 2 );
6621 comp = msi_get_loaded_component( package, component );
6622 if (!comp)
6623 return ERROR_SUCCESS;
6625 comp->Action = msi_get_component_action( package, comp );
6626 if (comp->Action != INSTALLSTATE_ABSENT)
6628 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6629 return ERROR_SUCCESS;
6632 desc = MSI_RecordGetString( rec, 3 );
6633 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6635 WARN("Failed to remove ODBC driver\n");
6637 else if (!usage)
6639 FIXME("Usage count reached 0\n");
6642 uirow = MSI_CreateRecord( 2 );
6643 MSI_RecordSetStringW( uirow, 1, desc );
6644 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6645 msi_ui_actiondata( package, szRemoveODBC, uirow );
6646 msiobj_release( &uirow->hdr );
6648 return ERROR_SUCCESS;
6651 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6653 MSIPACKAGE *package = param;
6654 MSICOMPONENT *comp;
6655 MSIRECORD *uirow;
6656 DWORD usage;
6657 LPCWSTR desc, component;
6659 component = MSI_RecordGetString( rec, 2 );
6660 comp = msi_get_loaded_component( package, component );
6661 if (!comp)
6662 return ERROR_SUCCESS;
6664 comp->Action = msi_get_component_action( package, comp );
6665 if (comp->Action != INSTALLSTATE_ABSENT)
6667 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6668 return ERROR_SUCCESS;
6671 desc = MSI_RecordGetString( rec, 3 );
6672 if (!SQLRemoveTranslatorW( desc, &usage ))
6674 WARN("Failed to remove ODBC translator\n");
6676 else if (!usage)
6678 FIXME("Usage count reached 0\n");
6681 uirow = MSI_CreateRecord( 2 );
6682 MSI_RecordSetStringW( uirow, 1, desc );
6683 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6684 msi_ui_actiondata( package, szRemoveODBC, uirow );
6685 msiobj_release( &uirow->hdr );
6687 return ERROR_SUCCESS;
6690 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6692 MSIPACKAGE *package = param;
6693 MSICOMPONENT *comp;
6694 MSIRECORD *uirow;
6695 LPWSTR attrs;
6696 LPCWSTR desc, driver, component;
6697 WORD request = ODBC_REMOVE_SYS_DSN;
6698 INT registration;
6699 DWORD len;
6701 static const WCHAR attrs_fmt[] = {
6702 'D','S','N','=','%','s',0 };
6704 component = MSI_RecordGetString( rec, 2 );
6705 comp = msi_get_loaded_component( package, component );
6706 if (!comp)
6707 return ERROR_SUCCESS;
6709 comp->Action = msi_get_component_action( package, comp );
6710 if (comp->Action != INSTALLSTATE_ABSENT)
6712 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6713 return ERROR_SUCCESS;
6716 desc = MSI_RecordGetString( rec, 3 );
6717 driver = MSI_RecordGetString( rec, 4 );
6718 registration = MSI_RecordGetInteger( rec, 5 );
6720 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6721 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6723 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6724 attrs = msi_alloc( len * sizeof(WCHAR) );
6725 if (!attrs)
6726 return ERROR_OUTOFMEMORY;
6728 FIXME("Use ODBCSourceAttribute table\n");
6730 len = sprintfW( attrs, attrs_fmt, desc );
6731 attrs[len + 1] = 0;
6733 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6735 WARN("Failed to remove ODBC data source\n");
6737 msi_free( attrs );
6739 uirow = MSI_CreateRecord( 3 );
6740 MSI_RecordSetStringW( uirow, 1, desc );
6741 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6742 MSI_RecordSetInteger( uirow, 3, request );
6743 msi_ui_actiondata( package, szRemoveODBC, uirow );
6744 msiobj_release( &uirow->hdr );
6746 return ERROR_SUCCESS;
6749 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6751 static const WCHAR driver_query[] = {
6752 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6753 'O','D','B','C','D','r','i','v','e','r',0};
6754 static const WCHAR translator_query[] = {
6755 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6756 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6757 static const WCHAR source_query[] = {
6758 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6759 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6760 MSIQUERY *view;
6761 UINT rc;
6763 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6764 if (rc == ERROR_SUCCESS)
6766 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6767 msiobj_release( &view->hdr );
6768 if (rc != ERROR_SUCCESS)
6769 return rc;
6771 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6772 if (rc == ERROR_SUCCESS)
6774 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6775 msiobj_release( &view->hdr );
6776 if (rc != ERROR_SUCCESS)
6777 return rc;
6779 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6780 if (rc == ERROR_SUCCESS)
6782 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6783 msiobj_release( &view->hdr );
6784 if (rc != ERROR_SUCCESS)
6785 return rc;
6787 return ERROR_SUCCESS;
6790 #define ENV_ACT_SETALWAYS 0x1
6791 #define ENV_ACT_SETABSENT 0x2
6792 #define ENV_ACT_REMOVE 0x4
6793 #define ENV_ACT_REMOVEMATCH 0x8
6795 #define ENV_MOD_MACHINE 0x20000000
6796 #define ENV_MOD_APPEND 0x40000000
6797 #define ENV_MOD_PREFIX 0x80000000
6798 #define ENV_MOD_MASK 0xC0000000
6800 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6802 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6804 LPCWSTR cptr = *name;
6806 static const WCHAR prefix[] = {'[','~',']',0};
6807 static const int prefix_len = 3;
6809 *flags = 0;
6810 while (*cptr)
6812 if (*cptr == '=')
6813 *flags |= ENV_ACT_SETALWAYS;
6814 else if (*cptr == '+')
6815 *flags |= ENV_ACT_SETABSENT;
6816 else if (*cptr == '-')
6817 *flags |= ENV_ACT_REMOVE;
6818 else if (*cptr == '!')
6819 *flags |= ENV_ACT_REMOVEMATCH;
6820 else if (*cptr == '*')
6821 *flags |= ENV_MOD_MACHINE;
6822 else
6823 break;
6825 cptr++;
6826 (*name)++;
6829 if (!*cptr)
6831 ERR("Missing environment variable\n");
6832 return ERROR_FUNCTION_FAILED;
6835 if (*value)
6837 LPCWSTR ptr = *value;
6838 if (!strncmpW(ptr, prefix, prefix_len))
6840 if (ptr[prefix_len] == szSemiColon[0])
6842 *flags |= ENV_MOD_APPEND;
6843 *value += lstrlenW(prefix);
6845 else
6847 *value = NULL;
6850 else if (lstrlenW(*value) >= prefix_len)
6852 ptr += lstrlenW(ptr) - prefix_len;
6853 if (!strcmpW( ptr, prefix ))
6855 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6857 *flags |= ENV_MOD_PREFIX;
6858 /* the "[~]" will be removed by deformat_string */;
6860 else
6862 *value = NULL;
6868 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6869 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6870 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6871 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6873 ERR("Invalid flags: %08x\n", *flags);
6874 return ERROR_FUNCTION_FAILED;
6877 if (!*flags)
6878 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6880 return ERROR_SUCCESS;
6883 static UINT open_env_key( DWORD flags, HKEY *key )
6885 static const WCHAR user_env[] =
6886 {'E','n','v','i','r','o','n','m','e','n','t',0};
6887 static const WCHAR machine_env[] =
6888 {'S','y','s','t','e','m','\\',
6889 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6890 'C','o','n','t','r','o','l','\\',
6891 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6892 'E','n','v','i','r','o','n','m','e','n','t',0};
6893 const WCHAR *env;
6894 HKEY root;
6895 LONG res;
6897 if (flags & ENV_MOD_MACHINE)
6899 env = machine_env;
6900 root = HKEY_LOCAL_MACHINE;
6902 else
6904 env = user_env;
6905 root = HKEY_CURRENT_USER;
6908 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6909 if (res != ERROR_SUCCESS)
6911 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6912 return ERROR_FUNCTION_FAILED;
6915 return ERROR_SUCCESS;
6918 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6920 MSIPACKAGE *package = param;
6921 LPCWSTR name, value, component;
6922 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6923 DWORD flags, type, size;
6924 UINT res;
6925 HKEY env = NULL;
6926 MSICOMPONENT *comp;
6927 MSIRECORD *uirow;
6928 int action = 0;
6930 component = MSI_RecordGetString(rec, 4);
6931 comp = msi_get_loaded_component(package, component);
6932 if (!comp)
6933 return ERROR_SUCCESS;
6935 comp->Action = msi_get_component_action( package, comp );
6936 if (comp->Action != INSTALLSTATE_LOCAL)
6938 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6939 return ERROR_SUCCESS;
6941 name = MSI_RecordGetString(rec, 2);
6942 value = MSI_RecordGetString(rec, 3);
6944 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6946 res = env_parse_flags(&name, &value, &flags);
6947 if (res != ERROR_SUCCESS || !value)
6948 goto done;
6950 if (value && !deformat_string(package, value, &deformatted))
6952 res = ERROR_OUTOFMEMORY;
6953 goto done;
6956 value = deformatted;
6958 res = open_env_key( flags, &env );
6959 if (res != ERROR_SUCCESS)
6960 goto done;
6962 if (flags & ENV_MOD_MACHINE)
6963 action |= 0x20000000;
6965 size = 0;
6966 type = REG_SZ;
6967 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6968 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6969 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6970 goto done;
6972 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6974 action = 0x2;
6976 /* Nothing to do. */
6977 if (!value)
6979 res = ERROR_SUCCESS;
6980 goto done;
6983 /* If we are appending but the string was empty, strip ; */
6984 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6986 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6987 newval = strdupW(value);
6988 if (!newval)
6990 res = ERROR_OUTOFMEMORY;
6991 goto done;
6994 else
6996 action = 0x1;
6998 /* Contrary to MSDN, +-variable to [~];path works */
6999 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7001 res = ERROR_SUCCESS;
7002 goto done;
7005 data = msi_alloc(size);
7006 if (!data)
7008 msi_free(deformatted);
7009 RegCloseKey(env);
7010 return ERROR_OUTOFMEMORY;
7013 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
7014 if (res != ERROR_SUCCESS)
7015 goto done;
7017 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7019 action = 0x4;
7020 res = RegDeleteValueW(env, name);
7021 if (res != ERROR_SUCCESS)
7022 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7023 goto done;
7026 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
7027 if (flags & ENV_MOD_MASK)
7029 DWORD mod_size;
7030 int multiplier = 0;
7031 if (flags & ENV_MOD_APPEND) multiplier++;
7032 if (flags & ENV_MOD_PREFIX) multiplier++;
7033 mod_size = lstrlenW(value) * multiplier;
7034 size += mod_size * sizeof(WCHAR);
7037 newval = msi_alloc(size);
7038 ptr = newval;
7039 if (!newval)
7041 res = ERROR_OUTOFMEMORY;
7042 goto done;
7045 if (flags & ENV_MOD_PREFIX)
7047 lstrcpyW(newval, value);
7048 ptr = newval + lstrlenW(value);
7049 action |= 0x80000000;
7052 lstrcpyW(ptr, data);
7054 if (flags & ENV_MOD_APPEND)
7056 lstrcatW(newval, value);
7057 action |= 0x40000000;
7060 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7061 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
7062 if (res)
7064 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7067 done:
7068 uirow = MSI_CreateRecord( 3 );
7069 MSI_RecordSetStringW( uirow, 1, name );
7070 MSI_RecordSetStringW( uirow, 2, newval );
7071 MSI_RecordSetInteger( uirow, 3, action );
7072 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7073 msiobj_release( &uirow->hdr );
7075 if (env) RegCloseKey(env);
7076 msi_free(deformatted);
7077 msi_free(data);
7078 msi_free(newval);
7079 return res;
7082 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7084 static const WCHAR query[] = {
7085 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7086 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7087 MSIQUERY *view;
7088 UINT rc;
7090 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7091 if (rc != ERROR_SUCCESS)
7092 return ERROR_SUCCESS;
7094 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7095 msiobj_release(&view->hdr);
7096 return rc;
7099 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7101 MSIPACKAGE *package = param;
7102 LPCWSTR name, value, component;
7103 LPWSTR deformatted = NULL;
7104 DWORD flags;
7105 HKEY env;
7106 MSICOMPONENT *comp;
7107 MSIRECORD *uirow;
7108 int action = 0;
7109 LONG res;
7110 UINT r;
7112 component = MSI_RecordGetString( rec, 4 );
7113 comp = msi_get_loaded_component( package, component );
7114 if (!comp)
7115 return ERROR_SUCCESS;
7117 comp->Action = msi_get_component_action( package, comp );
7118 if (comp->Action != INSTALLSTATE_ABSENT)
7120 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7121 return ERROR_SUCCESS;
7123 name = MSI_RecordGetString( rec, 2 );
7124 value = MSI_RecordGetString( rec, 3 );
7126 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7128 r = env_parse_flags( &name, &value, &flags );
7129 if (r != ERROR_SUCCESS)
7130 return r;
7132 if (!(flags & ENV_ACT_REMOVE))
7134 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7135 return ERROR_SUCCESS;
7138 if (value && !deformat_string( package, value, &deformatted ))
7139 return ERROR_OUTOFMEMORY;
7141 value = deformatted;
7143 r = open_env_key( flags, &env );
7144 if (r != ERROR_SUCCESS)
7146 r = ERROR_SUCCESS;
7147 goto done;
7150 if (flags & ENV_MOD_MACHINE)
7151 action |= 0x20000000;
7153 TRACE("Removing %s\n", debugstr_w(name));
7155 res = RegDeleteValueW( env, name );
7156 if (res != ERROR_SUCCESS)
7158 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
7159 r = ERROR_SUCCESS;
7162 done:
7163 uirow = MSI_CreateRecord( 3 );
7164 MSI_RecordSetStringW( uirow, 1, name );
7165 MSI_RecordSetStringW( uirow, 2, value );
7166 MSI_RecordSetInteger( uirow, 3, action );
7167 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7168 msiobj_release( &uirow->hdr );
7170 if (env) RegCloseKey( env );
7171 msi_free( deformatted );
7172 return r;
7175 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7177 static const WCHAR query[] = {
7178 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7179 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7180 MSIQUERY *view;
7181 UINT rc;
7183 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7184 if (rc != ERROR_SUCCESS)
7185 return ERROR_SUCCESS;
7187 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7188 msiobj_release( &view->hdr );
7189 return rc;
7192 UINT msi_validate_product_id( MSIPACKAGE *package )
7194 LPWSTR key, template, id;
7195 UINT r = ERROR_SUCCESS;
7197 id = msi_dup_property( package->db, szProductID );
7198 if (id)
7200 msi_free( id );
7201 return ERROR_SUCCESS;
7203 template = msi_dup_property( package->db, szPIDTemplate );
7204 key = msi_dup_property( package->db, szPIDKEY );
7205 if (key && template)
7207 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7208 r = msi_set_property( package->db, szProductID, key, -1 );
7210 msi_free( template );
7211 msi_free( key );
7212 return r;
7215 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7217 return msi_validate_product_id( package );
7220 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7222 TRACE("\n");
7223 package->need_reboot_at_end = 1;
7224 return ERROR_SUCCESS;
7227 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7229 static const WCHAR szAvailableFreeReg[] =
7230 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7231 MSIRECORD *uirow;
7232 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7234 TRACE("%p %d kilobytes\n", package, space);
7236 uirow = MSI_CreateRecord( 1 );
7237 MSI_RecordSetInteger( uirow, 1, space );
7238 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7239 msiobj_release( &uirow->hdr );
7241 return ERROR_SUCCESS;
7244 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7246 TRACE("%p\n", package);
7248 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7249 return ERROR_SUCCESS;
7252 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7254 FIXME("%p\n", package);
7255 return ERROR_SUCCESS;
7258 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7260 static const WCHAR driver_query[] = {
7261 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7262 'O','D','B','C','D','r','i','v','e','r',0};
7263 static const WCHAR translator_query[] = {
7264 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7265 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7266 MSIQUERY *view;
7267 UINT r, count;
7269 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7270 if (r == ERROR_SUCCESS)
7272 count = 0;
7273 r = MSI_IterateRecords( view, &count, NULL, package );
7274 msiobj_release( &view->hdr );
7275 if (r != ERROR_SUCCESS)
7276 return r;
7277 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7279 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7280 if (r == ERROR_SUCCESS)
7282 count = 0;
7283 r = MSI_IterateRecords( view, &count, NULL, package );
7284 msiobj_release( &view->hdr );
7285 if (r != ERROR_SUCCESS)
7286 return r;
7287 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7289 return ERROR_SUCCESS;
7292 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7294 static const WCHAR fmtW[] =
7295 {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7296 MSIPACKAGE *package = param;
7297 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7298 int attrs = MSI_RecordGetInteger( rec, 5 );
7299 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7300 WCHAR *product, *features, *cmd;
7301 STARTUPINFOW si;
7302 PROCESS_INFORMATION info;
7303 BOOL ret;
7305 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7306 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7308 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7310 len += strlenW( product );
7311 if (features)
7312 len += strlenW( features );
7313 else
7314 len += sizeof(szAll) / sizeof(szAll[0]);
7316 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7318 msi_free( product );
7319 msi_free( features );
7320 return ERROR_OUTOFMEMORY;
7322 sprintfW( cmd, fmtW, product, features ? features : szAll );
7323 msi_free( product );
7324 msi_free( features );
7326 memset( &si, 0, sizeof(STARTUPINFOW) );
7327 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7328 msi_free( cmd );
7329 if (!ret) return GetLastError();
7330 CloseHandle( info.hThread );
7332 WaitForSingleObject( info.hProcess, INFINITE );
7333 CloseHandle( info.hProcess );
7334 return ERROR_SUCCESS;
7337 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7339 static const WCHAR query[] = {
7340 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7341 MSIQUERY *view;
7342 UINT r;
7344 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7345 if (r == ERROR_SUCCESS)
7347 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7348 msiobj_release( &view->hdr );
7349 if (r != ERROR_SUCCESS)
7350 return r;
7352 return ERROR_SUCCESS;
7355 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7357 MSIPACKAGE *package = param;
7358 int attributes = MSI_RecordGetInteger( rec, 5 );
7360 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7362 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7363 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7364 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7365 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7366 HKEY hkey;
7367 UINT r;
7369 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7371 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7372 if (r != ERROR_SUCCESS)
7373 return ERROR_SUCCESS;
7375 else
7377 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7378 if (r != ERROR_SUCCESS)
7379 return ERROR_SUCCESS;
7381 RegCloseKey( hkey );
7383 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7384 debugstr_w(upgrade_code), debugstr_w(version_min),
7385 debugstr_w(version_max), debugstr_w(language));
7387 return ERROR_SUCCESS;
7390 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7392 static const WCHAR query[] = {
7393 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7394 'U','p','g','r','a','d','e',0};
7395 MSIQUERY *view;
7396 UINT r;
7398 if (msi_get_property_int( package->db, szInstalled, 0 ))
7400 TRACE("product is installed, skipping action\n");
7401 return ERROR_SUCCESS;
7403 if (msi_get_property_int( package->db, szPreselected, 0 ))
7405 TRACE("Preselected property is set, not migrating feature states\n");
7406 return ERROR_SUCCESS;
7408 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7409 if (r == ERROR_SUCCESS)
7411 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7412 msiobj_release( &view->hdr );
7413 if (r != ERROR_SUCCESS)
7414 return r;
7416 return ERROR_SUCCESS;
7419 static void bind_image( const char *filename, const char *path )
7421 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7423 WARN("failed to bind image %u\n", GetLastError());
7427 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7429 UINT i;
7430 MSIFILE *file;
7431 MSIPACKAGE *package = param;
7432 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7433 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7434 char *filenameA, *pathA;
7435 WCHAR *pathW, **path_list;
7437 if (!(file = msi_get_loaded_file( package, key )))
7439 WARN("file %s not found\n", debugstr_w(key));
7440 return ERROR_SUCCESS;
7442 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7443 path_list = msi_split_string( paths, ';' );
7444 if (!path_list) bind_image( filenameA, NULL );
7445 else
7447 for (i = 0; path_list[i] && path_list[i][0]; i++)
7449 deformat_string( package, path_list[i], &pathW );
7450 if ((pathA = strdupWtoA( pathW )))
7452 bind_image( filenameA, pathA );
7453 msi_free( pathA );
7455 msi_free( pathW );
7458 msi_free( path_list );
7459 msi_free( filenameA );
7460 return ERROR_SUCCESS;
7463 static UINT ACTION_BindImage( MSIPACKAGE *package )
7465 static const WCHAR query[] = {
7466 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7467 'B','i','n','d','I','m','a','g','e',0};
7468 MSIQUERY *view;
7469 UINT r;
7471 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7472 if (r == ERROR_SUCCESS)
7474 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7475 msiobj_release( &view->hdr );
7476 if (r != ERROR_SUCCESS)
7477 return r;
7479 return ERROR_SUCCESS;
7482 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7484 static const WCHAR query[] = {
7485 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7486 MSIQUERY *view;
7487 DWORD count = 0;
7488 UINT r;
7490 r = MSI_OpenQuery( package->db, &view, query, table );
7491 if (r == ERROR_SUCCESS)
7493 r = MSI_IterateRecords(view, &count, NULL, package);
7494 msiobj_release(&view->hdr);
7495 if (r != ERROR_SUCCESS)
7496 return r;
7498 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7499 return ERROR_SUCCESS;
7502 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7504 static const WCHAR table[] = {
7505 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7506 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7509 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7511 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7512 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7515 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7517 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7518 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7521 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7523 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7524 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7527 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7529 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7530 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7533 static const struct
7535 const WCHAR *action;
7536 UINT (*handler)(MSIPACKAGE *);
7537 const WCHAR *action_rollback;
7539 StandardActions[] =
7541 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7542 { szAppSearch, ACTION_AppSearch, NULL },
7543 { szBindImage, ACTION_BindImage, NULL },
7544 { szCCPSearch, ACTION_CCPSearch, NULL },
7545 { szCostFinalize, ACTION_CostFinalize, NULL },
7546 { szCostInitialize, ACTION_CostInitialize, NULL },
7547 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7548 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7549 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7550 { szDisableRollback, ACTION_DisableRollback, NULL },
7551 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7552 { szExecuteAction, ACTION_ExecuteAction, NULL },
7553 { szFileCost, ACTION_FileCost, NULL },
7554 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7555 { szForceReboot, ACTION_ForceReboot, NULL },
7556 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7557 { szInstallExecute, ACTION_InstallExecute, NULL },
7558 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7559 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7560 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7561 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7562 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7563 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7564 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7565 { szInstallValidate, ACTION_InstallValidate, NULL },
7566 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7567 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7568 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7569 { szMoveFiles, ACTION_MoveFiles, NULL },
7570 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7571 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7572 { szPatchFiles, ACTION_PatchFiles, NULL },
7573 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7574 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7575 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7576 { szPublishProduct, ACTION_PublishProduct, NULL },
7577 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7578 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7579 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7580 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7581 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7582 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7583 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7584 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7585 { szRegisterUser, ACTION_RegisterUser, NULL },
7586 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7587 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7588 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7589 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7590 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7591 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7592 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7593 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7594 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7595 { szResolveSource, ACTION_ResolveSource, NULL },
7596 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7597 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7598 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7599 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7600 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7601 { szStartServices, ACTION_StartServices, szStopServices },
7602 { szStopServices, ACTION_StopServices, szStartServices },
7603 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7604 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7605 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7606 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7607 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7608 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7609 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7610 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7611 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7612 { szValidateProductID, ACTION_ValidateProductID, NULL },
7613 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7614 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7615 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7616 { NULL, NULL, NULL }
7619 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7621 BOOL ret = FALSE;
7622 UINT i;
7624 i = 0;
7625 while (StandardActions[i].action != NULL)
7627 if (!strcmpW( StandardActions[i].action, action ))
7629 ui_actionstart( package, action );
7630 if (StandardActions[i].handler)
7632 ui_actioninfo( package, action, TRUE, 0 );
7633 *rc = StandardActions[i].handler( package );
7634 ui_actioninfo( package, action, FALSE, *rc );
7636 if (StandardActions[i].action_rollback && !package->need_rollback)
7638 TRACE("scheduling rollback action\n");
7639 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7642 else
7644 FIXME("unhandled standard action %s\n", debugstr_w(action));
7645 *rc = ERROR_SUCCESS;
7647 ret = TRUE;
7648 break;
7650 i++;
7652 return ret;
7655 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7657 UINT rc = ERROR_SUCCESS;
7658 BOOL handled;
7660 TRACE("Performing action (%s)\n", debugstr_w(action));
7662 handled = ACTION_HandleStandardAction(package, action, &rc);
7664 if (!handled)
7665 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7667 if (!handled)
7669 WARN("unhandled msi action %s\n", debugstr_w(action));
7670 rc = ERROR_FUNCTION_NOT_CALLED;
7673 return rc;
7676 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7678 UINT rc = ERROR_SUCCESS;
7679 BOOL handled = FALSE;
7681 TRACE("Performing action (%s)\n", debugstr_w(action));
7683 package->action_progress_increment = 0;
7684 handled = ACTION_HandleStandardAction(package, action, &rc);
7686 if (!handled)
7687 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7689 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7690 handled = TRUE;
7692 if (!handled)
7694 WARN("unhandled msi action %s\n", debugstr_w(action));
7695 rc = ERROR_FUNCTION_NOT_CALLED;
7698 return rc;
7701 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7703 UINT rc = ERROR_SUCCESS;
7704 MSIRECORD *row;
7706 static const WCHAR query[] =
7707 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7708 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7709 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7710 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7711 static const WCHAR ui_query[] =
7712 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7713 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7714 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7715 ' ', '=',' ','%','i',0};
7717 if (needs_ui_sequence(package))
7718 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7719 else
7720 row = MSI_QueryGetRecord(package->db, query, seq);
7722 if (row)
7724 LPCWSTR action, cond;
7726 TRACE("Running the actions\n");
7728 /* check conditions */
7729 cond = MSI_RecordGetString(row, 2);
7731 /* this is a hack to skip errors in the condition code */
7732 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7734 msiobj_release(&row->hdr);
7735 return ERROR_SUCCESS;
7738 action = MSI_RecordGetString(row, 1);
7739 if (!action)
7741 ERR("failed to fetch action\n");
7742 msiobj_release(&row->hdr);
7743 return ERROR_FUNCTION_FAILED;
7746 if (needs_ui_sequence(package))
7747 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7748 else
7749 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7751 msiobj_release(&row->hdr);
7754 return rc;
7757 /****************************************************
7758 * TOP level entry points
7759 *****************************************************/
7761 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7762 LPCWSTR szCommandLine )
7764 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7765 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7766 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7767 WCHAR *reinstall, *remove, *patch, *productcode;
7768 BOOL ui_exists;
7769 UINT rc;
7771 msi_set_property( package->db, szAction, szInstall, -1 );
7773 package->script->InWhatSequence = SEQUENCE_INSTALL;
7775 if (szPackagePath)
7777 LPWSTR p, dir;
7778 LPCWSTR file;
7780 dir = strdupW(szPackagePath);
7781 p = strrchrW(dir, '\\');
7782 if (p)
7784 *(++p) = 0;
7785 file = szPackagePath + (p - dir);
7787 else
7789 msi_free(dir);
7790 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7791 GetCurrentDirectoryW(MAX_PATH, dir);
7792 lstrcatW(dir, szBackSlash);
7793 file = szPackagePath;
7796 msi_free( package->PackagePath );
7797 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7798 if (!package->PackagePath)
7800 msi_free(dir);
7801 return ERROR_OUTOFMEMORY;
7804 lstrcpyW(package->PackagePath, dir);
7805 lstrcatW(package->PackagePath, file);
7806 msi_free(dir);
7808 msi_set_sourcedir_props(package, FALSE);
7811 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7812 if (rc != ERROR_SUCCESS)
7813 return rc;
7815 msi_apply_transforms( package );
7816 msi_apply_patches( package );
7818 patch = msi_dup_property( package->db, szPatch );
7819 remove = msi_dup_property( package->db, szRemove );
7820 reinstall = msi_dup_property( package->db, szReinstall );
7821 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7823 TRACE("setting REINSTALL property to ALL\n");
7824 msi_set_property( package->db, szReinstall, szAll, -1 );
7825 package->full_reinstall = 1;
7828 msi_set_original_database_property( package->db, szPackagePath );
7829 msi_parse_command_line( package, szCommandLine, FALSE );
7830 msi_adjust_privilege_properties( package );
7831 msi_set_context( package );
7833 productcode = msi_dup_property( package->db, szProductCode );
7834 if (strcmpiW( productcode, package->ProductCode ))
7836 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
7837 msi_free( package->ProductCode );
7838 package->ProductCode = productcode;
7840 else msi_free( productcode );
7842 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7844 TRACE("disabling rollback\n");
7845 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7848 if (needs_ui_sequence( package))
7850 package->script->InWhatSequence |= SEQUENCE_UI;
7851 rc = ACTION_ProcessUISequence(package);
7852 ui_exists = ui_sequence_exists(package);
7853 if (rc == ERROR_SUCCESS || !ui_exists)
7855 package->script->InWhatSequence |= SEQUENCE_EXEC;
7856 rc = ACTION_ProcessExecSequence(package, ui_exists);
7859 else
7860 rc = ACTION_ProcessExecSequence(package, FALSE);
7862 /* process the ending type action */
7863 if (rc == ERROR_SUCCESS)
7864 ACTION_PerformActionSequence(package, -1);
7865 else if (rc == ERROR_INSTALL_USEREXIT)
7866 ACTION_PerformActionSequence(package, -2);
7867 else if (rc == ERROR_INSTALL_SUSPEND)
7868 ACTION_PerformActionSequence(package, -4);
7869 else /* failed */
7871 ACTION_PerformActionSequence(package, -3);
7872 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7874 package->need_rollback = TRUE;
7878 /* finish up running custom actions */
7879 ACTION_FinishCustomActions(package);
7881 if (package->need_rollback && !reinstall)
7883 WARN("installation failed, running rollback script\n");
7884 execute_script( package, SCRIPT_ROLLBACK );
7886 msi_free( reinstall );
7887 msi_free( remove );
7888 msi_free( patch );
7890 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7891 return ERROR_SUCCESS_REBOOT_REQUIRED;
7893 return rc;