riched20: Don't apply paragraph formatting until the end of paragraph.
[wine.git] / dlls / msi / action.c
blob8c1bff3662489e14a3996c3107aca62936827682
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 );
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 SERVICE_DESCRIPTIONW sd = {NULL};
5815 UINT ret = ERROR_SUCCESS;
5817 comp = MSI_RecordGetString( rec, 12 );
5818 component = msi_get_loaded_component( package, comp );
5819 if (!component)
5821 WARN("service component not found\n");
5822 goto done;
5824 component->Action = msi_get_component_action( package, component );
5825 if (component->Action != INSTALLSTATE_LOCAL)
5827 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5828 goto done;
5830 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5831 if (!hscm)
5833 ERR("Failed to open the SC Manager!\n");
5834 goto done;
5837 start_type = MSI_RecordGetInteger(rec, 5);
5838 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5839 goto done;
5841 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5842 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5843 serv_type = MSI_RecordGetInteger(rec, 4);
5844 err_control = MSI_RecordGetInteger(rec, 6);
5845 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5846 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5847 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5848 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5849 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5850 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5852 /* fetch the service path */
5853 row = MSI_QueryGetRecord(package->db, query, comp);
5854 if (!row)
5856 ERR("Query failed\n");
5857 goto done;
5859 if (!(key = MSI_RecordGetString(row, 6)))
5861 msiobj_release(&row->hdr);
5862 goto done;
5864 file = msi_get_loaded_file(package, key);
5865 msiobj_release(&row->hdr);
5866 if (!file)
5868 ERR("Failed to load the service file\n");
5869 goto done;
5872 if (!args || !args[0]) image_path = file->TargetPath;
5873 else
5875 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5876 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5878 ret = ERROR_OUTOFMEMORY;
5879 goto done;
5882 strcpyW(image_path, file->TargetPath);
5883 strcatW(image_path, szSpace);
5884 strcatW(image_path, args);
5886 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5887 start_type, err_control, image_path, load_order,
5888 NULL, depends, serv_name, pass);
5890 if (!service)
5892 if (GetLastError() != ERROR_SERVICE_EXISTS)
5893 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5895 else if (sd.lpDescription)
5897 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5898 WARN("failed to set service description %u\n", GetLastError());
5901 if (image_path != file->TargetPath) msi_free(image_path);
5902 done:
5903 if (service) CloseServiceHandle(service);
5904 if (hscm) CloseServiceHandle(hscm);
5905 msi_free(name);
5906 msi_free(disp);
5907 msi_free(sd.lpDescription);
5908 msi_free(load_order);
5909 msi_free(serv_name);
5910 msi_free(pass);
5911 msi_free(depends);
5912 msi_free(args);
5914 return ret;
5917 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5919 static const WCHAR query[] = {
5920 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5921 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5922 MSIQUERY *view;
5923 UINT rc;
5925 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5926 if (rc != ERROR_SUCCESS)
5927 return ERROR_SUCCESS;
5929 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5930 msiobj_release(&view->hdr);
5931 return rc;
5934 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5935 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5937 LPCWSTR *vector, *temp_vector;
5938 LPWSTR p, q;
5939 DWORD sep_len;
5941 static const WCHAR separator[] = {'[','~',']',0};
5943 *numargs = 0;
5944 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5946 if (!args)
5947 return NULL;
5949 vector = msi_alloc(sizeof(LPWSTR));
5950 if (!vector)
5951 return NULL;
5953 p = args;
5956 (*numargs)++;
5957 vector[*numargs - 1] = p;
5959 if ((q = strstrW(p, separator)))
5961 *q = '\0';
5963 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5964 if (!temp_vector)
5966 msi_free(vector);
5967 return NULL;
5969 vector = temp_vector;
5971 p = q + sep_len;
5973 } while (q);
5975 return vector;
5978 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5980 MSIPACKAGE *package = param;
5981 MSICOMPONENT *comp;
5982 MSIRECORD *uirow;
5983 SC_HANDLE scm = NULL, service = NULL;
5984 LPCWSTR component, *vector = NULL;
5985 LPWSTR name, args, display_name = NULL;
5986 DWORD event, numargs, len, wait, dummy;
5987 UINT r = ERROR_FUNCTION_FAILED;
5988 SERVICE_STATUS_PROCESS status;
5989 ULONGLONG start_time;
5991 component = MSI_RecordGetString(rec, 6);
5992 comp = msi_get_loaded_component(package, component);
5993 if (!comp)
5994 return ERROR_SUCCESS;
5996 event = MSI_RecordGetInteger( rec, 3 );
5997 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5999 comp->Action = msi_get_component_action( package, comp );
6000 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
6001 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
6003 TRACE("not starting %s\n", debugstr_w(name));
6004 msi_free( name );
6005 return ERROR_SUCCESS;
6008 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
6009 wait = MSI_RecordGetInteger(rec, 5);
6011 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
6012 if (!scm)
6014 ERR("Failed to open the service control manager\n");
6015 goto done;
6018 len = 0;
6019 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6020 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6022 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6023 GetServiceDisplayNameW( scm, name, display_name, &len );
6026 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
6027 if (!service)
6029 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6030 goto done;
6033 vector = msi_service_args_to_vector(args, &numargs);
6035 if (!StartServiceW(service, numargs, vector) &&
6036 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6038 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6039 goto done;
6042 r = ERROR_SUCCESS;
6043 if (wait)
6045 /* wait for at most 30 seconds for the service to be up and running */
6046 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6047 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6049 TRACE("failed to query service status (%u)\n", GetLastError());
6050 goto done;
6052 start_time = GetTickCount64();
6053 while (status.dwCurrentState == SERVICE_START_PENDING)
6055 if (GetTickCount64() - start_time > 30000) break;
6056 Sleep(1000);
6057 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6058 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6060 TRACE("failed to query service status (%u)\n", GetLastError());
6061 goto done;
6064 if (status.dwCurrentState != SERVICE_RUNNING)
6066 WARN("service failed to start %u\n", status.dwCurrentState);
6067 r = ERROR_FUNCTION_FAILED;
6071 done:
6072 uirow = MSI_CreateRecord( 2 );
6073 MSI_RecordSetStringW( uirow, 1, display_name );
6074 MSI_RecordSetStringW( uirow, 2, name );
6075 msi_ui_actiondata( package, szStartServices, uirow );
6076 msiobj_release( &uirow->hdr );
6078 if (service) CloseServiceHandle(service);
6079 if (scm) CloseServiceHandle(scm);
6081 msi_free(name);
6082 msi_free(args);
6083 msi_free(vector);
6084 msi_free(display_name);
6085 return r;
6088 static UINT ACTION_StartServices( MSIPACKAGE *package )
6090 static const WCHAR query[] = {
6091 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6092 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6093 MSIQUERY *view;
6094 UINT rc;
6096 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6097 if (rc != ERROR_SUCCESS)
6098 return ERROR_SUCCESS;
6100 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6101 msiobj_release(&view->hdr);
6102 return rc;
6105 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6107 DWORD i, needed, count;
6108 ENUM_SERVICE_STATUSW *dependencies;
6109 SERVICE_STATUS ss;
6110 SC_HANDLE depserv;
6111 BOOL stopped, ret = FALSE;
6113 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6114 0, &needed, &count))
6115 return TRUE;
6117 if (GetLastError() != ERROR_MORE_DATA)
6118 return FALSE;
6120 dependencies = msi_alloc(needed);
6121 if (!dependencies)
6122 return FALSE;
6124 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6125 needed, &needed, &count))
6126 goto done;
6128 for (i = 0; i < count; i++)
6130 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6131 SERVICE_STOP | SERVICE_QUERY_STATUS);
6132 if (!depserv)
6133 goto done;
6135 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6136 CloseServiceHandle(depserv);
6137 if (!stopped)
6138 goto done;
6141 ret = TRUE;
6143 done:
6144 msi_free(dependencies);
6145 return ret;
6148 static UINT stop_service( LPCWSTR name )
6150 SC_HANDLE scm = NULL, service = NULL;
6151 SERVICE_STATUS status;
6152 SERVICE_STATUS_PROCESS ssp;
6153 DWORD needed;
6155 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6156 if (!scm)
6158 WARN("Failed to open the SCM: %d\n", GetLastError());
6159 goto done;
6162 service = OpenServiceW(scm, name,
6163 SERVICE_STOP |
6164 SERVICE_QUERY_STATUS |
6165 SERVICE_ENUMERATE_DEPENDENTS);
6166 if (!service)
6168 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6169 goto done;
6172 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6173 sizeof(SERVICE_STATUS_PROCESS), &needed))
6175 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6176 goto done;
6179 if (ssp.dwCurrentState == SERVICE_STOPPED)
6180 goto done;
6182 stop_service_dependents(scm, service);
6184 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6185 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6187 done:
6188 if (service) CloseServiceHandle(service);
6189 if (scm) CloseServiceHandle(scm);
6191 return ERROR_SUCCESS;
6194 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6196 MSIPACKAGE *package = param;
6197 MSICOMPONENT *comp;
6198 MSIRECORD *uirow;
6199 LPCWSTR component;
6200 WCHAR *name, *display_name = NULL;
6201 DWORD event, len;
6202 SC_HANDLE scm;
6204 component = MSI_RecordGetString( rec, 6 );
6205 comp = msi_get_loaded_component( package, component );
6206 if (!comp)
6207 return ERROR_SUCCESS;
6209 event = MSI_RecordGetInteger( rec, 3 );
6210 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6212 comp->Action = msi_get_component_action( package, comp );
6213 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6214 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6216 TRACE("not stopping %s\n", debugstr_w(name));
6217 msi_free( name );
6218 return ERROR_SUCCESS;
6221 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6222 if (!scm)
6224 ERR("Failed to open the service control manager\n");
6225 goto done;
6228 len = 0;
6229 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6230 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6232 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6233 GetServiceDisplayNameW( scm, name, display_name, &len );
6235 CloseServiceHandle( scm );
6237 stop_service( name );
6239 done:
6240 uirow = MSI_CreateRecord( 2 );
6241 MSI_RecordSetStringW( uirow, 1, display_name );
6242 MSI_RecordSetStringW( uirow, 2, name );
6243 msi_ui_actiondata( package, szStopServices, uirow );
6244 msiobj_release( &uirow->hdr );
6246 msi_free( name );
6247 msi_free( display_name );
6248 return ERROR_SUCCESS;
6251 static UINT ACTION_StopServices( MSIPACKAGE *package )
6253 static const WCHAR query[] = {
6254 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6255 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6256 MSIQUERY *view;
6257 UINT rc;
6259 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6260 if (rc != ERROR_SUCCESS)
6261 return ERROR_SUCCESS;
6263 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6264 msiobj_release(&view->hdr);
6265 return rc;
6268 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6270 MSIPACKAGE *package = param;
6271 MSICOMPONENT *comp;
6272 MSIRECORD *uirow;
6273 LPWSTR name = NULL, display_name = NULL;
6274 DWORD event, len;
6275 SC_HANDLE scm = NULL, service = NULL;
6277 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6278 if (!comp)
6279 return ERROR_SUCCESS;
6281 event = MSI_RecordGetInteger( rec, 3 );
6282 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6284 comp->Action = msi_get_component_action( package, comp );
6285 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6286 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6288 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6289 msi_free( name );
6290 return ERROR_SUCCESS;
6292 stop_service( name );
6294 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6295 if (!scm)
6297 WARN("Failed to open the SCM: %d\n", GetLastError());
6298 goto done;
6301 len = 0;
6302 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6303 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6305 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6306 GetServiceDisplayNameW( scm, name, display_name, &len );
6309 service = OpenServiceW( scm, name, DELETE );
6310 if (!service)
6312 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6313 goto done;
6316 if (!DeleteService( service ))
6317 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6319 done:
6320 uirow = MSI_CreateRecord( 2 );
6321 MSI_RecordSetStringW( uirow, 1, display_name );
6322 MSI_RecordSetStringW( uirow, 2, name );
6323 msi_ui_actiondata( package, szDeleteServices, uirow );
6324 msiobj_release( &uirow->hdr );
6326 if (service) CloseServiceHandle( service );
6327 if (scm) CloseServiceHandle( scm );
6328 msi_free( name );
6329 msi_free( display_name );
6331 return ERROR_SUCCESS;
6334 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6336 static const WCHAR query[] = {
6337 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6338 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6339 MSIQUERY *view;
6340 UINT rc;
6342 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6343 if (rc != ERROR_SUCCESS)
6344 return ERROR_SUCCESS;
6346 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6347 msiobj_release( &view->hdr );
6348 return rc;
6351 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6353 MSIPACKAGE *package = param;
6354 LPWSTR driver, driver_path, ptr;
6355 WCHAR outpath[MAX_PATH];
6356 MSIFILE *driver_file = NULL, *setup_file = NULL;
6357 MSICOMPONENT *comp;
6358 MSIRECORD *uirow;
6359 LPCWSTR desc, file_key, component;
6360 DWORD len, usage;
6361 UINT r = ERROR_SUCCESS;
6363 static const WCHAR driver_fmt[] = {
6364 'D','r','i','v','e','r','=','%','s',0};
6365 static const WCHAR setup_fmt[] = {
6366 'S','e','t','u','p','=','%','s',0};
6367 static const WCHAR usage_fmt[] = {
6368 'F','i','l','e','U','s','a','g','e','=','1',0};
6370 component = MSI_RecordGetString( rec, 2 );
6371 comp = msi_get_loaded_component( package, component );
6372 if (!comp)
6373 return ERROR_SUCCESS;
6375 comp->Action = msi_get_component_action( package, comp );
6376 if (comp->Action != INSTALLSTATE_LOCAL)
6378 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6379 return ERROR_SUCCESS;
6381 desc = MSI_RecordGetString(rec, 3);
6383 file_key = MSI_RecordGetString( rec, 4 );
6384 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6386 file_key = MSI_RecordGetString( rec, 5 );
6387 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6389 if (!driver_file)
6391 ERR("ODBC Driver entry not found!\n");
6392 return ERROR_FUNCTION_FAILED;
6395 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6396 if (setup_file)
6397 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6398 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6400 driver = msi_alloc(len * sizeof(WCHAR));
6401 if (!driver)
6402 return ERROR_OUTOFMEMORY;
6404 ptr = driver;
6405 lstrcpyW(ptr, desc);
6406 ptr += lstrlenW(ptr) + 1;
6408 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6409 ptr += len + 1;
6411 if (setup_file)
6413 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6414 ptr += len + 1;
6417 lstrcpyW(ptr, usage_fmt);
6418 ptr += lstrlenW(ptr) + 1;
6419 *ptr = '\0';
6421 if (!driver_file->TargetPath)
6423 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6424 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6426 driver_path = strdupW(driver_file->TargetPath);
6427 ptr = strrchrW(driver_path, '\\');
6428 if (ptr) *ptr = '\0';
6430 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6431 NULL, ODBC_INSTALL_COMPLETE, &usage))
6433 ERR("Failed to install SQL driver!\n");
6434 r = ERROR_FUNCTION_FAILED;
6437 uirow = MSI_CreateRecord( 5 );
6438 MSI_RecordSetStringW( uirow, 1, desc );
6439 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6440 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6441 msi_ui_actiondata( package, szInstallODBC, uirow );
6442 msiobj_release( &uirow->hdr );
6444 msi_free(driver);
6445 msi_free(driver_path);
6447 return r;
6450 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6452 MSIPACKAGE *package = param;
6453 LPWSTR translator, translator_path, ptr;
6454 WCHAR outpath[MAX_PATH];
6455 MSIFILE *translator_file = NULL, *setup_file = NULL;
6456 MSICOMPONENT *comp;
6457 MSIRECORD *uirow;
6458 LPCWSTR desc, file_key, component;
6459 DWORD len, usage;
6460 UINT r = ERROR_SUCCESS;
6462 static const WCHAR translator_fmt[] = {
6463 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6464 static const WCHAR setup_fmt[] = {
6465 'S','e','t','u','p','=','%','s',0};
6467 component = MSI_RecordGetString( rec, 2 );
6468 comp = msi_get_loaded_component( package, component );
6469 if (!comp)
6470 return ERROR_SUCCESS;
6472 comp->Action = msi_get_component_action( package, comp );
6473 if (comp->Action != INSTALLSTATE_LOCAL)
6475 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6476 return ERROR_SUCCESS;
6478 desc = MSI_RecordGetString(rec, 3);
6480 file_key = MSI_RecordGetString( rec, 4 );
6481 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6483 file_key = MSI_RecordGetString( rec, 5 );
6484 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6486 if (!translator_file)
6488 ERR("ODBC Translator entry not found!\n");
6489 return ERROR_FUNCTION_FAILED;
6492 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6493 if (setup_file)
6494 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6496 translator = msi_alloc(len * sizeof(WCHAR));
6497 if (!translator)
6498 return ERROR_OUTOFMEMORY;
6500 ptr = translator;
6501 lstrcpyW(ptr, desc);
6502 ptr += lstrlenW(ptr) + 1;
6504 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6505 ptr += len + 1;
6507 if (setup_file)
6509 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6510 ptr += len + 1;
6512 *ptr = '\0';
6514 translator_path = strdupW(translator_file->TargetPath);
6515 ptr = strrchrW(translator_path, '\\');
6516 if (ptr) *ptr = '\0';
6518 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6519 NULL, ODBC_INSTALL_COMPLETE, &usage))
6521 ERR("Failed to install SQL translator!\n");
6522 r = ERROR_FUNCTION_FAILED;
6525 uirow = MSI_CreateRecord( 5 );
6526 MSI_RecordSetStringW( uirow, 1, desc );
6527 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6528 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6529 msi_ui_actiondata( package, szInstallODBC, uirow );
6530 msiobj_release( &uirow->hdr );
6532 msi_free(translator);
6533 msi_free(translator_path);
6535 return r;
6538 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6540 MSIPACKAGE *package = param;
6541 MSICOMPONENT *comp;
6542 LPWSTR attrs;
6543 LPCWSTR desc, driver, component;
6544 WORD request = ODBC_ADD_SYS_DSN;
6545 INT registration;
6546 DWORD len;
6547 UINT r = ERROR_SUCCESS;
6548 MSIRECORD *uirow;
6550 static const WCHAR attrs_fmt[] = {
6551 'D','S','N','=','%','s',0 };
6553 component = MSI_RecordGetString( rec, 2 );
6554 comp = msi_get_loaded_component( package, component );
6555 if (!comp)
6556 return ERROR_SUCCESS;
6558 comp->Action = msi_get_component_action( package, comp );
6559 if (comp->Action != INSTALLSTATE_LOCAL)
6561 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6562 return ERROR_SUCCESS;
6565 desc = MSI_RecordGetString(rec, 3);
6566 driver = MSI_RecordGetString(rec, 4);
6567 registration = MSI_RecordGetInteger(rec, 5);
6569 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6570 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6572 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6573 attrs = msi_alloc(len * sizeof(WCHAR));
6574 if (!attrs)
6575 return ERROR_OUTOFMEMORY;
6577 len = sprintfW(attrs, attrs_fmt, desc);
6578 attrs[len + 1] = 0;
6580 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6582 ERR("Failed to install SQL data source!\n");
6583 r = ERROR_FUNCTION_FAILED;
6586 uirow = MSI_CreateRecord( 5 );
6587 MSI_RecordSetStringW( uirow, 1, desc );
6588 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6589 MSI_RecordSetInteger( uirow, 3, request );
6590 msi_ui_actiondata( package, szInstallODBC, uirow );
6591 msiobj_release( &uirow->hdr );
6593 msi_free(attrs);
6595 return r;
6598 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6600 static const WCHAR driver_query[] = {
6601 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6602 'O','D','B','C','D','r','i','v','e','r',0};
6603 static const WCHAR translator_query[] = {
6604 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6605 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6606 static const WCHAR source_query[] = {
6607 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6608 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6609 MSIQUERY *view;
6610 UINT rc;
6612 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6613 if (rc == ERROR_SUCCESS)
6615 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6616 msiobj_release(&view->hdr);
6617 if (rc != ERROR_SUCCESS)
6618 return rc;
6620 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6621 if (rc == ERROR_SUCCESS)
6623 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6624 msiobj_release(&view->hdr);
6625 if (rc != ERROR_SUCCESS)
6626 return rc;
6628 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6629 if (rc == ERROR_SUCCESS)
6631 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6632 msiobj_release(&view->hdr);
6633 if (rc != ERROR_SUCCESS)
6634 return rc;
6636 return ERROR_SUCCESS;
6639 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6641 MSIPACKAGE *package = param;
6642 MSICOMPONENT *comp;
6643 MSIRECORD *uirow;
6644 DWORD usage;
6645 LPCWSTR desc, component;
6647 component = MSI_RecordGetString( rec, 2 );
6648 comp = msi_get_loaded_component( package, component );
6649 if (!comp)
6650 return ERROR_SUCCESS;
6652 comp->Action = msi_get_component_action( package, comp );
6653 if (comp->Action != INSTALLSTATE_ABSENT)
6655 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6656 return ERROR_SUCCESS;
6659 desc = MSI_RecordGetString( rec, 3 );
6660 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6662 WARN("Failed to remove ODBC driver\n");
6664 else if (!usage)
6666 FIXME("Usage count reached 0\n");
6669 uirow = MSI_CreateRecord( 2 );
6670 MSI_RecordSetStringW( uirow, 1, desc );
6671 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6672 msi_ui_actiondata( package, szRemoveODBC, uirow );
6673 msiobj_release( &uirow->hdr );
6675 return ERROR_SUCCESS;
6678 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6680 MSIPACKAGE *package = param;
6681 MSICOMPONENT *comp;
6682 MSIRECORD *uirow;
6683 DWORD usage;
6684 LPCWSTR desc, component;
6686 component = MSI_RecordGetString( rec, 2 );
6687 comp = msi_get_loaded_component( package, component );
6688 if (!comp)
6689 return ERROR_SUCCESS;
6691 comp->Action = msi_get_component_action( package, comp );
6692 if (comp->Action != INSTALLSTATE_ABSENT)
6694 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6695 return ERROR_SUCCESS;
6698 desc = MSI_RecordGetString( rec, 3 );
6699 if (!SQLRemoveTranslatorW( desc, &usage ))
6701 WARN("Failed to remove ODBC translator\n");
6703 else if (!usage)
6705 FIXME("Usage count reached 0\n");
6708 uirow = MSI_CreateRecord( 2 );
6709 MSI_RecordSetStringW( uirow, 1, desc );
6710 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6711 msi_ui_actiondata( package, szRemoveODBC, uirow );
6712 msiobj_release( &uirow->hdr );
6714 return ERROR_SUCCESS;
6717 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6719 MSIPACKAGE *package = param;
6720 MSICOMPONENT *comp;
6721 MSIRECORD *uirow;
6722 LPWSTR attrs;
6723 LPCWSTR desc, driver, component;
6724 WORD request = ODBC_REMOVE_SYS_DSN;
6725 INT registration;
6726 DWORD len;
6728 static const WCHAR attrs_fmt[] = {
6729 'D','S','N','=','%','s',0 };
6731 component = MSI_RecordGetString( rec, 2 );
6732 comp = msi_get_loaded_component( package, component );
6733 if (!comp)
6734 return ERROR_SUCCESS;
6736 comp->Action = msi_get_component_action( package, comp );
6737 if (comp->Action != INSTALLSTATE_ABSENT)
6739 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6740 return ERROR_SUCCESS;
6743 desc = MSI_RecordGetString( rec, 3 );
6744 driver = MSI_RecordGetString( rec, 4 );
6745 registration = MSI_RecordGetInteger( rec, 5 );
6747 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6748 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6750 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6751 attrs = msi_alloc( len * sizeof(WCHAR) );
6752 if (!attrs)
6753 return ERROR_OUTOFMEMORY;
6755 FIXME("Use ODBCSourceAttribute table\n");
6757 len = sprintfW( attrs, attrs_fmt, desc );
6758 attrs[len + 1] = 0;
6760 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6762 WARN("Failed to remove ODBC data source\n");
6764 msi_free( attrs );
6766 uirow = MSI_CreateRecord( 3 );
6767 MSI_RecordSetStringW( uirow, 1, desc );
6768 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6769 MSI_RecordSetInteger( uirow, 3, request );
6770 msi_ui_actiondata( package, szRemoveODBC, uirow );
6771 msiobj_release( &uirow->hdr );
6773 return ERROR_SUCCESS;
6776 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6778 static const WCHAR driver_query[] = {
6779 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6780 'O','D','B','C','D','r','i','v','e','r',0};
6781 static const WCHAR translator_query[] = {
6782 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6783 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6784 static const WCHAR source_query[] = {
6785 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6786 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6787 MSIQUERY *view;
6788 UINT rc;
6790 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6791 if (rc == ERROR_SUCCESS)
6793 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6794 msiobj_release( &view->hdr );
6795 if (rc != ERROR_SUCCESS)
6796 return rc;
6798 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6799 if (rc == ERROR_SUCCESS)
6801 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6802 msiobj_release( &view->hdr );
6803 if (rc != ERROR_SUCCESS)
6804 return rc;
6806 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6807 if (rc == ERROR_SUCCESS)
6809 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6810 msiobj_release( &view->hdr );
6811 if (rc != ERROR_SUCCESS)
6812 return rc;
6814 return ERROR_SUCCESS;
6817 #define ENV_ACT_SETALWAYS 0x1
6818 #define ENV_ACT_SETABSENT 0x2
6819 #define ENV_ACT_REMOVE 0x4
6820 #define ENV_ACT_REMOVEMATCH 0x8
6822 #define ENV_MOD_MACHINE 0x20000000
6823 #define ENV_MOD_APPEND 0x40000000
6824 #define ENV_MOD_PREFIX 0x80000000
6825 #define ENV_MOD_MASK 0xC0000000
6827 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6829 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6831 LPCWSTR cptr = *name;
6833 static const WCHAR prefix[] = {'[','~',']',0};
6834 static const int prefix_len = 3;
6836 *flags = 0;
6837 while (*cptr)
6839 if (*cptr == '=')
6840 *flags |= ENV_ACT_SETALWAYS;
6841 else if (*cptr == '+')
6842 *flags |= ENV_ACT_SETABSENT;
6843 else if (*cptr == '-')
6844 *flags |= ENV_ACT_REMOVE;
6845 else if (*cptr == '!')
6846 *flags |= ENV_ACT_REMOVEMATCH;
6847 else if (*cptr == '*')
6848 *flags |= ENV_MOD_MACHINE;
6849 else
6850 break;
6852 cptr++;
6853 (*name)++;
6856 if (!*cptr)
6858 ERR("Missing environment variable\n");
6859 return ERROR_FUNCTION_FAILED;
6862 if (*value)
6864 LPCWSTR ptr = *value;
6865 if (!strncmpW(ptr, prefix, prefix_len))
6867 if (ptr[prefix_len] == szSemiColon[0])
6869 *flags |= ENV_MOD_APPEND;
6870 *value += lstrlenW(prefix);
6872 else
6874 *value = NULL;
6877 else if (lstrlenW(*value) >= prefix_len)
6879 ptr += lstrlenW(ptr) - prefix_len;
6880 if (!strcmpW( ptr, prefix ))
6882 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6884 *flags |= ENV_MOD_PREFIX;
6885 /* the "[~]" will be removed by deformat_string */;
6887 else
6889 *value = NULL;
6895 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6896 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6897 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6898 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6900 ERR("Invalid flags: %08x\n", *flags);
6901 return ERROR_FUNCTION_FAILED;
6904 if (!*flags)
6905 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6907 return ERROR_SUCCESS;
6910 static UINT open_env_key( DWORD flags, HKEY *key )
6912 static const WCHAR user_env[] =
6913 {'E','n','v','i','r','o','n','m','e','n','t',0};
6914 static const WCHAR machine_env[] =
6915 {'S','y','s','t','e','m','\\',
6916 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6917 'C','o','n','t','r','o','l','\\',
6918 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6919 'E','n','v','i','r','o','n','m','e','n','t',0};
6920 const WCHAR *env;
6921 HKEY root;
6922 LONG res;
6924 if (flags & ENV_MOD_MACHINE)
6926 env = machine_env;
6927 root = HKEY_LOCAL_MACHINE;
6929 else
6931 env = user_env;
6932 root = HKEY_CURRENT_USER;
6935 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6936 if (res != ERROR_SUCCESS)
6938 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6939 return ERROR_FUNCTION_FAILED;
6942 return ERROR_SUCCESS;
6945 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6947 MSIPACKAGE *package = param;
6948 LPCWSTR name, value, component;
6949 WCHAR *data = NULL, *newval = NULL, *deformatted = NULL, *p, *q;
6950 DWORD flags, type, size, len, len_value = 0;
6951 UINT res;
6952 HKEY env = NULL;
6953 MSICOMPONENT *comp;
6954 MSIRECORD *uirow;
6955 int action = 0, found = 0;
6957 component = MSI_RecordGetString(rec, 4);
6958 comp = msi_get_loaded_component(package, component);
6959 if (!comp)
6960 return ERROR_SUCCESS;
6962 comp->Action = msi_get_component_action( package, comp );
6963 if (comp->Action != INSTALLSTATE_LOCAL)
6965 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6966 return ERROR_SUCCESS;
6968 name = MSI_RecordGetString(rec, 2);
6969 value = MSI_RecordGetString(rec, 3);
6971 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6973 res = env_parse_flags(&name, &value, &flags);
6974 if (res != ERROR_SUCCESS || !value)
6975 goto done;
6977 if (value && !deformat_string(package, value, &deformatted))
6979 res = ERROR_OUTOFMEMORY;
6980 goto done;
6983 if ((value = deformatted))
6985 if (flags & ENV_MOD_PREFIX)
6987 p = strrchrW( value, ';' );
6988 len_value = p - value;
6990 else if (flags & ENV_MOD_APPEND)
6992 value = strchrW( value, ';' ) + 1;
6993 len_value = strlenW( value );
6995 else len_value = strlenW( value );
6998 res = open_env_key( flags, &env );
6999 if (res != ERROR_SUCCESS)
7000 goto done;
7002 if (flags & ENV_MOD_MACHINE)
7003 action |= 0x20000000;
7005 size = 0;
7006 type = REG_SZ;
7007 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
7008 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
7009 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
7010 goto done;
7012 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
7014 action = 0x2;
7016 /* Nothing to do. */
7017 if (!value)
7019 res = ERROR_SUCCESS;
7020 goto done;
7022 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
7023 newval = strdupW(value);
7024 if (!newval)
7026 res = ERROR_OUTOFMEMORY;
7027 goto done;
7030 else
7032 action = 0x1;
7034 /* Contrary to MSDN, +-variable to [~];path works */
7035 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7037 res = ERROR_SUCCESS;
7038 goto done;
7041 if (!(p = q = data = msi_alloc( size )))
7043 msi_free(deformatted);
7044 RegCloseKey(env);
7045 return ERROR_OUTOFMEMORY;
7048 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)data, &size );
7049 if (res != ERROR_SUCCESS)
7050 goto done;
7052 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7054 action = 0x4;
7055 res = RegDeleteValueW(env, name);
7056 if (res != ERROR_SUCCESS)
7057 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7058 goto done;
7061 for (;;)
7063 while (*q && *q != ';') q++;
7064 len = q - p;
7065 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ) &&
7066 (!p[len] || p[len] == ';'))
7068 found = 1;
7069 break;
7071 if (!*q) break;
7072 p = ++q;
7075 if (found)
7077 TRACE("string already set\n");
7078 goto done;
7081 size = (len_value + 1 + strlenW( data ) + 1) * sizeof(WCHAR);
7082 if (!(p = newval = msi_alloc( size )))
7084 res = ERROR_OUTOFMEMORY;
7085 goto done;
7088 if (flags & ENV_MOD_PREFIX)
7090 memcpy( newval, value, len_value * sizeof(WCHAR) );
7091 newval[len_value] = ';';
7092 p = newval + len_value + 1;
7093 action |= 0x80000000;
7096 strcpyW( p, data );
7098 if (flags & ENV_MOD_APPEND)
7100 p += strlenW( data );
7101 *p++ = ';';
7102 memcpy( p, value, (len_value + 1) * sizeof(WCHAR) );
7103 action |= 0x40000000;
7106 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7107 res = RegSetValueExW( env, name, 0, type, (BYTE *)newval, size );
7108 if (res)
7110 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7113 done:
7114 uirow = MSI_CreateRecord( 3 );
7115 MSI_RecordSetStringW( uirow, 1, name );
7116 MSI_RecordSetStringW( uirow, 2, newval );
7117 MSI_RecordSetInteger( uirow, 3, action );
7118 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7119 msiobj_release( &uirow->hdr );
7121 if (env) RegCloseKey(env);
7122 msi_free(deformatted);
7123 msi_free(data);
7124 msi_free(newval);
7125 return res;
7128 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7130 static const WCHAR query[] = {
7131 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7132 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7133 MSIQUERY *view;
7134 UINT rc;
7136 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7137 if (rc != ERROR_SUCCESS)
7138 return ERROR_SUCCESS;
7140 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7141 msiobj_release(&view->hdr);
7142 return rc;
7145 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7147 MSIPACKAGE *package = param;
7148 LPCWSTR name, value, component;
7149 WCHAR *p, *q, *deformatted = NULL, *new_value = NULL;
7150 DWORD flags, type, size, len, len_value = 0, len_new_value;
7151 HKEY env;
7152 MSICOMPONENT *comp;
7153 MSIRECORD *uirow;
7154 int action = 0;
7155 LONG res;
7156 UINT r;
7158 component = MSI_RecordGetString( rec, 4 );
7159 comp = msi_get_loaded_component( package, component );
7160 if (!comp)
7161 return ERROR_SUCCESS;
7163 comp->Action = msi_get_component_action( package, comp );
7164 if (comp->Action != INSTALLSTATE_ABSENT)
7166 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7167 return ERROR_SUCCESS;
7169 name = MSI_RecordGetString( rec, 2 );
7170 value = MSI_RecordGetString( rec, 3 );
7172 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7174 r = env_parse_flags( &name, &value, &flags );
7175 if (r != ERROR_SUCCESS)
7176 return r;
7178 if (!(flags & ENV_ACT_REMOVE))
7180 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7181 return ERROR_SUCCESS;
7184 if (value && !deformat_string( package, value, &deformatted ))
7185 return ERROR_OUTOFMEMORY;
7187 if ((value = deformatted))
7189 if (flags & ENV_MOD_PREFIX)
7191 p = strchrW( value, ';' );
7192 len_value = p - value;
7194 else if (flags & ENV_MOD_APPEND)
7196 value = strchrW( value, ';' ) + 1;
7197 len_value = strlenW( value );
7199 else len_value = strlenW( value );
7202 r = open_env_key( flags, &env );
7203 if (r != ERROR_SUCCESS)
7205 r = ERROR_SUCCESS;
7206 goto done;
7209 if (flags & ENV_MOD_MACHINE)
7210 action |= 0x20000000;
7212 size = 0;
7213 type = REG_SZ;
7214 res = RegQueryValueExW( env, name, NULL, &type, NULL, &size );
7215 if (res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ))
7216 goto done;
7218 if (!(new_value = msi_alloc( size ))) goto done;
7220 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)new_value, &size );
7221 if (res != ERROR_SUCCESS)
7222 goto done;
7224 len_new_value = size / sizeof(WCHAR) - 1;
7225 p = q = new_value;
7226 for (;;)
7228 while (*q && *q != ';') q++;
7229 len = q - p;
7230 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ))
7232 if (*q == ';') q++;
7233 memmove( p, q, (len_new_value - (q - new_value) + 1) * sizeof(WCHAR) );
7234 break;
7236 if (!*q) break;
7237 p = ++q;
7240 if (!new_value[0] || !value)
7242 TRACE("removing %s\n", debugstr_w(name));
7243 res = RegDeleteValueW( env, name );
7244 if (res != ERROR_SUCCESS)
7245 WARN("failed to delete value %s (%d)\n", debugstr_w(name), res);
7247 else
7249 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(new_value));
7250 size = (strlenW( new_value ) + 1) * sizeof(WCHAR);
7251 res = RegSetValueExW( env, name, 0, type, (BYTE *)new_value, size );
7252 if (res != ERROR_SUCCESS)
7253 WARN("failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(new_value), res);
7256 done:
7257 uirow = MSI_CreateRecord( 3 );
7258 MSI_RecordSetStringW( uirow, 1, name );
7259 MSI_RecordSetStringW( uirow, 2, value );
7260 MSI_RecordSetInteger( uirow, 3, action );
7261 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7262 msiobj_release( &uirow->hdr );
7264 if (env) RegCloseKey( env );
7265 msi_free( deformatted );
7266 msi_free( new_value );
7267 return r;
7270 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7272 static const WCHAR query[] = {
7273 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7274 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7275 MSIQUERY *view;
7276 UINT rc;
7278 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7279 if (rc != ERROR_SUCCESS)
7280 return ERROR_SUCCESS;
7282 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7283 msiobj_release( &view->hdr );
7284 return rc;
7287 UINT msi_validate_product_id( MSIPACKAGE *package )
7289 LPWSTR key, template, id;
7290 UINT r = ERROR_SUCCESS;
7292 id = msi_dup_property( package->db, szProductID );
7293 if (id)
7295 msi_free( id );
7296 return ERROR_SUCCESS;
7298 template = msi_dup_property( package->db, szPIDTemplate );
7299 key = msi_dup_property( package->db, szPIDKEY );
7300 if (key && template)
7302 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7303 r = msi_set_property( package->db, szProductID, key, -1 );
7305 msi_free( template );
7306 msi_free( key );
7307 return r;
7310 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7312 return msi_validate_product_id( package );
7315 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7317 TRACE("\n");
7318 package->need_reboot_at_end = 1;
7319 return ERROR_SUCCESS;
7322 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7324 static const WCHAR szAvailableFreeReg[] =
7325 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7326 MSIRECORD *uirow;
7327 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7329 TRACE("%p %d kilobytes\n", package, space);
7331 uirow = MSI_CreateRecord( 1 );
7332 MSI_RecordSetInteger( uirow, 1, space );
7333 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7334 msiobj_release( &uirow->hdr );
7336 return ERROR_SUCCESS;
7339 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7341 TRACE("%p\n", package);
7343 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7344 return ERROR_SUCCESS;
7347 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7349 FIXME("%p\n", package);
7350 return ERROR_SUCCESS;
7353 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7355 static const WCHAR driver_query[] = {
7356 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7357 'O','D','B','C','D','r','i','v','e','r',0};
7358 static const WCHAR translator_query[] = {
7359 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7360 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7361 MSIQUERY *view;
7362 UINT r, count;
7364 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7365 if (r == ERROR_SUCCESS)
7367 count = 0;
7368 r = MSI_IterateRecords( view, &count, NULL, package );
7369 msiobj_release( &view->hdr );
7370 if (r != ERROR_SUCCESS)
7371 return r;
7372 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7374 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7375 if (r == ERROR_SUCCESS)
7377 count = 0;
7378 r = MSI_IterateRecords( view, &count, NULL, package );
7379 msiobj_release( &view->hdr );
7380 if (r != ERROR_SUCCESS)
7381 return r;
7382 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7384 return ERROR_SUCCESS;
7387 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7389 static const WCHAR fmtW[] =
7390 {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7391 MSIPACKAGE *package = param;
7392 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7393 int attrs = MSI_RecordGetInteger( rec, 5 );
7394 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7395 WCHAR *product, *features, *cmd;
7396 STARTUPINFOW si;
7397 PROCESS_INFORMATION info;
7398 BOOL ret;
7400 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7401 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7403 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7405 len += strlenW( product );
7406 if (features)
7407 len += strlenW( features );
7408 else
7409 len += sizeof(szAll) / sizeof(szAll[0]);
7411 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7413 msi_free( product );
7414 msi_free( features );
7415 return ERROR_OUTOFMEMORY;
7417 sprintfW( cmd, fmtW, product, features ? features : szAll );
7418 msi_free( product );
7419 msi_free( features );
7421 memset( &si, 0, sizeof(STARTUPINFOW) );
7422 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7423 msi_free( cmd );
7424 if (!ret) return GetLastError();
7425 CloseHandle( info.hThread );
7427 WaitForSingleObject( info.hProcess, INFINITE );
7428 CloseHandle( info.hProcess );
7429 return ERROR_SUCCESS;
7432 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7434 static const WCHAR query[] = {
7435 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7436 MSIQUERY *view;
7437 UINT r;
7439 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7440 if (r == ERROR_SUCCESS)
7442 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7443 msiobj_release( &view->hdr );
7444 if (r != ERROR_SUCCESS)
7445 return r;
7447 return ERROR_SUCCESS;
7450 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7452 MSIPACKAGE *package = param;
7453 int attributes = MSI_RecordGetInteger( rec, 5 );
7455 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7457 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7458 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7459 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7460 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7461 HKEY hkey;
7462 UINT r;
7464 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7466 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7467 if (r != ERROR_SUCCESS)
7468 return ERROR_SUCCESS;
7470 else
7472 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7473 if (r != ERROR_SUCCESS)
7474 return ERROR_SUCCESS;
7476 RegCloseKey( hkey );
7478 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7479 debugstr_w(upgrade_code), debugstr_w(version_min),
7480 debugstr_w(version_max), debugstr_w(language));
7482 return ERROR_SUCCESS;
7485 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7487 static const WCHAR query[] = {
7488 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7489 'U','p','g','r','a','d','e',0};
7490 MSIQUERY *view;
7491 UINT r;
7493 if (msi_get_property_int( package->db, szInstalled, 0 ))
7495 TRACE("product is installed, skipping action\n");
7496 return ERROR_SUCCESS;
7498 if (msi_get_property_int( package->db, szPreselected, 0 ))
7500 TRACE("Preselected property is set, not migrating feature states\n");
7501 return ERROR_SUCCESS;
7503 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7504 if (r == ERROR_SUCCESS)
7506 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7507 msiobj_release( &view->hdr );
7508 if (r != ERROR_SUCCESS)
7509 return r;
7511 return ERROR_SUCCESS;
7514 static void bind_image( const char *filename, const char *path )
7516 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7518 WARN("failed to bind image %u\n", GetLastError());
7522 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7524 UINT i;
7525 MSIFILE *file;
7526 MSIPACKAGE *package = param;
7527 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7528 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7529 char *filenameA, *pathA;
7530 WCHAR *pathW, **path_list;
7532 if (!(file = msi_get_loaded_file( package, key )))
7534 WARN("file %s not found\n", debugstr_w(key));
7535 return ERROR_SUCCESS;
7537 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7538 path_list = msi_split_string( paths, ';' );
7539 if (!path_list) bind_image( filenameA, NULL );
7540 else
7542 for (i = 0; path_list[i] && path_list[i][0]; i++)
7544 deformat_string( package, path_list[i], &pathW );
7545 if ((pathA = strdupWtoA( pathW )))
7547 bind_image( filenameA, pathA );
7548 msi_free( pathA );
7550 msi_free( pathW );
7553 msi_free( path_list );
7554 msi_free( filenameA );
7555 return ERROR_SUCCESS;
7558 static UINT ACTION_BindImage( MSIPACKAGE *package )
7560 static const WCHAR query[] = {
7561 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7562 'B','i','n','d','I','m','a','g','e',0};
7563 MSIQUERY *view;
7564 UINT r;
7566 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7567 if (r == ERROR_SUCCESS)
7569 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7570 msiobj_release( &view->hdr );
7571 if (r != ERROR_SUCCESS)
7572 return r;
7574 return ERROR_SUCCESS;
7577 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7579 static const WCHAR query[] = {
7580 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7581 MSIQUERY *view;
7582 DWORD count = 0;
7583 UINT r;
7585 r = MSI_OpenQuery( package->db, &view, query, table );
7586 if (r == ERROR_SUCCESS)
7588 r = MSI_IterateRecords(view, &count, NULL, package);
7589 msiobj_release(&view->hdr);
7590 if (r != ERROR_SUCCESS)
7591 return r;
7593 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7594 return ERROR_SUCCESS;
7597 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7599 static const WCHAR table[] = {
7600 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7601 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7604 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7606 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7607 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7610 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7612 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7613 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7616 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7618 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7619 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7622 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7624 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7625 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7628 static const struct
7630 const WCHAR *action;
7631 UINT (*handler)(MSIPACKAGE *);
7632 const WCHAR *action_rollback;
7634 StandardActions[] =
7636 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7637 { szAppSearch, ACTION_AppSearch, NULL },
7638 { szBindImage, ACTION_BindImage, NULL },
7639 { szCCPSearch, ACTION_CCPSearch, NULL },
7640 { szCostFinalize, ACTION_CostFinalize, NULL },
7641 { szCostInitialize, ACTION_CostInitialize, NULL },
7642 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7643 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7644 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7645 { szDisableRollback, ACTION_DisableRollback, NULL },
7646 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7647 { szExecuteAction, ACTION_ExecuteAction, NULL },
7648 { szFileCost, ACTION_FileCost, NULL },
7649 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7650 { szForceReboot, ACTION_ForceReboot, NULL },
7651 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7652 { szInstallExecute, ACTION_InstallExecute, NULL },
7653 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7654 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7655 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7656 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7657 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7658 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7659 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7660 { szInstallValidate, ACTION_InstallValidate, NULL },
7661 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7662 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7663 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7664 { szMoveFiles, ACTION_MoveFiles, NULL },
7665 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7666 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7667 { szPatchFiles, ACTION_PatchFiles, NULL },
7668 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7669 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7670 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7671 { szPublishProduct, ACTION_PublishProduct, NULL },
7672 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7673 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7674 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7675 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7676 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7677 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7678 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7679 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7680 { szRegisterUser, ACTION_RegisterUser, NULL },
7681 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7682 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7683 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7684 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7685 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7686 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7687 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7688 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7689 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7690 { szResolveSource, ACTION_ResolveSource, NULL },
7691 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7692 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7693 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7694 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7695 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7696 { szStartServices, ACTION_StartServices, szStopServices },
7697 { szStopServices, ACTION_StopServices, szStartServices },
7698 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7699 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7700 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7701 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7702 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7703 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7704 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7705 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7706 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7707 { szValidateProductID, ACTION_ValidateProductID, NULL },
7708 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7709 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7710 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7711 { NULL, NULL, NULL }
7714 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7716 BOOL ret = FALSE;
7717 UINT i;
7719 i = 0;
7720 while (StandardActions[i].action != NULL)
7722 if (!strcmpW( StandardActions[i].action, action ))
7724 ui_actionstart( package, action );
7725 if (StandardActions[i].handler)
7727 ui_actioninfo( package, action, TRUE, 0 );
7728 *rc = StandardActions[i].handler( package );
7729 ui_actioninfo( package, action, FALSE, *rc );
7731 if (StandardActions[i].action_rollback && !package->need_rollback)
7733 TRACE("scheduling rollback action\n");
7734 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7737 else
7739 FIXME("unhandled standard action %s\n", debugstr_w(action));
7740 *rc = ERROR_SUCCESS;
7742 ret = TRUE;
7743 break;
7745 i++;
7747 return ret;
7750 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7752 UINT rc = ERROR_SUCCESS;
7753 BOOL handled;
7755 TRACE("Performing action (%s)\n", debugstr_w(action));
7757 handled = ACTION_HandleStandardAction(package, action, &rc);
7759 if (!handled)
7760 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7762 if (!handled)
7764 WARN("unhandled msi action %s\n", debugstr_w(action));
7765 rc = ERROR_FUNCTION_NOT_CALLED;
7768 return rc;
7771 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7773 UINT rc = ERROR_SUCCESS;
7774 BOOL handled = FALSE;
7776 TRACE("Performing action (%s)\n", debugstr_w(action));
7778 package->action_progress_increment = 0;
7779 handled = ACTION_HandleStandardAction(package, action, &rc);
7781 if (!handled)
7782 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7784 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7785 handled = TRUE;
7787 if (!handled)
7789 WARN("unhandled msi action %s\n", debugstr_w(action));
7790 rc = ERROR_FUNCTION_NOT_CALLED;
7793 return rc;
7796 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7798 UINT rc = ERROR_SUCCESS;
7799 MSIRECORD *row;
7801 static const WCHAR query[] =
7802 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7803 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7804 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7805 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7806 static const WCHAR ui_query[] =
7807 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7808 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7809 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7810 ' ', '=',' ','%','i',0};
7812 if (needs_ui_sequence(package))
7813 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7814 else
7815 row = MSI_QueryGetRecord(package->db, query, seq);
7817 if (row)
7819 LPCWSTR action, cond;
7821 TRACE("Running the actions\n");
7823 /* check conditions */
7824 cond = MSI_RecordGetString(row, 2);
7826 /* this is a hack to skip errors in the condition code */
7827 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7829 msiobj_release(&row->hdr);
7830 return ERROR_SUCCESS;
7833 action = MSI_RecordGetString(row, 1);
7834 if (!action)
7836 ERR("failed to fetch action\n");
7837 msiobj_release(&row->hdr);
7838 return ERROR_FUNCTION_FAILED;
7841 if (needs_ui_sequence(package))
7842 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7843 else
7844 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7846 msiobj_release(&row->hdr);
7849 return rc;
7852 /****************************************************
7853 * TOP level entry points
7854 *****************************************************/
7856 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7857 LPCWSTR szCommandLine )
7859 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7860 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7861 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7862 WCHAR *reinstall, *remove, *patch, *productcode;
7863 BOOL ui_exists;
7864 UINT rc;
7866 msi_set_property( package->db, szAction, szInstall, -1 );
7868 package->script->InWhatSequence = SEQUENCE_INSTALL;
7870 if (szPackagePath)
7872 LPWSTR p, dir;
7873 LPCWSTR file;
7875 dir = strdupW(szPackagePath);
7876 p = strrchrW(dir, '\\');
7877 if (p)
7879 *(++p) = 0;
7880 file = szPackagePath + (p - dir);
7882 else
7884 msi_free(dir);
7885 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7886 GetCurrentDirectoryW(MAX_PATH, dir);
7887 lstrcatW(dir, szBackSlash);
7888 file = szPackagePath;
7891 msi_free( package->PackagePath );
7892 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7893 if (!package->PackagePath)
7895 msi_free(dir);
7896 return ERROR_OUTOFMEMORY;
7899 lstrcpyW(package->PackagePath, dir);
7900 lstrcatW(package->PackagePath, file);
7901 msi_free(dir);
7903 msi_set_sourcedir_props(package, FALSE);
7906 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7907 if (rc != ERROR_SUCCESS)
7908 return rc;
7910 msi_apply_transforms( package );
7911 msi_apply_patches( package );
7913 patch = msi_dup_property( package->db, szPatch );
7914 remove = msi_dup_property( package->db, szRemove );
7915 reinstall = msi_dup_property( package->db, szReinstall );
7916 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7918 TRACE("setting REINSTALL property to ALL\n");
7919 msi_set_property( package->db, szReinstall, szAll, -1 );
7920 package->full_reinstall = 1;
7923 msi_set_original_database_property( package->db, szPackagePath );
7924 msi_parse_command_line( package, szCommandLine, FALSE );
7925 msi_adjust_privilege_properties( package );
7926 msi_set_context( package );
7928 productcode = msi_dup_property( package->db, szProductCode );
7929 if (strcmpiW( productcode, package->ProductCode ))
7931 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
7932 msi_free( package->ProductCode );
7933 package->ProductCode = productcode;
7935 else msi_free( productcode );
7937 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7939 TRACE("disabling rollback\n");
7940 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7943 if (needs_ui_sequence( package))
7945 package->script->InWhatSequence |= SEQUENCE_UI;
7946 rc = ACTION_ProcessUISequence(package);
7947 ui_exists = ui_sequence_exists(package);
7948 if (rc == ERROR_SUCCESS || !ui_exists)
7950 package->script->InWhatSequence |= SEQUENCE_EXEC;
7951 rc = ACTION_ProcessExecSequence(package, ui_exists);
7954 else
7955 rc = ACTION_ProcessExecSequence(package, FALSE);
7957 /* process the ending type action */
7958 if (rc == ERROR_SUCCESS)
7959 ACTION_PerformActionSequence(package, -1);
7960 else if (rc == ERROR_INSTALL_USEREXIT)
7961 ACTION_PerformActionSequence(package, -2);
7962 else if (rc == ERROR_INSTALL_SUSPEND)
7963 ACTION_PerformActionSequence(package, -4);
7964 else /* failed */
7966 ACTION_PerformActionSequence(package, -3);
7967 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7969 package->need_rollback = TRUE;
7973 /* finish up running custom actions */
7974 ACTION_FinishCustomActions(package);
7976 if (package->need_rollback && !reinstall)
7978 WARN("installation failed, running rollback script\n");
7979 execute_script( package, SCRIPT_ROLLBACK );
7981 msi_free( reinstall );
7982 msi_free( remove );
7983 msi_free( patch );
7985 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7986 return ERROR_SUCCESS_REBOOT_REQUIRED;
7988 return rc;