TESTING -- override pthreads to fix gstreamer v5
[wine/multimedia.git] / dlls / msi / action.c
blob136bfbb2dfb6f8b7cd3e3e4049f977738684aa3a
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 CloseHandle(the_file);
4139 DeleteFileW(FilePath);
4140 break;
4142 WriteFile(the_file,buffer,sz,&write,NULL);
4143 } while (sz == 1024);
4145 msi_free(FilePath);
4146 CloseHandle(the_file);
4148 return ERROR_SUCCESS;
4151 static UINT msi_publish_icons(MSIPACKAGE *package)
4153 static const WCHAR query[]= {
4154 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4155 '`','I','c','o','n','`',0};
4156 MSIQUERY *view;
4157 UINT r;
4159 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4160 if (r == ERROR_SUCCESS)
4162 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4163 msiobj_release(&view->hdr);
4164 if (r != ERROR_SUCCESS)
4165 return r;
4167 return ERROR_SUCCESS;
4170 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4172 UINT r;
4173 HKEY source;
4174 LPWSTR buffer;
4175 MSIMEDIADISK *disk;
4176 MSISOURCELISTINFO *info;
4178 r = RegCreateKeyW(hkey, szSourceList, &source);
4179 if (r != ERROR_SUCCESS)
4180 return r;
4182 RegCloseKey(source);
4184 buffer = strrchrW(package->PackagePath, '\\') + 1;
4185 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4186 package->Context, MSICODE_PRODUCT,
4187 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4188 if (r != ERROR_SUCCESS)
4189 return r;
4191 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4192 package->Context, MSICODE_PRODUCT,
4193 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4194 if (r != ERROR_SUCCESS)
4195 return r;
4197 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4198 package->Context, MSICODE_PRODUCT,
4199 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4200 if (r != ERROR_SUCCESS)
4201 return r;
4203 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4205 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4206 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4207 info->options, info->value);
4208 else
4209 MsiSourceListSetInfoW(package->ProductCode, NULL,
4210 info->context, info->options,
4211 info->property, info->value);
4214 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4216 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4217 disk->context, disk->options,
4218 disk->disk_id, disk->volume_label, disk->disk_prompt);
4221 return ERROR_SUCCESS;
4224 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4226 MSIHANDLE hdb, suminfo;
4227 WCHAR guids[MAX_PATH];
4228 WCHAR packcode[SQUISH_GUID_SIZE];
4229 LPWSTR buffer;
4230 LPWSTR ptr;
4231 DWORD langid;
4232 DWORD size;
4233 UINT r;
4235 static const WCHAR szARPProductIcon[] =
4236 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4237 static const WCHAR szAssignment[] =
4238 {'A','s','s','i','g','n','m','e','n','t',0};
4239 static const WCHAR szAdvertiseFlags[] =
4240 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4241 static const WCHAR szClients[] =
4242 {'C','l','i','e','n','t','s',0};
4243 static const WCHAR szColon[] = {':',0};
4245 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4246 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4247 msi_free(buffer);
4249 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4250 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4252 /* FIXME */
4253 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4255 buffer = msi_dup_property(package->db, szARPProductIcon);
4256 if (buffer)
4258 LPWSTR path = msi_build_icon_path(package, buffer);
4259 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4260 msi_free(path);
4261 msi_free(buffer);
4264 buffer = msi_dup_property(package->db, szProductVersion);
4265 if (buffer)
4267 DWORD verdword = msi_version_str_to_dword(buffer);
4268 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4269 msi_free(buffer);
4272 msi_reg_set_val_dword(hkey, szAssignment, 0);
4273 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4274 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4275 msi_reg_set_val_str(hkey, szClients, szColon);
4277 hdb = alloc_msihandle(&package->db->hdr);
4278 if (!hdb)
4279 return ERROR_NOT_ENOUGH_MEMORY;
4281 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4282 MsiCloseHandle(hdb);
4283 if (r != ERROR_SUCCESS)
4284 goto done;
4286 size = MAX_PATH;
4287 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4288 NULL, guids, &size);
4289 if (r != ERROR_SUCCESS)
4290 goto done;
4292 ptr = strchrW(guids, ';');
4293 if (ptr) *ptr = 0;
4294 squash_guid(guids, packcode);
4295 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4297 done:
4298 MsiCloseHandle(suminfo);
4299 return ERROR_SUCCESS;
4302 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4304 UINT r;
4305 HKEY hkey;
4306 LPWSTR upgrade;
4307 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4309 upgrade = msi_dup_property(package->db, szUpgradeCode);
4310 if (!upgrade)
4311 return ERROR_SUCCESS;
4313 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4314 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4315 else
4316 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4318 if (r != ERROR_SUCCESS)
4320 WARN("failed to open upgrade code key\n");
4321 msi_free(upgrade);
4322 return ERROR_SUCCESS;
4324 squash_guid(package->ProductCode, squashed_pc);
4325 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4326 RegCloseKey(hkey);
4327 msi_free(upgrade);
4328 return ERROR_SUCCESS;
4331 static BOOL msi_check_publish(MSIPACKAGE *package)
4333 MSIFEATURE *feature;
4335 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4337 feature->Action = msi_get_feature_action( package, feature );
4338 if (feature->Action == INSTALLSTATE_LOCAL)
4339 return TRUE;
4342 return FALSE;
4345 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4347 MSIFEATURE *feature;
4349 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4351 feature->Action = msi_get_feature_action( package, feature );
4352 if (feature->Action != INSTALLSTATE_ABSENT)
4353 return FALSE;
4356 return TRUE;
4359 static UINT msi_publish_patches( MSIPACKAGE *package )
4361 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4362 WCHAR patch_squashed[GUID_SIZE];
4363 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4364 LONG res;
4365 MSIPATCHINFO *patch;
4366 UINT r;
4367 WCHAR *p, *all_patches = NULL;
4368 DWORD len = 0;
4370 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4371 if (r != ERROR_SUCCESS)
4372 return ERROR_FUNCTION_FAILED;
4374 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4375 if (res != ERROR_SUCCESS)
4377 r = ERROR_FUNCTION_FAILED;
4378 goto done;
4381 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4382 if (r != ERROR_SUCCESS)
4383 goto done;
4385 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4387 squash_guid( patch->patchcode, patch_squashed );
4388 len += strlenW( patch_squashed ) + 1;
4391 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4392 if (!all_patches)
4393 goto done;
4395 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4397 HKEY patch_key;
4399 squash_guid( patch->patchcode, p );
4400 p += strlenW( p ) + 1;
4402 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4403 (const BYTE *)patch->transforms,
4404 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4405 if (res != ERROR_SUCCESS)
4406 goto done;
4408 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4409 if (r != ERROR_SUCCESS)
4410 goto done;
4412 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4413 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4414 RegCloseKey( patch_key );
4415 if (res != ERROR_SUCCESS)
4416 goto done;
4418 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4420 res = GetLastError();
4421 ERR("Unable to copy patch package %d\n", res);
4422 goto done;
4424 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4425 if (res != ERROR_SUCCESS)
4426 goto done;
4428 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4429 RegCloseKey( patch_key );
4430 if (res != ERROR_SUCCESS)
4431 goto done;
4434 all_patches[len] = 0;
4435 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4436 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4437 if (res != ERROR_SUCCESS)
4438 goto done;
4440 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4441 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4442 if (res != ERROR_SUCCESS)
4443 r = ERROR_FUNCTION_FAILED;
4445 done:
4446 RegCloseKey( product_patches_key );
4447 RegCloseKey( patches_key );
4448 RegCloseKey( product_key );
4449 msi_free( all_patches );
4450 return r;
4453 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4455 UINT rc;
4456 HKEY hukey = NULL, hudkey = NULL;
4457 MSIRECORD *uirow;
4459 if (!list_empty(&package->patches))
4461 rc = msi_publish_patches(package);
4462 if (rc != ERROR_SUCCESS)
4463 goto end;
4466 /* FIXME: also need to publish if the product is in advertise mode */
4467 if (!msi_check_publish(package))
4468 return ERROR_SUCCESS;
4470 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4471 &hukey, TRUE);
4472 if (rc != ERROR_SUCCESS)
4473 goto end;
4475 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4476 NULL, &hudkey, TRUE);
4477 if (rc != ERROR_SUCCESS)
4478 goto end;
4480 rc = msi_publish_upgrade_code(package);
4481 if (rc != ERROR_SUCCESS)
4482 goto end;
4484 rc = msi_publish_product_properties(package, hukey);
4485 if (rc != ERROR_SUCCESS)
4486 goto end;
4488 rc = msi_publish_sourcelist(package, hukey);
4489 if (rc != ERROR_SUCCESS)
4490 goto end;
4492 rc = msi_publish_icons(package);
4494 end:
4495 uirow = MSI_CreateRecord( 1 );
4496 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4497 msi_ui_actiondata( package, szPublishProduct, uirow );
4498 msiobj_release( &uirow->hdr );
4500 RegCloseKey(hukey);
4501 RegCloseKey(hudkey);
4502 return rc;
4505 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4507 WCHAR *filename, *ptr, *folder, *ret;
4508 const WCHAR *dirprop;
4510 filename = msi_dup_record_field( row, 2 );
4511 if (filename && (ptr = strchrW( filename, '|' )))
4512 ptr++;
4513 else
4514 ptr = filename;
4516 dirprop = MSI_RecordGetString( row, 3 );
4517 if (dirprop)
4519 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4520 if (!folder) folder = msi_dup_property( package->db, dirprop );
4522 else
4523 folder = msi_dup_property( package->db, szWindowsFolder );
4525 if (!folder)
4527 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4528 msi_free( filename );
4529 return NULL;
4532 ret = msi_build_directory_name( 2, folder, ptr );
4534 msi_free( filename );
4535 msi_free( folder );
4536 return ret;
4539 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4541 MSIPACKAGE *package = param;
4542 LPCWSTR component, section, key, value, identifier;
4543 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4544 MSIRECORD * uirow;
4545 INT action;
4546 MSICOMPONENT *comp;
4548 component = MSI_RecordGetString(row, 8);
4549 comp = msi_get_loaded_component(package,component);
4550 if (!comp)
4551 return ERROR_SUCCESS;
4553 comp->Action = msi_get_component_action( package, comp );
4554 if (comp->Action != INSTALLSTATE_LOCAL)
4556 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4557 return ERROR_SUCCESS;
4560 identifier = MSI_RecordGetString(row,1);
4561 section = MSI_RecordGetString(row,4);
4562 key = MSI_RecordGetString(row,5);
4563 value = MSI_RecordGetString(row,6);
4564 action = MSI_RecordGetInteger(row,7);
4566 deformat_string(package,section,&deformated_section);
4567 deformat_string(package,key,&deformated_key);
4568 deformat_string(package,value,&deformated_value);
4570 fullname = get_ini_file_name(package, row);
4572 if (action == 0)
4574 TRACE("Adding value %s to section %s in %s\n",
4575 debugstr_w(deformated_key), debugstr_w(deformated_section),
4576 debugstr_w(fullname));
4577 WritePrivateProfileStringW(deformated_section, deformated_key,
4578 deformated_value, fullname);
4580 else if (action == 1)
4582 WCHAR returned[10];
4583 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4584 returned, 10, fullname);
4585 if (returned[0] == 0)
4587 TRACE("Adding value %s to section %s in %s\n",
4588 debugstr_w(deformated_key), debugstr_w(deformated_section),
4589 debugstr_w(fullname));
4591 WritePrivateProfileStringW(deformated_section, deformated_key,
4592 deformated_value, fullname);
4595 else if (action == 3)
4596 FIXME("Append to existing section not yet implemented\n");
4598 uirow = MSI_CreateRecord(4);
4599 MSI_RecordSetStringW(uirow,1,identifier);
4600 MSI_RecordSetStringW(uirow,2,deformated_section);
4601 MSI_RecordSetStringW(uirow,3,deformated_key);
4602 MSI_RecordSetStringW(uirow,4,deformated_value);
4603 msi_ui_actiondata( package, szWriteIniValues, uirow );
4604 msiobj_release( &uirow->hdr );
4606 msi_free(fullname);
4607 msi_free(deformated_key);
4608 msi_free(deformated_value);
4609 msi_free(deformated_section);
4610 return ERROR_SUCCESS;
4613 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4615 static const WCHAR query[] = {
4616 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4617 '`','I','n','i','F','i','l','e','`',0};
4618 MSIQUERY *view;
4619 UINT rc;
4621 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4622 if (rc != ERROR_SUCCESS)
4623 return ERROR_SUCCESS;
4625 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4626 msiobj_release(&view->hdr);
4627 return rc;
4630 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4632 MSIPACKAGE *package = param;
4633 LPCWSTR component, section, key, value, identifier;
4634 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4635 MSICOMPONENT *comp;
4636 MSIRECORD *uirow;
4637 INT action;
4639 component = MSI_RecordGetString( row, 8 );
4640 comp = msi_get_loaded_component( package, component );
4641 if (!comp)
4642 return ERROR_SUCCESS;
4644 comp->Action = msi_get_component_action( package, comp );
4645 if (comp->Action != INSTALLSTATE_ABSENT)
4647 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4648 return ERROR_SUCCESS;
4651 identifier = MSI_RecordGetString( row, 1 );
4652 section = MSI_RecordGetString( row, 4 );
4653 key = MSI_RecordGetString( row, 5 );
4654 value = MSI_RecordGetString( row, 6 );
4655 action = MSI_RecordGetInteger( row, 7 );
4657 deformat_string( package, section, &deformated_section );
4658 deformat_string( package, key, &deformated_key );
4659 deformat_string( package, value, &deformated_value );
4661 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4663 filename = get_ini_file_name( package, row );
4665 TRACE("Removing key %s from section %s in %s\n",
4666 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4668 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4670 WARN("Unable to remove key %u\n", GetLastError());
4672 msi_free( filename );
4674 else
4675 FIXME("Unsupported action %d\n", action);
4678 uirow = MSI_CreateRecord( 4 );
4679 MSI_RecordSetStringW( uirow, 1, identifier );
4680 MSI_RecordSetStringW( uirow, 2, deformated_section );
4681 MSI_RecordSetStringW( uirow, 3, deformated_key );
4682 MSI_RecordSetStringW( uirow, 4, deformated_value );
4683 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4684 msiobj_release( &uirow->hdr );
4686 msi_free( deformated_key );
4687 msi_free( deformated_value );
4688 msi_free( deformated_section );
4689 return ERROR_SUCCESS;
4692 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4694 MSIPACKAGE *package = param;
4695 LPCWSTR component, section, key, value, identifier;
4696 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4697 MSICOMPONENT *comp;
4698 MSIRECORD *uirow;
4699 INT action;
4701 component = MSI_RecordGetString( row, 8 );
4702 comp = msi_get_loaded_component( package, component );
4703 if (!comp)
4704 return ERROR_SUCCESS;
4706 comp->Action = msi_get_component_action( package, comp );
4707 if (comp->Action != INSTALLSTATE_LOCAL)
4709 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4710 return ERROR_SUCCESS;
4713 identifier = MSI_RecordGetString( row, 1 );
4714 section = MSI_RecordGetString( row, 4 );
4715 key = MSI_RecordGetString( row, 5 );
4716 value = MSI_RecordGetString( row, 6 );
4717 action = MSI_RecordGetInteger( row, 7 );
4719 deformat_string( package, section, &deformated_section );
4720 deformat_string( package, key, &deformated_key );
4721 deformat_string( package, value, &deformated_value );
4723 if (action == msidbIniFileActionRemoveLine)
4725 filename = get_ini_file_name( package, row );
4727 TRACE("Removing key %s from section %s in %s\n",
4728 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4730 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4732 WARN("Unable to remove key %u\n", GetLastError());
4734 msi_free( filename );
4736 else
4737 FIXME("Unsupported action %d\n", action);
4739 uirow = MSI_CreateRecord( 4 );
4740 MSI_RecordSetStringW( uirow, 1, identifier );
4741 MSI_RecordSetStringW( uirow, 2, deformated_section );
4742 MSI_RecordSetStringW( uirow, 3, deformated_key );
4743 MSI_RecordSetStringW( uirow, 4, deformated_value );
4744 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4745 msiobj_release( &uirow->hdr );
4747 msi_free( deformated_key );
4748 msi_free( deformated_value );
4749 msi_free( deformated_section );
4750 return ERROR_SUCCESS;
4753 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4755 static const WCHAR query[] = {
4756 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4757 '`','I','n','i','F','i','l','e','`',0};
4758 static const WCHAR remove_query[] = {
4759 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4760 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4761 MSIQUERY *view;
4762 UINT rc;
4764 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4765 if (rc == ERROR_SUCCESS)
4767 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4768 msiobj_release( &view->hdr );
4769 if (rc != ERROR_SUCCESS)
4770 return rc;
4772 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4773 if (rc == ERROR_SUCCESS)
4775 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4776 msiobj_release( &view->hdr );
4777 if (rc != ERROR_SUCCESS)
4778 return rc;
4780 return ERROR_SUCCESS;
4783 static void register_dll( const WCHAR *dll, BOOL unregister )
4785 static const WCHAR regW[] =
4786 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4787 static const WCHAR unregW[] =
4788 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4789 PROCESS_INFORMATION pi;
4790 STARTUPINFOW si;
4791 WCHAR *cmd;
4793 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4795 if (unregister) sprintfW( cmd, unregW, dll );
4796 else sprintfW( cmd, regW, dll );
4798 memset( &si, 0, sizeof(STARTUPINFOW) );
4799 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4801 CloseHandle( pi.hThread );
4802 msi_dialog_check_messages( pi.hProcess );
4803 CloseHandle( pi.hProcess );
4805 msi_free( cmd );
4808 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4810 MSIPACKAGE *package = param;
4811 LPCWSTR filename;
4812 MSIFILE *file;
4813 MSIRECORD *uirow;
4815 filename = MSI_RecordGetString( row, 1 );
4816 file = msi_get_loaded_file( package, filename );
4817 if (!file)
4819 WARN("unable to find file %s\n", debugstr_w(filename));
4820 return ERROR_SUCCESS;
4822 file->Component->Action = msi_get_component_action( package, file->Component );
4823 if (file->Component->Action != INSTALLSTATE_LOCAL)
4825 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4826 return ERROR_SUCCESS;
4829 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4830 register_dll( file->TargetPath, FALSE );
4832 uirow = MSI_CreateRecord( 2 );
4833 MSI_RecordSetStringW( uirow, 1, file->File );
4834 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4835 msi_ui_actiondata( package, szSelfRegModules, uirow );
4836 msiobj_release( &uirow->hdr );
4838 return ERROR_SUCCESS;
4841 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4843 static const WCHAR query[] = {
4844 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4845 '`','S','e','l','f','R','e','g','`',0};
4846 MSIQUERY *view;
4847 UINT rc;
4849 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4850 if (rc != ERROR_SUCCESS)
4851 return ERROR_SUCCESS;
4853 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4854 msiobj_release(&view->hdr);
4855 return rc;
4858 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4860 MSIPACKAGE *package = param;
4861 LPCWSTR filename;
4862 MSIFILE *file;
4863 MSIRECORD *uirow;
4865 filename = MSI_RecordGetString( row, 1 );
4866 file = msi_get_loaded_file( package, filename );
4867 if (!file)
4869 WARN("unable to find file %s\n", debugstr_w(filename));
4870 return ERROR_SUCCESS;
4872 file->Component->Action = msi_get_component_action( package, file->Component );
4873 if (file->Component->Action != INSTALLSTATE_ABSENT)
4875 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4876 return ERROR_SUCCESS;
4879 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4880 register_dll( file->TargetPath, TRUE );
4882 uirow = MSI_CreateRecord( 2 );
4883 MSI_RecordSetStringW( uirow, 1, file->File );
4884 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4885 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4886 msiobj_release( &uirow->hdr );
4888 return ERROR_SUCCESS;
4891 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4893 static const WCHAR query[] = {
4894 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4895 '`','S','e','l','f','R','e','g','`',0};
4896 MSIQUERY *view;
4897 UINT rc;
4899 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4900 if (rc != ERROR_SUCCESS)
4901 return ERROR_SUCCESS;
4903 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4904 msiobj_release( &view->hdr );
4905 return rc;
4908 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4910 MSIFEATURE *feature;
4911 UINT rc;
4912 HKEY hkey = NULL, userdata = NULL;
4914 if (!msi_check_publish(package))
4915 return ERROR_SUCCESS;
4917 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4918 &hkey, TRUE);
4919 if (rc != ERROR_SUCCESS)
4920 goto end;
4922 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4923 &userdata, TRUE);
4924 if (rc != ERROR_SUCCESS)
4925 goto end;
4927 /* here the guids are base 85 encoded */
4928 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4930 ComponentList *cl;
4931 LPWSTR data = NULL;
4932 GUID clsid;
4933 INT size;
4934 BOOL absent = FALSE;
4935 MSIRECORD *uirow;
4937 if (feature->Level <= 0) continue;
4939 if (feature->Action != INSTALLSTATE_LOCAL &&
4940 feature->Action != INSTALLSTATE_SOURCE &&
4941 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4943 size = 1;
4944 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4946 size += 21;
4948 if (feature->Feature_Parent)
4949 size += strlenW( feature->Feature_Parent )+2;
4951 data = msi_alloc(size * sizeof(WCHAR));
4953 data[0] = 0;
4954 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4956 MSICOMPONENT* component = cl->component;
4957 WCHAR buf[21];
4959 buf[0] = 0;
4960 if (component->ComponentId)
4962 TRACE("From %s\n",debugstr_w(component->ComponentId));
4963 CLSIDFromString(component->ComponentId, &clsid);
4964 encode_base85_guid(&clsid,buf);
4965 TRACE("to %s\n",debugstr_w(buf));
4966 strcatW(data,buf);
4970 if (feature->Feature_Parent)
4972 static const WCHAR sep[] = {'\2',0};
4973 strcatW(data,sep);
4974 strcatW(data,feature->Feature_Parent);
4977 msi_reg_set_val_str( userdata, feature->Feature, data );
4978 msi_free(data);
4980 size = 0;
4981 if (feature->Feature_Parent)
4982 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4983 if (!absent)
4985 size += sizeof(WCHAR);
4986 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4987 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4989 else
4991 size += 2*sizeof(WCHAR);
4992 data = msi_alloc(size);
4993 data[0] = 0x6;
4994 data[1] = 0;
4995 if (feature->Feature_Parent)
4996 strcpyW( &data[1], feature->Feature_Parent );
4997 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4998 (LPBYTE)data,size);
4999 msi_free(data);
5002 /* the UI chunk */
5003 uirow = MSI_CreateRecord( 1 );
5004 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5005 msi_ui_actiondata( package, szPublishFeatures, uirow );
5006 msiobj_release( &uirow->hdr );
5007 /* FIXME: call msi_ui_progress? */
5010 end:
5011 RegCloseKey(hkey);
5012 RegCloseKey(userdata);
5013 return rc;
5016 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
5018 UINT r;
5019 HKEY hkey;
5020 MSIRECORD *uirow;
5022 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
5024 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
5025 &hkey, FALSE);
5026 if (r == ERROR_SUCCESS)
5028 RegDeleteValueW(hkey, feature->Feature);
5029 RegCloseKey(hkey);
5032 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
5033 &hkey, FALSE);
5034 if (r == ERROR_SUCCESS)
5036 RegDeleteValueW(hkey, feature->Feature);
5037 RegCloseKey(hkey);
5040 uirow = MSI_CreateRecord( 1 );
5041 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5042 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
5043 msiobj_release( &uirow->hdr );
5045 return ERROR_SUCCESS;
5048 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5050 MSIFEATURE *feature;
5052 if (!msi_check_unpublish(package))
5053 return ERROR_SUCCESS;
5055 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5057 msi_unpublish_feature(package, feature);
5060 return ERROR_SUCCESS;
5063 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5065 SYSTEMTIME systime;
5066 DWORD size, langid;
5067 WCHAR date[9], *val, *buffer;
5068 const WCHAR *prop, *key;
5070 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5071 static const WCHAR modpath_fmt[] =
5072 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5073 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5074 static const WCHAR szModifyPath[] =
5075 {'M','o','d','i','f','y','P','a','t','h',0};
5076 static const WCHAR szUninstallString[] =
5077 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5078 static const WCHAR szEstimatedSize[] =
5079 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5080 static const WCHAR szDisplayVersion[] =
5081 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5082 static const WCHAR szInstallSource[] =
5083 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5084 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5085 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5086 static const WCHAR szAuthorizedCDFPrefix[] =
5087 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5088 static const WCHAR szARPCONTACT[] =
5089 {'A','R','P','C','O','N','T','A','C','T',0};
5090 static const WCHAR szContact[] =
5091 {'C','o','n','t','a','c','t',0};
5092 static const WCHAR szARPCOMMENTS[] =
5093 {'A','R','P','C','O','M','M','E','N','T','S',0};
5094 static const WCHAR szComments[] =
5095 {'C','o','m','m','e','n','t','s',0};
5096 static const WCHAR szProductName[] =
5097 {'P','r','o','d','u','c','t','N','a','m','e',0};
5098 static const WCHAR szDisplayName[] =
5099 {'D','i','s','p','l','a','y','N','a','m','e',0};
5100 static const WCHAR szARPHELPLINK[] =
5101 {'A','R','P','H','E','L','P','L','I','N','K',0};
5102 static const WCHAR szHelpLink[] =
5103 {'H','e','l','p','L','i','n','k',0};
5104 static const WCHAR szARPHELPTELEPHONE[] =
5105 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5106 static const WCHAR szHelpTelephone[] =
5107 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5108 static const WCHAR szARPINSTALLLOCATION[] =
5109 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5110 static const WCHAR szManufacturer[] =
5111 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5112 static const WCHAR szPublisher[] =
5113 {'P','u','b','l','i','s','h','e','r',0};
5114 static const WCHAR szARPREADME[] =
5115 {'A','R','P','R','E','A','D','M','E',0};
5116 static const WCHAR szReadme[] =
5117 {'R','e','a','d','M','e',0};
5118 static const WCHAR szARPSIZE[] =
5119 {'A','R','P','S','I','Z','E',0};
5120 static const WCHAR szSize[] =
5121 {'S','i','z','e',0};
5122 static const WCHAR szARPURLINFOABOUT[] =
5123 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5124 static const WCHAR szURLInfoAbout[] =
5125 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5126 static const WCHAR szARPURLUPDATEINFO[] =
5127 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5128 static const WCHAR szURLUpdateInfo[] =
5129 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5130 static const WCHAR szARPSYSTEMCOMPONENT[] =
5131 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5132 static const WCHAR szSystemComponent[] =
5133 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5135 static const WCHAR *propval[] = {
5136 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5137 szARPCONTACT, szContact,
5138 szARPCOMMENTS, szComments,
5139 szProductName, szDisplayName,
5140 szARPHELPLINK, szHelpLink,
5141 szARPHELPTELEPHONE, szHelpTelephone,
5142 szARPINSTALLLOCATION, szInstallLocation,
5143 szSourceDir, szInstallSource,
5144 szManufacturer, szPublisher,
5145 szARPREADME, szReadme,
5146 szARPSIZE, szSize,
5147 szARPURLINFOABOUT, szURLInfoAbout,
5148 szARPURLUPDATEINFO, szURLUpdateInfo,
5149 NULL
5151 const WCHAR **p = propval;
5153 while (*p)
5155 prop = *p++;
5156 key = *p++;
5157 val = msi_dup_property(package->db, prop);
5158 msi_reg_set_val_str(hkey, key, val);
5159 msi_free(val);
5162 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5163 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5165 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5167 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5168 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5169 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5170 msi_free(buffer);
5172 /* FIXME: Write real Estimated Size when we have it */
5173 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5175 GetLocalTime(&systime);
5176 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5177 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5179 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5180 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5182 buffer = msi_dup_property(package->db, szProductVersion);
5183 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5184 if (buffer)
5186 DWORD verdword = msi_version_str_to_dword(buffer);
5188 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5189 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5190 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5191 msi_free(buffer);
5194 return ERROR_SUCCESS;
5197 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5199 WCHAR squashed_pc[SQUISH_GUID_SIZE];
5200 MSIRECORD *uirow;
5201 LPWSTR upgrade_code;
5202 HKEY hkey, props, upgrade_key;
5203 UINT rc;
5205 /* FIXME: also need to publish if the product is in advertise mode */
5206 if (!msi_check_publish(package))
5207 return ERROR_SUCCESS;
5209 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5210 if (rc != ERROR_SUCCESS)
5211 return rc;
5213 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5214 if (rc != ERROR_SUCCESS)
5215 goto done;
5217 rc = msi_publish_install_properties(package, hkey);
5218 if (rc != ERROR_SUCCESS)
5219 goto done;
5221 rc = msi_publish_install_properties(package, props);
5222 if (rc != ERROR_SUCCESS)
5223 goto done;
5225 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5226 if (upgrade_code)
5228 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5229 if (rc == ERROR_SUCCESS)
5231 squash_guid( package->ProductCode, squashed_pc );
5232 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5233 RegCloseKey( upgrade_key );
5235 msi_free( upgrade_code );
5237 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5238 package->delete_on_close = FALSE;
5240 done:
5241 uirow = MSI_CreateRecord( 1 );
5242 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5243 msi_ui_actiondata( package, szRegisterProduct, uirow );
5244 msiobj_release( &uirow->hdr );
5246 RegCloseKey(hkey);
5247 return ERROR_SUCCESS;
5250 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5252 return execute_script(package, SCRIPT_INSTALL);
5255 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5257 MSIPACKAGE *package = param;
5258 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5259 WCHAR *p, *icon_path;
5261 if (!icon) return ERROR_SUCCESS;
5262 if ((icon_path = msi_build_icon_path( package, icon )))
5264 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5265 DeleteFileW( icon_path );
5266 if ((p = strrchrW( icon_path, '\\' )))
5268 *p = 0;
5269 RemoveDirectoryW( icon_path );
5271 msi_free( icon_path );
5273 return ERROR_SUCCESS;
5276 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5278 static const WCHAR query[]= {
5279 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5280 MSIQUERY *view;
5281 UINT r;
5283 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5284 if (r == ERROR_SUCCESS)
5286 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5287 msiobj_release( &view->hdr );
5288 if (r != ERROR_SUCCESS)
5289 return r;
5291 return ERROR_SUCCESS;
5294 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
5296 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
5297 WCHAR *upgrade, **features;
5298 BOOL full_uninstall = TRUE;
5299 MSIFEATURE *feature;
5300 MSIPATCHINFO *patch;
5301 UINT i;
5303 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5305 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
5307 features = msi_split_string( remove, ',' );
5308 for (i = 0; features && features[i]; i++)
5310 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
5312 msi_free(features);
5314 if (!full_uninstall)
5315 return ERROR_SUCCESS;
5317 MSIREG_DeleteProductKey(package->ProductCode);
5318 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5319 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5321 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5322 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5323 MSIREG_DeleteUserProductKey(package->ProductCode);
5324 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5326 upgrade = msi_dup_property(package->db, szUpgradeCode);
5327 if (upgrade)
5329 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5330 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5331 msi_free(upgrade);
5334 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5336 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5337 if (!strcmpW( package->ProductCode, patch->products ))
5339 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5340 patch->delete_on_close = TRUE;
5342 /* FIXME: remove local patch package if this is the last product */
5344 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5345 package->delete_on_close = TRUE;
5347 msi_unpublish_icons( package );
5348 return ERROR_SUCCESS;
5351 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5353 UINT rc;
5354 WCHAR *remove;
5356 /* first do the same as an InstallExecute */
5357 rc = execute_script(package, SCRIPT_INSTALL);
5358 if (rc != ERROR_SUCCESS)
5359 return rc;
5361 /* then handle commit actions */
5362 rc = execute_script(package, SCRIPT_COMMIT);
5363 if (rc != ERROR_SUCCESS)
5364 return rc;
5366 remove = msi_dup_property(package->db, szRemove);
5367 rc = msi_unpublish_product(package, remove);
5368 msi_free(remove);
5369 return rc;
5372 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5374 static const WCHAR RunOnce[] = {
5375 'S','o','f','t','w','a','r','e','\\',
5376 'M','i','c','r','o','s','o','f','t','\\',
5377 'W','i','n','d','o','w','s','\\',
5378 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5379 'R','u','n','O','n','c','e',0};
5380 static const WCHAR InstallRunOnce[] = {
5381 'S','o','f','t','w','a','r','e','\\',
5382 'M','i','c','r','o','s','o','f','t','\\',
5383 'W','i','n','d','o','w','s','\\',
5384 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5385 'I','n','s','t','a','l','l','e','r','\\',
5386 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5388 static const WCHAR msiexec_fmt[] = {
5389 '%','s',
5390 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5391 '\"','%','s','\"',0};
5392 static const WCHAR install_fmt[] = {
5393 '/','I',' ','\"','%','s','\"',' ',
5394 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5395 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5396 WCHAR buffer[256], sysdir[MAX_PATH];
5397 HKEY hkey;
5398 WCHAR squished_pc[100];
5400 squash_guid(package->ProductCode,squished_pc);
5402 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5403 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5404 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5405 squished_pc);
5407 msi_reg_set_val_str( hkey, squished_pc, buffer );
5408 RegCloseKey(hkey);
5410 TRACE("Reboot command %s\n",debugstr_w(buffer));
5412 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5413 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5415 msi_reg_set_val_str( hkey, squished_pc, buffer );
5416 RegCloseKey(hkey);
5418 return ERROR_INSTALL_SUSPEND;
5421 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5423 static const WCHAR query[] =
5424 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5425 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5426 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5427 MSIRECORD *rec, *row;
5428 DWORD i, size = 0;
5429 va_list va;
5430 const WCHAR *str;
5431 WCHAR *data;
5433 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5435 rec = MSI_CreateRecord( count + 2 );
5436 str = MSI_RecordGetString( row, 1 );
5437 MSI_RecordSetStringW( rec, 0, str );
5438 msiobj_release( &row->hdr );
5439 MSI_RecordSetInteger( rec, 1, error );
5441 va_start( va, count );
5442 for (i = 0; i < count; i++)
5444 str = va_arg( va, const WCHAR *);
5445 MSI_RecordSetStringW( rec, i + 2, str );
5447 va_end( va );
5449 MSI_FormatRecordW( package, rec, NULL, &size );
5450 size++;
5451 data = msi_alloc( size * sizeof(WCHAR) );
5452 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5453 else data[0] = 0;
5454 msiobj_release( &rec->hdr );
5455 return data;
5458 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5460 DWORD attrib;
5461 UINT rc;
5464 * We are currently doing what should be done here in the top level Install
5465 * however for Administrative and uninstalls this step will be needed
5467 if (!package->PackagePath)
5468 return ERROR_SUCCESS;
5470 msi_set_sourcedir_props(package, TRUE);
5472 attrib = GetFileAttributesW(package->db->path);
5473 if (attrib == INVALID_FILE_ATTRIBUTES)
5475 LPWSTR prompt, msg;
5476 DWORD size = 0;
5478 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5479 package->Context, MSICODE_PRODUCT,
5480 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5481 if (rc == ERROR_MORE_DATA)
5483 prompt = msi_alloc(size * sizeof(WCHAR));
5484 MsiSourceListGetInfoW(package->ProductCode, NULL,
5485 package->Context, MSICODE_PRODUCT,
5486 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5488 else
5489 prompt = strdupW(package->db->path);
5491 msg = msi_build_error_string(package, 1302, 1, prompt);
5492 msi_free(prompt);
5493 while(attrib == INVALID_FILE_ATTRIBUTES)
5495 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5496 if (rc == IDCANCEL)
5498 msi_free(msg);
5499 return ERROR_INSTALL_USEREXIT;
5501 attrib = GetFileAttributesW(package->db->path);
5503 msi_free(msg);
5504 rc = ERROR_SUCCESS;
5506 else
5507 return ERROR_SUCCESS;
5509 return rc;
5512 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5514 HKEY hkey = 0;
5515 LPWSTR buffer, productid = NULL;
5516 UINT i, rc = ERROR_SUCCESS;
5517 MSIRECORD *uirow;
5519 static const WCHAR szPropKeys[][80] =
5521 {'P','r','o','d','u','c','t','I','D',0},
5522 {'U','S','E','R','N','A','M','E',0},
5523 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5524 {0},
5527 static const WCHAR szRegKeys[][80] =
5529 {'P','r','o','d','u','c','t','I','D',0},
5530 {'R','e','g','O','w','n','e','r',0},
5531 {'R','e','g','C','o','m','p','a','n','y',0},
5532 {0},
5535 if (msi_check_unpublish(package))
5537 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5538 goto end;
5541 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5542 if (!productid)
5543 goto end;
5545 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5546 NULL, &hkey, TRUE);
5547 if (rc != ERROR_SUCCESS)
5548 goto end;
5550 for( i = 0; szPropKeys[i][0]; i++ )
5552 buffer = msi_dup_property( package->db, szPropKeys[i] );
5553 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5554 msi_free( buffer );
5557 end:
5558 uirow = MSI_CreateRecord( 1 );
5559 MSI_RecordSetStringW( uirow, 1, productid );
5560 msi_ui_actiondata( package, szRegisterUser, uirow );
5561 msiobj_release( &uirow->hdr );
5563 msi_free(productid);
5564 RegCloseKey(hkey);
5565 return rc;
5569 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5571 UINT rc;
5573 package->script->InWhatSequence |= SEQUENCE_EXEC;
5574 rc = ACTION_ProcessExecSequence(package,FALSE);
5575 return rc;
5578 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5580 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5581 WCHAR productid_85[21], component_85[21], *ret;
5582 GUID clsid;
5583 DWORD sz;
5585 /* > is used if there is a component GUID and < if not. */
5587 productid_85[0] = 0;
5588 component_85[0] = 0;
5589 CLSIDFromString( package->ProductCode, &clsid );
5591 encode_base85_guid( &clsid, productid_85 );
5592 if (component)
5594 CLSIDFromString( component->ComponentId, &clsid );
5595 encode_base85_guid( &clsid, component_85 );
5598 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5599 debugstr_w(component_85));
5601 sz = 20 + strlenW( feature ) + 20 + 3;
5602 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5603 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5604 return ret;
5607 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5609 MSIPACKAGE *package = param;
5610 LPCWSTR compgroupid, component, feature, qualifier, text;
5611 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5612 HKEY hkey = NULL;
5613 UINT rc;
5614 MSICOMPONENT *comp;
5615 MSIFEATURE *feat;
5616 DWORD sz;
5617 MSIRECORD *uirow;
5618 int len;
5620 feature = MSI_RecordGetString(rec, 5);
5621 feat = msi_get_loaded_feature(package, feature);
5622 if (!feat)
5623 return ERROR_SUCCESS;
5625 feat->Action = msi_get_feature_action( package, feat );
5626 if (feat->Action != INSTALLSTATE_LOCAL &&
5627 feat->Action != INSTALLSTATE_SOURCE &&
5628 feat->Action != INSTALLSTATE_ADVERTISED)
5630 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5631 return ERROR_SUCCESS;
5634 component = MSI_RecordGetString(rec, 3);
5635 comp = msi_get_loaded_component(package, component);
5636 if (!comp)
5637 return ERROR_SUCCESS;
5639 compgroupid = MSI_RecordGetString(rec,1);
5640 qualifier = MSI_RecordGetString(rec,2);
5642 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5643 if (rc != ERROR_SUCCESS)
5644 goto end;
5646 advertise = msi_create_component_advertise_string( package, comp, feature );
5647 text = MSI_RecordGetString( rec, 4 );
5648 if (text)
5650 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5651 strcpyW( p, advertise );
5652 strcatW( p, text );
5653 msi_free( advertise );
5654 advertise = p;
5656 existing = msi_reg_get_val_str( hkey, qualifier );
5658 sz = strlenW( advertise ) + 1;
5659 if (existing)
5661 for (p = existing; *p; p += len)
5663 len = strlenW( p ) + 1;
5664 if (strcmpW( advertise, p )) sz += len;
5667 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5669 rc = ERROR_OUTOFMEMORY;
5670 goto end;
5672 q = output;
5673 if (existing)
5675 for (p = existing; *p; p += len)
5677 len = strlenW( p ) + 1;
5678 if (strcmpW( advertise, p ))
5680 memcpy( q, p, len * sizeof(WCHAR) );
5681 q += len;
5685 strcpyW( q, advertise );
5686 q[strlenW( q ) + 1] = 0;
5688 msi_reg_set_val_multi_str( hkey, qualifier, output );
5690 end:
5691 RegCloseKey(hkey);
5692 msi_free( output );
5693 msi_free( advertise );
5694 msi_free( existing );
5696 /* the UI chunk */
5697 uirow = MSI_CreateRecord( 2 );
5698 MSI_RecordSetStringW( uirow, 1, compgroupid );
5699 MSI_RecordSetStringW( uirow, 2, qualifier);
5700 msi_ui_actiondata( package, szPublishComponents, uirow );
5701 msiobj_release( &uirow->hdr );
5702 /* FIXME: call ui_progress? */
5704 return rc;
5708 * At present I am ignorning the advertised components part of this and only
5709 * focusing on the qualified component sets
5711 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5713 static const WCHAR query[] = {
5714 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5715 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5716 MSIQUERY *view;
5717 UINT rc;
5719 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5720 if (rc != ERROR_SUCCESS)
5721 return ERROR_SUCCESS;
5723 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5724 msiobj_release(&view->hdr);
5725 return rc;
5728 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5730 static const WCHAR szInstallerComponents[] = {
5731 'S','o','f','t','w','a','r','e','\\',
5732 'M','i','c','r','o','s','o','f','t','\\',
5733 'I','n','s','t','a','l','l','e','r','\\',
5734 'C','o','m','p','o','n','e','n','t','s','\\',0};
5736 MSIPACKAGE *package = param;
5737 LPCWSTR compgroupid, component, feature, qualifier;
5738 MSICOMPONENT *comp;
5739 MSIFEATURE *feat;
5740 MSIRECORD *uirow;
5741 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5742 LONG res;
5744 feature = MSI_RecordGetString( rec, 5 );
5745 feat = msi_get_loaded_feature( package, feature );
5746 if (!feat)
5747 return ERROR_SUCCESS;
5749 feat->Action = msi_get_feature_action( package, feat );
5750 if (feat->Action != INSTALLSTATE_ABSENT)
5752 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5753 return ERROR_SUCCESS;
5756 component = MSI_RecordGetString( rec, 3 );
5757 comp = msi_get_loaded_component( package, component );
5758 if (!comp)
5759 return ERROR_SUCCESS;
5761 compgroupid = MSI_RecordGetString( rec, 1 );
5762 qualifier = MSI_RecordGetString( rec, 2 );
5764 squash_guid( compgroupid, squashed );
5765 strcpyW( keypath, szInstallerComponents );
5766 strcatW( keypath, squashed );
5768 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5769 if (res != ERROR_SUCCESS)
5771 WARN("Unable to delete component key %d\n", res);
5774 uirow = MSI_CreateRecord( 2 );
5775 MSI_RecordSetStringW( uirow, 1, compgroupid );
5776 MSI_RecordSetStringW( uirow, 2, qualifier );
5777 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5778 msiobj_release( &uirow->hdr );
5780 return ERROR_SUCCESS;
5783 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5785 static const WCHAR query[] = {
5786 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5787 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5788 MSIQUERY *view;
5789 UINT rc;
5791 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5792 if (rc != ERROR_SUCCESS)
5793 return ERROR_SUCCESS;
5795 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5796 msiobj_release( &view->hdr );
5797 return rc;
5800 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5802 static const WCHAR query[] =
5803 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5804 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5805 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5806 MSIPACKAGE *package = param;
5807 MSICOMPONENT *component;
5808 MSIRECORD *row;
5809 MSIFILE *file;
5810 SC_HANDLE hscm = NULL, service = NULL;
5811 LPCWSTR comp, key;
5812 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5813 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5814 DWORD serv_type, start_type, err_control;
5815 SERVICE_DESCRIPTIONW sd = {NULL};
5816 UINT ret = ERROR_SUCCESS;
5818 comp = MSI_RecordGetString( rec, 12 );
5819 component = msi_get_loaded_component( package, comp );
5820 if (!component)
5822 WARN("service component not found\n");
5823 goto done;
5825 component->Action = msi_get_component_action( package, component );
5826 if (component->Action != INSTALLSTATE_LOCAL)
5828 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5829 goto done;
5831 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5832 if (!hscm)
5834 ERR("Failed to open the SC Manager!\n");
5835 goto done;
5838 start_type = MSI_RecordGetInteger(rec, 5);
5839 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5840 goto done;
5842 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5843 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5844 serv_type = MSI_RecordGetInteger(rec, 4);
5845 err_control = MSI_RecordGetInteger(rec, 6);
5846 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5847 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5848 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5849 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5850 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5851 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5853 /* fetch the service path */
5854 row = MSI_QueryGetRecord(package->db, query, comp);
5855 if (!row)
5857 ERR("Query failed\n");
5858 goto done;
5860 if (!(key = MSI_RecordGetString(row, 6)))
5862 msiobj_release(&row->hdr);
5863 goto done;
5865 file = msi_get_loaded_file(package, key);
5866 msiobj_release(&row->hdr);
5867 if (!file)
5869 ERR("Failed to load the service file\n");
5870 goto done;
5873 if (!args || !args[0]) image_path = file->TargetPath;
5874 else
5876 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5877 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5879 ret = ERROR_OUTOFMEMORY;
5880 goto done;
5883 strcpyW(image_path, file->TargetPath);
5884 strcatW(image_path, szSpace);
5885 strcatW(image_path, args);
5887 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5888 start_type, err_control, image_path, load_order,
5889 NULL, depends, serv_name, pass);
5891 if (!service)
5893 if (GetLastError() != ERROR_SERVICE_EXISTS)
5894 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5896 else if (sd.lpDescription)
5898 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5899 WARN("failed to set service description %u\n", GetLastError());
5902 if (image_path != file->TargetPath) msi_free(image_path);
5903 done:
5904 if (service) CloseServiceHandle(service);
5905 if (hscm) CloseServiceHandle(hscm);
5906 msi_free(name);
5907 msi_free(disp);
5908 msi_free(sd.lpDescription);
5909 msi_free(load_order);
5910 msi_free(serv_name);
5911 msi_free(pass);
5912 msi_free(depends);
5913 msi_free(args);
5915 return ret;
5918 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5920 static const WCHAR query[] = {
5921 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5922 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5923 MSIQUERY *view;
5924 UINT rc;
5926 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5927 if (rc != ERROR_SUCCESS)
5928 return ERROR_SUCCESS;
5930 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5931 msiobj_release(&view->hdr);
5932 return rc;
5935 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5936 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5938 LPCWSTR *vector, *temp_vector;
5939 LPWSTR p, q;
5940 DWORD sep_len;
5942 static const WCHAR separator[] = {'[','~',']',0};
5944 *numargs = 0;
5945 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5947 if (!args)
5948 return NULL;
5950 vector = msi_alloc(sizeof(LPWSTR));
5951 if (!vector)
5952 return NULL;
5954 p = args;
5957 (*numargs)++;
5958 vector[*numargs - 1] = p;
5960 if ((q = strstrW(p, separator)))
5962 *q = '\0';
5964 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5965 if (!temp_vector)
5967 msi_free(vector);
5968 return NULL;
5970 vector = temp_vector;
5972 p = q + sep_len;
5974 } while (q);
5976 return vector;
5979 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5981 MSIPACKAGE *package = param;
5982 MSICOMPONENT *comp;
5983 MSIRECORD *uirow;
5984 SC_HANDLE scm = NULL, service = NULL;
5985 LPCWSTR component, *vector = NULL;
5986 LPWSTR name, args, display_name = NULL;
5987 DWORD event, numargs, len, wait, dummy;
5988 UINT r = ERROR_FUNCTION_FAILED;
5989 SERVICE_STATUS_PROCESS status;
5990 ULONGLONG start_time;
5992 component = MSI_RecordGetString(rec, 6);
5993 comp = msi_get_loaded_component(package, component);
5994 if (!comp)
5995 return ERROR_SUCCESS;
5997 event = MSI_RecordGetInteger( rec, 3 );
5998 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6000 comp->Action = msi_get_component_action( package, comp );
6001 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
6002 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
6004 TRACE("not starting %s\n", debugstr_w(name));
6005 msi_free( name );
6006 return ERROR_SUCCESS;
6009 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
6010 wait = MSI_RecordGetInteger(rec, 5);
6012 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
6013 if (!scm)
6015 ERR("Failed to open the service control manager\n");
6016 goto done;
6019 len = 0;
6020 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6021 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6023 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6024 GetServiceDisplayNameW( scm, name, display_name, &len );
6027 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
6028 if (!service)
6030 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6031 goto done;
6034 vector = msi_service_args_to_vector(args, &numargs);
6036 if (!StartServiceW(service, numargs, vector) &&
6037 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6039 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6040 goto done;
6043 r = ERROR_SUCCESS;
6044 if (wait)
6046 /* wait for at most 30 seconds for the service to be up and running */
6047 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6048 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6050 TRACE("failed to query service status (%u)\n", GetLastError());
6051 goto done;
6053 start_time = GetTickCount64();
6054 while (status.dwCurrentState == SERVICE_START_PENDING)
6056 if (GetTickCount64() - start_time > 30000) break;
6057 Sleep(1000);
6058 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6059 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6061 TRACE("failed to query service status (%u)\n", GetLastError());
6062 goto done;
6065 if (status.dwCurrentState != SERVICE_RUNNING)
6067 WARN("service failed to start %u\n", status.dwCurrentState);
6068 r = ERROR_FUNCTION_FAILED;
6072 done:
6073 uirow = MSI_CreateRecord( 2 );
6074 MSI_RecordSetStringW( uirow, 1, display_name );
6075 MSI_RecordSetStringW( uirow, 2, name );
6076 msi_ui_actiondata( package, szStartServices, uirow );
6077 msiobj_release( &uirow->hdr );
6079 if (service) CloseServiceHandle(service);
6080 if (scm) CloseServiceHandle(scm);
6082 msi_free(name);
6083 msi_free(args);
6084 msi_free(vector);
6085 msi_free(display_name);
6086 return r;
6089 static UINT ACTION_StartServices( MSIPACKAGE *package )
6091 static const WCHAR query[] = {
6092 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6093 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6094 MSIQUERY *view;
6095 UINT rc;
6097 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6098 if (rc != ERROR_SUCCESS)
6099 return ERROR_SUCCESS;
6101 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6102 msiobj_release(&view->hdr);
6103 return rc;
6106 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6108 DWORD i, needed, count;
6109 ENUM_SERVICE_STATUSW *dependencies;
6110 SERVICE_STATUS ss;
6111 SC_HANDLE depserv;
6112 BOOL stopped, ret = FALSE;
6114 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6115 0, &needed, &count))
6116 return TRUE;
6118 if (GetLastError() != ERROR_MORE_DATA)
6119 return FALSE;
6121 dependencies = msi_alloc(needed);
6122 if (!dependencies)
6123 return FALSE;
6125 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6126 needed, &needed, &count))
6127 goto done;
6129 for (i = 0; i < count; i++)
6131 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6132 SERVICE_STOP | SERVICE_QUERY_STATUS);
6133 if (!depserv)
6134 goto done;
6136 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6137 CloseServiceHandle(depserv);
6138 if (!stopped)
6139 goto done;
6142 ret = TRUE;
6144 done:
6145 msi_free(dependencies);
6146 return ret;
6149 static UINT stop_service( LPCWSTR name )
6151 SC_HANDLE scm = NULL, service = NULL;
6152 SERVICE_STATUS status;
6153 SERVICE_STATUS_PROCESS ssp;
6154 DWORD needed;
6156 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6157 if (!scm)
6159 WARN("Failed to open the SCM: %d\n", GetLastError());
6160 goto done;
6163 service = OpenServiceW(scm, name,
6164 SERVICE_STOP |
6165 SERVICE_QUERY_STATUS |
6166 SERVICE_ENUMERATE_DEPENDENTS);
6167 if (!service)
6169 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6170 goto done;
6173 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6174 sizeof(SERVICE_STATUS_PROCESS), &needed))
6176 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6177 goto done;
6180 if (ssp.dwCurrentState == SERVICE_STOPPED)
6181 goto done;
6183 stop_service_dependents(scm, service);
6185 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6186 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6188 done:
6189 if (service) CloseServiceHandle(service);
6190 if (scm) CloseServiceHandle(scm);
6192 return ERROR_SUCCESS;
6195 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6197 MSIPACKAGE *package = param;
6198 MSICOMPONENT *comp;
6199 MSIRECORD *uirow;
6200 LPCWSTR component;
6201 WCHAR *name, *display_name = NULL;
6202 DWORD event, len;
6203 SC_HANDLE scm;
6205 component = MSI_RecordGetString( rec, 6 );
6206 comp = msi_get_loaded_component( package, component );
6207 if (!comp)
6208 return ERROR_SUCCESS;
6210 event = MSI_RecordGetInteger( rec, 3 );
6211 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6213 comp->Action = msi_get_component_action( package, comp );
6214 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6215 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6217 TRACE("not stopping %s\n", debugstr_w(name));
6218 msi_free( name );
6219 return ERROR_SUCCESS;
6222 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6223 if (!scm)
6225 ERR("Failed to open the service control manager\n");
6226 goto done;
6229 len = 0;
6230 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6231 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6233 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6234 GetServiceDisplayNameW( scm, name, display_name, &len );
6236 CloseServiceHandle( scm );
6238 stop_service( name );
6240 done:
6241 uirow = MSI_CreateRecord( 2 );
6242 MSI_RecordSetStringW( uirow, 1, display_name );
6243 MSI_RecordSetStringW( uirow, 2, name );
6244 msi_ui_actiondata( package, szStopServices, uirow );
6245 msiobj_release( &uirow->hdr );
6247 msi_free( name );
6248 msi_free( display_name );
6249 return ERROR_SUCCESS;
6252 static UINT ACTION_StopServices( MSIPACKAGE *package )
6254 static const WCHAR query[] = {
6255 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6256 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6257 MSIQUERY *view;
6258 UINT rc;
6260 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6261 if (rc != ERROR_SUCCESS)
6262 return ERROR_SUCCESS;
6264 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6265 msiobj_release(&view->hdr);
6266 return rc;
6269 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6271 MSIPACKAGE *package = param;
6272 MSICOMPONENT *comp;
6273 MSIRECORD *uirow;
6274 LPWSTR name = NULL, display_name = NULL;
6275 DWORD event, len;
6276 SC_HANDLE scm = NULL, service = NULL;
6278 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6279 if (!comp)
6280 return ERROR_SUCCESS;
6282 event = MSI_RecordGetInteger( rec, 3 );
6283 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6285 comp->Action = msi_get_component_action( package, comp );
6286 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6287 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6289 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6290 msi_free( name );
6291 return ERROR_SUCCESS;
6293 stop_service( name );
6295 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6296 if (!scm)
6298 WARN("Failed to open the SCM: %d\n", GetLastError());
6299 goto done;
6302 len = 0;
6303 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6304 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6306 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6307 GetServiceDisplayNameW( scm, name, display_name, &len );
6310 service = OpenServiceW( scm, name, DELETE );
6311 if (!service)
6313 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6314 goto done;
6317 if (!DeleteService( service ))
6318 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6320 done:
6321 uirow = MSI_CreateRecord( 2 );
6322 MSI_RecordSetStringW( uirow, 1, display_name );
6323 MSI_RecordSetStringW( uirow, 2, name );
6324 msi_ui_actiondata( package, szDeleteServices, uirow );
6325 msiobj_release( &uirow->hdr );
6327 if (service) CloseServiceHandle( service );
6328 if (scm) CloseServiceHandle( scm );
6329 msi_free( name );
6330 msi_free( display_name );
6332 return ERROR_SUCCESS;
6335 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6337 static const WCHAR query[] = {
6338 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6339 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6340 MSIQUERY *view;
6341 UINT rc;
6343 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6344 if (rc != ERROR_SUCCESS)
6345 return ERROR_SUCCESS;
6347 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6348 msiobj_release( &view->hdr );
6349 return rc;
6352 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6354 MSIPACKAGE *package = param;
6355 LPWSTR driver, driver_path, ptr;
6356 WCHAR outpath[MAX_PATH];
6357 MSIFILE *driver_file = NULL, *setup_file = NULL;
6358 MSICOMPONENT *comp;
6359 MSIRECORD *uirow;
6360 LPCWSTR desc, file_key, component;
6361 DWORD len, usage;
6362 UINT r = ERROR_SUCCESS;
6364 static const WCHAR driver_fmt[] = {
6365 'D','r','i','v','e','r','=','%','s',0};
6366 static const WCHAR setup_fmt[] = {
6367 'S','e','t','u','p','=','%','s',0};
6368 static const WCHAR usage_fmt[] = {
6369 'F','i','l','e','U','s','a','g','e','=','1',0};
6371 component = MSI_RecordGetString( rec, 2 );
6372 comp = msi_get_loaded_component( package, component );
6373 if (!comp)
6374 return ERROR_SUCCESS;
6376 comp->Action = msi_get_component_action( package, comp );
6377 if (comp->Action != INSTALLSTATE_LOCAL)
6379 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6380 return ERROR_SUCCESS;
6382 desc = MSI_RecordGetString(rec, 3);
6384 file_key = MSI_RecordGetString( rec, 4 );
6385 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6387 file_key = MSI_RecordGetString( rec, 5 );
6388 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6390 if (!driver_file)
6392 ERR("ODBC Driver entry not found!\n");
6393 return ERROR_FUNCTION_FAILED;
6396 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6397 if (setup_file)
6398 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6399 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6401 driver = msi_alloc(len * sizeof(WCHAR));
6402 if (!driver)
6403 return ERROR_OUTOFMEMORY;
6405 ptr = driver;
6406 lstrcpyW(ptr, desc);
6407 ptr += lstrlenW(ptr) + 1;
6409 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6410 ptr += len + 1;
6412 if (setup_file)
6414 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6415 ptr += len + 1;
6418 lstrcpyW(ptr, usage_fmt);
6419 ptr += lstrlenW(ptr) + 1;
6420 *ptr = '\0';
6422 if (!driver_file->TargetPath)
6424 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6425 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6427 driver_path = strdupW(driver_file->TargetPath);
6428 ptr = strrchrW(driver_path, '\\');
6429 if (ptr) *ptr = '\0';
6431 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6432 NULL, ODBC_INSTALL_COMPLETE, &usage))
6434 ERR("Failed to install SQL driver!\n");
6435 r = ERROR_FUNCTION_FAILED;
6438 uirow = MSI_CreateRecord( 5 );
6439 MSI_RecordSetStringW( uirow, 1, desc );
6440 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6441 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6442 msi_ui_actiondata( package, szInstallODBC, uirow );
6443 msiobj_release( &uirow->hdr );
6445 msi_free(driver);
6446 msi_free(driver_path);
6448 return r;
6451 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6453 MSIPACKAGE *package = param;
6454 LPWSTR translator, translator_path, ptr;
6455 WCHAR outpath[MAX_PATH];
6456 MSIFILE *translator_file = NULL, *setup_file = NULL;
6457 MSICOMPONENT *comp;
6458 MSIRECORD *uirow;
6459 LPCWSTR desc, file_key, component;
6460 DWORD len, usage;
6461 UINT r = ERROR_SUCCESS;
6463 static const WCHAR translator_fmt[] = {
6464 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6465 static const WCHAR setup_fmt[] = {
6466 'S','e','t','u','p','=','%','s',0};
6468 component = MSI_RecordGetString( rec, 2 );
6469 comp = msi_get_loaded_component( package, component );
6470 if (!comp)
6471 return ERROR_SUCCESS;
6473 comp->Action = msi_get_component_action( package, comp );
6474 if (comp->Action != INSTALLSTATE_LOCAL)
6476 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6477 return ERROR_SUCCESS;
6479 desc = MSI_RecordGetString(rec, 3);
6481 file_key = MSI_RecordGetString( rec, 4 );
6482 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6484 file_key = MSI_RecordGetString( rec, 5 );
6485 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6487 if (!translator_file)
6489 ERR("ODBC Translator entry not found!\n");
6490 return ERROR_FUNCTION_FAILED;
6493 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6494 if (setup_file)
6495 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6497 translator = msi_alloc(len * sizeof(WCHAR));
6498 if (!translator)
6499 return ERROR_OUTOFMEMORY;
6501 ptr = translator;
6502 lstrcpyW(ptr, desc);
6503 ptr += lstrlenW(ptr) + 1;
6505 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6506 ptr += len + 1;
6508 if (setup_file)
6510 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6511 ptr += len + 1;
6513 *ptr = '\0';
6515 translator_path = strdupW(translator_file->TargetPath);
6516 ptr = strrchrW(translator_path, '\\');
6517 if (ptr) *ptr = '\0';
6519 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6520 NULL, ODBC_INSTALL_COMPLETE, &usage))
6522 ERR("Failed to install SQL translator!\n");
6523 r = ERROR_FUNCTION_FAILED;
6526 uirow = MSI_CreateRecord( 5 );
6527 MSI_RecordSetStringW( uirow, 1, desc );
6528 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6529 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6530 msi_ui_actiondata( package, szInstallODBC, uirow );
6531 msiobj_release( &uirow->hdr );
6533 msi_free(translator);
6534 msi_free(translator_path);
6536 return r;
6539 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6541 MSIPACKAGE *package = param;
6542 MSICOMPONENT *comp;
6543 LPWSTR attrs;
6544 LPCWSTR desc, driver, component;
6545 WORD request = ODBC_ADD_SYS_DSN;
6546 INT registration;
6547 DWORD len;
6548 UINT r = ERROR_SUCCESS;
6549 MSIRECORD *uirow;
6551 static const WCHAR attrs_fmt[] = {
6552 'D','S','N','=','%','s',0 };
6554 component = MSI_RecordGetString( rec, 2 );
6555 comp = msi_get_loaded_component( package, component );
6556 if (!comp)
6557 return ERROR_SUCCESS;
6559 comp->Action = msi_get_component_action( package, comp );
6560 if (comp->Action != INSTALLSTATE_LOCAL)
6562 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6563 return ERROR_SUCCESS;
6566 desc = MSI_RecordGetString(rec, 3);
6567 driver = MSI_RecordGetString(rec, 4);
6568 registration = MSI_RecordGetInteger(rec, 5);
6570 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6571 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6573 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6574 attrs = msi_alloc(len * sizeof(WCHAR));
6575 if (!attrs)
6576 return ERROR_OUTOFMEMORY;
6578 len = sprintfW(attrs, attrs_fmt, desc);
6579 attrs[len + 1] = 0;
6581 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6583 ERR("Failed to install SQL data source!\n");
6584 r = ERROR_FUNCTION_FAILED;
6587 uirow = MSI_CreateRecord( 5 );
6588 MSI_RecordSetStringW( uirow, 1, desc );
6589 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6590 MSI_RecordSetInteger( uirow, 3, request );
6591 msi_ui_actiondata( package, szInstallODBC, uirow );
6592 msiobj_release( &uirow->hdr );
6594 msi_free(attrs);
6596 return r;
6599 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6601 static const WCHAR driver_query[] = {
6602 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6603 'O','D','B','C','D','r','i','v','e','r',0};
6604 static const WCHAR translator_query[] = {
6605 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6606 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6607 static const WCHAR source_query[] = {
6608 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6609 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6610 MSIQUERY *view;
6611 UINT rc;
6613 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6614 if (rc == ERROR_SUCCESS)
6616 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6617 msiobj_release(&view->hdr);
6618 if (rc != ERROR_SUCCESS)
6619 return rc;
6621 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6622 if (rc == ERROR_SUCCESS)
6624 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6625 msiobj_release(&view->hdr);
6626 if (rc != ERROR_SUCCESS)
6627 return rc;
6629 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6630 if (rc == ERROR_SUCCESS)
6632 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6633 msiobj_release(&view->hdr);
6634 if (rc != ERROR_SUCCESS)
6635 return rc;
6637 return ERROR_SUCCESS;
6640 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6642 MSIPACKAGE *package = param;
6643 MSICOMPONENT *comp;
6644 MSIRECORD *uirow;
6645 DWORD usage;
6646 LPCWSTR desc, component;
6648 component = MSI_RecordGetString( rec, 2 );
6649 comp = msi_get_loaded_component( package, component );
6650 if (!comp)
6651 return ERROR_SUCCESS;
6653 comp->Action = msi_get_component_action( package, comp );
6654 if (comp->Action != INSTALLSTATE_ABSENT)
6656 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6657 return ERROR_SUCCESS;
6660 desc = MSI_RecordGetString( rec, 3 );
6661 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6663 WARN("Failed to remove ODBC driver\n");
6665 else if (!usage)
6667 FIXME("Usage count reached 0\n");
6670 uirow = MSI_CreateRecord( 2 );
6671 MSI_RecordSetStringW( uirow, 1, desc );
6672 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6673 msi_ui_actiondata( package, szRemoveODBC, uirow );
6674 msiobj_release( &uirow->hdr );
6676 return ERROR_SUCCESS;
6679 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6681 MSIPACKAGE *package = param;
6682 MSICOMPONENT *comp;
6683 MSIRECORD *uirow;
6684 DWORD usage;
6685 LPCWSTR desc, component;
6687 component = MSI_RecordGetString( rec, 2 );
6688 comp = msi_get_loaded_component( package, component );
6689 if (!comp)
6690 return ERROR_SUCCESS;
6692 comp->Action = msi_get_component_action( package, comp );
6693 if (comp->Action != INSTALLSTATE_ABSENT)
6695 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6696 return ERROR_SUCCESS;
6699 desc = MSI_RecordGetString( rec, 3 );
6700 if (!SQLRemoveTranslatorW( desc, &usage ))
6702 WARN("Failed to remove ODBC translator\n");
6704 else if (!usage)
6706 FIXME("Usage count reached 0\n");
6709 uirow = MSI_CreateRecord( 2 );
6710 MSI_RecordSetStringW( uirow, 1, desc );
6711 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6712 msi_ui_actiondata( package, szRemoveODBC, uirow );
6713 msiobj_release( &uirow->hdr );
6715 return ERROR_SUCCESS;
6718 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6720 MSIPACKAGE *package = param;
6721 MSICOMPONENT *comp;
6722 MSIRECORD *uirow;
6723 LPWSTR attrs;
6724 LPCWSTR desc, driver, component;
6725 WORD request = ODBC_REMOVE_SYS_DSN;
6726 INT registration;
6727 DWORD len;
6729 static const WCHAR attrs_fmt[] = {
6730 'D','S','N','=','%','s',0 };
6732 component = MSI_RecordGetString( rec, 2 );
6733 comp = msi_get_loaded_component( package, component );
6734 if (!comp)
6735 return ERROR_SUCCESS;
6737 comp->Action = msi_get_component_action( package, comp );
6738 if (comp->Action != INSTALLSTATE_ABSENT)
6740 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6741 return ERROR_SUCCESS;
6744 desc = MSI_RecordGetString( rec, 3 );
6745 driver = MSI_RecordGetString( rec, 4 );
6746 registration = MSI_RecordGetInteger( rec, 5 );
6748 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6749 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6751 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6752 attrs = msi_alloc( len * sizeof(WCHAR) );
6753 if (!attrs)
6754 return ERROR_OUTOFMEMORY;
6756 FIXME("Use ODBCSourceAttribute table\n");
6758 len = sprintfW( attrs, attrs_fmt, desc );
6759 attrs[len + 1] = 0;
6761 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6763 WARN("Failed to remove ODBC data source\n");
6765 msi_free( attrs );
6767 uirow = MSI_CreateRecord( 3 );
6768 MSI_RecordSetStringW( uirow, 1, desc );
6769 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6770 MSI_RecordSetInteger( uirow, 3, request );
6771 msi_ui_actiondata( package, szRemoveODBC, uirow );
6772 msiobj_release( &uirow->hdr );
6774 return ERROR_SUCCESS;
6777 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6779 static const WCHAR driver_query[] = {
6780 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6781 'O','D','B','C','D','r','i','v','e','r',0};
6782 static const WCHAR translator_query[] = {
6783 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6784 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6785 static const WCHAR source_query[] = {
6786 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6787 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6788 MSIQUERY *view;
6789 UINT rc;
6791 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6792 if (rc == ERROR_SUCCESS)
6794 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6795 msiobj_release( &view->hdr );
6796 if (rc != ERROR_SUCCESS)
6797 return rc;
6799 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6800 if (rc == ERROR_SUCCESS)
6802 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6803 msiobj_release( &view->hdr );
6804 if (rc != ERROR_SUCCESS)
6805 return rc;
6807 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6808 if (rc == ERROR_SUCCESS)
6810 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6811 msiobj_release( &view->hdr );
6812 if (rc != ERROR_SUCCESS)
6813 return rc;
6815 return ERROR_SUCCESS;
6818 #define ENV_ACT_SETALWAYS 0x1
6819 #define ENV_ACT_SETABSENT 0x2
6820 #define ENV_ACT_REMOVE 0x4
6821 #define ENV_ACT_REMOVEMATCH 0x8
6823 #define ENV_MOD_MACHINE 0x20000000
6824 #define ENV_MOD_APPEND 0x40000000
6825 #define ENV_MOD_PREFIX 0x80000000
6826 #define ENV_MOD_MASK 0xC0000000
6828 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6830 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6832 LPCWSTR cptr = *name;
6834 static const WCHAR prefix[] = {'[','~',']',0};
6835 static const int prefix_len = 3;
6837 *flags = 0;
6838 while (*cptr)
6840 if (*cptr == '=')
6841 *flags |= ENV_ACT_SETALWAYS;
6842 else if (*cptr == '+')
6843 *flags |= ENV_ACT_SETABSENT;
6844 else if (*cptr == '-')
6845 *flags |= ENV_ACT_REMOVE;
6846 else if (*cptr == '!')
6847 *flags |= ENV_ACT_REMOVEMATCH;
6848 else if (*cptr == '*')
6849 *flags |= ENV_MOD_MACHINE;
6850 else
6851 break;
6853 cptr++;
6854 (*name)++;
6857 if (!*cptr)
6859 ERR("Missing environment variable\n");
6860 return ERROR_FUNCTION_FAILED;
6863 if (*value)
6865 LPCWSTR ptr = *value;
6866 if (!strncmpW(ptr, prefix, prefix_len))
6868 if (ptr[prefix_len] == szSemiColon[0])
6870 *flags |= ENV_MOD_APPEND;
6871 *value += lstrlenW(prefix);
6873 else
6875 *value = NULL;
6878 else if (lstrlenW(*value) >= prefix_len)
6880 ptr += lstrlenW(ptr) - prefix_len;
6881 if (!strcmpW( ptr, prefix ))
6883 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6885 *flags |= ENV_MOD_PREFIX;
6886 /* the "[~]" will be removed by deformat_string */;
6888 else
6890 *value = NULL;
6896 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6897 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6898 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6899 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6901 ERR("Invalid flags: %08x\n", *flags);
6902 return ERROR_FUNCTION_FAILED;
6905 if (!*flags)
6906 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6908 return ERROR_SUCCESS;
6911 static UINT open_env_key( DWORD flags, HKEY *key )
6913 static const WCHAR user_env[] =
6914 {'E','n','v','i','r','o','n','m','e','n','t',0};
6915 static const WCHAR machine_env[] =
6916 {'S','y','s','t','e','m','\\',
6917 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6918 'C','o','n','t','r','o','l','\\',
6919 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6920 'E','n','v','i','r','o','n','m','e','n','t',0};
6921 const WCHAR *env;
6922 HKEY root;
6923 LONG res;
6925 if (flags & ENV_MOD_MACHINE)
6927 env = machine_env;
6928 root = HKEY_LOCAL_MACHINE;
6930 else
6932 env = user_env;
6933 root = HKEY_CURRENT_USER;
6936 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6937 if (res != ERROR_SUCCESS)
6939 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6940 return ERROR_FUNCTION_FAILED;
6943 return ERROR_SUCCESS;
6946 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6948 MSIPACKAGE *package = param;
6949 LPCWSTR name, value, component;
6950 WCHAR *data = NULL, *newval = NULL, *deformatted = NULL, *p, *q;
6951 DWORD flags, type, size, len, len_value = 0;
6952 UINT res;
6953 HKEY env = NULL;
6954 MSICOMPONENT *comp;
6955 MSIRECORD *uirow;
6956 int action = 0, found = 0;
6958 component = MSI_RecordGetString(rec, 4);
6959 comp = msi_get_loaded_component(package, component);
6960 if (!comp)
6961 return ERROR_SUCCESS;
6963 comp->Action = msi_get_component_action( package, comp );
6964 if (comp->Action != INSTALLSTATE_LOCAL)
6966 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6967 return ERROR_SUCCESS;
6969 name = MSI_RecordGetString(rec, 2);
6970 value = MSI_RecordGetString(rec, 3);
6972 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6974 res = env_parse_flags(&name, &value, &flags);
6975 if (res != ERROR_SUCCESS || !value)
6976 goto done;
6978 if (value && !deformat_string(package, value, &deformatted))
6980 res = ERROR_OUTOFMEMORY;
6981 goto done;
6984 if ((value = deformatted))
6986 if (flags & ENV_MOD_PREFIX)
6988 p = strrchrW( value, ';' );
6989 len_value = p - value;
6991 else if (flags & ENV_MOD_APPEND)
6993 value = strchrW( value, ';' ) + 1;
6994 len_value = strlenW( value );
6996 else len_value = strlenW( value );
6999 res = open_env_key( flags, &env );
7000 if (res != ERROR_SUCCESS)
7001 goto done;
7003 if (flags & ENV_MOD_MACHINE)
7004 action |= 0x20000000;
7006 size = 0;
7007 type = REG_SZ;
7008 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
7009 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
7010 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
7011 goto done;
7013 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
7015 action = 0x2;
7017 /* Nothing to do. */
7018 if (!value)
7020 res = ERROR_SUCCESS;
7021 goto done;
7023 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
7024 newval = strdupW(value);
7025 if (!newval)
7027 res = ERROR_OUTOFMEMORY;
7028 goto done;
7031 else
7033 action = 0x1;
7035 /* Contrary to MSDN, +-variable to [~];path works */
7036 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7038 res = ERROR_SUCCESS;
7039 goto done;
7042 if (!(p = q = data = msi_alloc( size )))
7044 msi_free(deformatted);
7045 RegCloseKey(env);
7046 return ERROR_OUTOFMEMORY;
7049 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)data, &size );
7050 if (res != ERROR_SUCCESS)
7051 goto done;
7053 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7055 action = 0x4;
7056 res = RegDeleteValueW(env, name);
7057 if (res != ERROR_SUCCESS)
7058 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7059 goto done;
7062 for (;;)
7064 while (*q && *q != ';') q++;
7065 len = q - p;
7066 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ) &&
7067 (!p[len] || p[len] == ';'))
7069 found = 1;
7070 break;
7072 if (!*q) break;
7073 p = ++q;
7076 if (found)
7078 TRACE("string already set\n");
7079 goto done;
7082 size = (len_value + 1 + strlenW( data ) + 1) * sizeof(WCHAR);
7083 if (!(p = newval = msi_alloc( size )))
7085 res = ERROR_OUTOFMEMORY;
7086 goto done;
7089 if (flags & ENV_MOD_PREFIX)
7091 memcpy( newval, value, len_value * sizeof(WCHAR) );
7092 newval[len_value] = ';';
7093 p = newval + len_value + 1;
7094 action |= 0x80000000;
7097 strcpyW( p, data );
7099 if (flags & ENV_MOD_APPEND)
7101 p += strlenW( data );
7102 *p++ = ';';
7103 memcpy( p, value, (len_value + 1) * sizeof(WCHAR) );
7104 action |= 0x40000000;
7107 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7108 res = RegSetValueExW( env, name, 0, type, (BYTE *)newval, size );
7109 if (res)
7111 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7114 done:
7115 uirow = MSI_CreateRecord( 3 );
7116 MSI_RecordSetStringW( uirow, 1, name );
7117 MSI_RecordSetStringW( uirow, 2, newval );
7118 MSI_RecordSetInteger( uirow, 3, action );
7119 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7120 msiobj_release( &uirow->hdr );
7122 if (env) RegCloseKey(env);
7123 msi_free(deformatted);
7124 msi_free(data);
7125 msi_free(newval);
7126 return res;
7129 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7131 static const WCHAR query[] = {
7132 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7133 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7134 MSIQUERY *view;
7135 UINT rc;
7137 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7138 if (rc != ERROR_SUCCESS)
7139 return ERROR_SUCCESS;
7141 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7142 msiobj_release(&view->hdr);
7143 return rc;
7146 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7148 MSIPACKAGE *package = param;
7149 LPCWSTR name, value, component;
7150 WCHAR *p, *q, *deformatted = NULL, *new_value = NULL;
7151 DWORD flags, type, size, len, len_value = 0, len_new_value;
7152 HKEY env;
7153 MSICOMPONENT *comp;
7154 MSIRECORD *uirow;
7155 int action = 0;
7156 LONG res;
7157 UINT r;
7159 component = MSI_RecordGetString( rec, 4 );
7160 comp = msi_get_loaded_component( package, component );
7161 if (!comp)
7162 return ERROR_SUCCESS;
7164 comp->Action = msi_get_component_action( package, comp );
7165 if (comp->Action != INSTALLSTATE_ABSENT)
7167 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7168 return ERROR_SUCCESS;
7170 name = MSI_RecordGetString( rec, 2 );
7171 value = MSI_RecordGetString( rec, 3 );
7173 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7175 r = env_parse_flags( &name, &value, &flags );
7176 if (r != ERROR_SUCCESS)
7177 return r;
7179 if (!(flags & ENV_ACT_REMOVE))
7181 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7182 return ERROR_SUCCESS;
7185 if (value && !deformat_string( package, value, &deformatted ))
7186 return ERROR_OUTOFMEMORY;
7188 if ((value = deformatted))
7190 if (flags & ENV_MOD_PREFIX)
7192 p = strchrW( value, ';' );
7193 len_value = p - value;
7195 else if (flags & ENV_MOD_APPEND)
7197 value = strchrW( value, ';' ) + 1;
7198 len_value = strlenW( value );
7200 else len_value = strlenW( value );
7203 r = open_env_key( flags, &env );
7204 if (r != ERROR_SUCCESS)
7206 r = ERROR_SUCCESS;
7207 goto done;
7210 if (flags & ENV_MOD_MACHINE)
7211 action |= 0x20000000;
7213 size = 0;
7214 type = REG_SZ;
7215 res = RegQueryValueExW( env, name, NULL, &type, NULL, &size );
7216 if (res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ))
7217 goto done;
7219 if (!(new_value = msi_alloc( size ))) goto done;
7221 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)new_value, &size );
7222 if (res != ERROR_SUCCESS)
7223 goto done;
7225 len_new_value = size / sizeof(WCHAR) - 1;
7226 p = q = new_value;
7227 for (;;)
7229 while (*q && *q != ';') q++;
7230 len = q - p;
7231 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ))
7233 if (*q == ';') q++;
7234 memmove( p, q, (len_new_value - (q - new_value) + 1) * sizeof(WCHAR) );
7235 break;
7237 if (!*q) break;
7238 p = ++q;
7241 if (!new_value[0] || !value)
7243 TRACE("removing %s\n", debugstr_w(name));
7244 res = RegDeleteValueW( env, name );
7245 if (res != ERROR_SUCCESS)
7246 WARN("failed to delete value %s (%d)\n", debugstr_w(name), res);
7248 else
7250 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(new_value));
7251 size = (strlenW( new_value ) + 1) * sizeof(WCHAR);
7252 res = RegSetValueExW( env, name, 0, type, (BYTE *)new_value, size );
7253 if (res != ERROR_SUCCESS)
7254 WARN("failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(new_value), res);
7257 done:
7258 uirow = MSI_CreateRecord( 3 );
7259 MSI_RecordSetStringW( uirow, 1, name );
7260 MSI_RecordSetStringW( uirow, 2, value );
7261 MSI_RecordSetInteger( uirow, 3, action );
7262 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7263 msiobj_release( &uirow->hdr );
7265 if (env) RegCloseKey( env );
7266 msi_free( deformatted );
7267 msi_free( new_value );
7268 return r;
7271 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7273 static const WCHAR query[] = {
7274 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7275 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7276 MSIQUERY *view;
7277 UINT rc;
7279 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7280 if (rc != ERROR_SUCCESS)
7281 return ERROR_SUCCESS;
7283 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7284 msiobj_release( &view->hdr );
7285 return rc;
7288 UINT msi_validate_product_id( MSIPACKAGE *package )
7290 LPWSTR key, template, id;
7291 UINT r = ERROR_SUCCESS;
7293 id = msi_dup_property( package->db, szProductID );
7294 if (id)
7296 msi_free( id );
7297 return ERROR_SUCCESS;
7299 template = msi_dup_property( package->db, szPIDTemplate );
7300 key = msi_dup_property( package->db, szPIDKEY );
7301 if (key && template)
7303 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7304 r = msi_set_property( package->db, szProductID, key, -1 );
7306 msi_free( template );
7307 msi_free( key );
7308 return r;
7311 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7313 return msi_validate_product_id( package );
7316 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7318 TRACE("\n");
7319 package->need_reboot_at_end = 1;
7320 return ERROR_SUCCESS;
7323 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7325 static const WCHAR szAvailableFreeReg[] =
7326 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7327 MSIRECORD *uirow;
7328 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7330 TRACE("%p %d kilobytes\n", package, space);
7332 uirow = MSI_CreateRecord( 1 );
7333 MSI_RecordSetInteger( uirow, 1, space );
7334 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7335 msiobj_release( &uirow->hdr );
7337 return ERROR_SUCCESS;
7340 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7342 TRACE("%p\n", package);
7344 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7345 return ERROR_SUCCESS;
7348 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7350 FIXME("%p\n", package);
7351 return ERROR_SUCCESS;
7354 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7356 static const WCHAR driver_query[] = {
7357 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7358 'O','D','B','C','D','r','i','v','e','r',0};
7359 static const WCHAR translator_query[] = {
7360 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7361 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7362 MSIQUERY *view;
7363 UINT r, count;
7365 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7366 if (r == ERROR_SUCCESS)
7368 count = 0;
7369 r = MSI_IterateRecords( view, &count, NULL, package );
7370 msiobj_release( &view->hdr );
7371 if (r != ERROR_SUCCESS)
7372 return r;
7373 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7375 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7376 if (r == ERROR_SUCCESS)
7378 count = 0;
7379 r = MSI_IterateRecords( view, &count, NULL, package );
7380 msiobj_release( &view->hdr );
7381 if (r != ERROR_SUCCESS)
7382 return r;
7383 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7385 return ERROR_SUCCESS;
7388 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7390 static const WCHAR fmtW[] =
7391 {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7392 MSIPACKAGE *package = param;
7393 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7394 int attrs = MSI_RecordGetInteger( rec, 5 );
7395 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7396 WCHAR *product, *features, *cmd;
7397 STARTUPINFOW si;
7398 PROCESS_INFORMATION info;
7399 BOOL ret;
7401 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7402 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7404 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7406 len += strlenW( product );
7407 if (features)
7408 len += strlenW( features );
7409 else
7410 len += sizeof(szAll) / sizeof(szAll[0]);
7412 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7414 msi_free( product );
7415 msi_free( features );
7416 return ERROR_OUTOFMEMORY;
7418 sprintfW( cmd, fmtW, product, features ? features : szAll );
7419 msi_free( product );
7420 msi_free( features );
7422 memset( &si, 0, sizeof(STARTUPINFOW) );
7423 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7424 msi_free( cmd );
7425 if (!ret) return GetLastError();
7426 CloseHandle( info.hThread );
7428 WaitForSingleObject( info.hProcess, INFINITE );
7429 CloseHandle( info.hProcess );
7430 return ERROR_SUCCESS;
7433 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7435 static const WCHAR query[] = {
7436 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7437 MSIQUERY *view;
7438 UINT r;
7440 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7441 if (r == ERROR_SUCCESS)
7443 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7444 msiobj_release( &view->hdr );
7445 if (r != ERROR_SUCCESS)
7446 return r;
7448 return ERROR_SUCCESS;
7451 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7453 MSIPACKAGE *package = param;
7454 int attributes = MSI_RecordGetInteger( rec, 5 );
7456 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7458 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7459 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7460 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7461 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7462 HKEY hkey;
7463 UINT r;
7465 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7467 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7468 if (r != ERROR_SUCCESS)
7469 return ERROR_SUCCESS;
7471 else
7473 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7474 if (r != ERROR_SUCCESS)
7475 return ERROR_SUCCESS;
7477 RegCloseKey( hkey );
7479 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7480 debugstr_w(upgrade_code), debugstr_w(version_min),
7481 debugstr_w(version_max), debugstr_w(language));
7483 return ERROR_SUCCESS;
7486 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7488 static const WCHAR query[] = {
7489 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7490 'U','p','g','r','a','d','e',0};
7491 MSIQUERY *view;
7492 UINT r;
7494 if (msi_get_property_int( package->db, szInstalled, 0 ))
7496 TRACE("product is installed, skipping action\n");
7497 return ERROR_SUCCESS;
7499 if (msi_get_property_int( package->db, szPreselected, 0 ))
7501 TRACE("Preselected property is set, not migrating feature states\n");
7502 return ERROR_SUCCESS;
7504 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7505 if (r == ERROR_SUCCESS)
7507 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7508 msiobj_release( &view->hdr );
7509 if (r != ERROR_SUCCESS)
7510 return r;
7512 return ERROR_SUCCESS;
7515 static void bind_image( const char *filename, const char *path )
7517 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7519 WARN("failed to bind image %u\n", GetLastError());
7523 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7525 UINT i;
7526 MSIFILE *file;
7527 MSIPACKAGE *package = param;
7528 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7529 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7530 char *filenameA, *pathA;
7531 WCHAR *pathW, **path_list;
7533 if (!(file = msi_get_loaded_file( package, key )))
7535 WARN("file %s not found\n", debugstr_w(key));
7536 return ERROR_SUCCESS;
7538 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7539 path_list = msi_split_string( paths, ';' );
7540 if (!path_list) bind_image( filenameA, NULL );
7541 else
7543 for (i = 0; path_list[i] && path_list[i][0]; i++)
7545 deformat_string( package, path_list[i], &pathW );
7546 if ((pathA = strdupWtoA( pathW )))
7548 bind_image( filenameA, pathA );
7549 msi_free( pathA );
7551 msi_free( pathW );
7554 msi_free( path_list );
7555 msi_free( filenameA );
7556 return ERROR_SUCCESS;
7559 static UINT ACTION_BindImage( MSIPACKAGE *package )
7561 static const WCHAR query[] = {
7562 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7563 'B','i','n','d','I','m','a','g','e',0};
7564 MSIQUERY *view;
7565 UINT r;
7567 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7568 if (r == ERROR_SUCCESS)
7570 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7571 msiobj_release( &view->hdr );
7572 if (r != ERROR_SUCCESS)
7573 return r;
7575 return ERROR_SUCCESS;
7578 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7580 static const WCHAR query[] = {
7581 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7582 MSIQUERY *view;
7583 DWORD count = 0;
7584 UINT r;
7586 r = MSI_OpenQuery( package->db, &view, query, table );
7587 if (r == ERROR_SUCCESS)
7589 r = MSI_IterateRecords(view, &count, NULL, package);
7590 msiobj_release(&view->hdr);
7591 if (r != ERROR_SUCCESS)
7592 return r;
7594 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7595 return ERROR_SUCCESS;
7598 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7600 static const WCHAR table[] = {
7601 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7602 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7605 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7607 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7608 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7611 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7613 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7614 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7617 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7619 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7620 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7623 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7625 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7626 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7629 static const struct
7631 const WCHAR *action;
7632 UINT (*handler)(MSIPACKAGE *);
7633 const WCHAR *action_rollback;
7635 StandardActions[] =
7637 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7638 { szAppSearch, ACTION_AppSearch, NULL },
7639 { szBindImage, ACTION_BindImage, NULL },
7640 { szCCPSearch, ACTION_CCPSearch, NULL },
7641 { szCostFinalize, ACTION_CostFinalize, NULL },
7642 { szCostInitialize, ACTION_CostInitialize, NULL },
7643 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7644 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7645 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7646 { szDisableRollback, ACTION_DisableRollback, NULL },
7647 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7648 { szExecuteAction, ACTION_ExecuteAction, NULL },
7649 { szFileCost, ACTION_FileCost, NULL },
7650 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7651 { szForceReboot, ACTION_ForceReboot, NULL },
7652 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7653 { szInstallExecute, ACTION_InstallExecute, NULL },
7654 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7655 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7656 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7657 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7658 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7659 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7660 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7661 { szInstallValidate, ACTION_InstallValidate, NULL },
7662 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7663 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7664 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7665 { szMoveFiles, ACTION_MoveFiles, NULL },
7666 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7667 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7668 { szPatchFiles, ACTION_PatchFiles, NULL },
7669 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7670 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7671 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7672 { szPublishProduct, ACTION_PublishProduct, NULL },
7673 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7674 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7675 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7676 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7677 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7678 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7679 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7680 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7681 { szRegisterUser, ACTION_RegisterUser, NULL },
7682 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7683 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7684 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7685 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7686 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7687 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7688 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7689 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7690 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7691 { szResolveSource, ACTION_ResolveSource, NULL },
7692 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7693 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7694 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7695 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7696 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7697 { szStartServices, ACTION_StartServices, szStopServices },
7698 { szStopServices, ACTION_StopServices, szStartServices },
7699 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7700 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7701 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7702 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7703 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7704 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7705 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7706 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7707 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7708 { szValidateProductID, ACTION_ValidateProductID, NULL },
7709 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7710 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7711 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7712 { NULL, NULL, NULL }
7715 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7717 BOOL ret = FALSE;
7718 UINT i;
7720 i = 0;
7721 while (StandardActions[i].action != NULL)
7723 if (!strcmpW( StandardActions[i].action, action ))
7725 ui_actionstart( package, action );
7726 if (StandardActions[i].handler)
7728 ui_actioninfo( package, action, TRUE, 0 );
7729 *rc = StandardActions[i].handler( package );
7730 ui_actioninfo( package, action, FALSE, *rc );
7732 if (StandardActions[i].action_rollback && !package->need_rollback)
7734 TRACE("scheduling rollback action\n");
7735 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7738 else
7740 FIXME("unhandled standard action %s\n", debugstr_w(action));
7741 *rc = ERROR_SUCCESS;
7743 ret = TRUE;
7744 break;
7746 i++;
7748 return ret;
7751 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7753 UINT rc = ERROR_SUCCESS;
7754 BOOL handled;
7756 TRACE("Performing action (%s)\n", debugstr_w(action));
7758 handled = ACTION_HandleStandardAction(package, action, &rc);
7760 if (!handled)
7761 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7763 if (!handled)
7765 WARN("unhandled msi action %s\n", debugstr_w(action));
7766 rc = ERROR_FUNCTION_NOT_CALLED;
7769 return rc;
7772 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7774 UINT rc = ERROR_SUCCESS;
7775 BOOL handled = FALSE;
7777 TRACE("Performing action (%s)\n", debugstr_w(action));
7779 package->action_progress_increment = 0;
7780 handled = ACTION_HandleStandardAction(package, action, &rc);
7782 if (!handled)
7783 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7785 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7786 handled = TRUE;
7788 if (!handled)
7790 WARN("unhandled msi action %s\n", debugstr_w(action));
7791 rc = ERROR_FUNCTION_NOT_CALLED;
7794 return rc;
7797 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7799 UINT rc = ERROR_SUCCESS;
7800 MSIRECORD *row;
7802 static const WCHAR query[] =
7803 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7804 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7805 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7806 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7807 static const WCHAR ui_query[] =
7808 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7809 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7810 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7811 ' ', '=',' ','%','i',0};
7813 if (needs_ui_sequence(package))
7814 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7815 else
7816 row = MSI_QueryGetRecord(package->db, query, seq);
7818 if (row)
7820 LPCWSTR action, cond;
7822 TRACE("Running the actions\n");
7824 /* check conditions */
7825 cond = MSI_RecordGetString(row, 2);
7827 /* this is a hack to skip errors in the condition code */
7828 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7830 msiobj_release(&row->hdr);
7831 return ERROR_SUCCESS;
7834 action = MSI_RecordGetString(row, 1);
7835 if (!action)
7837 ERR("failed to fetch action\n");
7838 msiobj_release(&row->hdr);
7839 return ERROR_FUNCTION_FAILED;
7842 if (needs_ui_sequence(package))
7843 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7844 else
7845 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7847 msiobj_release(&row->hdr);
7850 return rc;
7853 /****************************************************
7854 * TOP level entry points
7855 *****************************************************/
7857 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7858 LPCWSTR szCommandLine )
7860 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7861 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7862 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7863 WCHAR *reinstall, *remove, *patch, *productcode;
7864 BOOL ui_exists;
7865 UINT rc;
7867 msi_set_property( package->db, szAction, szInstall, -1 );
7869 package->script->InWhatSequence = SEQUENCE_INSTALL;
7871 if (szPackagePath)
7873 LPWSTR p, dir;
7874 LPCWSTR file;
7876 dir = strdupW(szPackagePath);
7877 p = strrchrW(dir, '\\');
7878 if (p)
7880 *(++p) = 0;
7881 file = szPackagePath + (p - dir);
7883 else
7885 msi_free(dir);
7886 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7887 GetCurrentDirectoryW(MAX_PATH, dir);
7888 lstrcatW(dir, szBackSlash);
7889 file = szPackagePath;
7892 msi_free( package->PackagePath );
7893 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7894 if (!package->PackagePath)
7896 msi_free(dir);
7897 return ERROR_OUTOFMEMORY;
7900 lstrcpyW(package->PackagePath, dir);
7901 lstrcatW(package->PackagePath, file);
7902 msi_free(dir);
7904 msi_set_sourcedir_props(package, FALSE);
7907 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7908 if (rc != ERROR_SUCCESS)
7909 return rc;
7911 msi_apply_transforms( package );
7912 msi_apply_patches( package );
7914 patch = msi_dup_property( package->db, szPatch );
7915 remove = msi_dup_property( package->db, szRemove );
7916 reinstall = msi_dup_property( package->db, szReinstall );
7917 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7919 TRACE("setting REINSTALL property to ALL\n");
7920 msi_set_property( package->db, szReinstall, szAll, -1 );
7921 package->full_reinstall = 1;
7924 msi_set_original_database_property( package->db, szPackagePath );
7925 msi_parse_command_line( package, szCommandLine, FALSE );
7926 msi_adjust_privilege_properties( package );
7927 msi_set_context( package );
7929 productcode = msi_dup_property( package->db, szProductCode );
7930 if (strcmpiW( productcode, package->ProductCode ))
7932 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
7933 msi_free( package->ProductCode );
7934 package->ProductCode = productcode;
7936 else msi_free( productcode );
7938 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7940 TRACE("disabling rollback\n");
7941 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7944 if (needs_ui_sequence( package))
7946 package->script->InWhatSequence |= SEQUENCE_UI;
7947 rc = ACTION_ProcessUISequence(package);
7948 ui_exists = ui_sequence_exists(package);
7949 if (rc == ERROR_SUCCESS || !ui_exists)
7951 package->script->InWhatSequence |= SEQUENCE_EXEC;
7952 rc = ACTION_ProcessExecSequence(package, ui_exists);
7955 else
7956 rc = ACTION_ProcessExecSequence(package, FALSE);
7958 /* process the ending type action */
7959 if (rc == ERROR_SUCCESS)
7960 ACTION_PerformActionSequence(package, -1);
7961 else if (rc == ERROR_INSTALL_USEREXIT)
7962 ACTION_PerformActionSequence(package, -2);
7963 else if (rc == ERROR_INSTALL_SUSPEND)
7964 ACTION_PerformActionSequence(package, -4);
7965 else /* failed */
7967 ACTION_PerformActionSequence(package, -3);
7968 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7970 package->need_rollback = TRUE;
7974 /* finish up running custom actions */
7975 ACTION_FinishCustomActions(package);
7977 if (package->need_rollback && !reinstall)
7979 WARN("installation failed, running rollback script\n");
7980 execute_script( package, SCRIPT_ROLLBACK );
7982 msi_free( reinstall );
7983 msi_free( remove );
7984 msi_free( patch );
7986 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7987 return ERROR_SUCCESS_REBOOT_REQUIRED;
7989 return rc;