ws2_32/tests: Add tests for InetNtopW.
[wine.git] / dlls / msi / action.c
bloba3b84af51e8eb3648e1013c639a50c098dc6ca6b
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 = TRUE;
259 len++;
260 break;
262 break;
264 case state_quote:
265 switch (*p)
267 case '"':
268 if (in_quotes && p[1] != '\"') count--;
269 else count++;
270 break;
271 case ' ':
272 state = state_whitespace;
273 if (!count || (count > 1 && !len)) goto done;
274 in_quotes = TRUE;
275 len++;
276 break;
277 default:
278 state = state_token;
279 if (count) in_quotes = TRUE;
280 len++;
281 break;
283 break;
285 default: break;
287 if (!ignore) *out++ = *p;
288 if (!count) in_quotes = FALSE;
291 done:
292 if (!len) *value = 0;
293 else *out = 0;
295 *quotes = count;
296 return p - str;
299 static void remove_quotes( WCHAR *str )
301 WCHAR *p = str;
302 int len = strlenW( str );
304 while ((p = strchrW( p, '"' )))
306 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
307 p++;
311 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
312 BOOL preserve_case )
314 LPCWSTR ptr, ptr2;
315 int num_quotes;
316 DWORD len;
317 WCHAR *prop, *val;
318 UINT r;
320 if (!szCommandLine)
321 return ERROR_SUCCESS;
323 ptr = szCommandLine;
324 while (*ptr)
326 while (*ptr == ' ') ptr++;
327 if (!*ptr) break;
329 ptr2 = strchrW( ptr, '=' );
330 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
332 len = ptr2 - ptr;
333 if (!len) return ERROR_INVALID_COMMAND_LINE;
335 while (ptr[len - 1] == ' ') len--;
337 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
338 memcpy( prop, ptr, len * sizeof(WCHAR) );
339 prop[len] = 0;
340 if (!preserve_case) struprW( prop );
342 ptr2++;
343 while (*ptr2 == ' ') ptr2++;
345 num_quotes = 0;
346 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
347 len = parse_prop( ptr2, val, &num_quotes );
348 if (num_quotes % 2)
350 WARN("unbalanced quotes\n");
351 msi_free( val );
352 msi_free( prop );
353 return ERROR_INVALID_COMMAND_LINE;
355 remove_quotes( val );
356 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
358 r = msi_set_property( package->db, prop, val, -1 );
359 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
360 msi_reset_folders( package, TRUE );
362 msi_free( val );
363 msi_free( prop );
365 ptr = ptr2 + len;
368 return ERROR_SUCCESS;
371 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
373 LPCWSTR pc;
374 LPWSTR p, *ret = NULL;
375 UINT count = 0;
377 if (!str)
378 return ret;
380 /* count the number of substrings */
381 for ( pc = str, count = 0; pc; count++ )
383 pc = strchrW( pc, sep );
384 if (pc)
385 pc++;
388 /* allocate space for an array of substring pointers and the substrings */
389 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
390 (lstrlenW(str)+1) * sizeof(WCHAR) );
391 if (!ret)
392 return ret;
394 /* copy the string and set the pointers */
395 p = (LPWSTR) &ret[count+1];
396 lstrcpyW( p, str );
397 for( count = 0; (ret[count] = p); count++ )
399 p = strchrW( p, sep );
400 if (p)
401 *p++ = 0;
404 return ret;
407 static BOOL ui_sequence_exists( MSIPACKAGE *package )
409 static const WCHAR query [] = {
410 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
411 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
412 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',0};
413 MSIQUERY *view;
414 DWORD count = 0;
416 if (!(MSI_DatabaseOpenViewW( package->db, query, &view )))
418 MSI_IterateRecords( view, &count, NULL, package );
419 msiobj_release( &view->hdr );
421 return count != 0;
424 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
426 WCHAR *source, *check, *p, *db;
427 DWORD len;
429 if (!(db = msi_dup_property( package->db, szOriginalDatabase )))
430 return ERROR_OUTOFMEMORY;
432 if (!(p = strrchrW( db, '\\' )) && !(p = strrchrW( db, '/' )))
434 msi_free(db);
435 return ERROR_SUCCESS;
437 len = p - db + 2;
438 source = msi_alloc( len * sizeof(WCHAR) );
439 lstrcpynW( source, db, len );
440 msi_free( db );
442 check = msi_dup_property( package->db, szSourceDir );
443 if (!check || replace)
445 UINT r = msi_set_property( package->db, szSourceDir, source, -1 );
446 if (r == ERROR_SUCCESS)
447 msi_reset_folders( package, TRUE );
449 msi_free( check );
451 check = msi_dup_property( package->db, szSOURCEDIR );
452 if (!check || replace)
453 msi_set_property( package->db, szSOURCEDIR, source, -1 );
455 msi_free( check );
456 msi_free( source );
458 return ERROR_SUCCESS;
461 static BOOL needs_ui_sequence(MSIPACKAGE *package)
463 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
466 UINT msi_set_context(MSIPACKAGE *package)
468 UINT r = msi_locate_product( package->ProductCode, &package->Context );
469 if (r != ERROR_SUCCESS)
471 int num = msi_get_property_int( package->db, szAllUsers, 0 );
472 if (num == 1 || num == 2)
473 package->Context = MSIINSTALLCONTEXT_MACHINE;
474 else
475 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
477 return ERROR_SUCCESS;
480 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
482 UINT rc;
483 LPCWSTR cond, action;
484 MSIPACKAGE *package = param;
486 action = MSI_RecordGetString(row,1);
487 if (!action)
489 ERR("Error is retrieving action name\n");
490 return ERROR_FUNCTION_FAILED;
493 /* check conditions */
494 cond = MSI_RecordGetString(row,2);
496 /* this is a hack to skip errors in the condition code */
497 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
499 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
500 return ERROR_SUCCESS;
503 if (needs_ui_sequence(package))
504 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
505 else
506 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
508 msi_dialog_check_messages( NULL );
510 if (package->CurrentInstallState != ERROR_SUCCESS)
511 rc = package->CurrentInstallState;
513 if (rc == ERROR_FUNCTION_NOT_CALLED)
514 rc = ERROR_SUCCESS;
516 if (rc != ERROR_SUCCESS)
517 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
519 if (package->need_reboot_now)
521 TRACE("action %s asked for immediate reboot, suspending installation\n",
522 debugstr_w(action));
523 rc = ACTION_ForceReboot( package );
525 return rc;
528 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
530 static const WCHAR query[] = {
531 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
532 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
533 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
534 '`','S','e','q','u','e','n','c','e','`',0};
535 MSIQUERY *view;
536 UINT r;
538 TRACE("%p %s\n", package, debugstr_w(table));
540 r = MSI_OpenQuery( package->db, &view, query, table );
541 if (r == ERROR_SUCCESS)
543 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
544 msiobj_release(&view->hdr);
546 return r;
549 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
551 static const WCHAR query[] = {
552 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
553 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
554 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
555 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
556 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
557 static const WCHAR query_validate[] = {
558 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
559 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
560 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
561 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
562 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
563 MSIQUERY *view;
564 INT seq = 0;
565 UINT rc;
567 if (package->script->ExecuteSequenceRun)
569 TRACE("Execute Sequence already Run\n");
570 return ERROR_SUCCESS;
573 package->script->ExecuteSequenceRun = TRUE;
575 /* get the sequence number */
576 if (UIran)
578 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
579 if (!row) return ERROR_FUNCTION_FAILED;
580 seq = MSI_RecordGetInteger(row,1);
581 msiobj_release(&row->hdr);
583 rc = MSI_OpenQuery(package->db, &view, query, seq);
584 if (rc == ERROR_SUCCESS)
586 TRACE("Running the actions\n");
588 msi_set_property( package->db, szSourceDir, NULL, -1 );
589 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
590 msiobj_release(&view->hdr);
592 return rc;
595 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
597 static const WCHAR query[] = {
598 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
599 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
600 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
601 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
602 MSIQUERY *view;
603 UINT rc;
605 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
606 if (rc == ERROR_SUCCESS)
608 TRACE("Running the actions\n");
609 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
610 msiobj_release(&view->hdr);
612 return rc;
615 /********************************************************
616 * ACTION helper functions and functions that perform the actions
617 *******************************************************/
618 static BOOL ACTION_HandleCustomAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc, UINT script )
620 BOOL ret=FALSE;
621 UINT arc;
623 arc = ACTION_CustomAction( package, action, script );
624 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
626 *rc = arc;
627 ret = TRUE;
629 return ret;
632 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
634 MSICOMPONENT *comp;
636 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
638 if (!strcmpW( Component, comp->Component )) return comp;
640 return NULL;
643 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
645 MSIFEATURE *feature;
647 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
649 if (!strcmpW( Feature, feature->Feature )) return feature;
651 return NULL;
654 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
656 MSIFILE *file;
658 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
660 if (!strcmpW( key, file->File )) return file;
662 return NULL;
665 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
667 MSIFOLDER *folder;
669 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
671 if (!strcmpW( dir, folder->Directory )) return folder;
673 return NULL;
677 * Recursively create all directories in the path.
678 * shamelessly stolen from setupapi/queue.c
680 BOOL msi_create_full_path( const WCHAR *path )
682 BOOL ret = TRUE;
683 WCHAR *new_path;
684 int len;
686 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
687 strcpyW( new_path, path );
689 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
690 new_path[len - 1] = 0;
692 while (!CreateDirectoryW( new_path, NULL ))
694 WCHAR *slash;
695 DWORD last_error = GetLastError();
696 if (last_error == ERROR_ALREADY_EXISTS) break;
697 if (last_error != ERROR_PATH_NOT_FOUND)
699 ret = FALSE;
700 break;
702 if (!(slash = strrchrW( new_path, '\\' )))
704 ret = FALSE;
705 break;
707 len = slash - new_path;
708 new_path[len] = 0;
709 if (!msi_create_full_path( new_path ))
711 ret = FALSE;
712 break;
714 new_path[len] = '\\';
716 msi_free( new_path );
717 return ret;
720 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
722 MSIRECORD *row;
724 row = MSI_CreateRecord( 4 );
725 MSI_RecordSetInteger( row, 1, a );
726 MSI_RecordSetInteger( row, 2, b );
727 MSI_RecordSetInteger( row, 3, c );
728 MSI_RecordSetInteger( row, 4, d );
729 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
730 msiobj_release( &row->hdr );
732 msi_dialog_check_messages( NULL );
735 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
737 static const WCHAR query[] =
738 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
739 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
740 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
741 WCHAR message[1024];
742 MSIRECORD *row = 0;
743 DWORD size;
745 if (!package->LastAction || strcmpW( package->LastAction, action ))
747 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
749 if (MSI_RecordIsNull( row, 3 ))
751 msiobj_release( &row->hdr );
752 return;
754 /* update the cached action format */
755 msi_free( package->ActionFormat );
756 package->ActionFormat = msi_dup_record_field( row, 3 );
757 msi_free( package->LastAction );
758 package->LastAction = strdupW( action );
759 msiobj_release( &row->hdr );
761 size = 1024;
762 MSI_RecordSetStringW( record, 0, package->ActionFormat );
763 MSI_FormatRecordW( package, record, message, &size );
764 row = MSI_CreateRecord( 1 );
765 MSI_RecordSetStringW( row, 1, message );
766 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
767 msiobj_release( &row->hdr );
770 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
772 if (!comp->Enabled)
774 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
775 return INSTALLSTATE_UNKNOWN;
777 if (package->need_rollback) return comp->Installed;
778 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
780 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
781 return INSTALLSTATE_UNKNOWN;
783 return comp->ActionRequest;
786 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
788 if (package->need_rollback) return feature->Installed;
789 return feature->ActionRequest;
792 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
794 MSIPACKAGE *package = param;
795 LPCWSTR dir, component, full_path;
796 MSIRECORD *uirow;
797 MSIFOLDER *folder;
798 MSICOMPONENT *comp;
800 component = MSI_RecordGetString(row, 2);
801 if (!component)
802 return ERROR_SUCCESS;
804 comp = msi_get_loaded_component(package, component);
805 if (!comp)
806 return ERROR_SUCCESS;
808 comp->Action = msi_get_component_action( package, comp );
809 if (comp->Action != INSTALLSTATE_LOCAL)
811 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
812 return ERROR_SUCCESS;
815 dir = MSI_RecordGetString(row,1);
816 if (!dir)
818 ERR("Unable to get folder id\n");
819 return ERROR_SUCCESS;
822 uirow = MSI_CreateRecord(1);
823 MSI_RecordSetStringW(uirow, 1, dir);
824 msi_ui_actiondata(package, szCreateFolders, uirow);
825 msiobj_release(&uirow->hdr);
827 full_path = msi_get_target_folder( package, dir );
828 if (!full_path)
830 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
831 return ERROR_SUCCESS;
833 TRACE("folder is %s\n", debugstr_w(full_path));
835 folder = msi_get_loaded_folder( package, dir );
836 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
837 folder->State = FOLDER_STATE_CREATED;
838 return ERROR_SUCCESS;
841 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
843 static const WCHAR query[] = {
844 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
845 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
846 MSIQUERY *view;
847 UINT rc;
849 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
850 if (rc != ERROR_SUCCESS)
851 return ERROR_SUCCESS;
853 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
854 msiobj_release(&view->hdr);
855 return rc;
858 static void remove_persistent_folder( MSIFOLDER *folder )
860 FolderList *fl;
862 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
864 remove_persistent_folder( fl->folder );
866 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
868 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
872 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
874 MSIPACKAGE *package = param;
875 LPCWSTR dir, component, full_path;
876 MSIRECORD *uirow;
877 MSIFOLDER *folder;
878 MSICOMPONENT *comp;
880 component = MSI_RecordGetString(row, 2);
881 if (!component)
882 return ERROR_SUCCESS;
884 comp = msi_get_loaded_component(package, component);
885 if (!comp)
886 return ERROR_SUCCESS;
888 comp->Action = msi_get_component_action( package, comp );
889 if (comp->Action != INSTALLSTATE_ABSENT)
891 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
892 return ERROR_SUCCESS;
895 dir = MSI_RecordGetString( row, 1 );
896 if (!dir)
898 ERR("Unable to get folder id\n");
899 return ERROR_SUCCESS;
902 full_path = msi_get_target_folder( package, dir );
903 if (!full_path)
905 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
906 return ERROR_SUCCESS;
908 TRACE("folder is %s\n", debugstr_w(full_path));
910 uirow = MSI_CreateRecord( 1 );
911 MSI_RecordSetStringW( uirow, 1, dir );
912 msi_ui_actiondata( package, szRemoveFolders, uirow );
913 msiobj_release( &uirow->hdr );
915 folder = msi_get_loaded_folder( package, dir );
916 remove_persistent_folder( folder );
917 return ERROR_SUCCESS;
920 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
922 static const WCHAR query[] = {
923 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
924 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
925 MSIQUERY *view;
926 UINT rc;
928 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
929 if (rc != ERROR_SUCCESS)
930 return ERROR_SUCCESS;
932 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
933 msiobj_release( &view->hdr );
934 return rc;
937 static UINT load_component( MSIRECORD *row, LPVOID param )
939 MSIPACKAGE *package = param;
940 MSICOMPONENT *comp;
942 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
943 if (!comp)
944 return ERROR_FUNCTION_FAILED;
946 list_add_tail( &package->components, &comp->entry );
948 /* fill in the data */
949 comp->Component = msi_dup_record_field( row, 1 );
951 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
953 comp->ComponentId = msi_dup_record_field( row, 2 );
954 comp->Directory = msi_dup_record_field( row, 3 );
955 comp->Attributes = MSI_RecordGetInteger(row,4);
956 comp->Condition = msi_dup_record_field( row, 5 );
957 comp->KeyPath = msi_dup_record_field( row, 6 );
959 comp->Installed = INSTALLSTATE_UNKNOWN;
960 comp->Action = INSTALLSTATE_UNKNOWN;
961 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
963 comp->assembly = msi_load_assembly( package, comp );
964 return ERROR_SUCCESS;
967 UINT msi_load_all_components( MSIPACKAGE *package )
969 static const WCHAR query[] = {
970 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
971 '`','C','o','m','p','o','n','e','n','t','`',0};
972 MSIQUERY *view;
973 UINT r;
975 if (!list_empty(&package->components))
976 return ERROR_SUCCESS;
978 r = MSI_DatabaseOpenViewW( package->db, query, &view );
979 if (r != ERROR_SUCCESS)
980 return r;
982 if (!msi_init_assembly_caches( package ))
984 ERR("can't initialize assembly caches\n");
985 msiobj_release( &view->hdr );
986 return ERROR_FUNCTION_FAILED;
989 r = MSI_IterateRecords(view, NULL, load_component, package);
990 msiobj_release(&view->hdr);
991 return r;
994 typedef struct {
995 MSIPACKAGE *package;
996 MSIFEATURE *feature;
997 } _ilfs;
999 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1001 ComponentList *cl;
1003 cl = msi_alloc( sizeof (*cl) );
1004 if ( !cl )
1005 return ERROR_NOT_ENOUGH_MEMORY;
1006 cl->component = comp;
1007 list_add_tail( &feature->Components, &cl->entry );
1009 return ERROR_SUCCESS;
1012 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1014 FeatureList *fl;
1016 fl = msi_alloc( sizeof(*fl) );
1017 if ( !fl )
1018 return ERROR_NOT_ENOUGH_MEMORY;
1019 fl->feature = child;
1020 list_add_tail( &parent->Children, &fl->entry );
1022 return ERROR_SUCCESS;
1025 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1027 _ilfs* ilfs = param;
1028 LPCWSTR component;
1029 MSICOMPONENT *comp;
1031 component = MSI_RecordGetString(row,1);
1033 /* check to see if the component is already loaded */
1034 comp = msi_get_loaded_component( ilfs->package, component );
1035 if (!comp)
1037 WARN("ignoring unknown component %s\n", debugstr_w(component));
1038 return ERROR_SUCCESS;
1040 add_feature_component( ilfs->feature, comp );
1041 comp->Enabled = TRUE;
1043 return ERROR_SUCCESS;
1046 static UINT load_feature(MSIRECORD * row, LPVOID param)
1048 static const WCHAR query[] = {
1049 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1050 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1051 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1052 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1053 MSIPACKAGE *package = param;
1054 MSIFEATURE *feature;
1055 MSIQUERY *view;
1056 _ilfs ilfs;
1057 UINT rc;
1059 /* fill in the data */
1061 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1062 if (!feature)
1063 return ERROR_NOT_ENOUGH_MEMORY;
1065 list_init( &feature->Children );
1066 list_init( &feature->Components );
1068 feature->Feature = msi_dup_record_field( row, 1 );
1070 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1072 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1073 feature->Title = msi_dup_record_field( row, 3 );
1074 feature->Description = msi_dup_record_field( row, 4 );
1076 if (!MSI_RecordIsNull(row,5))
1077 feature->Display = MSI_RecordGetInteger(row,5);
1079 feature->Level= MSI_RecordGetInteger(row,6);
1080 feature->Directory = msi_dup_record_field( row, 7 );
1081 feature->Attributes = MSI_RecordGetInteger(row,8);
1083 feature->Installed = INSTALLSTATE_UNKNOWN;
1084 feature->Action = INSTALLSTATE_UNKNOWN;
1085 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1087 list_add_tail( &package->features, &feature->entry );
1089 /* load feature components */
1091 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1092 if (rc != ERROR_SUCCESS)
1093 return ERROR_SUCCESS;
1095 ilfs.package = package;
1096 ilfs.feature = feature;
1098 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1099 msiobj_release(&view->hdr);
1100 return rc;
1103 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1105 MSIPACKAGE *package = param;
1106 MSIFEATURE *parent, *child;
1108 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1109 if (!child)
1110 return ERROR_FUNCTION_FAILED;
1112 if (!child->Feature_Parent)
1113 return ERROR_SUCCESS;
1115 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1116 if (!parent)
1117 return ERROR_FUNCTION_FAILED;
1119 add_feature_child( parent, child );
1120 return ERROR_SUCCESS;
1123 UINT msi_load_all_features( MSIPACKAGE *package )
1125 static const WCHAR query[] = {
1126 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1127 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1128 '`','D','i','s','p','l','a','y','`',0};
1129 MSIQUERY *view;
1130 UINT r;
1132 if (!list_empty(&package->features))
1133 return ERROR_SUCCESS;
1135 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1136 if (r != ERROR_SUCCESS)
1137 return r;
1139 r = MSI_IterateRecords( view, NULL, load_feature, package );
1140 if (r != ERROR_SUCCESS)
1142 msiobj_release( &view->hdr );
1143 return r;
1145 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1146 msiobj_release( &view->hdr );
1147 return r;
1150 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1152 if (!p)
1153 return p;
1154 p = strchrW(p, ch);
1155 if (!p)
1156 return p;
1157 *p = 0;
1158 return p+1;
1161 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1163 static const WCHAR query[] = {
1164 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1165 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1166 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1167 MSIQUERY *view = NULL;
1168 MSIRECORD *row = NULL;
1169 UINT r;
1171 TRACE("%s\n", debugstr_w(file->File));
1173 r = MSI_OpenQuery(package->db, &view, query, file->File);
1174 if (r != ERROR_SUCCESS)
1175 goto done;
1177 r = MSI_ViewExecute(view, NULL);
1178 if (r != ERROR_SUCCESS)
1179 goto done;
1181 r = MSI_ViewFetch(view, &row);
1182 if (r != ERROR_SUCCESS)
1183 goto done;
1185 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1186 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1187 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1188 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1189 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1191 done:
1192 if (view) msiobj_release(&view->hdr);
1193 if (row) msiobj_release(&row->hdr);
1194 return r;
1197 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1199 MSIRECORD *row;
1200 static const WCHAR query[] = {
1201 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1202 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1203 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1205 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1206 if (!row)
1208 WARN("query failed\n");
1209 return ERROR_FUNCTION_FAILED;
1212 file->disk_id = MSI_RecordGetInteger( row, 1 );
1213 msiobj_release( &row->hdr );
1214 return ERROR_SUCCESS;
1217 static UINT load_file(MSIRECORD *row, LPVOID param)
1219 MSIPACKAGE* package = param;
1220 LPCWSTR component;
1221 MSIFILE *file;
1223 /* fill in the data */
1225 file = msi_alloc_zero( sizeof (MSIFILE) );
1226 if (!file)
1227 return ERROR_NOT_ENOUGH_MEMORY;
1229 file->File = msi_dup_record_field( row, 1 );
1231 component = MSI_RecordGetString( row, 2 );
1232 file->Component = msi_get_loaded_component( package, component );
1234 if (!file->Component)
1236 WARN("Component not found: %s\n", debugstr_w(component));
1237 msi_free(file->File);
1238 msi_free(file);
1239 return ERROR_SUCCESS;
1242 file->FileName = msi_dup_record_field( row, 3 );
1243 msi_reduce_to_long_filename( file->FileName );
1245 file->ShortName = msi_dup_record_field( row, 3 );
1246 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1248 file->FileSize = MSI_RecordGetInteger( row, 4 );
1249 file->Version = msi_dup_record_field( row, 5 );
1250 file->Language = msi_dup_record_field( row, 6 );
1251 file->Attributes = MSI_RecordGetInteger( row, 7 );
1252 file->Sequence = MSI_RecordGetInteger( row, 8 );
1254 file->state = msifs_invalid;
1256 /* if the compressed bits are not set in the file attributes,
1257 * then read the information from the package word count property
1259 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1261 file->IsCompressed = FALSE;
1263 else if (file->Attributes &
1264 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1266 file->IsCompressed = TRUE;
1268 else if (file->Attributes & msidbFileAttributesNoncompressed)
1270 file->IsCompressed = FALSE;
1272 else
1274 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1277 load_file_hash(package, file);
1278 load_file_disk_id(package, file);
1280 TRACE("File loaded (file %s sequence %u)\n", debugstr_w(file->File), file->Sequence);
1282 list_add_tail( &package->files, &file->entry );
1284 return ERROR_SUCCESS;
1287 static UINT load_all_files(MSIPACKAGE *package)
1289 static const WCHAR query[] = {
1290 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1291 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1292 '`','S','e','q','u','e','n','c','e','`', 0};
1293 MSIQUERY *view;
1294 UINT rc;
1296 if (!list_empty(&package->files))
1297 return ERROR_SUCCESS;
1299 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1300 if (rc != ERROR_SUCCESS)
1301 return ERROR_SUCCESS;
1303 rc = MSI_IterateRecords(view, NULL, load_file, package);
1304 msiobj_release(&view->hdr);
1305 return rc;
1308 static UINT load_media( MSIRECORD *row, LPVOID param )
1310 MSIPACKAGE *package = param;
1311 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1312 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1314 /* FIXME: load external cabinets and directory sources too */
1315 if (!cabinet || cabinet[0] != '#' || disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
1316 return ERROR_SUCCESS;
1318 return msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1321 static UINT load_all_media( MSIPACKAGE *package )
1323 static const WCHAR query[] = {
1324 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1325 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1326 '`','D','i','s','k','I','d','`',0};
1327 MSIQUERY *view;
1328 UINT r;
1330 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1331 if (r != ERROR_SUCCESS)
1332 return ERROR_SUCCESS;
1334 r = MSI_IterateRecords( view, NULL, load_media, package );
1335 msiobj_release( &view->hdr );
1336 return r;
1339 static UINT load_patch_disk_id( MSIPACKAGE *package, MSIFILEPATCH *patch )
1341 static const WCHAR query[] =
1342 {'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1343 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1344 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','u',0};
1345 MSIRECORD *rec;
1347 if (!(rec = MSI_QueryGetRecord( package->db, query, patch->Sequence )))
1349 WARN("query failed\n");
1350 return ERROR_FUNCTION_FAILED;
1353 patch->disk_id = MSI_RecordGetInteger( rec, 1 );
1354 msiobj_release( &rec->hdr );
1355 return ERROR_SUCCESS;
1358 static UINT load_patch(MSIRECORD *row, LPVOID param)
1360 MSIPACKAGE *package = param;
1361 MSIFILEPATCH *patch;
1362 const WCHAR *file_key;
1364 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1365 if (!patch)
1366 return ERROR_NOT_ENOUGH_MEMORY;
1368 file_key = MSI_RecordGetString( row, 1 );
1369 patch->File = msi_get_loaded_file( package, file_key );
1370 if (!patch->File)
1372 ERR("Failed to find target for patch in File table\n");
1373 msi_free(patch);
1374 return ERROR_FUNCTION_FAILED;
1377 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1378 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1379 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1381 /* FIXME:
1382 * Header field - for patch validation.
1383 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1386 load_patch_disk_id( package, patch );
1388 TRACE("Patch loaded (file %s sequence %u)\n", debugstr_w(patch->File->File), patch->Sequence);
1390 list_add_tail( &package->filepatches, &patch->entry );
1392 return ERROR_SUCCESS;
1395 static UINT load_all_patches(MSIPACKAGE *package)
1397 static const WCHAR query[] = {
1398 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1399 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1400 '`','S','e','q','u','e','n','c','e','`',0};
1401 MSIQUERY *view;
1402 UINT rc;
1404 if (!list_empty(&package->filepatches))
1405 return ERROR_SUCCESS;
1407 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1408 if (rc != ERROR_SUCCESS)
1409 return ERROR_SUCCESS;
1411 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1412 msiobj_release(&view->hdr);
1413 return rc;
1416 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1418 static const WCHAR query[] = {
1419 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1420 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1421 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1422 MSIQUERY *view;
1424 folder->persistent = FALSE;
1425 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1427 if (!MSI_ViewExecute( view, NULL ))
1429 MSIRECORD *rec;
1430 if (!MSI_ViewFetch( view, &rec ))
1432 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1433 folder->persistent = TRUE;
1434 msiobj_release( &rec->hdr );
1437 msiobj_release( &view->hdr );
1439 return ERROR_SUCCESS;
1442 static UINT load_folder( MSIRECORD *row, LPVOID param )
1444 MSIPACKAGE *package = param;
1445 static WCHAR szEmpty[] = { 0 };
1446 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1447 MSIFOLDER *folder;
1449 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1450 list_init( &folder->children );
1451 folder->Directory = msi_dup_record_field( row, 1 );
1452 folder->Parent = msi_dup_record_field( row, 2 );
1453 p = msi_dup_record_field(row, 3);
1455 TRACE("%s\n", debugstr_w(folder->Directory));
1457 /* split src and target dir */
1458 tgt_short = p;
1459 src_short = folder_split_path( p, ':' );
1461 /* split the long and short paths */
1462 tgt_long = folder_split_path( tgt_short, '|' );
1463 src_long = folder_split_path( src_short, '|' );
1465 /* check for no-op dirs */
1466 if (tgt_short && !strcmpW( szDot, tgt_short ))
1467 tgt_short = szEmpty;
1468 if (src_short && !strcmpW( szDot, src_short ))
1469 src_short = szEmpty;
1471 if (!tgt_long)
1472 tgt_long = tgt_short;
1474 if (!src_short) {
1475 src_short = tgt_short;
1476 src_long = tgt_long;
1479 if (!src_long)
1480 src_long = src_short;
1482 /* FIXME: use the target short path too */
1483 folder->TargetDefault = strdupW(tgt_long);
1484 folder->SourceShortPath = strdupW(src_short);
1485 folder->SourceLongPath = strdupW(src_long);
1486 msi_free(p);
1488 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1489 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1490 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1492 load_folder_persistence( package, folder );
1494 list_add_tail( &package->folders, &folder->entry );
1495 return ERROR_SUCCESS;
1498 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1500 FolderList *fl;
1502 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1503 fl->folder = child;
1504 list_add_tail( &parent->children, &fl->entry );
1505 return ERROR_SUCCESS;
1508 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1510 MSIPACKAGE *package = param;
1511 MSIFOLDER *parent, *child;
1513 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1514 return ERROR_FUNCTION_FAILED;
1516 if (!child->Parent) return ERROR_SUCCESS;
1518 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1519 return ERROR_FUNCTION_FAILED;
1521 return add_folder_child( parent, child );
1524 static UINT load_all_folders( MSIPACKAGE *package )
1526 static const WCHAR query[] = {
1527 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1528 '`','D','i','r','e','c','t','o','r','y','`',0};
1529 MSIQUERY *view;
1530 UINT r;
1532 if (!list_empty(&package->folders))
1533 return ERROR_SUCCESS;
1535 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1536 if (r != ERROR_SUCCESS)
1537 return r;
1539 r = MSI_IterateRecords( view, NULL, load_folder, package );
1540 if (r != ERROR_SUCCESS)
1542 msiobj_release( &view->hdr );
1543 return r;
1545 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1546 msiobj_release( &view->hdr );
1547 return r;
1550 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1552 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1553 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1555 load_all_folders( package );
1556 msi_load_all_components( package );
1557 msi_load_all_features( package );
1558 load_all_files( package );
1559 load_all_patches( package );
1560 load_all_media( package );
1562 return ERROR_SUCCESS;
1565 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1567 const WCHAR *action = package->script->Actions[script][index];
1568 ui_actionstart( package, action );
1569 TRACE("executing %s\n", debugstr_w(action));
1570 return ACTION_PerformAction( package, action, script );
1573 static UINT execute_script( MSIPACKAGE *package, UINT script )
1575 UINT i, rc = ERROR_SUCCESS;
1577 TRACE("executing script %u\n", script);
1579 if (!package->script)
1581 ERR("no script!\n");
1582 return ERROR_FUNCTION_FAILED;
1584 if (script == SCRIPT_ROLLBACK)
1586 for (i = package->script->ActionCount[script]; i > 0; i--)
1588 rc = execute_script_action( package, script, i - 1 );
1589 if (rc != ERROR_SUCCESS) break;
1592 else
1594 for (i = 0; i < package->script->ActionCount[script]; i++)
1596 rc = execute_script_action( package, script, i );
1597 if (rc != ERROR_SUCCESS) break;
1600 msi_free_action_script(package, script);
1601 return rc;
1604 static UINT ACTION_FileCost(MSIPACKAGE *package)
1606 return ERROR_SUCCESS;
1609 static void get_client_counts( MSIPACKAGE *package )
1611 MSICOMPONENT *comp;
1612 HKEY hkey;
1614 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1616 if (!comp->ComponentId) continue;
1618 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1619 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1621 comp->num_clients = 0;
1622 continue;
1624 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1625 NULL, NULL, NULL, NULL );
1626 RegCloseKey( hkey );
1630 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1632 MSICOMPONENT *comp;
1633 UINT r;
1635 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1637 if (!comp->ComponentId) continue;
1639 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1640 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1641 &comp->Installed );
1642 if (r == ERROR_SUCCESS) continue;
1644 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1645 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1646 &comp->Installed );
1647 if (r == ERROR_SUCCESS) continue;
1649 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1650 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1651 &comp->Installed );
1652 if (r == ERROR_SUCCESS) continue;
1654 comp->Installed = INSTALLSTATE_ABSENT;
1658 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1660 MSIFEATURE *feature;
1662 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1664 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1666 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1667 feature->Installed = INSTALLSTATE_ABSENT;
1668 else
1669 feature->Installed = state;
1673 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1675 return (feature->Level > 0 && feature->Level <= level);
1678 static BOOL process_state_property(MSIPACKAGE* package, int level,
1679 LPCWSTR property, INSTALLSTATE state)
1681 LPWSTR override;
1682 MSIFEATURE *feature;
1683 BOOL remove = !strcmpW(property, szRemove);
1684 BOOL reinstall = !strcmpW(property, szReinstall);
1686 override = msi_dup_property( package->db, property );
1687 if (!override)
1688 return FALSE;
1690 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1692 if (feature->Level <= 0)
1693 continue;
1695 if (reinstall)
1696 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1697 else if (remove)
1698 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1700 if (!strcmpiW( override, szAll ))
1702 feature->Action = state;
1703 feature->ActionRequest = state;
1705 else
1707 LPWSTR ptr = override;
1708 LPWSTR ptr2 = strchrW(override,',');
1710 while (ptr)
1712 int len = ptr2 - ptr;
1714 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1715 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1717 feature->Action = state;
1718 feature->ActionRequest = state;
1719 break;
1721 if (ptr2)
1723 ptr=ptr2+1;
1724 ptr2 = strchrW(ptr,',');
1726 else
1727 break;
1731 msi_free(override);
1732 return TRUE;
1735 static BOOL process_overrides( MSIPACKAGE *package, int level )
1737 static const WCHAR szAddLocal[] =
1738 {'A','D','D','L','O','C','A','L',0};
1739 static const WCHAR szAddSource[] =
1740 {'A','D','D','S','O','U','R','C','E',0};
1741 static const WCHAR szAdvertise[] =
1742 {'A','D','V','E','R','T','I','S','E',0};
1743 BOOL ret = FALSE;
1745 /* all these activation/deactivation things happen in order and things
1746 * later on the list override things earlier on the list.
1748 * 0 INSTALLLEVEL processing
1749 * 1 ADDLOCAL
1750 * 2 REMOVE
1751 * 3 ADDSOURCE
1752 * 4 ADDDEFAULT
1753 * 5 REINSTALL
1754 * 6 ADVERTISE
1755 * 7 COMPADDLOCAL
1756 * 8 COMPADDSOURCE
1757 * 9 FILEADDLOCAL
1758 * 10 FILEADDSOURCE
1759 * 11 FILEADDDEFAULT
1761 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1762 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1763 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1764 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1765 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1767 if (ret && !package->full_reinstall)
1768 msi_set_property( package->db, szPreselected, szOne, -1 );
1770 return ret;
1773 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1775 int level;
1776 MSICOMPONENT* component;
1777 MSIFEATURE *feature;
1779 TRACE("Checking Install Level\n");
1781 level = msi_get_property_int(package->db, szInstallLevel, 1);
1783 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1785 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1787 if (!is_feature_selected( feature, level )) continue;
1789 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1791 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1793 feature->Action = INSTALLSTATE_SOURCE;
1794 feature->ActionRequest = INSTALLSTATE_SOURCE;
1796 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1798 feature->Action = INSTALLSTATE_ADVERTISED;
1799 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1801 else
1803 feature->Action = INSTALLSTATE_LOCAL;
1804 feature->ActionRequest = INSTALLSTATE_LOCAL;
1808 /* disable child features of unselected parent or follow parent */
1809 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1811 FeatureList *fl;
1813 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1815 if (!is_feature_selected( feature, level ))
1817 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1818 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1820 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1822 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1823 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1824 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1825 fl->feature->Action = feature->Action;
1826 fl->feature->ActionRequest = feature->ActionRequest;
1831 else /* preselected */
1833 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1835 if (!is_feature_selected( feature, level )) continue;
1837 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1839 if (feature->Installed == INSTALLSTATE_ABSENT)
1841 feature->Action = INSTALLSTATE_UNKNOWN;
1842 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1844 else
1846 feature->Action = feature->Installed;
1847 feature->ActionRequest = feature->Installed;
1851 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1853 FeatureList *fl;
1855 if (!is_feature_selected( feature, level )) continue;
1857 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1859 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1860 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1862 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1863 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1864 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1865 fl->feature->Action = feature->Action;
1866 fl->feature->ActionRequest = feature->ActionRequest;
1872 /* now we want to set component state based based on feature state */
1873 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1875 ComponentList *cl;
1877 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1878 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1879 feature->ActionRequest, feature->Action);
1881 /* features with components that have compressed files are made local */
1882 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1884 if (cl->component->ForceLocalState &&
1885 feature->ActionRequest == INSTALLSTATE_SOURCE)
1887 feature->Action = INSTALLSTATE_LOCAL;
1888 feature->ActionRequest = INSTALLSTATE_LOCAL;
1889 break;
1893 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1895 component = cl->component;
1897 switch (feature->ActionRequest)
1899 case INSTALLSTATE_ABSENT:
1900 component->anyAbsent = 1;
1901 break;
1902 case INSTALLSTATE_ADVERTISED:
1903 component->hasAdvertisedFeature = 1;
1904 break;
1905 case INSTALLSTATE_SOURCE:
1906 component->hasSourceFeature = 1;
1907 break;
1908 case INSTALLSTATE_LOCAL:
1909 component->hasLocalFeature = 1;
1910 break;
1911 case INSTALLSTATE_DEFAULT:
1912 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1913 component->hasAdvertisedFeature = 1;
1914 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1915 component->hasSourceFeature = 1;
1916 else
1917 component->hasLocalFeature = 1;
1918 break;
1919 default:
1920 break;
1925 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1927 /* check if it's local or source */
1928 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1929 (component->hasLocalFeature || component->hasSourceFeature))
1931 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1932 !component->ForceLocalState)
1934 component->Action = INSTALLSTATE_SOURCE;
1935 component->ActionRequest = INSTALLSTATE_SOURCE;
1937 else
1939 component->Action = INSTALLSTATE_LOCAL;
1940 component->ActionRequest = INSTALLSTATE_LOCAL;
1942 continue;
1945 /* if any feature is local, the component must be local too */
1946 if (component->hasLocalFeature)
1948 component->Action = INSTALLSTATE_LOCAL;
1949 component->ActionRequest = INSTALLSTATE_LOCAL;
1950 continue;
1952 if (component->hasSourceFeature)
1954 component->Action = INSTALLSTATE_SOURCE;
1955 component->ActionRequest = INSTALLSTATE_SOURCE;
1956 continue;
1958 if (component->hasAdvertisedFeature)
1960 component->Action = INSTALLSTATE_ADVERTISED;
1961 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1962 continue;
1964 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1965 if (component->anyAbsent && component->ComponentId)
1967 component->Action = INSTALLSTATE_ABSENT;
1968 component->ActionRequest = INSTALLSTATE_ABSENT;
1972 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1974 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1976 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1977 component->Action = INSTALLSTATE_LOCAL;
1978 component->ActionRequest = INSTALLSTATE_LOCAL;
1981 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1982 component->Installed == INSTALLSTATE_SOURCE &&
1983 component->hasSourceFeature)
1985 component->Action = INSTALLSTATE_UNKNOWN;
1986 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1989 TRACE("component %s (installed %d request %d action %d)\n",
1990 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1992 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1993 component->num_clients++;
1994 else if (component->Action == INSTALLSTATE_ABSENT)
1995 component->num_clients--;
1998 return ERROR_SUCCESS;
2001 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2003 MSIPACKAGE *package = param;
2004 LPCWSTR name;
2005 MSIFEATURE *feature;
2007 name = MSI_RecordGetString( row, 1 );
2009 feature = msi_get_loaded_feature( package, name );
2010 if (!feature)
2011 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2012 else
2014 LPCWSTR Condition;
2015 Condition = MSI_RecordGetString(row,3);
2017 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2019 int level = MSI_RecordGetInteger(row,2);
2020 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2021 feature->Level = level;
2024 return ERROR_SUCCESS;
2027 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2029 static const WCHAR name[] = {'\\',0};
2030 VS_FIXEDFILEINFO *ptr, *ret;
2031 LPVOID version;
2032 DWORD versize, handle;
2033 UINT sz;
2035 versize = GetFileVersionInfoSizeW( filename, &handle );
2036 if (!versize)
2037 return NULL;
2039 version = msi_alloc( versize );
2040 if (!version)
2041 return NULL;
2043 GetFileVersionInfoW( filename, 0, versize, version );
2045 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2047 msi_free( version );
2048 return NULL;
2051 ret = msi_alloc( sz );
2052 memcpy( ret, ptr, sz );
2054 msi_free( version );
2055 return ret;
2058 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2060 DWORD ms, ls;
2062 msi_parse_version_string( version, &ms, &ls );
2064 if (fi->dwFileVersionMS > ms) return 1;
2065 else if (fi->dwFileVersionMS < ms) return -1;
2066 else if (fi->dwFileVersionLS > ls) return 1;
2067 else if (fi->dwFileVersionLS < ls) return -1;
2068 return 0;
2071 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2073 DWORD ms1, ms2;
2075 msi_parse_version_string( ver1, &ms1, NULL );
2076 msi_parse_version_string( ver2, &ms2, NULL );
2078 if (ms1 > ms2) return 1;
2079 else if (ms1 < ms2) return -1;
2080 return 0;
2083 DWORD msi_get_disk_file_size( LPCWSTR filename )
2085 HANDLE file;
2086 DWORD size;
2088 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2089 if (file == INVALID_HANDLE_VALUE)
2090 return INVALID_FILE_SIZE;
2092 size = GetFileSize( file, NULL );
2093 CloseHandle( file );
2094 return size;
2097 BOOL msi_file_hash_matches( MSIFILE *file )
2099 UINT r;
2100 MSIFILEHASHINFO hash;
2102 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2103 r = msi_get_filehash( file->TargetPath, &hash );
2104 if (r != ERROR_SUCCESS)
2105 return FALSE;
2107 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2110 static WCHAR *create_temp_dir( MSIDATABASE *db )
2112 static UINT id;
2113 WCHAR *ret;
2115 if (!db->tempfolder)
2117 WCHAR tmp[MAX_PATH];
2118 UINT len = sizeof(tmp)/sizeof(tmp[0]);
2120 if (msi_get_property( db, szTempFolder, tmp, &len ) ||
2121 GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY)
2123 GetTempPathW( MAX_PATH, tmp );
2125 if (!(db->tempfolder = strdupW( tmp ))) return NULL;
2128 if ((ret = msi_alloc( (strlenW( db->tempfolder ) + 20) * sizeof(WCHAR) )))
2130 for (;;)
2132 if (!GetTempFileNameW( db->tempfolder, szMsi, ++id, ret ))
2134 msi_free( ret );
2135 return NULL;
2137 if (CreateDirectoryW( ret, NULL )) break;
2141 return ret;
2145 * msi_build_directory_name()
2147 * This function is to save messing round with directory names
2148 * It handles adding backslashes between path segments,
2149 * and can add \ at the end of the directory name if told to.
2151 * It takes a variable number of arguments.
2152 * It always allocates a new string for the result, so make sure
2153 * to free the return value when finished with it.
2155 * The first arg is the number of path segments that follow.
2156 * The arguments following count are a list of path segments.
2157 * A path segment may be NULL.
2159 * Path segments will be added with a \ separating them.
2160 * A \ will not be added after the last segment, however if the
2161 * last segment is NULL, then the last character will be a \
2163 WCHAR *msi_build_directory_name( DWORD count, ... )
2165 DWORD sz = 1, i;
2166 WCHAR *dir;
2167 va_list va;
2169 va_start( va, count );
2170 for (i = 0; i < count; i++)
2172 const WCHAR *str = va_arg( va, const WCHAR * );
2173 if (str) sz += strlenW( str ) + 1;
2175 va_end( va );
2177 dir = msi_alloc( sz * sizeof(WCHAR) );
2178 dir[0] = 0;
2180 va_start( va, count );
2181 for (i = 0; i < count; i++)
2183 const WCHAR *str = va_arg( va, const WCHAR * );
2184 if (!str) continue;
2185 strcatW( dir, str );
2186 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2188 va_end( va );
2189 return dir;
2192 BOOL msi_is_global_assembly( MSICOMPONENT *comp )
2194 return comp->assembly && !comp->assembly->application;
2197 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2199 msi_free( file->TargetPath );
2200 if (msi_is_global_assembly( file->Component ))
2202 MSIASSEMBLY *assembly = file->Component->assembly;
2204 if (!assembly->tempdir) assembly->tempdir = create_temp_dir( package->db );
2205 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2207 else
2209 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2210 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2213 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2216 static UINT calculate_file_cost( MSIPACKAGE *package )
2218 VS_FIXEDFILEINFO *file_version;
2219 WCHAR *font_version;
2220 MSIFILE *file;
2222 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2224 MSICOMPONENT *comp = file->Component;
2225 DWORD file_size;
2227 if (!comp->Enabled) continue;
2229 if (file->IsCompressed)
2230 comp->ForceLocalState = TRUE;
2232 set_target_path( package, file );
2234 if ((comp->assembly && !comp->assembly->installed) ||
2235 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2237 comp->Cost += file->FileSize;
2238 continue;
2240 file_size = msi_get_disk_file_size( file->TargetPath );
2241 TRACE("%s (size %u)\n", debugstr_w(file->TargetPath), file_size);
2243 if (file->Version)
2245 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2247 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2249 comp->Cost += file->FileSize - file_size;
2251 msi_free( file_version );
2252 continue;
2254 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2256 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2258 comp->Cost += file->FileSize - file_size;
2260 msi_free( font_version );
2261 continue;
2264 if (file_size != file->FileSize)
2266 comp->Cost += file->FileSize - file_size;
2269 return ERROR_SUCCESS;
2272 WCHAR *msi_normalize_path( const WCHAR *in )
2274 const WCHAR *p = in;
2275 WCHAR *q, *ret;
2276 int n, len = strlenW( in ) + 2;
2278 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2280 len = 0;
2281 while (1)
2283 /* copy until the end of the string or a space */
2284 while (*p != ' ' && (*q = *p))
2286 p++, len++;
2287 /* reduce many backslashes to one */
2288 if (*p != '\\' || *q != '\\')
2289 q++;
2292 /* quit at the end of the string */
2293 if (!*p)
2294 break;
2296 /* count the number of spaces */
2297 n = 0;
2298 while (p[n] == ' ')
2299 n++;
2301 /* if it's leading or trailing space, skip it */
2302 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2303 p += n;
2304 else /* copy n spaces */
2305 while (n && (*q++ = *p++)) n--;
2307 while (q - ret > 0 && q[-1] == ' ') q--;
2308 if (q - ret > 0 && q[-1] != '\\')
2310 q[0] = '\\';
2311 q[1] = 0;
2313 return ret;
2316 static WCHAR *get_install_location( MSIPACKAGE *package )
2318 HKEY hkey;
2319 WCHAR *path;
2321 if (!package->ProductCode) return NULL;
2322 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2323 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2325 msi_free( path );
2326 path = NULL;
2328 RegCloseKey( hkey );
2329 return path;
2332 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2334 FolderList *fl;
2335 MSIFOLDER *folder, *parent, *child;
2336 WCHAR *path, *normalized_path;
2338 TRACE("resolving %s\n", debugstr_w(name));
2340 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2342 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2344 if (!(path = get_install_location( package )) &&
2345 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2347 path = msi_dup_property( package->db, szRootDrive );
2350 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2352 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2354 parent = msi_get_loaded_folder( package, folder->Parent );
2355 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2357 else
2358 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2360 normalized_path = msi_normalize_path( path );
2361 msi_free( path );
2362 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2364 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2365 msi_free( normalized_path );
2366 return;
2368 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2369 msi_free( folder->ResolvedTarget );
2370 folder->ResolvedTarget = normalized_path;
2372 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2374 child = fl->folder;
2375 msi_resolve_target_folder( package, child->Directory, load_prop );
2377 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2380 static ULONGLONG get_volume_space_required( MSIPACKAGE *package )
2382 MSICOMPONENT *comp;
2383 ULONGLONG ret = 0;
2385 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2387 if (comp->Action == INSTALLSTATE_LOCAL) ret += comp->Cost;
2389 return ret;
2392 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2394 static const WCHAR query[] =
2395 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2396 '`','C','o','n','d','i','t','i','o','n','`',0};
2397 static const WCHAR szOutOfDiskSpace[] =
2398 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2399 static const WCHAR szPrimaryFolder[] =
2400 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2401 static const WCHAR szPrimaryVolumePath[] =
2402 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2403 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2404 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2405 'A','v','a','i','l','a','b','l','e',0};
2406 static const WCHAR szPrimaryVolumeSpaceRequired[] =
2407 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2408 'R','e','q','u','i','r','e','d',0};
2409 static const WCHAR szPrimaryVolumeSpaceRemaining[] =
2410 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2411 'R','e','m','a','i','n','i','n','g',0};
2412 static const WCHAR szOutOfNoRbDiskSpace[] =
2413 {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0};
2414 MSICOMPONENT *comp;
2415 MSIQUERY *view;
2416 WCHAR *level, *primary_key, *primary_folder;
2417 UINT rc;
2419 TRACE("Building directory properties\n");
2420 msi_resolve_target_folder( package, szTargetDir, TRUE );
2422 TRACE("Evaluating component conditions\n");
2423 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2425 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2427 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2428 comp->Enabled = FALSE;
2430 else
2431 comp->Enabled = TRUE;
2433 get_client_counts( package );
2435 /* read components states from the registry */
2436 ACTION_GetComponentInstallStates(package);
2437 ACTION_GetFeatureInstallStates(package);
2439 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2441 TRACE("Evaluating feature conditions\n");
2443 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2444 if (rc == ERROR_SUCCESS)
2446 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2447 msiobj_release( &view->hdr );
2448 if (rc != ERROR_SUCCESS)
2449 return rc;
2453 TRACE("Calculating file cost\n");
2454 calculate_file_cost( package );
2456 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2457 /* set default run level if not set */
2458 level = msi_dup_property( package->db, szInstallLevel );
2459 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2460 msi_free(level);
2462 if ((rc = MSI_SetFeatureStates( package ))) return rc;
2464 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2466 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2468 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2469 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2471 static const WCHAR fmtW[] = {'%','l','u',0};
2472 ULARGE_INTEGER free;
2473 ULONGLONG required;
2474 WCHAR buf[21];
2476 primary_folder[2] = 0;
2477 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2479 sprintfW( buf, fmtW, free.QuadPart / 512 );
2480 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2482 required = get_volume_space_required( package );
2483 sprintfW( buf, fmtW, required / 512 );
2484 msi_set_property( package->db, szPrimaryVolumeSpaceRequired, buf, -1 );
2486 sprintfW( buf, fmtW, (free.QuadPart - required) / 512 );
2487 msi_set_property( package->db, szPrimaryVolumeSpaceRemaining, buf, -1 );
2488 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2490 msi_free( primary_folder );
2492 msi_free( primary_key );
2495 /* FIXME: check volume disk space */
2496 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2497 msi_set_property( package->db, szOutOfNoRbDiskSpace, szZero, -1 );
2499 return ERROR_SUCCESS;
2502 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size )
2504 BYTE *data;
2506 if (!value)
2508 *size = sizeof(WCHAR);
2509 *type = REG_SZ;
2510 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2511 return data;
2513 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2515 if (value[1]=='x')
2517 LPWSTR ptr;
2518 CHAR byte[5];
2519 LPWSTR deformated = NULL;
2520 int count;
2522 deformat_string(package, &value[2], &deformated);
2524 /* binary value type */
2525 ptr = deformated;
2526 *type = REG_BINARY;
2527 if (strlenW(ptr)%2)
2528 *size = (strlenW(ptr)/2)+1;
2529 else
2530 *size = strlenW(ptr)/2;
2532 data = msi_alloc(*size);
2534 byte[0] = '0';
2535 byte[1] = 'x';
2536 byte[4] = 0;
2537 count = 0;
2538 /* if uneven pad with a zero in front */
2539 if (strlenW(ptr)%2)
2541 byte[2]= '0';
2542 byte[3]= *ptr;
2543 ptr++;
2544 data[count] = (BYTE)strtol(byte,NULL,0);
2545 count ++;
2546 TRACE("Uneven byte count\n");
2548 while (*ptr)
2550 byte[2]= *ptr;
2551 ptr++;
2552 byte[3]= *ptr;
2553 ptr++;
2554 data[count] = (BYTE)strtol(byte,NULL,0);
2555 count ++;
2557 msi_free(deformated);
2559 TRACE("Data %i bytes(%i)\n",*size,count);
2561 else
2563 LPWSTR deformated;
2564 LPWSTR p;
2565 DWORD d = 0;
2566 deformat_string(package, &value[1], &deformated);
2568 *type=REG_DWORD;
2569 *size = sizeof(DWORD);
2570 data = msi_alloc(*size);
2571 p = deformated;
2572 if (*p == '-')
2573 p++;
2574 while (*p)
2576 if ( (*p < '0') || (*p > '9') )
2577 break;
2578 d *= 10;
2579 d += (*p - '0');
2580 p++;
2582 if (deformated[0] == '-')
2583 d = -d;
2584 *(LPDWORD)data = d;
2585 TRACE("DWORD %i\n",*(LPDWORD)data);
2587 msi_free(deformated);
2590 else
2592 const WCHAR *ptr = value;
2594 *type = REG_SZ;
2595 if (value[0] == '#')
2597 ptr++; len--;
2598 if (value[1] == '%')
2600 ptr++; len--;
2601 *type = REG_EXPAND_SZ;
2604 data = (BYTE *)msi_strdupW( ptr, len );
2605 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2606 *size = (len + 1) * sizeof(WCHAR);
2608 return data;
2611 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2613 const WCHAR *ret;
2615 switch (root)
2617 case -1:
2618 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2620 *root_key = HKEY_LOCAL_MACHINE;
2621 ret = szHLM;
2623 else
2625 *root_key = HKEY_CURRENT_USER;
2626 ret = szHCU;
2628 break;
2629 case 0:
2630 *root_key = HKEY_CLASSES_ROOT;
2631 ret = szHCR;
2632 break;
2633 case 1:
2634 *root_key = HKEY_CURRENT_USER;
2635 ret = szHCU;
2636 break;
2637 case 2:
2638 *root_key = HKEY_LOCAL_MACHINE;
2639 ret = szHLM;
2640 break;
2641 case 3:
2642 *root_key = HKEY_USERS;
2643 ret = szHU;
2644 break;
2645 default:
2646 ERR("Unknown root %i\n", root);
2647 return NULL;
2650 return ret;
2653 static inline REGSAM get_registry_view( const MSICOMPONENT *comp )
2655 REGSAM view = 0;
2656 if (is_wow64 || is_64bit)
2657 view |= (comp->Attributes & msidbComponentAttributes64bit) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
2658 return view;
2661 static HKEY open_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, BOOL create, REGSAM access )
2663 WCHAR *subkey, *p, *q;
2664 HKEY hkey, ret = NULL;
2665 LONG res;
2667 access |= get_registry_view( comp );
2669 if (!(subkey = strdupW( path ))) return NULL;
2670 p = subkey;
2671 if ((q = strchrW( p, '\\' ))) *q = 0;
2672 if (create)
2673 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2674 else
2675 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2676 if (res)
2678 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2679 msi_free( subkey );
2680 return NULL;
2682 if (q && q[1])
2684 ret = open_key( comp, hkey, q + 1, create, access );
2685 RegCloseKey( hkey );
2687 else ret = hkey;
2688 msi_free( subkey );
2689 return ret;
2692 static BOOL is_special_entry( const WCHAR *name )
2694 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2697 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2699 const WCHAR *p = str;
2700 WCHAR **ret;
2701 int i = 0;
2703 *count = 0;
2704 if (!str) return NULL;
2705 while ((p - str) < len)
2707 p += strlenW( p ) + 1;
2708 (*count)++;
2710 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2711 p = str;
2712 while ((p - str) < len)
2714 if (!(ret[i] = strdupW( p )))
2716 for (; i >= 0; i--) msi_free( ret[i] );
2717 msi_free( ret );
2718 return NULL;
2720 p += strlenW( p ) + 1;
2721 i++;
2723 return ret;
2726 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2727 WCHAR **right, DWORD right_count, DWORD *size )
2729 WCHAR *ret, *p;
2730 unsigned int i;
2732 *size = sizeof(WCHAR);
2733 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2734 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2736 if (!(ret = p = msi_alloc( *size ))) return NULL;
2738 for (i = 0; i < left_count; i++)
2740 strcpyW( p, left[i] );
2741 p += strlenW( p ) + 1;
2743 for (i = 0; i < right_count; i++)
2745 strcpyW( p, right[i] );
2746 p += strlenW( p ) + 1;
2748 *p = 0;
2749 return ret;
2752 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2753 WCHAR **new, DWORD new_count )
2755 DWORD ret = old_count;
2756 unsigned int i, j, k;
2758 for (i = 0; i < new_count; i++)
2760 for (j = 0; j < old_count; j++)
2762 if (old[j] && !strcmpW( new[i], old[j] ))
2764 msi_free( old[j] );
2765 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2766 old[k] = NULL;
2767 ret--;
2771 return ret;
2774 enum join_op
2776 JOIN_OP_APPEND,
2777 JOIN_OP_PREPEND,
2778 JOIN_OP_REPLACE
2781 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2782 WCHAR **new, DWORD new_count, DWORD *size )
2784 switch (op)
2786 case JOIN_OP_APPEND:
2787 old_count = remove_duplicate_values( old, old_count, new, new_count );
2788 return flatten_multi_string_values( old, old_count, new, new_count, size );
2790 case JOIN_OP_PREPEND:
2791 old_count = remove_duplicate_values( old, old_count, new, new_count );
2792 return flatten_multi_string_values( new, new_count, old, old_count, size );
2794 case JOIN_OP_REPLACE:
2795 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2797 default:
2798 ERR("unhandled join op %u\n", op);
2799 return NULL;
2803 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2804 BYTE *new_value, DWORD new_size, DWORD *size )
2806 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2807 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2808 enum join_op op = JOIN_OP_REPLACE;
2809 WCHAR **old = NULL, **new = NULL;
2810 BYTE *ret;
2812 if (new_size / sizeof(WCHAR) - 1 > 1)
2814 new_ptr = (const WCHAR *)new_value;
2815 new_len = new_size / sizeof(WCHAR) - 1;
2817 if (!new_ptr[0] && new_ptr[new_len - 1])
2819 op = JOIN_OP_APPEND;
2820 new_len--;
2821 new_ptr++;
2823 else if (new_ptr[0] && !new_ptr[new_len - 1])
2825 op = JOIN_OP_PREPEND;
2826 new_len--;
2828 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2830 op = JOIN_OP_REPLACE;
2831 new_len -= 2;
2832 new_ptr++;
2834 new = split_multi_string_values( new_ptr, new_len, &new_count );
2836 if (old_size / sizeof(WCHAR) - 1 > 1)
2838 old_ptr = (const WCHAR *)old_value;
2839 old_len = old_size / sizeof(WCHAR) - 1;
2840 old = split_multi_string_values( old_ptr, old_len, &old_count );
2842 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2843 for (i = 0; i < old_count; i++) msi_free( old[i] );
2844 for (i = 0; i < new_count; i++) msi_free( new[i] );
2845 msi_free( old );
2846 msi_free( new );
2847 return ret;
2850 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2852 BYTE *ret;
2853 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2854 if (!(ret = msi_alloc( *size ))) return NULL;
2855 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2856 return ret;
2859 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2861 MSIPACKAGE *package = param;
2862 BYTE *new_value, *old_value = NULL;
2863 HKEY root_key, hkey;
2864 DWORD type, old_type, new_size, old_size = 0;
2865 LPWSTR deformated, uikey;
2866 const WCHAR *szRoot, *component, *name, *key, *str;
2867 MSICOMPONENT *comp;
2868 MSIRECORD * uirow;
2869 INT root;
2870 BOOL check_first = FALSE;
2871 int len;
2873 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2875 component = MSI_RecordGetString(row, 6);
2876 comp = msi_get_loaded_component(package,component);
2877 if (!comp)
2878 return ERROR_SUCCESS;
2880 comp->Action = msi_get_component_action( package, comp );
2881 if (comp->Action != INSTALLSTATE_LOCAL)
2883 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2884 return ERROR_SUCCESS;
2887 name = MSI_RecordGetString(row, 4);
2888 if( MSI_RecordIsNull(row,5) && name )
2890 /* null values can have special meanings */
2891 if (name[0]=='-' && name[1] == 0)
2892 return ERROR_SUCCESS;
2893 if ((name[0] == '+' || name[0] == '*') && !name[1])
2894 check_first = TRUE;
2897 root = MSI_RecordGetInteger(row,2);
2898 key = MSI_RecordGetString(row, 3);
2900 szRoot = get_root_key( package, root, &root_key );
2901 if (!szRoot)
2902 return ERROR_SUCCESS;
2904 deformat_string(package, key , &deformated);
2905 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2906 strcpyW(uikey,szRoot);
2907 strcatW(uikey,deformated);
2909 if (!(hkey = open_key( comp, root_key, deformated, TRUE, KEY_QUERY_VALUE | KEY_SET_VALUE )))
2911 ERR("Could not create key %s\n", debugstr_w(deformated));
2912 msi_free(uikey);
2913 msi_free(deformated);
2914 return ERROR_FUNCTION_FAILED;
2916 msi_free( deformated );
2917 str = msi_record_get_string( row, 5, NULL );
2918 len = deformat_string( package, str, &deformated );
2919 new_value = parse_value( package, deformated, len, &type, &new_size );
2921 msi_free( deformated );
2922 deformat_string(package, name, &deformated);
2924 if (!is_special_entry( name ))
2926 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2927 if (type == REG_MULTI_SZ)
2929 BYTE *new;
2930 if (old_value && old_type != REG_MULTI_SZ)
2932 msi_free( old_value );
2933 old_value = NULL;
2934 old_size = 0;
2936 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2937 msi_free( new_value );
2938 new_value = new;
2940 if (!check_first)
2942 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2943 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2945 else if (!old_value)
2947 if (deformated || new_size)
2949 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2950 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2953 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2955 RegCloseKey(hkey);
2957 uirow = MSI_CreateRecord(3);
2958 MSI_RecordSetStringW(uirow,2,deformated);
2959 MSI_RecordSetStringW(uirow,1,uikey);
2960 if (type == REG_SZ || type == REG_EXPAND_SZ)
2961 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2962 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2963 msiobj_release( &uirow->hdr );
2965 msi_free(new_value);
2966 msi_free(old_value);
2967 msi_free(deformated);
2968 msi_free(uikey);
2970 return ERROR_SUCCESS;
2973 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2975 static const WCHAR query[] = {
2976 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2977 '`','R','e','g','i','s','t','r','y','`',0};
2978 MSIQUERY *view;
2979 UINT rc;
2981 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2982 if (rc != ERROR_SUCCESS)
2983 return ERROR_SUCCESS;
2985 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2986 msiobj_release(&view->hdr);
2987 return rc;
2990 static void delete_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2992 REGSAM access = 0;
2993 WCHAR *subkey, *p;
2994 HKEY hkey;
2995 LONG res;
2997 access |= get_registry_view( comp );
2999 if (!(subkey = strdupW( path ))) return;
3002 if ((p = strrchrW( subkey, '\\' )))
3004 *p = 0;
3005 if (!p[1]) continue; /* trailing backslash */
3006 hkey = open_key( comp, root, subkey, FALSE, access | READ_CONTROL );
3007 if (!hkey) break;
3008 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
3009 RegCloseKey( hkey );
3011 else
3012 res = RegDeleteKeyExW( root, subkey, access, 0 );
3013 if (res)
3015 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
3016 break;
3018 } while (p);
3019 msi_free( subkey );
3022 static void delete_value( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, const WCHAR *value )
3024 LONG res;
3025 HKEY hkey;
3026 DWORD num_subkeys, num_values;
3028 if ((hkey = open_key( comp, root, path, FALSE, KEY_SET_VALUE | KEY_QUERY_VALUE )))
3030 if ((res = RegDeleteValueW( hkey, value )))
3031 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
3033 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
3034 NULL, NULL, NULL, NULL );
3035 RegCloseKey( hkey );
3036 if (!res && !num_subkeys && !num_values)
3038 TRACE("removing empty key %s\n", debugstr_w(path));
3039 delete_key( comp, root, path );
3044 static void delete_tree( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
3046 LONG res;
3047 HKEY hkey;
3049 if (!(hkey = open_key( comp, root, path, FALSE, KEY_ALL_ACCESS ))) return;
3050 res = RegDeleteTreeW( hkey, NULL );
3051 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3052 delete_key( comp, root, path );
3053 RegCloseKey( hkey );
3056 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3058 MSIPACKAGE *package = param;
3059 LPCWSTR component, name, key_str, root_key_str;
3060 LPWSTR deformated_key, deformated_name, ui_key_str;
3061 MSICOMPONENT *comp;
3062 MSIRECORD *uirow;
3063 BOOL delete_key = FALSE;
3064 HKEY hkey_root;
3065 UINT size;
3066 INT root;
3068 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3070 component = MSI_RecordGetString( row, 6 );
3071 comp = msi_get_loaded_component( package, component );
3072 if (!comp)
3073 return ERROR_SUCCESS;
3075 comp->Action = msi_get_component_action( package, comp );
3076 if (comp->Action != INSTALLSTATE_ABSENT)
3078 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3079 return ERROR_SUCCESS;
3082 name = MSI_RecordGetString( row, 4 );
3083 if (MSI_RecordIsNull( row, 5 ) && name )
3085 if (name[0] == '+' && !name[1])
3086 return ERROR_SUCCESS;
3087 if ((name[0] == '-' || name[0] == '*') && !name[1])
3089 delete_key = TRUE;
3090 name = NULL;
3094 root = MSI_RecordGetInteger( row, 2 );
3095 key_str = MSI_RecordGetString( row, 3 );
3097 root_key_str = get_root_key( package, root, &hkey_root );
3098 if (!root_key_str)
3099 return ERROR_SUCCESS;
3101 deformat_string( package, key_str, &deformated_key );
3102 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3103 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3104 strcpyW( ui_key_str, root_key_str );
3105 strcatW( ui_key_str, deformated_key );
3107 deformat_string( package, name, &deformated_name );
3109 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3110 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3111 msi_free( deformated_key );
3113 uirow = MSI_CreateRecord( 2 );
3114 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3115 MSI_RecordSetStringW( uirow, 2, deformated_name );
3116 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3117 msiobj_release( &uirow->hdr );
3119 msi_free( ui_key_str );
3120 msi_free( deformated_name );
3121 return ERROR_SUCCESS;
3124 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3126 MSIPACKAGE *package = param;
3127 LPCWSTR component, name, key_str, root_key_str;
3128 LPWSTR deformated_key, deformated_name, ui_key_str;
3129 MSICOMPONENT *comp;
3130 MSIRECORD *uirow;
3131 BOOL delete_key = FALSE;
3132 HKEY hkey_root;
3133 UINT size;
3134 INT root;
3136 component = MSI_RecordGetString( row, 5 );
3137 comp = msi_get_loaded_component( package, component );
3138 if (!comp)
3139 return ERROR_SUCCESS;
3141 comp->Action = msi_get_component_action( package, comp );
3142 if (comp->Action != INSTALLSTATE_LOCAL)
3144 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3145 return ERROR_SUCCESS;
3148 if ((name = MSI_RecordGetString( row, 4 )))
3150 if (name[0] == '-' && !name[1])
3152 delete_key = TRUE;
3153 name = NULL;
3157 root = MSI_RecordGetInteger( row, 2 );
3158 key_str = MSI_RecordGetString( row, 3 );
3160 root_key_str = get_root_key( package, root, &hkey_root );
3161 if (!root_key_str)
3162 return ERROR_SUCCESS;
3164 deformat_string( package, key_str, &deformated_key );
3165 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3166 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3167 strcpyW( ui_key_str, root_key_str );
3168 strcatW( ui_key_str, deformated_key );
3170 deformat_string( package, name, &deformated_name );
3172 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3173 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3174 msi_free( deformated_key );
3176 uirow = MSI_CreateRecord( 2 );
3177 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3178 MSI_RecordSetStringW( uirow, 2, deformated_name );
3179 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3180 msiobj_release( &uirow->hdr );
3182 msi_free( ui_key_str );
3183 msi_free( deformated_name );
3184 return ERROR_SUCCESS;
3187 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3189 static const WCHAR registry_query[] = {
3190 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3191 '`','R','e','g','i','s','t','r','y','`',0};
3192 static const WCHAR remove_registry_query[] = {
3193 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3194 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3195 MSIQUERY *view;
3196 UINT rc;
3198 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3199 if (rc == ERROR_SUCCESS)
3201 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3202 msiobj_release( &view->hdr );
3203 if (rc != ERROR_SUCCESS)
3204 return rc;
3206 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3207 if (rc == ERROR_SUCCESS)
3209 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3210 msiobj_release( &view->hdr );
3211 if (rc != ERROR_SUCCESS)
3212 return rc;
3214 return ERROR_SUCCESS;
3217 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3219 return ERROR_SUCCESS;
3223 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3225 static const WCHAR query[]= {
3226 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3227 '`','R','e','g','i','s','t','r','y','`',0};
3228 MSICOMPONENT *comp;
3229 DWORD total = 0, count = 0;
3230 MSIQUERY *view;
3231 MSIFEATURE *feature;
3232 MSIFILE *file;
3233 UINT rc;
3235 TRACE("InstallValidate\n");
3237 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3238 if (rc == ERROR_SUCCESS)
3240 rc = MSI_IterateRecords( view, &count, NULL, package );
3241 msiobj_release( &view->hdr );
3242 if (rc != ERROR_SUCCESS)
3243 return rc;
3244 total += count * REG_PROGRESS_VALUE;
3246 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3247 total += COMPONENT_PROGRESS_VALUE;
3249 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3250 total += file->FileSize;
3252 msi_ui_progress( package, 0, total, 0, 0 );
3254 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3256 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3257 debugstr_w(feature->Feature), feature->Installed,
3258 feature->ActionRequest, feature->Action);
3260 return ERROR_SUCCESS;
3263 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3265 MSIPACKAGE* package = param;
3266 LPCWSTR cond = NULL;
3267 LPCWSTR message = NULL;
3268 UINT r;
3270 static const WCHAR title[]=
3271 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3273 cond = MSI_RecordGetString(row,1);
3275 r = MSI_EvaluateConditionW(package,cond);
3276 if (r == MSICONDITION_FALSE)
3278 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3280 LPWSTR deformated;
3281 message = MSI_RecordGetString(row,2);
3282 deformat_string(package,message,&deformated);
3283 MessageBoxW(NULL,deformated,title,MB_OK);
3284 msi_free(deformated);
3287 return ERROR_INSTALL_FAILURE;
3290 return ERROR_SUCCESS;
3293 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3295 static const WCHAR query[] = {
3296 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3297 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3298 MSIQUERY *view;
3299 UINT rc;
3301 TRACE("Checking launch conditions\n");
3303 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3304 if (rc != ERROR_SUCCESS)
3305 return ERROR_SUCCESS;
3307 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3308 msiobj_release(&view->hdr);
3309 return rc;
3312 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3315 if (!cmp->KeyPath)
3316 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3318 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3320 static const WCHAR query[] = {
3321 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3322 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3323 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3324 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3325 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3326 MSIRECORD *row;
3327 UINT root, len;
3328 LPWSTR deformated, buffer, deformated_name;
3329 LPCWSTR key, name;
3331 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3332 if (!row)
3333 return NULL;
3335 root = MSI_RecordGetInteger(row,2);
3336 key = MSI_RecordGetString(row, 3);
3337 name = MSI_RecordGetString(row, 4);
3338 deformat_string(package, key , &deformated);
3339 deformat_string(package, name, &deformated_name);
3341 len = strlenW(deformated) + 6;
3342 if (deformated_name)
3343 len+=strlenW(deformated_name);
3345 buffer = msi_alloc( len *sizeof(WCHAR));
3347 if (deformated_name)
3348 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3349 else
3350 sprintfW(buffer,fmt,root,deformated);
3352 msi_free(deformated);
3353 msi_free(deformated_name);
3354 msiobj_release(&row->hdr);
3356 return buffer;
3358 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3360 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3361 return NULL;
3363 else
3365 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3367 if (file)
3368 return strdupW( file->TargetPath );
3370 return NULL;
3373 static HKEY openSharedDLLsKey(void)
3375 HKEY hkey=0;
3376 static const WCHAR path[] =
3377 {'S','o','f','t','w','a','r','e','\\',
3378 'M','i','c','r','o','s','o','f','t','\\',
3379 'W','i','n','d','o','w','s','\\',
3380 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3381 'S','h','a','r','e','d','D','L','L','s',0};
3383 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3384 return hkey;
3387 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3389 HKEY hkey;
3390 DWORD count=0;
3391 DWORD type;
3392 DWORD sz = sizeof(count);
3393 DWORD rc;
3395 hkey = openSharedDLLsKey();
3396 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3397 if (rc != ERROR_SUCCESS)
3398 count = 0;
3399 RegCloseKey(hkey);
3400 return count;
3403 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3405 HKEY hkey;
3407 hkey = openSharedDLLsKey();
3408 if (count > 0)
3409 msi_reg_set_val_dword( hkey, path, count );
3410 else
3411 RegDeleteValueW(hkey,path);
3412 RegCloseKey(hkey);
3413 return count;
3416 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3418 MSIFEATURE *feature;
3419 INT count = 0;
3420 BOOL write = FALSE;
3422 /* only refcount DLLs */
3423 if (comp->KeyPath == NULL ||
3424 comp->assembly ||
3425 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3426 comp->Attributes & msidbComponentAttributesODBCDataSource)
3427 write = FALSE;
3428 else
3430 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3431 write = (count > 0);
3433 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3434 write = TRUE;
3437 /* increment counts */
3438 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3440 ComponentList *cl;
3442 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3443 continue;
3445 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3447 if ( cl->component == comp )
3448 count++;
3452 /* decrement counts */
3453 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3455 ComponentList *cl;
3457 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3458 continue;
3460 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3462 if ( cl->component == comp )
3463 count--;
3467 /* ref count all the files in the component */
3468 if (write)
3470 MSIFILE *file;
3472 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3474 if (file->Component == comp)
3475 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3479 /* add a count for permanent */
3480 if (comp->Attributes & msidbComponentAttributesPermanent)
3481 count ++;
3483 comp->RefCount = count;
3485 if (write)
3486 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3489 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3491 if (comp->assembly)
3493 const WCHAR prefixW[] = {'<','\\',0};
3494 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3495 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3497 if (keypath)
3499 strcpyW( keypath, prefixW );
3500 strcatW( keypath, comp->assembly->display_name );
3502 return keypath;
3504 return resolve_keypath( package, comp );
3507 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3509 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3510 UINT rc;
3511 MSICOMPONENT *comp;
3512 HKEY hkey;
3514 TRACE("\n");
3516 squash_guid(package->ProductCode,squished_pc);
3517 msi_set_sourcedir_props(package, FALSE);
3519 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3521 MSIRECORD *uirow;
3522 INSTALLSTATE action;
3524 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3525 if (!comp->ComponentId)
3526 continue;
3528 squash_guid( comp->ComponentId, squished_cc );
3529 msi_free( comp->FullKeypath );
3530 comp->FullKeypath = build_full_keypath( package, comp );
3532 ACTION_RefCountComponent( package, comp );
3534 if (package->need_rollback) action = comp->Installed;
3535 else action = comp->ActionRequest;
3537 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3538 debugstr_w(comp->Component), debugstr_w(squished_cc),
3539 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3541 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3543 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3544 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3545 else
3546 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3548 if (rc != ERROR_SUCCESS)
3549 continue;
3551 if (comp->Attributes & msidbComponentAttributesPermanent)
3553 static const WCHAR szPermKey[] =
3554 { '0','0','0','0','0','0','0','0','0','0','0','0',
3555 '0','0','0','0','0','0','0','0','0','0','0','0',
3556 '0','0','0','0','0','0','0','0',0 };
3558 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3560 if (action == INSTALLSTATE_LOCAL)
3561 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3562 else
3564 MSIFILE *file;
3565 MSIRECORD *row;
3566 LPWSTR ptr, ptr2;
3567 WCHAR source[MAX_PATH];
3568 WCHAR base[MAX_PATH];
3569 LPWSTR sourcepath;
3571 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3572 static const WCHAR query[] = {
3573 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3574 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3575 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3576 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3577 '`','D','i','s','k','I','d','`',0};
3579 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3580 continue;
3582 if (!(row = MSI_QueryGetRecord(package->db, query, file->Sequence)))
3583 return ERROR_FUNCTION_FAILED;
3585 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3586 ptr2 = strrchrW(source, '\\') + 1;
3587 msiobj_release(&row->hdr);
3589 lstrcpyW(base, package->PackagePath);
3590 ptr = strrchrW(base, '\\');
3591 *(ptr + 1) = '\0';
3593 sourcepath = msi_resolve_file_source(package, file);
3594 ptr = sourcepath + lstrlenW(base);
3595 lstrcpyW(ptr2, ptr);
3596 msi_free(sourcepath);
3598 msi_reg_set_val_str(hkey, squished_pc, source);
3600 RegCloseKey(hkey);
3602 else if (action == INSTALLSTATE_ABSENT)
3604 if (comp->num_clients <= 0)
3606 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3607 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3608 else
3609 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3611 if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
3613 else
3615 LONG res;
3617 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3618 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE );
3619 else
3620 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );
3622 if (rc != ERROR_SUCCESS)
3624 WARN( "failed to open component key %u\n", rc );
3625 continue;
3627 res = RegDeleteValueW( hkey, squished_pc );
3628 RegCloseKey(hkey);
3629 if (res) WARN( "failed to delete component value %d\n", res );
3633 /* UI stuff */
3634 uirow = MSI_CreateRecord(3);
3635 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3636 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3637 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3638 msi_ui_actiondata( package, szProcessComponents, uirow );
3639 msiobj_release( &uirow->hdr );
3641 return ERROR_SUCCESS;
3644 typedef struct {
3645 CLSID clsid;
3646 LPWSTR source;
3648 LPWSTR path;
3649 ITypeLib *ptLib;
3650 } typelib_struct;
3652 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3653 LPWSTR lpszName, LONG_PTR lParam)
3655 TLIBATTR *attr;
3656 typelib_struct *tl_struct = (typelib_struct*) lParam;
3657 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3658 int sz;
3659 HRESULT res;
3661 if (!IS_INTRESOURCE(lpszName))
3663 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3664 return TRUE;
3667 sz = strlenW(tl_struct->source)+4;
3668 sz *= sizeof(WCHAR);
3670 if ((INT_PTR)lpszName == 1)
3671 tl_struct->path = strdupW(tl_struct->source);
3672 else
3674 tl_struct->path = msi_alloc(sz);
3675 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3678 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3679 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3680 if (FAILED(res))
3682 msi_free(tl_struct->path);
3683 tl_struct->path = NULL;
3685 return TRUE;
3688 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3689 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3691 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3692 return FALSE;
3695 msi_free(tl_struct->path);
3696 tl_struct->path = NULL;
3698 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3699 ITypeLib_Release(tl_struct->ptLib);
3701 return TRUE;
3704 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3706 MSIPACKAGE* package = param;
3707 LPCWSTR component;
3708 MSICOMPONENT *comp;
3709 MSIFILE *file;
3710 typelib_struct tl_struct;
3711 ITypeLib *tlib;
3712 HMODULE module;
3713 HRESULT hr;
3715 component = MSI_RecordGetString(row,3);
3716 comp = msi_get_loaded_component(package,component);
3717 if (!comp)
3718 return ERROR_SUCCESS;
3720 comp->Action = msi_get_component_action( package, comp );
3721 if (comp->Action != INSTALLSTATE_LOCAL)
3723 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3724 return ERROR_SUCCESS;
3727 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3729 TRACE("component has no key path\n");
3730 return ERROR_SUCCESS;
3732 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3734 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3735 if (module)
3737 LPCWSTR guid;
3738 guid = MSI_RecordGetString(row,1);
3739 CLSIDFromString( guid, &tl_struct.clsid);
3740 tl_struct.source = strdupW( file->TargetPath );
3741 tl_struct.path = NULL;
3743 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3744 (LONG_PTR)&tl_struct);
3746 if (tl_struct.path)
3748 LPCWSTR helpid, help_path = NULL;
3749 HRESULT res;
3751 helpid = MSI_RecordGetString(row,6);
3753 if (helpid) help_path = msi_get_target_folder( package, helpid );
3754 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3756 if (FAILED(res))
3757 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3758 else
3759 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3761 ITypeLib_Release(tl_struct.ptLib);
3762 msi_free(tl_struct.path);
3764 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3766 FreeLibrary(module);
3767 msi_free(tl_struct.source);
3769 else
3771 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3772 if (FAILED(hr))
3774 ERR("Failed to load type library: %08x\n", hr);
3775 return ERROR_INSTALL_FAILURE;
3778 ITypeLib_Release(tlib);
3781 return ERROR_SUCCESS;
3784 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3786 static const WCHAR query[] = {
3787 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3788 '`','T','y','p','e','L','i','b','`',0};
3789 MSIQUERY *view;
3790 UINT rc;
3792 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3793 if (rc != ERROR_SUCCESS)
3794 return ERROR_SUCCESS;
3796 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3797 msiobj_release(&view->hdr);
3798 return rc;
3801 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3803 MSIPACKAGE *package = param;
3804 LPCWSTR component, guid;
3805 MSICOMPONENT *comp;
3806 GUID libid;
3807 UINT version;
3808 LCID language;
3809 SYSKIND syskind;
3810 HRESULT hr;
3812 component = MSI_RecordGetString( row, 3 );
3813 comp = msi_get_loaded_component( package, component );
3814 if (!comp)
3815 return ERROR_SUCCESS;
3817 comp->Action = msi_get_component_action( package, comp );
3818 if (comp->Action != INSTALLSTATE_ABSENT)
3820 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3821 return ERROR_SUCCESS;
3823 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3825 guid = MSI_RecordGetString( row, 1 );
3826 CLSIDFromString( guid, &libid );
3827 version = MSI_RecordGetInteger( row, 4 );
3828 language = MSI_RecordGetInteger( row, 2 );
3830 #ifdef _WIN64
3831 syskind = SYS_WIN64;
3832 #else
3833 syskind = SYS_WIN32;
3834 #endif
3836 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3837 if (FAILED(hr))
3839 WARN("Failed to unregister typelib: %08x\n", hr);
3842 return ERROR_SUCCESS;
3845 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3847 static const WCHAR query[] = {
3848 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3849 '`','T','y','p','e','L','i','b','`',0};
3850 MSIQUERY *view;
3851 UINT rc;
3853 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3854 if (rc != ERROR_SUCCESS)
3855 return ERROR_SUCCESS;
3857 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3858 msiobj_release( &view->hdr );
3859 return rc;
3862 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3864 static const WCHAR szlnk[] = {'.','l','n','k',0};
3865 LPCWSTR directory, extension, link_folder;
3866 LPWSTR link_file, filename;
3868 directory = MSI_RecordGetString( row, 2 );
3869 link_folder = msi_get_target_folder( package, directory );
3870 if (!link_folder)
3872 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3873 return NULL;
3875 /* may be needed because of a bug somewhere else */
3876 msi_create_full_path( link_folder );
3878 filename = msi_dup_record_field( row, 3 );
3879 msi_reduce_to_long_filename( filename );
3881 extension = strrchrW( filename, '.' );
3882 if (!extension || strcmpiW( extension, szlnk ))
3884 int len = strlenW( filename );
3885 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3886 memcpy( filename + len, szlnk, sizeof(szlnk) );
3888 link_file = msi_build_directory_name( 2, link_folder, filename );
3889 msi_free( filename );
3891 return link_file;
3894 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3896 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3897 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3898 WCHAR *folder, *dest, *path;
3900 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3901 folder = msi_dup_property( package->db, szWindowsFolder );
3902 else
3904 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3905 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3906 msi_free( appdata );
3908 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3909 msi_create_full_path( dest );
3910 path = msi_build_directory_name( 2, dest, icon_name );
3911 msi_free( folder );
3912 msi_free( dest );
3913 return path;
3916 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3918 MSIPACKAGE *package = param;
3919 LPWSTR link_file, deformated, path;
3920 LPCWSTR component, target;
3921 MSICOMPONENT *comp;
3922 IShellLinkW *sl = NULL;
3923 IPersistFile *pf = NULL;
3924 HRESULT res;
3926 component = MSI_RecordGetString(row, 4);
3927 comp = msi_get_loaded_component(package, component);
3928 if (!comp)
3929 return ERROR_SUCCESS;
3931 comp->Action = msi_get_component_action( package, comp );
3932 if (comp->Action != INSTALLSTATE_LOCAL)
3934 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3935 return ERROR_SUCCESS;
3937 msi_ui_actiondata( package, szCreateShortcuts, row );
3939 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3940 &IID_IShellLinkW, (LPVOID *) &sl );
3942 if (FAILED( res ))
3944 ERR("CLSID_ShellLink not available\n");
3945 goto err;
3948 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3949 if (FAILED( res ))
3951 ERR("QueryInterface(IID_IPersistFile) failed\n");
3952 goto err;
3955 target = MSI_RecordGetString(row, 5);
3956 if (strchrW(target, '['))
3958 deformat_string( package, target, &path );
3959 TRACE("target path is %s\n", debugstr_w(path));
3960 IShellLinkW_SetPath( sl, path );
3961 msi_free( path );
3963 else
3965 FIXME("poorly handled shortcut format, advertised shortcut\n");
3966 path = resolve_keypath( package, comp );
3967 IShellLinkW_SetPath( sl, path );
3968 msi_free( path );
3971 if (!MSI_RecordIsNull(row,6))
3973 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3974 deformat_string(package, arguments, &deformated);
3975 IShellLinkW_SetArguments(sl,deformated);
3976 msi_free(deformated);
3979 if (!MSI_RecordIsNull(row,7))
3981 LPCWSTR description = MSI_RecordGetString(row, 7);
3982 IShellLinkW_SetDescription(sl, description);
3985 if (!MSI_RecordIsNull(row,8))
3986 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3988 if (!MSI_RecordIsNull(row,9))
3990 INT index;
3991 LPCWSTR icon = MSI_RecordGetString(row, 9);
3993 path = msi_build_icon_path(package, icon);
3994 index = MSI_RecordGetInteger(row,10);
3996 /* no value means 0 */
3997 if (index == MSI_NULL_INTEGER)
3998 index = 0;
4000 IShellLinkW_SetIconLocation(sl, path, index);
4001 msi_free(path);
4004 if (!MSI_RecordIsNull(row,11))
4005 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
4007 if (!MSI_RecordIsNull(row,12))
4009 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
4010 full_path = msi_get_target_folder( package, wkdir );
4011 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
4013 link_file = get_link_file(package, row);
4015 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
4016 IPersistFile_Save(pf, link_file, FALSE);
4017 msi_free(link_file);
4019 err:
4020 if (pf)
4021 IPersistFile_Release( pf );
4022 if (sl)
4023 IShellLinkW_Release( sl );
4025 return ERROR_SUCCESS;
4028 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
4030 static const WCHAR query[] = {
4031 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4032 '`','S','h','o','r','t','c','u','t','`',0};
4033 MSIQUERY *view;
4034 HRESULT res;
4035 UINT rc;
4037 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4038 if (rc != ERROR_SUCCESS)
4039 return ERROR_SUCCESS;
4041 res = CoInitialize( NULL );
4043 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4044 msiobj_release(&view->hdr);
4046 if (SUCCEEDED(res)) CoUninitialize();
4047 return rc;
4050 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4052 MSIPACKAGE *package = param;
4053 LPWSTR link_file;
4054 LPCWSTR component;
4055 MSICOMPONENT *comp;
4057 component = MSI_RecordGetString( row, 4 );
4058 comp = msi_get_loaded_component( package, component );
4059 if (!comp)
4060 return ERROR_SUCCESS;
4062 comp->Action = msi_get_component_action( package, comp );
4063 if (comp->Action != INSTALLSTATE_ABSENT)
4065 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4066 return ERROR_SUCCESS;
4068 msi_ui_actiondata( package, szRemoveShortcuts, row );
4070 link_file = get_link_file( package, row );
4072 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4073 if (!DeleteFileW( link_file ))
4075 WARN("Failed to remove shortcut file %u\n", GetLastError());
4077 msi_free( link_file );
4079 return ERROR_SUCCESS;
4082 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4084 static const WCHAR query[] = {
4085 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4086 '`','S','h','o','r','t','c','u','t','`',0};
4087 MSIQUERY *view;
4088 UINT rc;
4090 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4091 if (rc != ERROR_SUCCESS)
4092 return ERROR_SUCCESS;
4094 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4095 msiobj_release( &view->hdr );
4096 return rc;
4099 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4101 MSIPACKAGE* package = param;
4102 HANDLE the_file;
4103 LPWSTR FilePath;
4104 LPCWSTR FileName;
4105 CHAR buffer[1024];
4106 DWORD sz;
4107 UINT rc;
4109 FileName = MSI_RecordGetString(row,1);
4110 if (!FileName)
4112 ERR("Unable to get FileName\n");
4113 return ERROR_SUCCESS;
4116 FilePath = msi_build_icon_path(package, FileName);
4118 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4120 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4121 FILE_ATTRIBUTE_NORMAL, NULL);
4123 if (the_file == INVALID_HANDLE_VALUE)
4125 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4126 msi_free(FilePath);
4127 return ERROR_SUCCESS;
4132 DWORD write;
4133 sz = 1024;
4134 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4135 if (rc != ERROR_SUCCESS)
4137 ERR("Failed to get stream\n");
4138 DeleteFileW(FilePath);
4139 break;
4141 WriteFile(the_file,buffer,sz,&write,NULL);
4142 } while (sz == 1024);
4144 msi_free(FilePath);
4145 CloseHandle(the_file);
4147 return ERROR_SUCCESS;
4150 static UINT msi_publish_icons(MSIPACKAGE *package)
4152 static const WCHAR query[]= {
4153 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4154 '`','I','c','o','n','`',0};
4155 MSIQUERY *view;
4156 UINT r;
4158 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4159 if (r == ERROR_SUCCESS)
4161 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4162 msiobj_release(&view->hdr);
4163 if (r != ERROR_SUCCESS)
4164 return r;
4166 return ERROR_SUCCESS;
4169 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4171 UINT r;
4172 HKEY source;
4173 LPWSTR buffer;
4174 MSIMEDIADISK *disk;
4175 MSISOURCELISTINFO *info;
4177 r = RegCreateKeyW(hkey, szSourceList, &source);
4178 if (r != ERROR_SUCCESS)
4179 return r;
4181 RegCloseKey(source);
4183 buffer = strrchrW(package->PackagePath, '\\') + 1;
4184 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4185 package->Context, MSICODE_PRODUCT,
4186 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4187 if (r != ERROR_SUCCESS)
4188 return r;
4190 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4191 package->Context, MSICODE_PRODUCT,
4192 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4193 if (r != ERROR_SUCCESS)
4194 return r;
4196 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4197 package->Context, MSICODE_PRODUCT,
4198 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4199 if (r != ERROR_SUCCESS)
4200 return r;
4202 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4204 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4205 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4206 info->options, info->value);
4207 else
4208 MsiSourceListSetInfoW(package->ProductCode, NULL,
4209 info->context, info->options,
4210 info->property, info->value);
4213 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4215 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4216 disk->context, disk->options,
4217 disk->disk_id, disk->volume_label, disk->disk_prompt);
4220 return ERROR_SUCCESS;
4223 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4225 MSIHANDLE hdb, suminfo;
4226 WCHAR guids[MAX_PATH];
4227 WCHAR packcode[SQUISH_GUID_SIZE];
4228 LPWSTR buffer;
4229 LPWSTR ptr;
4230 DWORD langid;
4231 DWORD size;
4232 UINT r;
4234 static const WCHAR szARPProductIcon[] =
4235 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4236 static const WCHAR szAssignment[] =
4237 {'A','s','s','i','g','n','m','e','n','t',0};
4238 static const WCHAR szAdvertiseFlags[] =
4239 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4240 static const WCHAR szClients[] =
4241 {'C','l','i','e','n','t','s',0};
4242 static const WCHAR szColon[] = {':',0};
4244 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4245 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4246 msi_free(buffer);
4248 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4249 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4251 /* FIXME */
4252 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4254 buffer = msi_dup_property(package->db, szARPProductIcon);
4255 if (buffer)
4257 LPWSTR path = msi_build_icon_path(package, buffer);
4258 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4259 msi_free(path);
4260 msi_free(buffer);
4263 buffer = msi_dup_property(package->db, szProductVersion);
4264 if (buffer)
4266 DWORD verdword = msi_version_str_to_dword(buffer);
4267 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4268 msi_free(buffer);
4271 msi_reg_set_val_dword(hkey, szAssignment, 0);
4272 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4273 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4274 msi_reg_set_val_str(hkey, szClients, szColon);
4276 hdb = alloc_msihandle(&package->db->hdr);
4277 if (!hdb)
4278 return ERROR_NOT_ENOUGH_MEMORY;
4280 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4281 MsiCloseHandle(hdb);
4282 if (r != ERROR_SUCCESS)
4283 goto done;
4285 size = MAX_PATH;
4286 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4287 NULL, guids, &size);
4288 if (r != ERROR_SUCCESS)
4289 goto done;
4291 ptr = strchrW(guids, ';');
4292 if (ptr) *ptr = 0;
4293 squash_guid(guids, packcode);
4294 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4296 done:
4297 MsiCloseHandle(suminfo);
4298 return ERROR_SUCCESS;
4301 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4303 UINT r;
4304 HKEY hkey;
4305 LPWSTR upgrade;
4306 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4308 upgrade = msi_dup_property(package->db, szUpgradeCode);
4309 if (!upgrade)
4310 return ERROR_SUCCESS;
4312 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4313 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4314 else
4315 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4317 if (r != ERROR_SUCCESS)
4319 WARN("failed to open upgrade code key\n");
4320 msi_free(upgrade);
4321 return ERROR_SUCCESS;
4323 squash_guid(package->ProductCode, squashed_pc);
4324 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4325 RegCloseKey(hkey);
4326 msi_free(upgrade);
4327 return ERROR_SUCCESS;
4330 static BOOL msi_check_publish(MSIPACKAGE *package)
4332 MSIFEATURE *feature;
4334 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4336 feature->Action = msi_get_feature_action( package, feature );
4337 if (feature->Action == INSTALLSTATE_LOCAL)
4338 return TRUE;
4341 return FALSE;
4344 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4346 MSIFEATURE *feature;
4348 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4350 feature->Action = msi_get_feature_action( package, feature );
4351 if (feature->Action != INSTALLSTATE_ABSENT)
4352 return FALSE;
4355 return TRUE;
4358 static UINT msi_publish_patches( MSIPACKAGE *package )
4360 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4361 WCHAR patch_squashed[GUID_SIZE];
4362 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4363 LONG res;
4364 MSIPATCHINFO *patch;
4365 UINT r;
4366 WCHAR *p, *all_patches = NULL;
4367 DWORD len = 0;
4369 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4370 if (r != ERROR_SUCCESS)
4371 return ERROR_FUNCTION_FAILED;
4373 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4374 if (res != ERROR_SUCCESS)
4376 r = ERROR_FUNCTION_FAILED;
4377 goto done;
4380 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4381 if (r != ERROR_SUCCESS)
4382 goto done;
4384 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4386 squash_guid( patch->patchcode, patch_squashed );
4387 len += strlenW( patch_squashed ) + 1;
4390 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4391 if (!all_patches)
4392 goto done;
4394 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4396 HKEY patch_key;
4398 squash_guid( patch->patchcode, p );
4399 p += strlenW( p ) + 1;
4401 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4402 (const BYTE *)patch->transforms,
4403 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4404 if (res != ERROR_SUCCESS)
4405 goto done;
4407 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4408 if (r != ERROR_SUCCESS)
4409 goto done;
4411 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4412 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4413 RegCloseKey( patch_key );
4414 if (res != ERROR_SUCCESS)
4415 goto done;
4417 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4419 res = GetLastError();
4420 ERR("Unable to copy patch package %d\n", res);
4421 goto done;
4423 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4424 if (res != ERROR_SUCCESS)
4425 goto done;
4427 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4428 RegCloseKey( patch_key );
4429 if (res != ERROR_SUCCESS)
4430 goto done;
4433 all_patches[len] = 0;
4434 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4435 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4436 if (res != ERROR_SUCCESS)
4437 goto done;
4439 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4440 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4441 if (res != ERROR_SUCCESS)
4442 r = ERROR_FUNCTION_FAILED;
4444 done:
4445 RegCloseKey( product_patches_key );
4446 RegCloseKey( patches_key );
4447 RegCloseKey( product_key );
4448 msi_free( all_patches );
4449 return r;
4452 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4454 UINT rc;
4455 HKEY hukey = NULL, hudkey = NULL;
4456 MSIRECORD *uirow;
4458 if (!list_empty(&package->patches))
4460 rc = msi_publish_patches(package);
4461 if (rc != ERROR_SUCCESS)
4462 goto end;
4465 /* FIXME: also need to publish if the product is in advertise mode */
4466 if (!msi_check_publish(package))
4467 return ERROR_SUCCESS;
4469 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4470 &hukey, TRUE);
4471 if (rc != ERROR_SUCCESS)
4472 goto end;
4474 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4475 NULL, &hudkey, TRUE);
4476 if (rc != ERROR_SUCCESS)
4477 goto end;
4479 rc = msi_publish_upgrade_code(package);
4480 if (rc != ERROR_SUCCESS)
4481 goto end;
4483 rc = msi_publish_product_properties(package, hukey);
4484 if (rc != ERROR_SUCCESS)
4485 goto end;
4487 rc = msi_publish_sourcelist(package, hukey);
4488 if (rc != ERROR_SUCCESS)
4489 goto end;
4491 rc = msi_publish_icons(package);
4493 end:
4494 uirow = MSI_CreateRecord( 1 );
4495 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4496 msi_ui_actiondata( package, szPublishProduct, uirow );
4497 msiobj_release( &uirow->hdr );
4499 RegCloseKey(hukey);
4500 RegCloseKey(hudkey);
4501 return rc;
4504 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4506 WCHAR *filename, *ptr, *folder, *ret;
4507 const WCHAR *dirprop;
4509 filename = msi_dup_record_field( row, 2 );
4510 if (filename && (ptr = strchrW( filename, '|' )))
4511 ptr++;
4512 else
4513 ptr = filename;
4515 dirprop = MSI_RecordGetString( row, 3 );
4516 if (dirprop)
4518 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4519 if (!folder) folder = msi_dup_property( package->db, dirprop );
4521 else
4522 folder = msi_dup_property( package->db, szWindowsFolder );
4524 if (!folder)
4526 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4527 msi_free( filename );
4528 return NULL;
4531 ret = msi_build_directory_name( 2, folder, ptr );
4533 msi_free( filename );
4534 msi_free( folder );
4535 return ret;
4538 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4540 MSIPACKAGE *package = param;
4541 LPCWSTR component, section, key, value, identifier;
4542 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4543 MSIRECORD * uirow;
4544 INT action;
4545 MSICOMPONENT *comp;
4547 component = MSI_RecordGetString(row, 8);
4548 comp = msi_get_loaded_component(package,component);
4549 if (!comp)
4550 return ERROR_SUCCESS;
4552 comp->Action = msi_get_component_action( package, comp );
4553 if (comp->Action != INSTALLSTATE_LOCAL)
4555 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4556 return ERROR_SUCCESS;
4559 identifier = MSI_RecordGetString(row,1);
4560 section = MSI_RecordGetString(row,4);
4561 key = MSI_RecordGetString(row,5);
4562 value = MSI_RecordGetString(row,6);
4563 action = MSI_RecordGetInteger(row,7);
4565 deformat_string(package,section,&deformated_section);
4566 deformat_string(package,key,&deformated_key);
4567 deformat_string(package,value,&deformated_value);
4569 fullname = get_ini_file_name(package, row);
4571 if (action == 0)
4573 TRACE("Adding value %s to section %s in %s\n",
4574 debugstr_w(deformated_key), debugstr_w(deformated_section),
4575 debugstr_w(fullname));
4576 WritePrivateProfileStringW(deformated_section, deformated_key,
4577 deformated_value, fullname);
4579 else if (action == 1)
4581 WCHAR returned[10];
4582 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4583 returned, 10, fullname);
4584 if (returned[0] == 0)
4586 TRACE("Adding value %s to section %s in %s\n",
4587 debugstr_w(deformated_key), debugstr_w(deformated_section),
4588 debugstr_w(fullname));
4590 WritePrivateProfileStringW(deformated_section, deformated_key,
4591 deformated_value, fullname);
4594 else if (action == 3)
4595 FIXME("Append to existing section not yet implemented\n");
4597 uirow = MSI_CreateRecord(4);
4598 MSI_RecordSetStringW(uirow,1,identifier);
4599 MSI_RecordSetStringW(uirow,2,deformated_section);
4600 MSI_RecordSetStringW(uirow,3,deformated_key);
4601 MSI_RecordSetStringW(uirow,4,deformated_value);
4602 msi_ui_actiondata( package, szWriteIniValues, uirow );
4603 msiobj_release( &uirow->hdr );
4605 msi_free(fullname);
4606 msi_free(deformated_key);
4607 msi_free(deformated_value);
4608 msi_free(deformated_section);
4609 return ERROR_SUCCESS;
4612 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4614 static const WCHAR query[] = {
4615 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4616 '`','I','n','i','F','i','l','e','`',0};
4617 MSIQUERY *view;
4618 UINT rc;
4620 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4621 if (rc != ERROR_SUCCESS)
4622 return ERROR_SUCCESS;
4624 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4625 msiobj_release(&view->hdr);
4626 return rc;
4629 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4631 MSIPACKAGE *package = param;
4632 LPCWSTR component, section, key, value, identifier;
4633 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4634 MSICOMPONENT *comp;
4635 MSIRECORD *uirow;
4636 INT action;
4638 component = MSI_RecordGetString( row, 8 );
4639 comp = msi_get_loaded_component( package, component );
4640 if (!comp)
4641 return ERROR_SUCCESS;
4643 comp->Action = msi_get_component_action( package, comp );
4644 if (comp->Action != INSTALLSTATE_ABSENT)
4646 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4647 return ERROR_SUCCESS;
4650 identifier = MSI_RecordGetString( row, 1 );
4651 section = MSI_RecordGetString( row, 4 );
4652 key = MSI_RecordGetString( row, 5 );
4653 value = MSI_RecordGetString( row, 6 );
4654 action = MSI_RecordGetInteger( row, 7 );
4656 deformat_string( package, section, &deformated_section );
4657 deformat_string( package, key, &deformated_key );
4658 deformat_string( package, value, &deformated_value );
4660 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4662 filename = get_ini_file_name( package, row );
4664 TRACE("Removing key %s from section %s in %s\n",
4665 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4667 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4669 WARN("Unable to remove key %u\n", GetLastError());
4671 msi_free( filename );
4673 else
4674 FIXME("Unsupported action %d\n", action);
4677 uirow = MSI_CreateRecord( 4 );
4678 MSI_RecordSetStringW( uirow, 1, identifier );
4679 MSI_RecordSetStringW( uirow, 2, deformated_section );
4680 MSI_RecordSetStringW( uirow, 3, deformated_key );
4681 MSI_RecordSetStringW( uirow, 4, deformated_value );
4682 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4683 msiobj_release( &uirow->hdr );
4685 msi_free( deformated_key );
4686 msi_free( deformated_value );
4687 msi_free( deformated_section );
4688 return ERROR_SUCCESS;
4691 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4693 MSIPACKAGE *package = param;
4694 LPCWSTR component, section, key, value, identifier;
4695 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4696 MSICOMPONENT *comp;
4697 MSIRECORD *uirow;
4698 INT action;
4700 component = MSI_RecordGetString( row, 8 );
4701 comp = msi_get_loaded_component( package, component );
4702 if (!comp)
4703 return ERROR_SUCCESS;
4705 comp->Action = msi_get_component_action( package, comp );
4706 if (comp->Action != INSTALLSTATE_LOCAL)
4708 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4709 return ERROR_SUCCESS;
4712 identifier = MSI_RecordGetString( row, 1 );
4713 section = MSI_RecordGetString( row, 4 );
4714 key = MSI_RecordGetString( row, 5 );
4715 value = MSI_RecordGetString( row, 6 );
4716 action = MSI_RecordGetInteger( row, 7 );
4718 deformat_string( package, section, &deformated_section );
4719 deformat_string( package, key, &deformated_key );
4720 deformat_string( package, value, &deformated_value );
4722 if (action == msidbIniFileActionRemoveLine)
4724 filename = get_ini_file_name( package, row );
4726 TRACE("Removing key %s from section %s in %s\n",
4727 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4729 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4731 WARN("Unable to remove key %u\n", GetLastError());
4733 msi_free( filename );
4735 else
4736 FIXME("Unsupported action %d\n", action);
4738 uirow = MSI_CreateRecord( 4 );
4739 MSI_RecordSetStringW( uirow, 1, identifier );
4740 MSI_RecordSetStringW( uirow, 2, deformated_section );
4741 MSI_RecordSetStringW( uirow, 3, deformated_key );
4742 MSI_RecordSetStringW( uirow, 4, deformated_value );
4743 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4744 msiobj_release( &uirow->hdr );
4746 msi_free( deformated_key );
4747 msi_free( deformated_value );
4748 msi_free( deformated_section );
4749 return ERROR_SUCCESS;
4752 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4754 static const WCHAR query[] = {
4755 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4756 '`','I','n','i','F','i','l','e','`',0};
4757 static const WCHAR remove_query[] = {
4758 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4759 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4760 MSIQUERY *view;
4761 UINT rc;
4763 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4764 if (rc == ERROR_SUCCESS)
4766 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4767 msiobj_release( &view->hdr );
4768 if (rc != ERROR_SUCCESS)
4769 return rc;
4771 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4772 if (rc == ERROR_SUCCESS)
4774 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4775 msiobj_release( &view->hdr );
4776 if (rc != ERROR_SUCCESS)
4777 return rc;
4779 return ERROR_SUCCESS;
4782 static void register_dll( const WCHAR *dll, BOOL unregister )
4784 static const WCHAR regW[] =
4785 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4786 static const WCHAR unregW[] =
4787 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4788 PROCESS_INFORMATION pi;
4789 STARTUPINFOW si;
4790 WCHAR *cmd;
4792 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4794 if (unregister) sprintfW( cmd, unregW, dll );
4795 else sprintfW( cmd, regW, dll );
4797 memset( &si, 0, sizeof(STARTUPINFOW) );
4798 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4800 CloseHandle( pi.hThread );
4801 msi_dialog_check_messages( pi.hProcess );
4802 CloseHandle( pi.hProcess );
4804 msi_free( cmd );
4807 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4809 MSIPACKAGE *package = param;
4810 LPCWSTR filename;
4811 MSIFILE *file;
4812 MSIRECORD *uirow;
4814 filename = MSI_RecordGetString( row, 1 );
4815 file = msi_get_loaded_file( package, filename );
4816 if (!file)
4818 WARN("unable to find file %s\n", debugstr_w(filename));
4819 return ERROR_SUCCESS;
4821 file->Component->Action = msi_get_component_action( package, file->Component );
4822 if (file->Component->Action != INSTALLSTATE_LOCAL)
4824 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4825 return ERROR_SUCCESS;
4828 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4829 register_dll( file->TargetPath, FALSE );
4831 uirow = MSI_CreateRecord( 2 );
4832 MSI_RecordSetStringW( uirow, 1, file->File );
4833 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4834 msi_ui_actiondata( package, szSelfRegModules, uirow );
4835 msiobj_release( &uirow->hdr );
4837 return ERROR_SUCCESS;
4840 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4842 static const WCHAR query[] = {
4843 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4844 '`','S','e','l','f','R','e','g','`',0};
4845 MSIQUERY *view;
4846 UINT rc;
4848 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4849 if (rc != ERROR_SUCCESS)
4850 return ERROR_SUCCESS;
4852 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4853 msiobj_release(&view->hdr);
4854 return rc;
4857 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4859 MSIPACKAGE *package = param;
4860 LPCWSTR filename;
4861 MSIFILE *file;
4862 MSIRECORD *uirow;
4864 filename = MSI_RecordGetString( row, 1 );
4865 file = msi_get_loaded_file( package, filename );
4866 if (!file)
4868 WARN("unable to find file %s\n", debugstr_w(filename));
4869 return ERROR_SUCCESS;
4871 file->Component->Action = msi_get_component_action( package, file->Component );
4872 if (file->Component->Action != INSTALLSTATE_ABSENT)
4874 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4875 return ERROR_SUCCESS;
4878 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4879 register_dll( file->TargetPath, TRUE );
4881 uirow = MSI_CreateRecord( 2 );
4882 MSI_RecordSetStringW( uirow, 1, file->File );
4883 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4884 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4885 msiobj_release( &uirow->hdr );
4887 return ERROR_SUCCESS;
4890 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4892 static const WCHAR query[] = {
4893 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4894 '`','S','e','l','f','R','e','g','`',0};
4895 MSIQUERY *view;
4896 UINT rc;
4898 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4899 if (rc != ERROR_SUCCESS)
4900 return ERROR_SUCCESS;
4902 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4903 msiobj_release( &view->hdr );
4904 return rc;
4907 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4909 MSIFEATURE *feature;
4910 UINT rc;
4911 HKEY hkey = NULL, userdata = NULL;
4913 if (!msi_check_publish(package))
4914 return ERROR_SUCCESS;
4916 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4917 &hkey, TRUE);
4918 if (rc != ERROR_SUCCESS)
4919 goto end;
4921 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4922 &userdata, TRUE);
4923 if (rc != ERROR_SUCCESS)
4924 goto end;
4926 /* here the guids are base 85 encoded */
4927 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4929 ComponentList *cl;
4930 LPWSTR data = NULL;
4931 GUID clsid;
4932 INT size;
4933 BOOL absent = FALSE;
4934 MSIRECORD *uirow;
4936 if (feature->Level <= 0) continue;
4938 if (feature->Action != INSTALLSTATE_LOCAL &&
4939 feature->Action != INSTALLSTATE_SOURCE &&
4940 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4942 size = 1;
4943 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4945 size += 21;
4947 if (feature->Feature_Parent)
4948 size += strlenW( feature->Feature_Parent )+2;
4950 data = msi_alloc(size * sizeof(WCHAR));
4952 data[0] = 0;
4953 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4955 MSICOMPONENT* component = cl->component;
4956 WCHAR buf[21];
4958 buf[0] = 0;
4959 if (component->ComponentId)
4961 TRACE("From %s\n",debugstr_w(component->ComponentId));
4962 CLSIDFromString(component->ComponentId, &clsid);
4963 encode_base85_guid(&clsid,buf);
4964 TRACE("to %s\n",debugstr_w(buf));
4965 strcatW(data,buf);
4969 if (feature->Feature_Parent)
4971 static const WCHAR sep[] = {'\2',0};
4972 strcatW(data,sep);
4973 strcatW(data,feature->Feature_Parent);
4976 msi_reg_set_val_str( userdata, feature->Feature, data );
4977 msi_free(data);
4979 size = 0;
4980 if (feature->Feature_Parent)
4981 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4982 if (!absent)
4984 size += sizeof(WCHAR);
4985 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4986 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4988 else
4990 size += 2*sizeof(WCHAR);
4991 data = msi_alloc(size);
4992 data[0] = 0x6;
4993 data[1] = 0;
4994 if (feature->Feature_Parent)
4995 strcpyW( &data[1], feature->Feature_Parent );
4996 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4997 (LPBYTE)data,size);
4998 msi_free(data);
5001 /* the UI chunk */
5002 uirow = MSI_CreateRecord( 1 );
5003 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5004 msi_ui_actiondata( package, szPublishFeatures, uirow );
5005 msiobj_release( &uirow->hdr );
5006 /* FIXME: call msi_ui_progress? */
5009 end:
5010 RegCloseKey(hkey);
5011 RegCloseKey(userdata);
5012 return rc;
5015 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
5017 UINT r;
5018 HKEY hkey;
5019 MSIRECORD *uirow;
5021 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
5023 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
5024 &hkey, FALSE);
5025 if (r == ERROR_SUCCESS)
5027 RegDeleteValueW(hkey, feature->Feature);
5028 RegCloseKey(hkey);
5031 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
5032 &hkey, FALSE);
5033 if (r == ERROR_SUCCESS)
5035 RegDeleteValueW(hkey, feature->Feature);
5036 RegCloseKey(hkey);
5039 uirow = MSI_CreateRecord( 1 );
5040 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5041 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
5042 msiobj_release( &uirow->hdr );
5044 return ERROR_SUCCESS;
5047 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5049 MSIFEATURE *feature;
5051 if (!msi_check_unpublish(package))
5052 return ERROR_SUCCESS;
5054 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5056 msi_unpublish_feature(package, feature);
5059 return ERROR_SUCCESS;
5062 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5064 SYSTEMTIME systime;
5065 DWORD size, langid;
5066 WCHAR date[9], *val, *buffer;
5067 const WCHAR *prop, *key;
5069 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5070 static const WCHAR modpath_fmt[] =
5071 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5072 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5073 static const WCHAR szModifyPath[] =
5074 {'M','o','d','i','f','y','P','a','t','h',0};
5075 static const WCHAR szUninstallString[] =
5076 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5077 static const WCHAR szEstimatedSize[] =
5078 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5079 static const WCHAR szDisplayVersion[] =
5080 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5081 static const WCHAR szInstallSource[] =
5082 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5083 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5084 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5085 static const WCHAR szAuthorizedCDFPrefix[] =
5086 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5087 static const WCHAR szARPCONTACT[] =
5088 {'A','R','P','C','O','N','T','A','C','T',0};
5089 static const WCHAR szContact[] =
5090 {'C','o','n','t','a','c','t',0};
5091 static const WCHAR szARPCOMMENTS[] =
5092 {'A','R','P','C','O','M','M','E','N','T','S',0};
5093 static const WCHAR szComments[] =
5094 {'C','o','m','m','e','n','t','s',0};
5095 static const WCHAR szProductName[] =
5096 {'P','r','o','d','u','c','t','N','a','m','e',0};
5097 static const WCHAR szDisplayName[] =
5098 {'D','i','s','p','l','a','y','N','a','m','e',0};
5099 static const WCHAR szARPHELPLINK[] =
5100 {'A','R','P','H','E','L','P','L','I','N','K',0};
5101 static const WCHAR szHelpLink[] =
5102 {'H','e','l','p','L','i','n','k',0};
5103 static const WCHAR szARPHELPTELEPHONE[] =
5104 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5105 static const WCHAR szHelpTelephone[] =
5106 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5107 static const WCHAR szARPINSTALLLOCATION[] =
5108 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5109 static const WCHAR szManufacturer[] =
5110 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5111 static const WCHAR szPublisher[] =
5112 {'P','u','b','l','i','s','h','e','r',0};
5113 static const WCHAR szARPREADME[] =
5114 {'A','R','P','R','E','A','D','M','E',0};
5115 static const WCHAR szReadme[] =
5116 {'R','e','a','d','M','e',0};
5117 static const WCHAR szARPSIZE[] =
5118 {'A','R','P','S','I','Z','E',0};
5119 static const WCHAR szSize[] =
5120 {'S','i','z','e',0};
5121 static const WCHAR szARPURLINFOABOUT[] =
5122 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5123 static const WCHAR szURLInfoAbout[] =
5124 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5125 static const WCHAR szARPURLUPDATEINFO[] =
5126 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5127 static const WCHAR szURLUpdateInfo[] =
5128 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5129 static const WCHAR szARPSYSTEMCOMPONENT[] =
5130 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5131 static const WCHAR szSystemComponent[] =
5132 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5134 static const WCHAR *propval[] = {
5135 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5136 szARPCONTACT, szContact,
5137 szARPCOMMENTS, szComments,
5138 szProductName, szDisplayName,
5139 szARPHELPLINK, szHelpLink,
5140 szARPHELPTELEPHONE, szHelpTelephone,
5141 szARPINSTALLLOCATION, szInstallLocation,
5142 szSourceDir, szInstallSource,
5143 szManufacturer, szPublisher,
5144 szARPREADME, szReadme,
5145 szARPSIZE, szSize,
5146 szARPURLINFOABOUT, szURLInfoAbout,
5147 szARPURLUPDATEINFO, szURLUpdateInfo,
5148 NULL
5150 const WCHAR **p = propval;
5152 while (*p)
5154 prop = *p++;
5155 key = *p++;
5156 val = msi_dup_property(package->db, prop);
5157 msi_reg_set_val_str(hkey, key, val);
5158 msi_free(val);
5161 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5162 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5164 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5166 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5167 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5168 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5169 msi_free(buffer);
5171 /* FIXME: Write real Estimated Size when we have it */
5172 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5174 GetLocalTime(&systime);
5175 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5176 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5178 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5179 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5181 buffer = msi_dup_property(package->db, szProductVersion);
5182 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5183 if (buffer)
5185 DWORD verdword = msi_version_str_to_dword(buffer);
5187 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5188 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5189 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5190 msi_free(buffer);
5193 return ERROR_SUCCESS;
5196 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5198 WCHAR squashed_pc[SQUISH_GUID_SIZE];
5199 MSIRECORD *uirow;
5200 LPWSTR upgrade_code;
5201 HKEY hkey, props, upgrade_key;
5202 UINT rc;
5204 /* FIXME: also need to publish if the product is in advertise mode */
5205 if (!msi_check_publish(package))
5206 return ERROR_SUCCESS;
5208 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5209 if (rc != ERROR_SUCCESS)
5210 return rc;
5212 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5213 if (rc != ERROR_SUCCESS)
5214 goto done;
5216 rc = msi_publish_install_properties(package, hkey);
5217 if (rc != ERROR_SUCCESS)
5218 goto done;
5220 rc = msi_publish_install_properties(package, props);
5221 if (rc != ERROR_SUCCESS)
5222 goto done;
5224 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5225 if (upgrade_code)
5227 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5228 if (rc == ERROR_SUCCESS)
5230 squash_guid( package->ProductCode, squashed_pc );
5231 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5232 RegCloseKey( upgrade_key );
5234 msi_free( upgrade_code );
5236 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5237 package->delete_on_close = FALSE;
5239 done:
5240 uirow = MSI_CreateRecord( 1 );
5241 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5242 msi_ui_actiondata( package, szRegisterProduct, uirow );
5243 msiobj_release( &uirow->hdr );
5245 RegCloseKey(hkey);
5246 return ERROR_SUCCESS;
5249 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5251 return execute_script(package, SCRIPT_INSTALL);
5254 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5256 MSIPACKAGE *package = param;
5257 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5258 WCHAR *p, *icon_path;
5260 if (!icon) return ERROR_SUCCESS;
5261 if ((icon_path = msi_build_icon_path( package, icon )))
5263 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5264 DeleteFileW( icon_path );
5265 if ((p = strrchrW( icon_path, '\\' )))
5267 *p = 0;
5268 RemoveDirectoryW( icon_path );
5270 msi_free( icon_path );
5272 return ERROR_SUCCESS;
5275 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5277 static const WCHAR query[]= {
5278 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5279 MSIQUERY *view;
5280 UINT r;
5282 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5283 if (r == ERROR_SUCCESS)
5285 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5286 msiobj_release( &view->hdr );
5287 if (r != ERROR_SUCCESS)
5288 return r;
5290 return ERROR_SUCCESS;
5293 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
5295 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
5296 WCHAR *upgrade, **features;
5297 BOOL full_uninstall = TRUE;
5298 MSIFEATURE *feature;
5299 MSIPATCHINFO *patch;
5300 UINT i;
5302 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5304 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
5306 features = msi_split_string( remove, ',' );
5307 for (i = 0; features && features[i]; i++)
5309 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
5311 msi_free(features);
5313 if (!full_uninstall)
5314 return ERROR_SUCCESS;
5316 MSIREG_DeleteProductKey(package->ProductCode);
5317 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5318 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5320 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5321 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5322 MSIREG_DeleteUserProductKey(package->ProductCode);
5323 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5325 upgrade = msi_dup_property(package->db, szUpgradeCode);
5326 if (upgrade)
5328 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5329 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5330 msi_free(upgrade);
5333 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5335 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5336 if (!strcmpW( package->ProductCode, patch->products ))
5338 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5339 patch->delete_on_close = TRUE;
5341 /* FIXME: remove local patch package if this is the last product */
5343 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5344 package->delete_on_close = TRUE;
5346 msi_unpublish_icons( package );
5347 return ERROR_SUCCESS;
5350 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5352 UINT rc;
5353 WCHAR *remove;
5355 /* first do the same as an InstallExecute */
5356 rc = execute_script(package, SCRIPT_INSTALL);
5357 if (rc != ERROR_SUCCESS)
5358 return rc;
5360 /* then handle commit actions */
5361 rc = execute_script(package, SCRIPT_COMMIT);
5362 if (rc != ERROR_SUCCESS)
5363 return rc;
5365 remove = msi_dup_property(package->db, szRemove);
5366 rc = msi_unpublish_product(package, remove);
5367 msi_free(remove);
5368 return rc;
5371 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5373 static const WCHAR RunOnce[] = {
5374 'S','o','f','t','w','a','r','e','\\',
5375 'M','i','c','r','o','s','o','f','t','\\',
5376 'W','i','n','d','o','w','s','\\',
5377 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5378 'R','u','n','O','n','c','e',0};
5379 static const WCHAR InstallRunOnce[] = {
5380 'S','o','f','t','w','a','r','e','\\',
5381 'M','i','c','r','o','s','o','f','t','\\',
5382 'W','i','n','d','o','w','s','\\',
5383 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5384 'I','n','s','t','a','l','l','e','r','\\',
5385 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5387 static const WCHAR msiexec_fmt[] = {
5388 '%','s',
5389 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5390 '\"','%','s','\"',0};
5391 static const WCHAR install_fmt[] = {
5392 '/','I',' ','\"','%','s','\"',' ',
5393 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5394 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5395 WCHAR buffer[256], sysdir[MAX_PATH];
5396 HKEY hkey;
5397 WCHAR squished_pc[100];
5399 squash_guid(package->ProductCode,squished_pc);
5401 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5402 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5403 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5404 squished_pc);
5406 msi_reg_set_val_str( hkey, squished_pc, buffer );
5407 RegCloseKey(hkey);
5409 TRACE("Reboot command %s\n",debugstr_w(buffer));
5411 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5412 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5414 msi_reg_set_val_str( hkey, squished_pc, buffer );
5415 RegCloseKey(hkey);
5417 return ERROR_INSTALL_SUSPEND;
5420 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5422 static const WCHAR query[] =
5423 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5424 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5425 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5426 MSIRECORD *rec, *row;
5427 DWORD i, size = 0;
5428 va_list va;
5429 const WCHAR *str;
5430 WCHAR *data;
5432 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5434 rec = MSI_CreateRecord( count + 2 );
5435 str = MSI_RecordGetString( row, 1 );
5436 MSI_RecordSetStringW( rec, 0, str );
5437 msiobj_release( &row->hdr );
5438 MSI_RecordSetInteger( rec, 1, error );
5440 va_start( va, count );
5441 for (i = 0; i < count; i++)
5443 str = va_arg( va, const WCHAR *);
5444 MSI_RecordSetStringW( rec, i + 2, str );
5446 va_end( va );
5448 MSI_FormatRecordW( package, rec, NULL, &size );
5449 size++;
5450 data = msi_alloc( size * sizeof(WCHAR) );
5451 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5452 else data[0] = 0;
5453 msiobj_release( &rec->hdr );
5454 return data;
5457 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5459 DWORD attrib;
5460 UINT rc;
5463 * We are currently doing what should be done here in the top level Install
5464 * however for Administrative and uninstalls this step will be needed
5466 if (!package->PackagePath)
5467 return ERROR_SUCCESS;
5469 msi_set_sourcedir_props(package, TRUE);
5471 attrib = GetFileAttributesW(package->db->path);
5472 if (attrib == INVALID_FILE_ATTRIBUTES)
5474 LPWSTR prompt, msg;
5475 DWORD size = 0;
5477 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5478 package->Context, MSICODE_PRODUCT,
5479 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5480 if (rc == ERROR_MORE_DATA)
5482 prompt = msi_alloc(size * sizeof(WCHAR));
5483 MsiSourceListGetInfoW(package->ProductCode, NULL,
5484 package->Context, MSICODE_PRODUCT,
5485 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5487 else
5488 prompt = strdupW(package->db->path);
5490 msg = msi_build_error_string(package, 1302, 1, prompt);
5491 msi_free(prompt);
5492 while(attrib == INVALID_FILE_ATTRIBUTES)
5494 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5495 if (rc == IDCANCEL)
5497 msi_free(msg);
5498 return ERROR_INSTALL_USEREXIT;
5500 attrib = GetFileAttributesW(package->db->path);
5502 msi_free(msg);
5503 rc = ERROR_SUCCESS;
5505 else
5506 return ERROR_SUCCESS;
5508 return rc;
5511 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5513 HKEY hkey = 0;
5514 LPWSTR buffer, productid = NULL;
5515 UINT i, rc = ERROR_SUCCESS;
5516 MSIRECORD *uirow;
5518 static const WCHAR szPropKeys[][80] =
5520 {'P','r','o','d','u','c','t','I','D',0},
5521 {'U','S','E','R','N','A','M','E',0},
5522 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5523 {0},
5526 static const WCHAR szRegKeys[][80] =
5528 {'P','r','o','d','u','c','t','I','D',0},
5529 {'R','e','g','O','w','n','e','r',0},
5530 {'R','e','g','C','o','m','p','a','n','y',0},
5531 {0},
5534 if (msi_check_unpublish(package))
5536 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5537 goto end;
5540 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5541 if (!productid)
5542 goto end;
5544 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5545 NULL, &hkey, TRUE);
5546 if (rc != ERROR_SUCCESS)
5547 goto end;
5549 for( i = 0; szPropKeys[i][0]; i++ )
5551 buffer = msi_dup_property( package->db, szPropKeys[i] );
5552 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5553 msi_free( buffer );
5556 end:
5557 uirow = MSI_CreateRecord( 1 );
5558 MSI_RecordSetStringW( uirow, 1, productid );
5559 msi_ui_actiondata( package, szRegisterUser, uirow );
5560 msiobj_release( &uirow->hdr );
5562 msi_free(productid);
5563 RegCloseKey(hkey);
5564 return rc;
5568 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5570 UINT rc;
5572 package->script->InWhatSequence |= SEQUENCE_EXEC;
5573 rc = ACTION_ProcessExecSequence(package,FALSE);
5574 return rc;
5577 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5579 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5580 WCHAR productid_85[21], component_85[21], *ret;
5581 GUID clsid;
5582 DWORD sz;
5584 /* > is used if there is a component GUID and < if not. */
5586 productid_85[0] = 0;
5587 component_85[0] = 0;
5588 CLSIDFromString( package->ProductCode, &clsid );
5590 encode_base85_guid( &clsid, productid_85 );
5591 if (component)
5593 CLSIDFromString( component->ComponentId, &clsid );
5594 encode_base85_guid( &clsid, component_85 );
5597 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5598 debugstr_w(component_85));
5600 sz = 20 + strlenW( feature ) + 20 + 3;
5601 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5602 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5603 return ret;
5606 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5608 MSIPACKAGE *package = param;
5609 LPCWSTR compgroupid, component, feature, qualifier, text;
5610 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5611 HKEY hkey = NULL;
5612 UINT rc;
5613 MSICOMPONENT *comp;
5614 MSIFEATURE *feat;
5615 DWORD sz;
5616 MSIRECORD *uirow;
5617 int len;
5619 feature = MSI_RecordGetString(rec, 5);
5620 feat = msi_get_loaded_feature(package, feature);
5621 if (!feat)
5622 return ERROR_SUCCESS;
5624 feat->Action = msi_get_feature_action( package, feat );
5625 if (feat->Action != INSTALLSTATE_LOCAL &&
5626 feat->Action != INSTALLSTATE_SOURCE &&
5627 feat->Action != INSTALLSTATE_ADVERTISED)
5629 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5630 return ERROR_SUCCESS;
5633 component = MSI_RecordGetString(rec, 3);
5634 comp = msi_get_loaded_component(package, component);
5635 if (!comp)
5636 return ERROR_SUCCESS;
5638 compgroupid = MSI_RecordGetString(rec,1);
5639 qualifier = MSI_RecordGetString(rec,2);
5641 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5642 if (rc != ERROR_SUCCESS)
5643 goto end;
5645 advertise = msi_create_component_advertise_string( package, comp, feature );
5646 text = MSI_RecordGetString( rec, 4 );
5647 if (text)
5649 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5650 strcpyW( p, advertise );
5651 strcatW( p, text );
5652 msi_free( advertise );
5653 advertise = p;
5655 existing = msi_reg_get_val_str( hkey, qualifier );
5657 sz = strlenW( advertise ) + 1;
5658 if (existing)
5660 for (p = existing; *p; p += len)
5662 len = strlenW( p ) + 1;
5663 if (strcmpW( advertise, p )) sz += len;
5666 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5668 rc = ERROR_OUTOFMEMORY;
5669 goto end;
5671 q = output;
5672 if (existing)
5674 for (p = existing; *p; p += len)
5676 len = strlenW( p ) + 1;
5677 if (strcmpW( advertise, p ))
5679 memcpy( q, p, len * sizeof(WCHAR) );
5680 q += len;
5684 strcpyW( q, advertise );
5685 q[strlenW( q ) + 1] = 0;
5687 msi_reg_set_val_multi_str( hkey, qualifier, output );
5689 end:
5690 RegCloseKey(hkey);
5691 msi_free( output );
5692 msi_free( advertise );
5693 msi_free( existing );
5695 /* the UI chunk */
5696 uirow = MSI_CreateRecord( 2 );
5697 MSI_RecordSetStringW( uirow, 1, compgroupid );
5698 MSI_RecordSetStringW( uirow, 2, qualifier);
5699 msi_ui_actiondata( package, szPublishComponents, uirow );
5700 msiobj_release( &uirow->hdr );
5701 /* FIXME: call ui_progress? */
5703 return rc;
5707 * At present I am ignorning the advertised components part of this and only
5708 * focusing on the qualified component sets
5710 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5712 static const WCHAR query[] = {
5713 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5714 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5715 MSIQUERY *view;
5716 UINT rc;
5718 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5719 if (rc != ERROR_SUCCESS)
5720 return ERROR_SUCCESS;
5722 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5723 msiobj_release(&view->hdr);
5724 return rc;
5727 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5729 static const WCHAR szInstallerComponents[] = {
5730 'S','o','f','t','w','a','r','e','\\',
5731 'M','i','c','r','o','s','o','f','t','\\',
5732 'I','n','s','t','a','l','l','e','r','\\',
5733 'C','o','m','p','o','n','e','n','t','s','\\',0};
5735 MSIPACKAGE *package = param;
5736 LPCWSTR compgroupid, component, feature, qualifier;
5737 MSICOMPONENT *comp;
5738 MSIFEATURE *feat;
5739 MSIRECORD *uirow;
5740 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5741 LONG res;
5743 feature = MSI_RecordGetString( rec, 5 );
5744 feat = msi_get_loaded_feature( package, feature );
5745 if (!feat)
5746 return ERROR_SUCCESS;
5748 feat->Action = msi_get_feature_action( package, feat );
5749 if (feat->Action != INSTALLSTATE_ABSENT)
5751 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5752 return ERROR_SUCCESS;
5755 component = MSI_RecordGetString( rec, 3 );
5756 comp = msi_get_loaded_component( package, component );
5757 if (!comp)
5758 return ERROR_SUCCESS;
5760 compgroupid = MSI_RecordGetString( rec, 1 );
5761 qualifier = MSI_RecordGetString( rec, 2 );
5763 squash_guid( compgroupid, squashed );
5764 strcpyW( keypath, szInstallerComponents );
5765 strcatW( keypath, squashed );
5767 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5768 if (res != ERROR_SUCCESS)
5770 WARN("Unable to delete component key %d\n", res);
5773 uirow = MSI_CreateRecord( 2 );
5774 MSI_RecordSetStringW( uirow, 1, compgroupid );
5775 MSI_RecordSetStringW( uirow, 2, qualifier );
5776 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5777 msiobj_release( &uirow->hdr );
5779 return ERROR_SUCCESS;
5782 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5784 static const WCHAR query[] = {
5785 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5786 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5787 MSIQUERY *view;
5788 UINT rc;
5790 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5791 if (rc != ERROR_SUCCESS)
5792 return ERROR_SUCCESS;
5794 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5795 msiobj_release( &view->hdr );
5796 return rc;
5799 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5801 static const WCHAR query[] =
5802 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5803 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5804 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5805 MSIPACKAGE *package = param;
5806 MSICOMPONENT *component;
5807 MSIRECORD *row;
5808 MSIFILE *file;
5809 SC_HANDLE hscm = NULL, service = NULL;
5810 LPCWSTR comp, key;
5811 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5812 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5813 DWORD serv_type, start_type, err_control;
5814 BOOL is_vital;
5815 SERVICE_DESCRIPTIONW sd = {NULL};
5816 UINT ret = ERROR_SUCCESS;
5818 comp = MSI_RecordGetString( rec, 12 );
5819 component = msi_get_loaded_component( package, comp );
5820 if (!component)
5822 WARN("service component not found\n");
5823 goto done;
5825 component->Action = msi_get_component_action( package, component );
5826 if (component->Action != INSTALLSTATE_LOCAL)
5828 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5829 goto done;
5831 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5832 if (!hscm)
5834 ERR("Failed to open the SC Manager!\n");
5835 goto done;
5838 start_type = MSI_RecordGetInteger(rec, 5);
5839 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5840 goto done;
5842 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5843 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5844 serv_type = MSI_RecordGetInteger(rec, 4);
5845 err_control = MSI_RecordGetInteger(rec, 6);
5846 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5847 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5848 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5849 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5850 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5851 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5853 /* Should the complete install fail if CreateService fails? */
5854 is_vital = (err_control & msidbServiceInstallErrorControlVital);
5856 /* Remove the msidbServiceInstallErrorControlVital-flag from err_control.
5857 CreateService (under Windows) would fail if not. */
5858 err_control &= ~msidbServiceInstallErrorControlVital;
5860 /* fetch the service path */
5861 row = MSI_QueryGetRecord(package->db, query, comp);
5862 if (!row)
5864 ERR("Query failed\n");
5865 goto done;
5867 if (!(key = MSI_RecordGetString(row, 6)))
5869 msiobj_release(&row->hdr);
5870 goto done;
5872 file = msi_get_loaded_file(package, key);
5873 msiobj_release(&row->hdr);
5874 if (!file)
5876 ERR("Failed to load the service file\n");
5877 goto done;
5880 if (!args || !args[0]) image_path = file->TargetPath;
5881 else
5883 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5884 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5886 ret = ERROR_OUTOFMEMORY;
5887 goto done;
5890 strcpyW(image_path, file->TargetPath);
5891 strcatW(image_path, szSpace);
5892 strcatW(image_path, args);
5894 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5895 start_type, err_control, image_path, load_order,
5896 NULL, depends, serv_name, pass);
5898 if (!service)
5900 if (GetLastError() != ERROR_SERVICE_EXISTS)
5902 WARN("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5903 if (is_vital)
5904 ret = ERROR_INSTALL_FAILURE;
5908 else if (sd.lpDescription)
5910 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5911 WARN("failed to set service description %u\n", GetLastError());
5914 if (image_path != file->TargetPath) msi_free(image_path);
5915 done:
5916 if (service) CloseServiceHandle(service);
5917 if (hscm) CloseServiceHandle(hscm);
5918 msi_free(name);
5919 msi_free(disp);
5920 msi_free(sd.lpDescription);
5921 msi_free(load_order);
5922 msi_free(serv_name);
5923 msi_free(pass);
5924 msi_free(depends);
5925 msi_free(args);
5927 return ret;
5930 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5932 static const WCHAR query[] = {
5933 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5934 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5935 MSIQUERY *view;
5936 UINT rc;
5938 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5939 if (rc != ERROR_SUCCESS)
5940 return ERROR_SUCCESS;
5942 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5943 msiobj_release(&view->hdr);
5944 return rc;
5947 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5948 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5950 LPCWSTR *vector, *temp_vector;
5951 LPWSTR p, q;
5952 DWORD sep_len;
5954 static const WCHAR separator[] = {'[','~',']',0};
5956 *numargs = 0;
5957 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5959 if (!args)
5960 return NULL;
5962 vector = msi_alloc(sizeof(LPWSTR));
5963 if (!vector)
5964 return NULL;
5966 p = args;
5969 (*numargs)++;
5970 vector[*numargs - 1] = p;
5972 if ((q = strstrW(p, separator)))
5974 *q = '\0';
5976 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5977 if (!temp_vector)
5979 msi_free(vector);
5980 return NULL;
5982 vector = temp_vector;
5984 p = q + sep_len;
5986 } while (q);
5988 return vector;
5991 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5993 MSIPACKAGE *package = param;
5994 MSICOMPONENT *comp;
5995 MSIRECORD *uirow;
5996 SC_HANDLE scm = NULL, service = NULL;
5997 LPCWSTR component, *vector = NULL;
5998 LPWSTR name, args, display_name = NULL;
5999 DWORD event, numargs, len, wait, dummy;
6000 UINT r = ERROR_FUNCTION_FAILED;
6001 SERVICE_STATUS_PROCESS status;
6002 ULONGLONG start_time;
6004 component = MSI_RecordGetString(rec, 6);
6005 comp = msi_get_loaded_component(package, component);
6006 if (!comp)
6007 return ERROR_SUCCESS;
6009 event = MSI_RecordGetInteger( rec, 3 );
6010 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6012 comp->Action = msi_get_component_action( package, comp );
6013 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
6014 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
6016 TRACE("not starting %s\n", debugstr_w(name));
6017 msi_free( name );
6018 return ERROR_SUCCESS;
6021 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
6022 wait = MSI_RecordGetInteger(rec, 5);
6024 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
6025 if (!scm)
6027 ERR("Failed to open the service control manager\n");
6028 goto done;
6031 len = 0;
6032 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6033 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6035 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6036 GetServiceDisplayNameW( scm, name, display_name, &len );
6039 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
6040 if (!service)
6042 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6043 goto done;
6046 vector = msi_service_args_to_vector(args, &numargs);
6048 if (!StartServiceW(service, numargs, vector) &&
6049 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6051 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6052 goto done;
6055 r = ERROR_SUCCESS;
6056 if (wait)
6058 /* wait for at most 30 seconds for the service to be up and running */
6059 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6060 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6062 TRACE("failed to query service status (%u)\n", GetLastError());
6063 goto done;
6065 start_time = GetTickCount64();
6066 while (status.dwCurrentState == SERVICE_START_PENDING)
6068 if (GetTickCount64() - start_time > 30000) break;
6069 Sleep(1000);
6070 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6071 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6073 TRACE("failed to query service status (%u)\n", GetLastError());
6074 goto done;
6077 if (status.dwCurrentState != SERVICE_RUNNING)
6079 WARN("service failed to start %u\n", status.dwCurrentState);
6080 r = ERROR_FUNCTION_FAILED;
6084 done:
6085 uirow = MSI_CreateRecord( 2 );
6086 MSI_RecordSetStringW( uirow, 1, display_name );
6087 MSI_RecordSetStringW( uirow, 2, name );
6088 msi_ui_actiondata( package, szStartServices, uirow );
6089 msiobj_release( &uirow->hdr );
6091 if (service) CloseServiceHandle(service);
6092 if (scm) CloseServiceHandle(scm);
6094 msi_free(name);
6095 msi_free(args);
6096 msi_free(vector);
6097 msi_free(display_name);
6098 return r;
6101 static UINT ACTION_StartServices( MSIPACKAGE *package )
6103 static const WCHAR query[] = {
6104 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6105 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6106 MSIQUERY *view;
6107 UINT rc;
6109 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6110 if (rc != ERROR_SUCCESS)
6111 return ERROR_SUCCESS;
6113 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6114 msiobj_release(&view->hdr);
6115 return rc;
6118 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6120 DWORD i, needed, count;
6121 ENUM_SERVICE_STATUSW *dependencies;
6122 SERVICE_STATUS ss;
6123 SC_HANDLE depserv;
6124 BOOL stopped, ret = FALSE;
6126 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6127 0, &needed, &count))
6128 return TRUE;
6130 if (GetLastError() != ERROR_MORE_DATA)
6131 return FALSE;
6133 dependencies = msi_alloc(needed);
6134 if (!dependencies)
6135 return FALSE;
6137 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6138 needed, &needed, &count))
6139 goto done;
6141 for (i = 0; i < count; i++)
6143 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6144 SERVICE_STOP | SERVICE_QUERY_STATUS);
6145 if (!depserv)
6146 goto done;
6148 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6149 CloseServiceHandle(depserv);
6150 if (!stopped)
6151 goto done;
6154 ret = TRUE;
6156 done:
6157 msi_free(dependencies);
6158 return ret;
6161 static UINT stop_service( LPCWSTR name )
6163 SC_HANDLE scm = NULL, service = NULL;
6164 SERVICE_STATUS status;
6165 SERVICE_STATUS_PROCESS ssp;
6166 DWORD needed;
6168 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6169 if (!scm)
6171 WARN("Failed to open the SCM: %d\n", GetLastError());
6172 goto done;
6175 service = OpenServiceW(scm, name,
6176 SERVICE_STOP |
6177 SERVICE_QUERY_STATUS |
6178 SERVICE_ENUMERATE_DEPENDENTS);
6179 if (!service)
6181 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6182 goto done;
6185 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6186 sizeof(SERVICE_STATUS_PROCESS), &needed))
6188 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6189 goto done;
6192 if (ssp.dwCurrentState == SERVICE_STOPPED)
6193 goto done;
6195 stop_service_dependents(scm, service);
6197 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6198 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6200 done:
6201 if (service) CloseServiceHandle(service);
6202 if (scm) CloseServiceHandle(scm);
6204 return ERROR_SUCCESS;
6207 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6209 MSIPACKAGE *package = param;
6210 MSICOMPONENT *comp;
6211 MSIRECORD *uirow;
6212 LPCWSTR component;
6213 WCHAR *name, *display_name = NULL;
6214 DWORD event, len;
6215 SC_HANDLE scm;
6217 component = MSI_RecordGetString( rec, 6 );
6218 comp = msi_get_loaded_component( package, component );
6219 if (!comp)
6220 return ERROR_SUCCESS;
6222 event = MSI_RecordGetInteger( rec, 3 );
6223 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6225 comp->Action = msi_get_component_action( package, comp );
6226 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6227 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6229 TRACE("not stopping %s\n", debugstr_w(name));
6230 msi_free( name );
6231 return ERROR_SUCCESS;
6234 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6235 if (!scm)
6237 ERR("Failed to open the service control manager\n");
6238 goto done;
6241 len = 0;
6242 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6243 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6245 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6246 GetServiceDisplayNameW( scm, name, display_name, &len );
6248 CloseServiceHandle( scm );
6250 stop_service( name );
6252 done:
6253 uirow = MSI_CreateRecord( 2 );
6254 MSI_RecordSetStringW( uirow, 1, display_name );
6255 MSI_RecordSetStringW( uirow, 2, name );
6256 msi_ui_actiondata( package, szStopServices, uirow );
6257 msiobj_release( &uirow->hdr );
6259 msi_free( name );
6260 msi_free( display_name );
6261 return ERROR_SUCCESS;
6264 static UINT ACTION_StopServices( MSIPACKAGE *package )
6266 static const WCHAR query[] = {
6267 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6268 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6269 MSIQUERY *view;
6270 UINT rc;
6272 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6273 if (rc != ERROR_SUCCESS)
6274 return ERROR_SUCCESS;
6276 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6277 msiobj_release(&view->hdr);
6278 return rc;
6281 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6283 MSIPACKAGE *package = param;
6284 MSICOMPONENT *comp;
6285 MSIRECORD *uirow;
6286 LPWSTR name = NULL, display_name = NULL;
6287 DWORD event, len;
6288 SC_HANDLE scm = NULL, service = NULL;
6290 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6291 if (!comp)
6292 return ERROR_SUCCESS;
6294 event = MSI_RecordGetInteger( rec, 3 );
6295 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6297 comp->Action = msi_get_component_action( package, comp );
6298 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6299 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6301 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6302 msi_free( name );
6303 return ERROR_SUCCESS;
6305 stop_service( name );
6307 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6308 if (!scm)
6310 WARN("Failed to open the SCM: %d\n", GetLastError());
6311 goto done;
6314 len = 0;
6315 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6316 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6318 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6319 GetServiceDisplayNameW( scm, name, display_name, &len );
6322 service = OpenServiceW( scm, name, DELETE );
6323 if (!service)
6325 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6326 goto done;
6329 if (!DeleteService( service ))
6330 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6332 done:
6333 uirow = MSI_CreateRecord( 2 );
6334 MSI_RecordSetStringW( uirow, 1, display_name );
6335 MSI_RecordSetStringW( uirow, 2, name );
6336 msi_ui_actiondata( package, szDeleteServices, uirow );
6337 msiobj_release( &uirow->hdr );
6339 if (service) CloseServiceHandle( service );
6340 if (scm) CloseServiceHandle( scm );
6341 msi_free( name );
6342 msi_free( display_name );
6344 return ERROR_SUCCESS;
6347 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6349 static const WCHAR query[] = {
6350 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6351 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6352 MSIQUERY *view;
6353 UINT rc;
6355 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6356 if (rc != ERROR_SUCCESS)
6357 return ERROR_SUCCESS;
6359 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6360 msiobj_release( &view->hdr );
6361 return rc;
6364 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6366 MSIPACKAGE *package = param;
6367 LPWSTR driver, driver_path, ptr;
6368 WCHAR outpath[MAX_PATH];
6369 MSIFILE *driver_file = NULL, *setup_file = NULL;
6370 MSICOMPONENT *comp;
6371 MSIRECORD *uirow;
6372 LPCWSTR desc, file_key, component;
6373 DWORD len, usage;
6374 UINT r = ERROR_SUCCESS;
6376 static const WCHAR driver_fmt[] = {
6377 'D','r','i','v','e','r','=','%','s',0};
6378 static const WCHAR setup_fmt[] = {
6379 'S','e','t','u','p','=','%','s',0};
6380 static const WCHAR usage_fmt[] = {
6381 'F','i','l','e','U','s','a','g','e','=','1',0};
6383 component = MSI_RecordGetString( rec, 2 );
6384 comp = msi_get_loaded_component( package, component );
6385 if (!comp)
6386 return ERROR_SUCCESS;
6388 comp->Action = msi_get_component_action( package, comp );
6389 if (comp->Action != INSTALLSTATE_LOCAL)
6391 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6392 return ERROR_SUCCESS;
6394 desc = MSI_RecordGetString(rec, 3);
6396 file_key = MSI_RecordGetString( rec, 4 );
6397 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6399 file_key = MSI_RecordGetString( rec, 5 );
6400 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6402 if (!driver_file)
6404 ERR("ODBC Driver entry not found!\n");
6405 return ERROR_FUNCTION_FAILED;
6408 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6409 if (setup_file)
6410 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6411 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6413 driver = msi_alloc(len * sizeof(WCHAR));
6414 if (!driver)
6415 return ERROR_OUTOFMEMORY;
6417 ptr = driver;
6418 lstrcpyW(ptr, desc);
6419 ptr += lstrlenW(ptr) + 1;
6421 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6422 ptr += len + 1;
6424 if (setup_file)
6426 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6427 ptr += len + 1;
6430 lstrcpyW(ptr, usage_fmt);
6431 ptr += lstrlenW(ptr) + 1;
6432 *ptr = '\0';
6434 if (!driver_file->TargetPath)
6436 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6437 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6439 driver_path = strdupW(driver_file->TargetPath);
6440 ptr = strrchrW(driver_path, '\\');
6441 if (ptr) *ptr = '\0';
6443 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6444 NULL, ODBC_INSTALL_COMPLETE, &usage))
6446 ERR("Failed to install SQL driver!\n");
6447 r = ERROR_FUNCTION_FAILED;
6450 uirow = MSI_CreateRecord( 5 );
6451 MSI_RecordSetStringW( uirow, 1, desc );
6452 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6453 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6454 msi_ui_actiondata( package, szInstallODBC, uirow );
6455 msiobj_release( &uirow->hdr );
6457 msi_free(driver);
6458 msi_free(driver_path);
6460 return r;
6463 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6465 MSIPACKAGE *package = param;
6466 LPWSTR translator, translator_path, ptr;
6467 WCHAR outpath[MAX_PATH];
6468 MSIFILE *translator_file = NULL, *setup_file = NULL;
6469 MSICOMPONENT *comp;
6470 MSIRECORD *uirow;
6471 LPCWSTR desc, file_key, component;
6472 DWORD len, usage;
6473 UINT r = ERROR_SUCCESS;
6475 static const WCHAR translator_fmt[] = {
6476 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6477 static const WCHAR setup_fmt[] = {
6478 'S','e','t','u','p','=','%','s',0};
6480 component = MSI_RecordGetString( rec, 2 );
6481 comp = msi_get_loaded_component( package, component );
6482 if (!comp)
6483 return ERROR_SUCCESS;
6485 comp->Action = msi_get_component_action( package, comp );
6486 if (comp->Action != INSTALLSTATE_LOCAL)
6488 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6489 return ERROR_SUCCESS;
6491 desc = MSI_RecordGetString(rec, 3);
6493 file_key = MSI_RecordGetString( rec, 4 );
6494 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6496 file_key = MSI_RecordGetString( rec, 5 );
6497 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6499 if (!translator_file)
6501 ERR("ODBC Translator entry not found!\n");
6502 return ERROR_FUNCTION_FAILED;
6505 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6506 if (setup_file)
6507 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6509 translator = msi_alloc(len * sizeof(WCHAR));
6510 if (!translator)
6511 return ERROR_OUTOFMEMORY;
6513 ptr = translator;
6514 lstrcpyW(ptr, desc);
6515 ptr += lstrlenW(ptr) + 1;
6517 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6518 ptr += len + 1;
6520 if (setup_file)
6522 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6523 ptr += len + 1;
6525 *ptr = '\0';
6527 translator_path = strdupW(translator_file->TargetPath);
6528 ptr = strrchrW(translator_path, '\\');
6529 if (ptr) *ptr = '\0';
6531 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6532 NULL, ODBC_INSTALL_COMPLETE, &usage))
6534 ERR("Failed to install SQL translator!\n");
6535 r = ERROR_FUNCTION_FAILED;
6538 uirow = MSI_CreateRecord( 5 );
6539 MSI_RecordSetStringW( uirow, 1, desc );
6540 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6541 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6542 msi_ui_actiondata( package, szInstallODBC, uirow );
6543 msiobj_release( &uirow->hdr );
6545 msi_free(translator);
6546 msi_free(translator_path);
6548 return r;
6551 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6553 MSIPACKAGE *package = param;
6554 MSICOMPONENT *comp;
6555 LPWSTR attrs;
6556 LPCWSTR desc, driver, component;
6557 WORD request = ODBC_ADD_SYS_DSN;
6558 INT registration;
6559 DWORD len;
6560 UINT r = ERROR_SUCCESS;
6561 MSIRECORD *uirow;
6563 static const WCHAR attrs_fmt[] = {
6564 'D','S','N','=','%','s',0 };
6566 component = MSI_RecordGetString( rec, 2 );
6567 comp = msi_get_loaded_component( package, component );
6568 if (!comp)
6569 return ERROR_SUCCESS;
6571 comp->Action = msi_get_component_action( package, comp );
6572 if (comp->Action != INSTALLSTATE_LOCAL)
6574 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6575 return ERROR_SUCCESS;
6578 desc = MSI_RecordGetString(rec, 3);
6579 driver = MSI_RecordGetString(rec, 4);
6580 registration = MSI_RecordGetInteger(rec, 5);
6582 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6583 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6585 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6586 attrs = msi_alloc(len * sizeof(WCHAR));
6587 if (!attrs)
6588 return ERROR_OUTOFMEMORY;
6590 len = sprintfW(attrs, attrs_fmt, desc);
6591 attrs[len + 1] = 0;
6593 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6595 ERR("Failed to install SQL data source!\n");
6596 r = ERROR_FUNCTION_FAILED;
6599 uirow = MSI_CreateRecord( 5 );
6600 MSI_RecordSetStringW( uirow, 1, desc );
6601 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6602 MSI_RecordSetInteger( uirow, 3, request );
6603 msi_ui_actiondata( package, szInstallODBC, uirow );
6604 msiobj_release( &uirow->hdr );
6606 msi_free(attrs);
6608 return r;
6611 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6613 static const WCHAR driver_query[] = {
6614 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6615 'O','D','B','C','D','r','i','v','e','r',0};
6616 static const WCHAR translator_query[] = {
6617 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6618 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6619 static const WCHAR source_query[] = {
6620 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6621 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6622 MSIQUERY *view;
6623 UINT rc;
6625 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6626 if (rc == ERROR_SUCCESS)
6628 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6629 msiobj_release(&view->hdr);
6630 if (rc != ERROR_SUCCESS)
6631 return rc;
6633 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6634 if (rc == ERROR_SUCCESS)
6636 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6637 msiobj_release(&view->hdr);
6638 if (rc != ERROR_SUCCESS)
6639 return rc;
6641 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6642 if (rc == ERROR_SUCCESS)
6644 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6645 msiobj_release(&view->hdr);
6646 if (rc != ERROR_SUCCESS)
6647 return rc;
6649 return ERROR_SUCCESS;
6652 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6654 MSIPACKAGE *package = param;
6655 MSICOMPONENT *comp;
6656 MSIRECORD *uirow;
6657 DWORD usage;
6658 LPCWSTR desc, component;
6660 component = MSI_RecordGetString( rec, 2 );
6661 comp = msi_get_loaded_component( package, component );
6662 if (!comp)
6663 return ERROR_SUCCESS;
6665 comp->Action = msi_get_component_action( package, comp );
6666 if (comp->Action != INSTALLSTATE_ABSENT)
6668 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6669 return ERROR_SUCCESS;
6672 desc = MSI_RecordGetString( rec, 3 );
6673 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6675 WARN("Failed to remove ODBC driver\n");
6677 else if (!usage)
6679 FIXME("Usage count reached 0\n");
6682 uirow = MSI_CreateRecord( 2 );
6683 MSI_RecordSetStringW( uirow, 1, desc );
6684 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6685 msi_ui_actiondata( package, szRemoveODBC, uirow );
6686 msiobj_release( &uirow->hdr );
6688 return ERROR_SUCCESS;
6691 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6693 MSIPACKAGE *package = param;
6694 MSICOMPONENT *comp;
6695 MSIRECORD *uirow;
6696 DWORD usage;
6697 LPCWSTR desc, component;
6699 component = MSI_RecordGetString( rec, 2 );
6700 comp = msi_get_loaded_component( package, component );
6701 if (!comp)
6702 return ERROR_SUCCESS;
6704 comp->Action = msi_get_component_action( package, comp );
6705 if (comp->Action != INSTALLSTATE_ABSENT)
6707 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6708 return ERROR_SUCCESS;
6711 desc = MSI_RecordGetString( rec, 3 );
6712 if (!SQLRemoveTranslatorW( desc, &usage ))
6714 WARN("Failed to remove ODBC translator\n");
6716 else if (!usage)
6718 FIXME("Usage count reached 0\n");
6721 uirow = MSI_CreateRecord( 2 );
6722 MSI_RecordSetStringW( uirow, 1, desc );
6723 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6724 msi_ui_actiondata( package, szRemoveODBC, uirow );
6725 msiobj_release( &uirow->hdr );
6727 return ERROR_SUCCESS;
6730 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6732 MSIPACKAGE *package = param;
6733 MSICOMPONENT *comp;
6734 MSIRECORD *uirow;
6735 LPWSTR attrs;
6736 LPCWSTR desc, driver, component;
6737 WORD request = ODBC_REMOVE_SYS_DSN;
6738 INT registration;
6739 DWORD len;
6741 static const WCHAR attrs_fmt[] = {
6742 'D','S','N','=','%','s',0 };
6744 component = MSI_RecordGetString( rec, 2 );
6745 comp = msi_get_loaded_component( package, component );
6746 if (!comp)
6747 return ERROR_SUCCESS;
6749 comp->Action = msi_get_component_action( package, comp );
6750 if (comp->Action != INSTALLSTATE_ABSENT)
6752 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6753 return ERROR_SUCCESS;
6756 desc = MSI_RecordGetString( rec, 3 );
6757 driver = MSI_RecordGetString( rec, 4 );
6758 registration = MSI_RecordGetInteger( rec, 5 );
6760 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6761 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6763 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6764 attrs = msi_alloc( len * sizeof(WCHAR) );
6765 if (!attrs)
6766 return ERROR_OUTOFMEMORY;
6768 FIXME("Use ODBCSourceAttribute table\n");
6770 len = sprintfW( attrs, attrs_fmt, desc );
6771 attrs[len + 1] = 0;
6773 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6775 WARN("Failed to remove ODBC data source\n");
6777 msi_free( attrs );
6779 uirow = MSI_CreateRecord( 3 );
6780 MSI_RecordSetStringW( uirow, 1, desc );
6781 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6782 MSI_RecordSetInteger( uirow, 3, request );
6783 msi_ui_actiondata( package, szRemoveODBC, uirow );
6784 msiobj_release( &uirow->hdr );
6786 return ERROR_SUCCESS;
6789 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6791 static const WCHAR driver_query[] = {
6792 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6793 'O','D','B','C','D','r','i','v','e','r',0};
6794 static const WCHAR translator_query[] = {
6795 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6796 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6797 static const WCHAR source_query[] = {
6798 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6799 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6800 MSIQUERY *view;
6801 UINT rc;
6803 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6804 if (rc == ERROR_SUCCESS)
6806 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6807 msiobj_release( &view->hdr );
6808 if (rc != ERROR_SUCCESS)
6809 return rc;
6811 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6812 if (rc == ERROR_SUCCESS)
6814 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6815 msiobj_release( &view->hdr );
6816 if (rc != ERROR_SUCCESS)
6817 return rc;
6819 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6820 if (rc == ERROR_SUCCESS)
6822 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6823 msiobj_release( &view->hdr );
6824 if (rc != ERROR_SUCCESS)
6825 return rc;
6827 return ERROR_SUCCESS;
6830 #define ENV_ACT_SETALWAYS 0x1
6831 #define ENV_ACT_SETABSENT 0x2
6832 #define ENV_ACT_REMOVE 0x4
6833 #define ENV_ACT_REMOVEMATCH 0x8
6835 #define ENV_MOD_MACHINE 0x20000000
6836 #define ENV_MOD_APPEND 0x40000000
6837 #define ENV_MOD_PREFIX 0x80000000
6838 #define ENV_MOD_MASK 0xC0000000
6840 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6842 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6844 LPCWSTR cptr = *name;
6846 static const WCHAR prefix[] = {'[','~',']',0};
6847 static const int prefix_len = 3;
6849 *flags = 0;
6850 while (*cptr)
6852 if (*cptr == '=')
6853 *flags |= ENV_ACT_SETALWAYS;
6854 else if (*cptr == '+')
6855 *flags |= ENV_ACT_SETABSENT;
6856 else if (*cptr == '-')
6857 *flags |= ENV_ACT_REMOVE;
6858 else if (*cptr == '!')
6859 *flags |= ENV_ACT_REMOVEMATCH;
6860 else if (*cptr == '*')
6861 *flags |= ENV_MOD_MACHINE;
6862 else
6863 break;
6865 cptr++;
6866 (*name)++;
6869 if (!*cptr)
6871 ERR("Missing environment variable\n");
6872 return ERROR_FUNCTION_FAILED;
6875 if (*value)
6877 LPCWSTR ptr = *value;
6878 if (!strncmpW(ptr, prefix, prefix_len))
6880 if (ptr[prefix_len] == szSemiColon[0])
6882 *flags |= ENV_MOD_APPEND;
6883 *value += lstrlenW(prefix);
6885 else
6887 *value = NULL;
6890 else if (lstrlenW(*value) >= prefix_len)
6892 ptr += lstrlenW(ptr) - prefix_len;
6893 if (!strcmpW( ptr, prefix ))
6895 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6897 *flags |= ENV_MOD_PREFIX;
6898 /* the "[~]" will be removed by deformat_string */;
6900 else
6902 *value = NULL;
6908 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6909 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6910 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6911 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6913 ERR("Invalid flags: %08x\n", *flags);
6914 return ERROR_FUNCTION_FAILED;
6917 if (!*flags)
6918 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6920 return ERROR_SUCCESS;
6923 static UINT open_env_key( DWORD flags, HKEY *key )
6925 static const WCHAR user_env[] =
6926 {'E','n','v','i','r','o','n','m','e','n','t',0};
6927 static const WCHAR machine_env[] =
6928 {'S','y','s','t','e','m','\\',
6929 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6930 'C','o','n','t','r','o','l','\\',
6931 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6932 'E','n','v','i','r','o','n','m','e','n','t',0};
6933 const WCHAR *env;
6934 HKEY root;
6935 LONG res;
6937 if (flags & ENV_MOD_MACHINE)
6939 env = machine_env;
6940 root = HKEY_LOCAL_MACHINE;
6942 else
6944 env = user_env;
6945 root = HKEY_CURRENT_USER;
6948 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6949 if (res != ERROR_SUCCESS)
6951 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6952 return ERROR_FUNCTION_FAILED;
6955 return ERROR_SUCCESS;
6958 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6960 MSIPACKAGE *package = param;
6961 LPCWSTR name, value, component;
6962 WCHAR *data = NULL, *newval = NULL, *deformatted = NULL, *p, *q;
6963 DWORD flags, type, size, len, len_value = 0;
6964 UINT res;
6965 HKEY env = NULL;
6966 MSICOMPONENT *comp;
6967 MSIRECORD *uirow;
6968 int action = 0, found = 0;
6970 component = MSI_RecordGetString(rec, 4);
6971 comp = msi_get_loaded_component(package, component);
6972 if (!comp)
6973 return ERROR_SUCCESS;
6975 comp->Action = msi_get_component_action( package, comp );
6976 if (comp->Action != INSTALLSTATE_LOCAL)
6978 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6979 return ERROR_SUCCESS;
6981 name = MSI_RecordGetString(rec, 2);
6982 value = MSI_RecordGetString(rec, 3);
6984 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6986 res = env_parse_flags(&name, &value, &flags);
6987 if (res != ERROR_SUCCESS || !value)
6988 goto done;
6990 if (value && !deformat_string(package, value, &deformatted))
6992 res = ERROR_OUTOFMEMORY;
6993 goto done;
6996 if ((value = deformatted))
6998 if (flags & ENV_MOD_PREFIX)
7000 p = strrchrW( value, ';' );
7001 len_value = p - value;
7003 else if (flags & ENV_MOD_APPEND)
7005 value = strchrW( value, ';' ) + 1;
7006 len_value = strlenW( value );
7008 else len_value = strlenW( value );
7011 res = open_env_key( flags, &env );
7012 if (res != ERROR_SUCCESS)
7013 goto done;
7015 if (flags & ENV_MOD_MACHINE)
7016 action |= 0x20000000;
7018 size = 0;
7019 type = REG_SZ;
7020 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
7021 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
7022 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
7023 goto done;
7025 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
7027 action = 0x2;
7029 /* Nothing to do. */
7030 if (!value)
7032 res = ERROR_SUCCESS;
7033 goto done;
7035 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
7036 newval = strdupW(value);
7037 if (!newval)
7039 res = ERROR_OUTOFMEMORY;
7040 goto done;
7043 else
7045 action = 0x1;
7047 /* Contrary to MSDN, +-variable to [~];path works */
7048 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7050 res = ERROR_SUCCESS;
7051 goto done;
7054 if (!(p = q = data = msi_alloc( size )))
7056 msi_free(deformatted);
7057 RegCloseKey(env);
7058 return ERROR_OUTOFMEMORY;
7061 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)data, &size );
7062 if (res != ERROR_SUCCESS)
7063 goto done;
7065 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7067 action = 0x4;
7068 res = RegDeleteValueW(env, name);
7069 if (res != ERROR_SUCCESS)
7070 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7071 goto done;
7074 for (;;)
7076 while (*q && *q != ';') q++;
7077 len = q - p;
7078 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ) &&
7079 (!p[len] || p[len] == ';'))
7081 found = 1;
7082 break;
7084 if (!*q) break;
7085 p = ++q;
7088 if (found)
7090 TRACE("string already set\n");
7091 goto done;
7094 size = (len_value + 1 + strlenW( data ) + 1) * sizeof(WCHAR);
7095 if (!(p = newval = msi_alloc( size )))
7097 res = ERROR_OUTOFMEMORY;
7098 goto done;
7101 if (flags & ENV_MOD_PREFIX)
7103 memcpy( newval, value, len_value * sizeof(WCHAR) );
7104 newval[len_value] = ';';
7105 p = newval + len_value + 1;
7106 action |= 0x80000000;
7109 strcpyW( p, data );
7111 if (flags & ENV_MOD_APPEND)
7113 p += strlenW( data );
7114 *p++ = ';';
7115 memcpy( p, value, (len_value + 1) * sizeof(WCHAR) );
7116 action |= 0x40000000;
7119 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7120 res = RegSetValueExW( env, name, 0, type, (BYTE *)newval, size );
7121 if (res)
7123 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7126 done:
7127 uirow = MSI_CreateRecord( 3 );
7128 MSI_RecordSetStringW( uirow, 1, name );
7129 MSI_RecordSetStringW( uirow, 2, newval );
7130 MSI_RecordSetInteger( uirow, 3, action );
7131 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7132 msiobj_release( &uirow->hdr );
7134 if (env) RegCloseKey(env);
7135 msi_free(deformatted);
7136 msi_free(data);
7137 msi_free(newval);
7138 return res;
7141 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7143 static const WCHAR query[] = {
7144 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7145 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7146 MSIQUERY *view;
7147 UINT rc;
7149 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7150 if (rc != ERROR_SUCCESS)
7151 return ERROR_SUCCESS;
7153 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7154 msiobj_release(&view->hdr);
7155 return rc;
7158 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7160 MSIPACKAGE *package = param;
7161 LPCWSTR name, value, component;
7162 WCHAR *p, *q, *deformatted = NULL, *new_value = NULL;
7163 DWORD flags, type, size, len, len_value = 0, len_new_value;
7164 HKEY env;
7165 MSICOMPONENT *comp;
7166 MSIRECORD *uirow;
7167 int action = 0;
7168 LONG res;
7169 UINT r;
7171 component = MSI_RecordGetString( rec, 4 );
7172 comp = msi_get_loaded_component( package, component );
7173 if (!comp)
7174 return ERROR_SUCCESS;
7176 comp->Action = msi_get_component_action( package, comp );
7177 if (comp->Action != INSTALLSTATE_ABSENT)
7179 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7180 return ERROR_SUCCESS;
7182 name = MSI_RecordGetString( rec, 2 );
7183 value = MSI_RecordGetString( rec, 3 );
7185 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7187 r = env_parse_flags( &name, &value, &flags );
7188 if (r != ERROR_SUCCESS)
7189 return r;
7191 if (!(flags & ENV_ACT_REMOVE))
7193 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7194 return ERROR_SUCCESS;
7197 if (value && !deformat_string( package, value, &deformatted ))
7198 return ERROR_OUTOFMEMORY;
7200 if ((value = deformatted))
7202 if (flags & ENV_MOD_PREFIX)
7204 p = strchrW( value, ';' );
7205 len_value = p - value;
7207 else if (flags & ENV_MOD_APPEND)
7209 value = strchrW( value, ';' ) + 1;
7210 len_value = strlenW( value );
7212 else len_value = strlenW( value );
7215 r = open_env_key( flags, &env );
7216 if (r != ERROR_SUCCESS)
7218 r = ERROR_SUCCESS;
7219 goto done;
7222 if (flags & ENV_MOD_MACHINE)
7223 action |= 0x20000000;
7225 size = 0;
7226 type = REG_SZ;
7227 res = RegQueryValueExW( env, name, NULL, &type, NULL, &size );
7228 if (res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ))
7229 goto done;
7231 if (!(new_value = msi_alloc( size ))) goto done;
7233 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)new_value, &size );
7234 if (res != ERROR_SUCCESS)
7235 goto done;
7237 len_new_value = size / sizeof(WCHAR) - 1;
7238 p = q = new_value;
7239 for (;;)
7241 while (*q && *q != ';') q++;
7242 len = q - p;
7243 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ))
7245 if (*q == ';') q++;
7246 memmove( p, q, (len_new_value - (q - new_value) + 1) * sizeof(WCHAR) );
7247 break;
7249 if (!*q) break;
7250 p = ++q;
7253 if (!new_value[0] || !value)
7255 TRACE("removing %s\n", debugstr_w(name));
7256 res = RegDeleteValueW( env, name );
7257 if (res != ERROR_SUCCESS)
7258 WARN("failed to delete value %s (%d)\n", debugstr_w(name), res);
7260 else
7262 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(new_value));
7263 size = (strlenW( new_value ) + 1) * sizeof(WCHAR);
7264 res = RegSetValueExW( env, name, 0, type, (BYTE *)new_value, size );
7265 if (res != ERROR_SUCCESS)
7266 WARN("failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(new_value), res);
7269 done:
7270 uirow = MSI_CreateRecord( 3 );
7271 MSI_RecordSetStringW( uirow, 1, name );
7272 MSI_RecordSetStringW( uirow, 2, value );
7273 MSI_RecordSetInteger( uirow, 3, action );
7274 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7275 msiobj_release( &uirow->hdr );
7277 if (env) RegCloseKey( env );
7278 msi_free( deformatted );
7279 msi_free( new_value );
7280 return r;
7283 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7285 static const WCHAR query[] = {
7286 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7287 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7288 MSIQUERY *view;
7289 UINT rc;
7291 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7292 if (rc != ERROR_SUCCESS)
7293 return ERROR_SUCCESS;
7295 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7296 msiobj_release( &view->hdr );
7297 return rc;
7300 UINT msi_validate_product_id( MSIPACKAGE *package )
7302 LPWSTR key, template, id;
7303 UINT r = ERROR_SUCCESS;
7305 id = msi_dup_property( package->db, szProductID );
7306 if (id)
7308 msi_free( id );
7309 return ERROR_SUCCESS;
7311 template = msi_dup_property( package->db, szPIDTemplate );
7312 key = msi_dup_property( package->db, szPIDKEY );
7313 if (key && template)
7315 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7316 r = msi_set_property( package->db, szProductID, key, -1 );
7318 msi_free( template );
7319 msi_free( key );
7320 return r;
7323 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7325 return msi_validate_product_id( package );
7328 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7330 TRACE("\n");
7331 package->need_reboot_at_end = 1;
7332 return ERROR_SUCCESS;
7335 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7337 static const WCHAR szAvailableFreeReg[] =
7338 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7339 MSIRECORD *uirow;
7340 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7342 TRACE("%p %d kilobytes\n", package, space);
7344 uirow = MSI_CreateRecord( 1 );
7345 MSI_RecordSetInteger( uirow, 1, space );
7346 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7347 msiobj_release( &uirow->hdr );
7349 return ERROR_SUCCESS;
7352 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7354 TRACE("%p\n", package);
7356 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7357 return ERROR_SUCCESS;
7360 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7362 FIXME("%p\n", package);
7363 return ERROR_SUCCESS;
7366 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7368 static const WCHAR driver_query[] = {
7369 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7370 'O','D','B','C','D','r','i','v','e','r',0};
7371 static const WCHAR translator_query[] = {
7372 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7373 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7374 MSIQUERY *view;
7375 UINT r, count;
7377 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7378 if (r == ERROR_SUCCESS)
7380 count = 0;
7381 r = MSI_IterateRecords( view, &count, NULL, package );
7382 msiobj_release( &view->hdr );
7383 if (r != ERROR_SUCCESS)
7384 return r;
7385 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7387 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7388 if (r == ERROR_SUCCESS)
7390 count = 0;
7391 r = MSI_IterateRecords( view, &count, NULL, package );
7392 msiobj_release( &view->hdr );
7393 if (r != ERROR_SUCCESS)
7394 return r;
7395 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7397 return ERROR_SUCCESS;
7400 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7402 static const WCHAR fmtW[] =
7403 {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7404 MSIPACKAGE *package = param;
7405 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7406 int attrs = MSI_RecordGetInteger( rec, 5 );
7407 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7408 WCHAR *product, *features, *cmd;
7409 STARTUPINFOW si;
7410 PROCESS_INFORMATION info;
7411 BOOL ret;
7413 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7414 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7416 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7418 len += strlenW( product );
7419 if (features)
7420 len += strlenW( features );
7421 else
7422 len += sizeof(szAll) / sizeof(szAll[0]);
7424 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7426 msi_free( product );
7427 msi_free( features );
7428 return ERROR_OUTOFMEMORY;
7430 sprintfW( cmd, fmtW, product, features ? features : szAll );
7431 msi_free( product );
7432 msi_free( features );
7434 memset( &si, 0, sizeof(STARTUPINFOW) );
7435 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7436 msi_free( cmd );
7437 if (!ret) return GetLastError();
7438 CloseHandle( info.hThread );
7440 WaitForSingleObject( info.hProcess, INFINITE );
7441 CloseHandle( info.hProcess );
7442 return ERROR_SUCCESS;
7445 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7447 static const WCHAR query[] = {
7448 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7449 MSIQUERY *view;
7450 UINT r;
7452 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7453 if (r == ERROR_SUCCESS)
7455 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7456 msiobj_release( &view->hdr );
7457 if (r != ERROR_SUCCESS)
7458 return r;
7460 return ERROR_SUCCESS;
7463 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7465 MSIPACKAGE *package = param;
7466 int attributes = MSI_RecordGetInteger( rec, 5 );
7468 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7470 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7471 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7472 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7473 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7474 HKEY hkey;
7475 UINT r;
7477 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7479 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7480 if (r != ERROR_SUCCESS)
7481 return ERROR_SUCCESS;
7483 else
7485 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7486 if (r != ERROR_SUCCESS)
7487 return ERROR_SUCCESS;
7489 RegCloseKey( hkey );
7491 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7492 debugstr_w(upgrade_code), debugstr_w(version_min),
7493 debugstr_w(version_max), debugstr_w(language));
7495 return ERROR_SUCCESS;
7498 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7500 static const WCHAR query[] = {
7501 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7502 'U','p','g','r','a','d','e',0};
7503 MSIQUERY *view;
7504 UINT r;
7506 if (msi_get_property_int( package->db, szInstalled, 0 ))
7508 TRACE("product is installed, skipping action\n");
7509 return ERROR_SUCCESS;
7511 if (msi_get_property_int( package->db, szPreselected, 0 ))
7513 TRACE("Preselected property is set, not migrating feature states\n");
7514 return ERROR_SUCCESS;
7516 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7517 if (r == ERROR_SUCCESS)
7519 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7520 msiobj_release( &view->hdr );
7521 if (r != ERROR_SUCCESS)
7522 return r;
7524 return ERROR_SUCCESS;
7527 static void bind_image( const char *filename, const char *path )
7529 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7531 WARN("failed to bind image %u\n", GetLastError());
7535 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7537 UINT i;
7538 MSIFILE *file;
7539 MSIPACKAGE *package = param;
7540 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7541 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7542 char *filenameA, *pathA;
7543 WCHAR *pathW, **path_list;
7545 if (!(file = msi_get_loaded_file( package, key )))
7547 WARN("file %s not found\n", debugstr_w(key));
7548 return ERROR_SUCCESS;
7550 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7551 path_list = msi_split_string( paths, ';' );
7552 if (!path_list) bind_image( filenameA, NULL );
7553 else
7555 for (i = 0; path_list[i] && path_list[i][0]; i++)
7557 deformat_string( package, path_list[i], &pathW );
7558 if ((pathA = strdupWtoA( pathW )))
7560 bind_image( filenameA, pathA );
7561 msi_free( pathA );
7563 msi_free( pathW );
7566 msi_free( path_list );
7567 msi_free( filenameA );
7568 return ERROR_SUCCESS;
7571 static UINT ACTION_BindImage( MSIPACKAGE *package )
7573 static const WCHAR query[] = {
7574 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7575 'B','i','n','d','I','m','a','g','e',0};
7576 MSIQUERY *view;
7577 UINT r;
7579 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7580 if (r == ERROR_SUCCESS)
7582 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7583 msiobj_release( &view->hdr );
7584 if (r != ERROR_SUCCESS)
7585 return r;
7587 return ERROR_SUCCESS;
7590 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7592 static const WCHAR query[] = {
7593 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7594 MSIQUERY *view;
7595 DWORD count = 0;
7596 UINT r;
7598 r = MSI_OpenQuery( package->db, &view, query, table );
7599 if (r == ERROR_SUCCESS)
7601 r = MSI_IterateRecords(view, &count, NULL, package);
7602 msiobj_release(&view->hdr);
7603 if (r != ERROR_SUCCESS)
7604 return r;
7606 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7607 return ERROR_SUCCESS;
7610 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7612 static const WCHAR table[] = {
7613 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7614 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7617 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7619 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7620 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7623 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7625 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7626 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7629 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7631 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7632 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7635 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7637 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7638 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7641 static const struct
7643 const WCHAR *action;
7644 UINT (*handler)(MSIPACKAGE *);
7645 const WCHAR *action_rollback;
7647 StandardActions[] =
7649 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7650 { szAppSearch, ACTION_AppSearch, NULL },
7651 { szBindImage, ACTION_BindImage, NULL },
7652 { szCCPSearch, ACTION_CCPSearch, NULL },
7653 { szCostFinalize, ACTION_CostFinalize, NULL },
7654 { szCostInitialize, ACTION_CostInitialize, NULL },
7655 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7656 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7657 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7658 { szDisableRollback, ACTION_DisableRollback, NULL },
7659 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7660 { szExecuteAction, ACTION_ExecuteAction, NULL },
7661 { szFileCost, ACTION_FileCost, NULL },
7662 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7663 { szForceReboot, ACTION_ForceReboot, NULL },
7664 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7665 { szInstallExecute, ACTION_InstallExecute, NULL },
7666 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7667 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7668 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7669 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7670 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7671 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7672 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7673 { szInstallValidate, ACTION_InstallValidate, NULL },
7674 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7675 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7676 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7677 { szMoveFiles, ACTION_MoveFiles, NULL },
7678 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7679 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7680 { szPatchFiles, ACTION_PatchFiles, NULL },
7681 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7682 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7683 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7684 { szPublishProduct, ACTION_PublishProduct, NULL },
7685 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7686 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7687 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7688 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7689 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7690 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7691 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7692 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7693 { szRegisterUser, ACTION_RegisterUser, NULL },
7694 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7695 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7696 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7697 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7698 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7699 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7700 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7701 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7702 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7703 { szResolveSource, ACTION_ResolveSource, NULL },
7704 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7705 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7706 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7707 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7708 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7709 { szStartServices, ACTION_StartServices, szStopServices },
7710 { szStopServices, ACTION_StopServices, szStartServices },
7711 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7712 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7713 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7714 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7715 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7716 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7717 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7718 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7719 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7720 { szValidateProductID, ACTION_ValidateProductID, NULL },
7721 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7722 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7723 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7724 { NULL, NULL, NULL }
7727 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7729 BOOL ret = FALSE;
7730 UINT i;
7732 i = 0;
7733 while (StandardActions[i].action != NULL)
7735 if (!strcmpW( StandardActions[i].action, action ))
7737 ui_actionstart( package, action );
7738 if (StandardActions[i].handler)
7740 ui_actioninfo( package, action, TRUE, 0 );
7741 *rc = StandardActions[i].handler( package );
7742 ui_actioninfo( package, action, FALSE, *rc );
7744 if (StandardActions[i].action_rollback && !package->need_rollback)
7746 TRACE("scheduling rollback action\n");
7747 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7750 else
7752 FIXME("unhandled standard action %s\n", debugstr_w(action));
7753 *rc = ERROR_SUCCESS;
7755 ret = TRUE;
7756 break;
7758 i++;
7760 return ret;
7763 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7765 UINT rc = ERROR_SUCCESS;
7766 BOOL handled;
7768 TRACE("Performing action (%s)\n", debugstr_w(action));
7770 handled = ACTION_HandleStandardAction(package, action, &rc);
7772 if (!handled)
7773 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7775 if (!handled)
7777 WARN("unhandled msi action %s\n", debugstr_w(action));
7778 rc = ERROR_FUNCTION_NOT_CALLED;
7781 return rc;
7784 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7786 UINT rc = ERROR_SUCCESS;
7787 BOOL handled = FALSE;
7789 TRACE("Performing action (%s)\n", debugstr_w(action));
7791 package->action_progress_increment = 0;
7792 handled = ACTION_HandleStandardAction(package, action, &rc);
7794 if (!handled)
7795 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7797 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7798 handled = TRUE;
7800 if (!handled)
7802 WARN("unhandled msi action %s\n", debugstr_w(action));
7803 rc = ERROR_FUNCTION_NOT_CALLED;
7806 return rc;
7809 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7811 UINT rc = ERROR_SUCCESS;
7812 MSIRECORD *row;
7814 static const WCHAR query[] =
7815 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7816 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7817 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7818 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7819 static const WCHAR ui_query[] =
7820 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7821 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7822 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7823 ' ', '=',' ','%','i',0};
7825 if (needs_ui_sequence(package))
7826 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7827 else
7828 row = MSI_QueryGetRecord(package->db, query, seq);
7830 if (row)
7832 LPCWSTR action, cond;
7834 TRACE("Running the actions\n");
7836 /* check conditions */
7837 cond = MSI_RecordGetString(row, 2);
7839 /* this is a hack to skip errors in the condition code */
7840 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7842 msiobj_release(&row->hdr);
7843 return ERROR_SUCCESS;
7846 action = MSI_RecordGetString(row, 1);
7847 if (!action)
7849 ERR("failed to fetch action\n");
7850 msiobj_release(&row->hdr);
7851 return ERROR_FUNCTION_FAILED;
7854 if (needs_ui_sequence(package))
7855 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7856 else
7857 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7859 msiobj_release(&row->hdr);
7862 return rc;
7865 /****************************************************
7866 * TOP level entry points
7867 *****************************************************/
7869 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7870 LPCWSTR szCommandLine )
7872 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7873 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7874 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7875 WCHAR *reinstall, *remove, *patch, *productcode;
7876 BOOL ui_exists;
7877 UINT rc;
7879 msi_set_property( package->db, szAction, szInstall, -1 );
7881 package->script->InWhatSequence = SEQUENCE_INSTALL;
7883 if (szPackagePath)
7885 LPWSTR p, dir;
7886 LPCWSTR file;
7888 dir = strdupW(szPackagePath);
7889 p = strrchrW(dir, '\\');
7890 if (p)
7892 *(++p) = 0;
7893 file = szPackagePath + (p - dir);
7895 else
7897 msi_free(dir);
7898 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7899 GetCurrentDirectoryW(MAX_PATH, dir);
7900 lstrcatW(dir, szBackSlash);
7901 file = szPackagePath;
7904 msi_free( package->PackagePath );
7905 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7906 if (!package->PackagePath)
7908 msi_free(dir);
7909 return ERROR_OUTOFMEMORY;
7912 lstrcpyW(package->PackagePath, dir);
7913 lstrcatW(package->PackagePath, file);
7914 msi_free(dir);
7916 msi_set_sourcedir_props(package, FALSE);
7919 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7920 if (rc != ERROR_SUCCESS)
7921 return rc;
7923 msi_apply_transforms( package );
7924 msi_apply_patches( package );
7926 patch = msi_dup_property( package->db, szPatch );
7927 remove = msi_dup_property( package->db, szRemove );
7928 reinstall = msi_dup_property( package->db, szReinstall );
7929 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7931 TRACE("setting REINSTALL property to ALL\n");
7932 msi_set_property( package->db, szReinstall, szAll, -1 );
7933 package->full_reinstall = 1;
7936 msi_set_original_database_property( package->db, szPackagePath );
7937 msi_parse_command_line( package, szCommandLine, FALSE );
7938 msi_adjust_privilege_properties( package );
7939 msi_set_context( package );
7941 productcode = msi_dup_property( package->db, szProductCode );
7942 if (strcmpiW( productcode, package->ProductCode ))
7944 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
7945 msi_free( package->ProductCode );
7946 package->ProductCode = productcode;
7948 else msi_free( productcode );
7950 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7952 TRACE("disabling rollback\n");
7953 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7956 if (needs_ui_sequence( package))
7958 package->script->InWhatSequence |= SEQUENCE_UI;
7959 rc = ACTION_ProcessUISequence(package);
7960 ui_exists = ui_sequence_exists(package);
7961 if (rc == ERROR_SUCCESS || !ui_exists)
7963 package->script->InWhatSequence |= SEQUENCE_EXEC;
7964 rc = ACTION_ProcessExecSequence(package, ui_exists);
7967 else
7968 rc = ACTION_ProcessExecSequence(package, FALSE);
7970 /* process the ending type action */
7971 if (rc == ERROR_SUCCESS)
7972 ACTION_PerformActionSequence(package, -1);
7973 else if (rc == ERROR_INSTALL_USEREXIT)
7974 ACTION_PerformActionSequence(package, -2);
7975 else if (rc == ERROR_INSTALL_SUSPEND)
7976 ACTION_PerformActionSequence(package, -4);
7977 else /* failed */
7979 ACTION_PerformActionSequence(package, -3);
7980 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7982 package->need_rollback = TRUE;
7986 /* finish up running custom actions */
7987 ACTION_FinishCustomActions(package);
7989 if (package->need_rollback && !reinstall)
7991 WARN("installation failed, running rollback script\n");
7992 execute_script( package, SCRIPT_ROLLBACK );
7994 msi_free( reinstall );
7995 msi_free( remove );
7996 msi_free( patch );
7998 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7999 return ERROR_SUCCESS_REBOOT_REQUIRED;
8001 return rc;