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