advapi32: Add some tests for RegDeleteTreeA.
[wine/multimedia.git] / dlls / msi / action.c
blob51d827e6ae24c091108af858cd9e82995b38fbfa
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
22 * Pages I need
24 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
26 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
29 #include <stdarg.h>
31 #define COBJMACROS
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winerror.h"
36 #include "winreg.h"
37 #include "winsvc.h"
38 #include "odbcinst.h"
39 #include "wine/debug.h"
40 #include "msidefs.h"
41 #include "msipriv.h"
42 #include "winuser.h"
43 #include "shlobj.h"
44 #include "wine/unicode.h"
45 #include "winver.h"
47 #define REG_PROGRESS_VALUE 13200
48 #define COMPONENT_PROGRESS_VALUE 24000
50 WINE_DEFAULT_DEBUG_CHANNEL(msi);
53 * Prototypes
55 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
56 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
57 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
60 * consts and values used
62 static const WCHAR c_colon[] = {'C',':','\\',0};
64 static const WCHAR szCreateFolders[] =
65 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
66 static const WCHAR szCostFinalize[] =
67 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
68 const WCHAR szInstallFiles[] =
69 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
70 const WCHAR szDuplicateFiles[] =
71 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
72 static const WCHAR szWriteRegistryValues[] =
73 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
74 'V','a','l','u','e','s',0};
75 static const WCHAR szCostInitialize[] =
76 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
77 static const WCHAR szFileCost[] =
78 {'F','i','l','e','C','o','s','t',0};
79 static const WCHAR szInstallInitialize[] =
80 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
81 static const WCHAR szInstallValidate[] =
82 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
83 static const WCHAR szLaunchConditions[] =
84 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
85 static const WCHAR szProcessComponents[] =
86 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
87 static const WCHAR szRegisterTypeLibraries[] =
88 {'R','e','g','i','s','t','e','r','T','y','p','e',
89 'L','i','b','r','a','r','i','e','s',0};
90 const WCHAR szRegisterClassInfo[] =
91 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
92 const WCHAR szRegisterProgIdInfo[] =
93 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
94 static const WCHAR szCreateShortcuts[] =
95 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
96 static const WCHAR szPublishProduct[] =
97 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
98 static const WCHAR szWriteIniValues[] =
99 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
100 static const WCHAR szSelfRegModules[] =
101 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
102 static const WCHAR szPublishFeatures[] =
103 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
104 static const WCHAR szRegisterProduct[] =
105 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
106 static const WCHAR szInstallExecute[] =
107 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
108 static const WCHAR szInstallExecuteAgain[] =
109 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
110 'A','g','a','i','n',0};
111 static const WCHAR szInstallFinalize[] =
112 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
113 static const WCHAR szForceReboot[] =
114 {'F','o','r','c','e','R','e','b','o','o','t',0};
115 static const WCHAR szResolveSource[] =
116 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
117 static const WCHAR szAppSearch[] =
118 {'A','p','p','S','e','a','r','c','h',0};
119 static const WCHAR szAllocateRegistrySpace[] =
120 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
121 'S','p','a','c','e',0};
122 static const WCHAR szBindImage[] =
123 {'B','i','n','d','I','m','a','g','e',0};
124 static const WCHAR szCCPSearch[] =
125 {'C','C','P','S','e','a','r','c','h',0};
126 static const WCHAR szDeleteServices[] =
127 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
128 static const WCHAR szDisableRollback[] =
129 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
130 static const WCHAR szExecuteAction[] =
131 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
132 const WCHAR szFindRelatedProducts[] =
133 {'F','i','n','d','R','e','l','a','t','e','d',
134 'P','r','o','d','u','c','t','s',0};
135 static const WCHAR szInstallAdminPackage[] =
136 {'I','n','s','t','a','l','l','A','d','m','i','n',
137 'P','a','c','k','a','g','e',0};
138 static const WCHAR szInstallSFPCatalogFile[] =
139 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
140 'F','i','l','e',0};
141 static const WCHAR szIsolateComponents[] =
142 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
143 const WCHAR szMigrateFeatureStates[] =
144 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
145 'S','t','a','t','e','s',0};
146 const WCHAR szMoveFiles[] =
147 {'M','o','v','e','F','i','l','e','s',0};
148 static const WCHAR szMsiPublishAssemblies[] =
149 {'M','s','i','P','u','b','l','i','s','h',
150 'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szMsiUnpublishAssemblies[] =
152 {'M','s','i','U','n','p','u','b','l','i','s','h',
153 'A','s','s','e','m','b','l','i','e','s',0};
154 static const WCHAR szInstallODBC[] =
155 {'I','n','s','t','a','l','l','O','D','B','C',0};
156 static const WCHAR szInstallServices[] =
157 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
158 const WCHAR szPatchFiles[] =
159 {'P','a','t','c','h','F','i','l','e','s',0};
160 static const WCHAR szPublishComponents[] =
161 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
162 static const WCHAR szRegisterComPlus[] =
163 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
164 const WCHAR szRegisterExtensionInfo[] =
165 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
166 'I','n','f','o',0};
167 static const WCHAR szRegisterFonts[] =
168 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
169 const WCHAR szRegisterMIMEInfo[] =
170 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
171 static const WCHAR szRegisterUser[] =
172 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
173 const WCHAR szRemoveDuplicateFiles[] =
174 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
175 'F','i','l','e','s',0};
176 static const WCHAR szRemoveEnvironmentStrings[] =
177 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
178 'S','t','r','i','n','g','s',0};
179 const WCHAR szRemoveExistingProducts[] =
180 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
181 'P','r','o','d','u','c','t','s',0};
182 const WCHAR szRemoveFiles[] =
183 {'R','e','m','o','v','e','F','i','l','e','s',0};
184 static const WCHAR szRemoveFolders[] =
185 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
186 static const WCHAR szRemoveIniValues[] =
187 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
188 static const WCHAR szRemoveODBC[] =
189 {'R','e','m','o','v','e','O','D','B','C',0};
190 static const WCHAR szRemoveRegistryValues[] =
191 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
192 'V','a','l','u','e','s',0};
193 static const WCHAR szRemoveShortcuts[] =
194 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
195 static const WCHAR szRMCCPSearch[] =
196 {'R','M','C','C','P','S','e','a','r','c','h',0};
197 static const WCHAR szScheduleReboot[] =
198 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
199 static const WCHAR szSelfUnregModules[] =
200 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
201 static const WCHAR szSetODBCFolders[] =
202 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
203 static const WCHAR szStartServices[] =
204 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
205 static const WCHAR szStopServices[] =
206 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
207 static const WCHAR szUnpublishComponents[] =
208 {'U','n','p','u','b','l','i','s','h',
209 'C','o','m','p','o','n','e','n','t','s',0};
210 static const WCHAR szUnpublishFeatures[] =
211 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
212 const WCHAR szUnregisterClassInfo[] =
213 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
214 'I','n','f','o',0};
215 static const WCHAR szUnregisterComPlus[] =
216 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
217 const WCHAR szUnregisterExtensionInfo[] =
218 {'U','n','r','e','g','i','s','t','e','r',
219 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
220 static const WCHAR szUnregisterFonts[] =
221 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
222 const WCHAR szUnregisterMIMEInfo[] =
223 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
224 const WCHAR szUnregisterProgIdInfo[] =
225 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
226 'I','n','f','o',0};
227 static const WCHAR szUnregisterTypeLibraries[] =
228 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
229 'L','i','b','r','a','r','i','e','s',0};
230 static const WCHAR szValidateProductID[] =
231 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
232 static const WCHAR szWriteEnvironmentStrings[] =
233 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
234 'S','t','r','i','n','g','s',0};
236 /* action handlers */
237 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
239 struct _actions {
240 LPCWSTR action;
241 STANDARDACTIONHANDLER handler;
244 static const struct _actions StandardActions[];
247 /********************************************************
248 * helper functions
249 ********************************************************/
251 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
253 static const WCHAR Query_t[] =
254 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
255 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
256 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
257 ' ','\'','%','s','\'',0};
258 MSIRECORD * row;
260 row = MSI_QueryGetRecord( package->db, Query_t, action );
261 if (!row)
262 return;
263 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
264 msiobj_release(&row->hdr);
267 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
268 UINT rc)
270 MSIRECORD * row;
271 static const WCHAR template_s[]=
272 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
273 '%','s', '.',0};
274 static const WCHAR template_e[]=
275 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
276 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
277 '%','i','.',0};
278 static const WCHAR format[] =
279 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
280 WCHAR message[1024];
281 WCHAR timet[0x100];
283 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
284 if (start)
285 sprintfW(message,template_s,timet,action);
286 else
287 sprintfW(message,template_e,timet,action,rc);
289 row = MSI_CreateRecord(1);
290 MSI_RecordSetStringW(row,1,message);
292 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
293 msiobj_release(&row->hdr);
296 static UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine )
298 LPCWSTR ptr,ptr2;
299 BOOL quote;
300 DWORD len;
301 LPWSTR prop = NULL, val = NULL;
303 if (!szCommandLine)
304 return ERROR_SUCCESS;
306 ptr = szCommandLine;
308 while (*ptr)
310 if (*ptr==' ')
312 ptr++;
313 continue;
316 TRACE("Looking at %s\n",debugstr_w(ptr));
318 ptr2 = strchrW(ptr,'=');
319 if (!ptr2)
321 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
322 break;
325 quote = FALSE;
327 len = ptr2-ptr;
328 prop = msi_alloc((len+1)*sizeof(WCHAR));
329 memcpy(prop,ptr,len*sizeof(WCHAR));
330 prop[len]=0;
331 ptr2++;
333 len = 0;
334 ptr = ptr2;
335 while (*ptr && (quote || (!quote && *ptr!=' ')))
337 if (*ptr == '"')
338 quote = !quote;
339 ptr++;
340 len++;
343 if (*ptr2=='"')
345 ptr2++;
346 len -= 2;
348 val = msi_alloc((len+1)*sizeof(WCHAR));
349 memcpy(val,ptr2,len*sizeof(WCHAR));
350 val[len] = 0;
352 if (lstrlenW(prop) > 0)
354 TRACE("Found commandline property (%s) = (%s)\n",
355 debugstr_w(prop), debugstr_w(val));
356 MSI_SetPropertyW(package,prop,val);
358 msi_free(val);
359 msi_free(prop);
362 return ERROR_SUCCESS;
366 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
368 LPCWSTR pc;
369 LPWSTR p, *ret = NULL;
370 UINT count = 0;
372 if (!str)
373 return ret;
375 /* count the number of substrings */
376 for ( pc = str, count = 0; pc; count++ )
378 pc = strchrW( pc, sep );
379 if (pc)
380 pc++;
383 /* allocate space for an array of substring pointers and the substrings */
384 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
385 (lstrlenW(str)+1) * sizeof(WCHAR) );
386 if (!ret)
387 return ret;
389 /* copy the string and set the pointers */
390 p = (LPWSTR) &ret[count+1];
391 lstrcpyW( p, str );
392 for( count = 0; (ret[count] = p); count++ )
394 p = strchrW( p, sep );
395 if (p)
396 *p++ = 0;
399 return ret;
402 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
404 WCHAR szProductCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
405 LPWSTR prod_code, patch_product;
406 UINT ret;
408 prod_code = msi_dup_property( package, szProductCode );
409 patch_product = msi_get_suminfo_product( patch );
411 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
413 if ( strstrW( patch_product, prod_code ) )
414 ret = ERROR_SUCCESS;
415 else
416 ret = ERROR_FUNCTION_FAILED;
418 msi_free( patch_product );
419 msi_free( prod_code );
421 return ret;
424 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
425 MSIDATABASE *patch_db, LPCWSTR name )
427 UINT ret = ERROR_FUNCTION_FAILED;
428 IStorage *stg = NULL;
429 HRESULT r;
431 TRACE("%p %s\n", package, debugstr_w(name) );
433 if (*name++ != ':')
435 ERR("expected a colon in %s\n", debugstr_w(name));
436 return ERROR_FUNCTION_FAILED;
439 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
440 if (SUCCEEDED(r))
442 ret = msi_check_transform_applicable( package, stg );
443 if (ret == ERROR_SUCCESS)
444 msi_table_apply_transform( package->db, stg );
445 else
446 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
447 IStorage_Release( stg );
449 else
450 ERR("failed to open substorage %s\n", debugstr_w(name));
452 return ERROR_SUCCESS;
455 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
457 static const WCHAR szProdID[] = { 'P','r','o','d','u','c','t','I','D',0 };
458 LPWSTR guid_list, *guids, product_id;
459 UINT i, ret = ERROR_FUNCTION_FAILED;
461 product_id = msi_dup_property( package, szProdID );
462 if (!product_id)
464 /* FIXME: the property ProductID should be written into the DB somewhere */
465 ERR("no product ID to check\n");
466 return ERROR_SUCCESS;
469 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
470 guids = msi_split_string( guid_list, ';' );
471 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
473 if (!lstrcmpW( guids[i], product_id ))
474 ret = ERROR_SUCCESS;
476 msi_free( guids );
477 msi_free( guid_list );
478 msi_free( product_id );
480 return ret;
483 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
485 MSISUMMARYINFO *si;
486 LPWSTR str, *substorage;
487 UINT i, r = ERROR_SUCCESS;
489 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
490 if (!si)
491 return ERROR_FUNCTION_FAILED;
493 msi_check_patch_applicable( package, si );
495 /* enumerate the substorage */
496 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
497 substorage = msi_split_string( str, ';' );
498 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
499 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
500 msi_free( substorage );
501 msi_free( str );
503 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
505 msiobj_release( &si->hdr );
507 return r;
510 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
512 MSIDATABASE *patch_db = NULL;
513 UINT r;
515 TRACE("%p %s\n", package, debugstr_w( file ) );
517 /* FIXME:
518 * We probably want to make sure we only open a patch collection here.
519 * Patch collections (.msp) and databases (.msi) have different GUIDs
520 * but currently MSI_OpenDatabaseW will accept both.
522 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
523 if ( r != ERROR_SUCCESS )
525 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
526 return r;
529 msi_parse_patch_summary( package, patch_db );
532 * There might be a CAB file in the patch package,
533 * so append it to the list of storage to search for streams.
535 append_storage_to_db( package->db, patch_db->storage );
537 msiobj_release( &patch_db->hdr );
539 return ERROR_SUCCESS;
542 /* get the PATCH property, and apply all the patches it specifies */
543 static UINT msi_apply_patches( MSIPACKAGE *package )
545 static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
546 LPWSTR patch_list, *patches;
547 UINT i, r = ERROR_SUCCESS;
549 patch_list = msi_dup_property( package, szPatch );
551 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
553 patches = msi_split_string( patch_list, ';' );
554 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
555 r = msi_apply_patch_package( package, patches[i] );
557 msi_free( patches );
558 msi_free( patch_list );
560 return r;
563 static UINT msi_apply_transforms( MSIPACKAGE *package )
565 static const WCHAR szTransforms[] = {
566 'T','R','A','N','S','F','O','R','M','S',0 };
567 LPWSTR xform_list, *xforms;
568 UINT i, r = ERROR_SUCCESS;
570 xform_list = msi_dup_property( package, szTransforms );
571 xforms = msi_split_string( xform_list, ';' );
573 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
575 if (xforms[i][0] == ':')
576 r = msi_apply_substorage_transform( package, package->db, &xforms[i][1] );
577 else
578 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
581 msi_free( xforms );
582 msi_free( xform_list );
584 return r;
587 /****************************************************
588 * TOP level entry points
589 *****************************************************/
591 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
592 LPCWSTR szCommandLine )
594 UINT rc;
595 BOOL ui = FALSE;
596 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
597 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
598 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
600 MSI_SetPropertyW(package, szAction, szInstall);
602 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
604 package->script->InWhatSequence = SEQUENCE_INSTALL;
606 if (szPackagePath)
608 LPWSTR p, check, path;
610 path = strdupW(szPackagePath);
611 p = strrchrW(path,'\\');
612 if (p)
614 p++;
615 *p=0;
617 else
619 msi_free(path);
620 path = msi_alloc(MAX_PATH*sizeof(WCHAR));
621 GetCurrentDirectoryW(MAX_PATH,path);
622 strcatW(path,cszbs);
625 check = msi_dup_property( package, cszSourceDir );
626 if (!check)
627 MSI_SetPropertyW(package, cszSourceDir, path);
628 msi_free(check);
630 check = msi_dup_property( package, cszSOURCEDIR );
631 if (!check)
632 MSI_SetPropertyW(package, cszSOURCEDIR, path);
634 msi_free( package->PackagePath );
635 package->PackagePath = path;
637 msi_free(check);
640 msi_parse_command_line( package, szCommandLine );
642 msi_apply_transforms( package );
643 msi_apply_patches( package );
645 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
647 package->script->InWhatSequence |= SEQUENCE_UI;
648 rc = ACTION_ProcessUISequence(package);
649 ui = TRUE;
650 if (rc == ERROR_SUCCESS)
652 package->script->InWhatSequence |= SEQUENCE_EXEC;
653 rc = ACTION_ProcessExecSequence(package,TRUE);
656 else
657 rc = ACTION_ProcessExecSequence(package,FALSE);
659 if (rc == -1)
661 /* install was halted but should be considered a success */
662 rc = ERROR_SUCCESS;
665 package->script->CurrentlyScripting= FALSE;
667 /* process the ending type action */
668 if (rc == ERROR_SUCCESS)
669 ACTION_PerformActionSequence(package,-1,ui);
670 else if (rc == ERROR_INSTALL_USEREXIT)
671 ACTION_PerformActionSequence(package,-2,ui);
672 else if (rc == ERROR_INSTALL_SUSPEND)
673 ACTION_PerformActionSequence(package,-4,ui);
674 else /* failed */
675 ACTION_PerformActionSequence(package,-3,ui);
677 /* finish up running custom actions */
678 ACTION_FinishCustomActions(package);
680 return rc;
683 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
685 UINT rc = ERROR_SUCCESS;
686 MSIRECORD * row = 0;
687 static const WCHAR ExecSeqQuery[] =
688 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
689 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
690 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
691 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
693 static const WCHAR UISeqQuery[] =
694 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
695 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
696 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
697 ' ', '=',' ','%','i',0};
699 if (UI)
700 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
701 else
702 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
704 if (row)
706 LPCWSTR action, cond;
708 TRACE("Running the actions\n");
710 /* check conditions */
711 cond = MSI_RecordGetString(row,2);
713 /* this is a hack to skip errors in the condition code */
714 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
715 goto end;
717 action = MSI_RecordGetString(row,1);
718 if (!action)
720 ERR("failed to fetch action\n");
721 rc = ERROR_FUNCTION_FAILED;
722 goto end;
725 if (UI)
726 rc = ACTION_PerformUIAction(package,action);
727 else
728 rc = ACTION_PerformAction(package,action,FALSE);
729 end:
730 msiobj_release(&row->hdr);
732 else
733 rc = ERROR_SUCCESS;
735 return rc;
738 typedef struct {
739 MSIPACKAGE* package;
740 BOOL UI;
741 } iterate_action_param;
743 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
745 iterate_action_param *iap= (iterate_action_param*)param;
746 UINT rc;
747 LPCWSTR cond, action;
749 action = MSI_RecordGetString(row,1);
750 if (!action)
752 ERR("Error is retrieving action name\n");
753 return ERROR_FUNCTION_FAILED;
756 /* check conditions */
757 cond = MSI_RecordGetString(row,2);
759 /* this is a hack to skip errors in the condition code */
760 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
762 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
763 return ERROR_SUCCESS;
766 if (iap->UI)
767 rc = ACTION_PerformUIAction(iap->package,action);
768 else
769 rc = ACTION_PerformAction(iap->package,action,FALSE);
771 msi_dialog_check_messages( NULL );
773 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
774 rc = iap->package->CurrentInstallState;
776 if (rc == ERROR_FUNCTION_NOT_CALLED)
777 rc = ERROR_SUCCESS;
779 if (rc != ERROR_SUCCESS)
780 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
782 return rc;
785 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
787 MSIQUERY * view;
788 UINT r;
789 static const WCHAR query[] =
790 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
791 '`','%','s','`',
792 ' ','W','H','E','R','E',' ',
793 '`','S','e','q','u','e','n','c','e','`',' ',
794 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
795 '`','S','e','q','u','e','n','c','e','`',0};
796 iterate_action_param iap;
799 * FIXME: probably should be checking UILevel in the
800 * ACTION_PerformUIAction/ACTION_PerformAction
801 * rather than saving the UI level here. Those
802 * two functions can be merged too.
804 iap.package = package;
805 iap.UI = TRUE;
807 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
809 r = MSI_OpenQuery( package->db, &view, query, szTable );
810 if (r == ERROR_SUCCESS)
812 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
813 msiobj_release(&view->hdr);
816 return r;
819 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
821 MSIQUERY * view;
822 UINT rc;
823 static const WCHAR ExecSeqQuery[] =
824 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
825 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
826 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
827 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
828 'O','R','D','E','R',' ', 'B','Y',' ',
829 '`','S','e','q','u','e','n','c','e','`',0 };
830 MSIRECORD * row = 0;
831 static const WCHAR IVQuery[] =
832 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
833 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
834 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
835 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
836 ' ','\'', 'I','n','s','t','a','l','l',
837 'V','a','l','i','d','a','t','e','\'', 0};
838 INT seq = 0;
839 iterate_action_param iap;
841 iap.package = package;
842 iap.UI = FALSE;
844 if (package->script->ExecuteSequenceRun)
846 TRACE("Execute Sequence already Run\n");
847 return ERROR_SUCCESS;
850 package->script->ExecuteSequenceRun = TRUE;
852 /* get the sequence number */
853 if (UIran)
855 row = MSI_QueryGetRecord(package->db, IVQuery);
856 if( !row )
857 return ERROR_FUNCTION_FAILED;
858 seq = MSI_RecordGetInteger(row,1);
859 msiobj_release(&row->hdr);
862 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
863 if (rc == ERROR_SUCCESS)
865 TRACE("Running the actions\n");
867 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
868 msiobj_release(&view->hdr);
871 return rc;
874 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
876 MSIQUERY * view;
877 UINT rc;
878 static const WCHAR ExecSeqQuery [] =
879 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
880 '`','I','n','s','t','a','l','l',
881 'U','I','S','e','q','u','e','n','c','e','`',
882 ' ','W','H','E','R','E',' ',
883 '`','S','e','q','u','e','n','c','e','`',' ',
884 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
885 '`','S','e','q','u','e','n','c','e','`',0};
886 iterate_action_param iap;
888 iap.package = package;
889 iap.UI = TRUE;
891 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
893 if (rc == ERROR_SUCCESS)
895 TRACE("Running the actions\n");
897 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
898 msiobj_release(&view->hdr);
901 return rc;
904 /********************************************************
905 * ACTION helper functions and functions that perform the actions
906 *******************************************************/
907 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
908 UINT* rc, BOOL force )
910 BOOL ret = FALSE;
911 BOOL run = force;
912 int i;
914 if (!run && !package->script->CurrentlyScripting)
915 run = TRUE;
917 if (!run)
919 if (strcmpW(action,szInstallFinalize) == 0 ||
920 strcmpW(action,szInstallExecute) == 0 ||
921 strcmpW(action,szInstallExecuteAgain) == 0)
922 run = TRUE;
925 i = 0;
926 while (StandardActions[i].action != NULL)
928 if (strcmpW(StandardActions[i].action, action)==0)
930 if (!run)
932 ui_actioninfo(package, action, TRUE, 0);
933 *rc = schedule_action(package,INSTALL_SCRIPT,action);
934 ui_actioninfo(package, action, FALSE, *rc);
936 else
938 ui_actionstart(package, action);
939 if (StandardActions[i].handler)
941 *rc = StandardActions[i].handler(package);
943 else
945 FIXME("unhandled standard action %s\n",debugstr_w(action));
946 *rc = ERROR_SUCCESS;
949 ret = TRUE;
950 break;
952 i++;
954 return ret;
957 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
958 UINT* rc, BOOL force )
960 BOOL ret=FALSE;
961 UINT arc;
963 arc = ACTION_CustomAction(package,action, force);
965 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
967 *rc = arc;
968 ret = TRUE;
970 return ret;
974 * A lot of actions are really important even if they don't do anything
975 * explicit... Lots of properties are set at the beginning of the installation
976 * CostFinalize does a bunch of work to translate the directories and such
978 * But until I get write access to the database that is hard, so I am going to
979 * hack it to see if I can get something to run.
981 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, BOOL force)
983 UINT rc = ERROR_SUCCESS;
984 BOOL handled;
986 TRACE("Performing action (%s)\n",debugstr_w(action));
988 handled = ACTION_HandleStandardAction(package, action, &rc, force);
990 if (!handled)
991 handled = ACTION_HandleCustomAction(package, action, &rc, force);
993 if (!handled)
995 FIXME("unhandled msi action %s\n",debugstr_w(action));
996 rc = ERROR_FUNCTION_NOT_CALLED;
999 return rc;
1002 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action)
1004 UINT rc = ERROR_SUCCESS;
1005 BOOL handled = FALSE;
1007 TRACE("Performing action (%s)\n",debugstr_w(action));
1009 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1011 if (!handled)
1012 handled = ACTION_HandleCustomAction(package, action, &rc, FALSE);
1014 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1015 handled = TRUE;
1017 if (!handled)
1019 FIXME("unhandled msi action %s\n",debugstr_w(action));
1020 rc = ERROR_FUNCTION_NOT_CALLED;
1023 return rc;
1028 * Actual Action Handlers
1031 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1033 MSIPACKAGE *package = (MSIPACKAGE*)param;
1034 LPCWSTR dir;
1035 LPWSTR full_path;
1036 MSIRECORD *uirow;
1037 MSIFOLDER *folder;
1039 dir = MSI_RecordGetString(row,1);
1040 if (!dir)
1042 ERR("Unable to get folder id\n");
1043 return ERROR_SUCCESS;
1046 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1047 if (!full_path)
1049 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1050 return ERROR_SUCCESS;
1053 TRACE("Folder is %s\n",debugstr_w(full_path));
1055 /* UI stuff */
1056 uirow = MSI_CreateRecord(1);
1057 MSI_RecordSetStringW(uirow,1,full_path);
1058 ui_actiondata(package,szCreateFolders,uirow);
1059 msiobj_release( &uirow->hdr );
1061 if (folder->State == 0)
1062 create_full_pathW(full_path);
1064 folder->State = 3;
1066 msi_free(full_path);
1067 return ERROR_SUCCESS;
1070 /* FIXME: probably should merge this with the above function */
1071 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1073 UINT rc = ERROR_SUCCESS;
1074 MSIFOLDER *folder;
1075 LPWSTR install_path;
1077 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1078 if (!install_path)
1079 return ERROR_FUNCTION_FAILED;
1081 /* create the path */
1082 if (folder->State == 0)
1084 create_full_pathW(install_path);
1085 folder->State = 2;
1087 msi_free(install_path);
1089 return rc;
1092 UINT msi_create_component_directories( MSIPACKAGE *package )
1094 MSICOMPONENT *comp;
1096 /* create all the folders required by the components are going to install */
1097 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1099 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1100 continue;
1101 msi_create_directory( package, comp->Directory );
1104 return ERROR_SUCCESS;
1108 * Also we cannot enable/disable components either, so for now I am just going
1109 * to do all the directories for all the components.
1111 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1113 static const WCHAR ExecSeqQuery[] =
1114 {'S','E','L','E','C','T',' ',
1115 '`','D','i','r','e','c','t','o','r','y','_','`',
1116 ' ','F','R','O','M',' ',
1117 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1118 UINT rc;
1119 MSIQUERY *view;
1121 /* create all the empty folders specified in the CreateFolder table */
1122 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1123 if (rc != ERROR_SUCCESS)
1124 return ERROR_SUCCESS;
1126 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1127 msiobj_release(&view->hdr);
1129 msi_create_component_directories( package );
1131 return rc;
1134 static UINT load_component( MSIRECORD *row, LPVOID param )
1136 MSIPACKAGE *package = param;
1137 MSICOMPONENT *comp;
1139 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1140 if (!comp)
1141 return ERROR_FUNCTION_FAILED;
1143 list_add_tail( &package->components, &comp->entry );
1145 /* fill in the data */
1146 comp->Component = msi_dup_record_field( row, 1 );
1148 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1150 comp->ComponentId = msi_dup_record_field( row, 2 );
1151 comp->Directory = msi_dup_record_field( row, 3 );
1152 comp->Attributes = MSI_RecordGetInteger(row,4);
1153 comp->Condition = msi_dup_record_field( row, 5 );
1154 comp->KeyPath = msi_dup_record_field( row, 6 );
1156 comp->Installed = INSTALLSTATE_UNKNOWN;
1157 msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1159 return ERROR_SUCCESS;
1162 static UINT load_all_components( MSIPACKAGE *package )
1164 static const WCHAR query[] = {
1165 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1166 '`','C','o','m','p','o','n','e','n','t','`',0 };
1167 MSIQUERY *view;
1168 UINT r;
1170 if (!list_empty(&package->components))
1171 return ERROR_SUCCESS;
1173 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1174 if (r != ERROR_SUCCESS)
1175 return r;
1177 r = MSI_IterateRecords(view, NULL, load_component, package);
1178 msiobj_release(&view->hdr);
1179 return r;
1182 typedef struct {
1183 MSIPACKAGE *package;
1184 MSIFEATURE *feature;
1185 } _ilfs;
1187 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1189 ComponentList *cl;
1191 cl = msi_alloc( sizeof (*cl) );
1192 if ( !cl )
1193 return ERROR_NOT_ENOUGH_MEMORY;
1194 cl->component = comp;
1195 list_add_tail( &feature->Components, &cl->entry );
1197 return ERROR_SUCCESS;
1200 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1202 FeatureList *fl;
1204 fl = msi_alloc( sizeof(*fl) );
1205 if ( !fl )
1206 return ERROR_NOT_ENOUGH_MEMORY;
1207 fl->feature = child;
1208 list_add_tail( &parent->Children, &fl->entry );
1210 return ERROR_SUCCESS;
1213 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1215 _ilfs* ilfs= (_ilfs*)param;
1216 LPCWSTR component;
1217 MSICOMPONENT *comp;
1219 component = MSI_RecordGetString(row,1);
1221 /* check to see if the component is already loaded */
1222 comp = get_loaded_component( ilfs->package, component );
1223 if (!comp)
1225 ERR("unknown component %s\n", debugstr_w(component));
1226 return ERROR_FUNCTION_FAILED;
1229 add_feature_component( ilfs->feature, comp );
1230 comp->Enabled = TRUE;
1232 return ERROR_SUCCESS;
1235 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1237 MSIFEATURE *feature;
1239 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1241 if ( !lstrcmpW( feature->Feature, name ) )
1242 return feature;
1245 return NULL;
1248 static UINT load_feature(MSIRECORD * row, LPVOID param)
1250 MSIPACKAGE* package = (MSIPACKAGE*)param;
1251 MSIFEATURE* feature;
1252 static const WCHAR Query1[] =
1253 {'S','E','L','E','C','T',' ',
1254 '`','C','o','m','p','o','n','e','n','t','_','`',
1255 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1256 'C','o','m','p','o','n','e','n','t','s','`',' ',
1257 'W','H','E','R','E',' ',
1258 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1259 MSIQUERY * view;
1260 UINT rc;
1261 _ilfs ilfs;
1263 /* fill in the data */
1265 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1266 if (!feature)
1267 return ERROR_NOT_ENOUGH_MEMORY;
1269 list_init( &feature->Children );
1270 list_init( &feature->Components );
1272 feature->Feature = msi_dup_record_field( row, 1 );
1274 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1276 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1277 feature->Title = msi_dup_record_field( row, 3 );
1278 feature->Description = msi_dup_record_field( row, 4 );
1280 if (!MSI_RecordIsNull(row,5))
1281 feature->Display = MSI_RecordGetInteger(row,5);
1283 feature->Level= MSI_RecordGetInteger(row,6);
1284 feature->Directory = msi_dup_record_field( row, 7 );
1285 feature->Attributes = MSI_RecordGetInteger(row,8);
1287 feature->Installed = INSTALLSTATE_UNKNOWN;
1288 msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1290 list_add_tail( &package->features, &feature->entry );
1292 /* load feature components */
1294 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1295 if (rc != ERROR_SUCCESS)
1296 return ERROR_SUCCESS;
1298 ilfs.package = package;
1299 ilfs.feature = feature;
1301 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1302 msiobj_release(&view->hdr);
1304 return ERROR_SUCCESS;
1307 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1309 MSIPACKAGE* package = (MSIPACKAGE*)param;
1310 MSIFEATURE *parent, *child;
1312 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1313 if (!child)
1314 return ERROR_FUNCTION_FAILED;
1316 if (!child->Feature_Parent)
1317 return ERROR_SUCCESS;
1319 parent = find_feature_by_name( package, child->Feature_Parent );
1320 if (!parent)
1321 return ERROR_FUNCTION_FAILED;
1323 add_feature_child( parent, child );
1324 return ERROR_SUCCESS;
1327 static UINT load_all_features( MSIPACKAGE *package )
1329 static const WCHAR query[] = {
1330 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1331 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1332 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1333 MSIQUERY *view;
1334 UINT r;
1336 if (!list_empty(&package->features))
1337 return ERROR_SUCCESS;
1339 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1340 if (r != ERROR_SUCCESS)
1341 return r;
1343 r = MSI_IterateRecords( view, NULL, load_feature, package );
1344 if (r != ERROR_SUCCESS)
1345 return r;
1347 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1348 msiobj_release( &view->hdr );
1350 return r;
1353 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1355 if (!p)
1356 return p;
1357 p = strchrW(p, ch);
1358 if (!p)
1359 return p;
1360 *p = 0;
1361 return p+1;
1364 static UINT load_file(MSIRECORD *row, LPVOID param)
1366 MSIPACKAGE* package = (MSIPACKAGE*)param;
1367 LPCWSTR component;
1368 MSIFILE *file;
1370 /* fill in the data */
1372 file = msi_alloc_zero( sizeof (MSIFILE) );
1373 if (!file)
1374 return ERROR_NOT_ENOUGH_MEMORY;
1376 file->File = msi_dup_record_field( row, 1 );
1378 component = MSI_RecordGetString( row, 2 );
1379 file->Component = get_loaded_component( package, component );
1381 if (!file->Component)
1382 ERR("Unfound Component %s\n",debugstr_w(component));
1384 file->FileName = msi_dup_record_field( row, 3 );
1385 reduce_to_longfilename( file->FileName );
1387 file->ShortName = msi_dup_record_field( row, 3 );
1388 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1390 file->FileSize = MSI_RecordGetInteger( row, 4 );
1391 file->Version = msi_dup_record_field( row, 5 );
1392 file->Language = msi_dup_record_field( row, 6 );
1393 file->Attributes = MSI_RecordGetInteger( row, 7 );
1394 file->Sequence = MSI_RecordGetInteger( row, 8 );
1396 file->state = msifs_invalid;
1398 /* if the compressed bits are not set in the file attributes,
1399 * then read the information from the package word count property
1401 if (file->Attributes & msidbFileAttributesCompressed)
1403 file->IsCompressed = TRUE;
1405 else if (file->Attributes & msidbFileAttributesNoncompressed)
1407 file->IsCompressed = FALSE;
1409 else
1411 file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1414 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1416 list_add_tail( &package->files, &file->entry );
1418 return ERROR_SUCCESS;
1421 static UINT load_all_files(MSIPACKAGE *package)
1423 MSIQUERY * view;
1424 UINT rc;
1425 static const WCHAR Query[] =
1426 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1427 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1428 '`','S','e','q','u','e','n','c','e','`', 0};
1430 if (!list_empty(&package->files))
1431 return ERROR_SUCCESS;
1433 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1434 if (rc != ERROR_SUCCESS)
1435 return ERROR_SUCCESS;
1437 rc = MSI_IterateRecords(view, NULL, load_file, package);
1438 msiobj_release(&view->hdr);
1440 return ERROR_SUCCESS;
1443 static UINT load_folder( MSIRECORD *row, LPVOID param )
1445 MSIPACKAGE *package = param;
1446 static const WCHAR szDot[] = { '.',0 };
1447 static WCHAR szEmpty[] = { 0 };
1448 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1449 MSIFOLDER *folder;
1451 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1452 if (!folder)
1453 return ERROR_NOT_ENOUGH_MEMORY;
1455 folder->Directory = msi_dup_record_field( row, 1 );
1457 TRACE("%s\n", debugstr_w(folder->Directory));
1459 p = msi_dup_record_field(row, 3);
1461 /* split src and target dir */
1462 tgt_short = p;
1463 src_short = folder_split_path( p, ':' );
1465 /* split the long and short paths */
1466 tgt_long = folder_split_path( tgt_short, '|' );
1467 src_long = folder_split_path( src_short, '|' );
1469 /* check for no-op dirs */
1470 if (!lstrcmpW(szDot, tgt_short))
1471 tgt_short = szEmpty;
1472 if (!lstrcmpW(szDot, src_short))
1473 src_short = szEmpty;
1475 if (!tgt_long)
1476 tgt_long = tgt_short;
1478 if (!src_short) {
1479 src_short = tgt_short;
1480 src_long = tgt_long;
1483 if (!src_long)
1484 src_long = src_short;
1486 /* FIXME: use the target short path too */
1487 folder->TargetDefault = strdupW(tgt_long);
1488 folder->SourceShortPath = strdupW(src_short);
1489 folder->SourceLongPath = strdupW(src_long);
1490 msi_free(p);
1492 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1493 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1494 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1496 folder->Parent = msi_dup_record_field( row, 2 );
1498 folder->Property = msi_dup_property( package, folder->Directory );
1500 list_add_tail( &package->folders, &folder->entry );
1502 TRACE("returning %p\n", folder);
1504 return ERROR_SUCCESS;
1507 static UINT load_all_folders( MSIPACKAGE *package )
1509 static const WCHAR query[] = {
1510 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1511 '`','D','i','r','e','c','t','o','r','y','`',0 };
1512 MSIQUERY *view;
1513 UINT r;
1515 if (!list_empty(&package->folders))
1516 return ERROR_SUCCESS;
1518 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1519 if (r != ERROR_SUCCESS)
1520 return r;
1522 r = MSI_IterateRecords(view, NULL, load_folder, package);
1523 msiobj_release(&view->hdr);
1524 return r;
1528 * I am not doing any of the costing functionality yet.
1529 * Mostly looking at doing the Component and Feature loading
1531 * The native MSI does A LOT of modification to tables here. Mostly adding
1532 * a lot of temporary columns to the Feature and Component tables.
1534 * note: Native msi also tracks the short filename. But I am only going to
1535 * track the long ones. Also looking at this directory table
1536 * it appears that the directory table does not get the parents
1537 * resolved base on property only based on their entries in the
1538 * directory table.
1540 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1542 static const WCHAR szCosting[] =
1543 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1544 static const WCHAR szZero[] = { '0', 0 };
1546 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
1547 return ERROR_SUCCESS;
1549 MSI_SetPropertyW(package, szCosting, szZero);
1550 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1552 load_all_components( package );
1553 load_all_features( package );
1554 load_all_files( package );
1555 load_all_folders( package );
1557 return ERROR_SUCCESS;
1560 static UINT execute_script(MSIPACKAGE *package, UINT script )
1562 int i;
1563 UINT rc = ERROR_SUCCESS;
1565 TRACE("Executing Script %i\n",script);
1567 if (!package->script)
1569 ERR("no script!\n");
1570 return ERROR_FUNCTION_FAILED;
1573 for (i = 0; i < package->script->ActionCount[script]; i++)
1575 LPWSTR action;
1576 action = package->script->Actions[script][i];
1577 ui_actionstart(package, action);
1578 TRACE("Executing Action (%s)\n",debugstr_w(action));
1579 rc = ACTION_PerformAction(package, action, TRUE);
1580 if (rc != ERROR_SUCCESS)
1581 break;
1583 msi_free_action_script(package, script);
1584 return rc;
1587 static UINT ACTION_FileCost(MSIPACKAGE *package)
1589 return ERROR_SUCCESS;
1592 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1594 MSICOMPONENT *comp;
1596 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1598 INSTALLSTATE res;
1600 if (!comp->ComponentId)
1601 continue;
1603 res = MsiGetComponentPathW( package->ProductCode,
1604 comp->ComponentId, NULL, NULL);
1605 if (res < 0)
1606 res = INSTALLSTATE_ABSENT;
1607 comp->Installed = res;
1611 /* scan for and update current install states */
1612 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1614 MSICOMPONENT *comp;
1615 MSIFEATURE *feature;
1617 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1619 ComponentList *cl;
1620 INSTALLSTATE res = INSTALLSTATE_ABSENT;
1622 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1624 comp= cl->component;
1626 if (!comp->ComponentId)
1628 res = INSTALLSTATE_ABSENT;
1629 break;
1632 if (res == INSTALLSTATE_ABSENT)
1633 res = comp->Installed;
1634 else
1636 if (res == comp->Installed)
1637 continue;
1639 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1640 res != INSTALLSTATE_SOURCE)
1642 res = INSTALLSTATE_INCOMPLETE;
1646 feature->Installed = res;
1650 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
1651 INSTALLSTATE state)
1653 static const WCHAR all[]={'A','L','L',0};
1654 LPWSTR override;
1655 MSIFEATURE *feature;
1657 override = msi_dup_property( package, property );
1658 if (!override)
1659 return FALSE;
1661 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1663 if (strcmpiW(override,all)==0)
1664 msi_feature_set_state( feature, state );
1665 else
1667 LPWSTR ptr = override;
1668 LPWSTR ptr2 = strchrW(override,',');
1670 while (ptr)
1672 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1673 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1675 msi_feature_set_state( feature, state );
1676 break;
1678 if (ptr2)
1680 ptr=ptr2+1;
1681 ptr2 = strchrW(ptr,',');
1683 else
1684 break;
1688 msi_free(override);
1690 return TRUE;
1693 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1695 int install_level;
1696 static const WCHAR szlevel[] =
1697 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1698 static const WCHAR szAddLocal[] =
1699 {'A','D','D','L','O','C','A','L',0};
1700 static const WCHAR szRemove[] =
1701 {'R','E','M','O','V','E',0};
1702 static const WCHAR szReinstall[] =
1703 {'R','E','I','N','S','T','A','L','L',0};
1704 BOOL override = FALSE;
1705 MSICOMPONENT* component;
1706 MSIFEATURE *feature;
1709 /* I do not know if this is where it should happen.. but */
1711 TRACE("Checking Install Level\n");
1713 install_level = msi_get_property_int( package, szlevel, 1 );
1715 /* ok here is the _real_ rub
1716 * all these activation/deactivation things happen in order and things
1717 * later on the list override things earlier on the list.
1718 * 1) INSTALLLEVEL processing
1719 * 2) ADDLOCAL
1720 * 3) REMOVE
1721 * 4) ADDSOURCE
1722 * 5) ADDDEFAULT
1723 * 6) REINSTALL
1724 * 7) COMPADDLOCAL
1725 * 8) COMPADDSOURCE
1726 * 9) FILEADDLOCAL
1727 * 10) FILEADDSOURCE
1728 * 11) FILEADDDEFAULT
1729 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1730 * ignored for all the features. seems strange, especially since it is not
1731 * documented anywhere, but it is how it works.
1733 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1734 * REMOVE are the big ones, since we don't handle administrative installs
1735 * yet anyway.
1737 override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1738 override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1739 override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1741 if (!override)
1743 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1745 BOOL feature_state = ((feature->Level > 0) &&
1746 (feature->Level <= install_level));
1748 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1750 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1751 msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1752 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1753 msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1754 else
1755 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1759 /* disable child features of unselected parent features */
1760 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1762 FeatureList *fl;
1764 if (feature->Level > 0 && feature->Level <= install_level)
1765 continue;
1767 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1768 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1771 else
1773 /* set the Preselected Property */
1774 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1775 static const WCHAR szOne[] = { '1', 0 };
1777 MSI_SetPropertyW(package,szPreselected,szOne);
1781 * now we want to enable or disable components base on feature
1784 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1786 ComponentList *cl;
1788 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1789 debugstr_w(feature->Feature), feature->Installed, feature->Action);
1791 /* features with components that have compressed files are made local */
1792 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1794 if (cl->component->Enabled &&
1795 cl->component->ForceLocalState &&
1796 feature->Action == INSTALLSTATE_SOURCE)
1798 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1799 break;
1803 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1805 component = cl->component;
1807 if (!component->Enabled)
1808 continue;
1810 switch (feature->Action)
1812 case INSTALLSTATE_ADVERTISED:
1813 component->hasAdvertiseFeature = 1;
1814 break;
1815 case INSTALLSTATE_SOURCE:
1816 component->hasSourceFeature = 1;
1817 break;
1818 case INSTALLSTATE_LOCAL:
1819 component->hasLocalFeature = 1;
1820 break;
1821 case INSTALLSTATE_DEFAULT:
1822 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1823 component->hasAdvertiseFeature = 1;
1824 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1825 component->hasSourceFeature = 1;
1826 else
1827 component->hasLocalFeature = 1;
1828 break;
1829 default:
1830 break;
1835 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1837 /* if the component isn't enabled, leave it alone */
1838 if (!component->Enabled)
1839 continue;
1841 /* check if it's local or source */
1842 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1843 (component->hasLocalFeature || component->hasSourceFeature))
1845 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1846 !component->ForceLocalState)
1847 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1848 else
1849 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1850 continue;
1853 /* if any feature is local, the component must be local too */
1854 if (component->hasLocalFeature)
1856 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1857 continue;
1860 if (component->hasSourceFeature)
1862 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1863 continue;
1866 if (component->hasAdvertiseFeature)
1868 msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1869 continue;
1872 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1875 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1877 if (component->Action == INSTALLSTATE_DEFAULT)
1879 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1880 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1883 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1884 debugstr_w(component->Component), component->Installed, component->Action);
1888 return ERROR_SUCCESS;
1891 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1893 MSIPACKAGE *package = (MSIPACKAGE*)param;
1894 LPCWSTR name;
1895 LPWSTR path;
1897 name = MSI_RecordGetString(row,1);
1899 /* This helper function now does ALL the work */
1900 TRACE("Dir %s ...\n",debugstr_w(name));
1901 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1902 TRACE("resolves to %s\n",debugstr_w(path));
1903 msi_free(path);
1905 return ERROR_SUCCESS;
1908 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1910 MSIPACKAGE *package = (MSIPACKAGE*)param;
1911 LPCWSTR name;
1912 MSIFEATURE *feature;
1914 name = MSI_RecordGetString( row, 1 );
1916 feature = get_loaded_feature( package, name );
1917 if (!feature)
1918 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1919 else
1921 LPCWSTR Condition;
1922 Condition = MSI_RecordGetString(row,3);
1924 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1926 int level = MSI_RecordGetInteger(row,2);
1927 TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
1928 feature->Level = level;
1931 return ERROR_SUCCESS;
1934 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1936 static const WCHAR name_fmt[] =
1937 {'%','u','.','%','u','.','%','u','.','%','u',0};
1938 static WCHAR name[] = {'\\',0};
1939 VS_FIXEDFILEINFO *lpVer;
1940 WCHAR filever[0x100];
1941 LPVOID version;
1942 DWORD versize;
1943 DWORD handle;
1944 UINT sz;
1946 TRACE("%s\n", debugstr_w(filename));
1948 versize = GetFileVersionInfoSizeW( filename, &handle );
1949 if (!versize)
1950 return NULL;
1952 version = msi_alloc( versize );
1953 GetFileVersionInfoW( filename, 0, versize, version );
1955 VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz );
1956 msi_free( version );
1958 sprintfW( filever, name_fmt,
1959 HIWORD(lpVer->dwFileVersionMS),
1960 LOWORD(lpVer->dwFileVersionMS),
1961 HIWORD(lpVer->dwFileVersionLS),
1962 LOWORD(lpVer->dwFileVersionLS));
1964 return strdupW( filever );
1967 static UINT msi_check_file_install_states( MSIPACKAGE *package )
1969 LPWSTR file_version;
1970 MSIFILE *file;
1972 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1974 MSICOMPONENT* comp = file->Component;
1975 LPWSTR p;
1977 if (!comp)
1978 continue;
1980 if (file->IsCompressed)
1981 comp->ForceLocalState = TRUE;
1983 /* calculate target */
1984 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
1986 msi_free(file->TargetPath);
1988 TRACE("file %s is named %s\n",
1989 debugstr_w(file->File), debugstr_w(file->FileName));
1991 file->TargetPath = build_directory_name(2, p, file->FileName);
1993 msi_free(p);
1995 TRACE("file %s resolves to %s\n",
1996 debugstr_w(file->File), debugstr_w(file->TargetPath));
1998 /* don't check files of components that aren't installed */
1999 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2000 comp->Installed == INSTALLSTATE_ABSENT)
2002 file->state = msifs_missing; /* assume files are missing */
2003 continue;
2006 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2008 file->state = msifs_missing;
2009 comp->Cost += file->FileSize;
2010 comp->Installed = INSTALLSTATE_INCOMPLETE;
2011 continue;
2014 if (file->Version &&
2015 (file_version = msi_get_disk_file_version( file->TargetPath )))
2017 TRACE("new %s old %s\n", debugstr_w(file->Version),
2018 debugstr_w(file_version));
2019 /* FIXME: seems like a bad way to compare version numbers */
2020 if (lstrcmpiW(file_version, file->Version)<0)
2022 file->state = msifs_overwrite;
2023 comp->Cost += file->FileSize;
2024 comp->Installed = INSTALLSTATE_INCOMPLETE;
2026 else
2027 file->state = msifs_present;
2028 msi_free( file_version );
2030 else
2031 file->state = msifs_present;
2034 return ERROR_SUCCESS;
2038 * A lot is done in this function aside from just the costing.
2039 * The costing needs to be implemented at some point but for now I am going
2040 * to focus on the directory building
2043 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2045 static const WCHAR ExecSeqQuery[] =
2046 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2047 '`','D','i','r','e','c','t','o','r','y','`',0};
2048 static const WCHAR ConditionQuery[] =
2049 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2050 '`','C','o','n','d','i','t','i','o','n','`',0};
2051 static const WCHAR szCosting[] =
2052 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2053 static const WCHAR szlevel[] =
2054 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2055 static const WCHAR szOne[] = { '1', 0 };
2056 MSICOMPONENT *comp;
2057 UINT rc;
2058 MSIQUERY * view;
2059 LPWSTR level;
2061 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2062 return ERROR_SUCCESS;
2064 TRACE("Building Directory properties\n");
2066 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2067 if (rc == ERROR_SUCCESS)
2069 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2070 package);
2071 msiobj_release(&view->hdr);
2074 /* read components states from the registry */
2075 ACTION_GetComponentInstallStates(package);
2077 TRACE("File calculations\n");
2078 msi_check_file_install_states( package );
2080 TRACE("Evaluating Condition Table\n");
2082 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2083 if (rc == ERROR_SUCCESS)
2085 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2086 package);
2087 msiobj_release(&view->hdr);
2090 TRACE("Enabling or Disabling Components\n");
2091 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2093 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2095 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2096 comp->Enabled = FALSE;
2100 MSI_SetPropertyW(package,szCosting,szOne);
2101 /* set default run level if not set */
2102 level = msi_dup_property( package, szlevel );
2103 if (!level)
2104 MSI_SetPropertyW(package,szlevel, szOne);
2105 msi_free(level);
2107 ACTION_UpdateFeatureInstallStates(package);
2109 return MSI_SetFeatureStates(package);
2112 /* OK this value is "interpreted" and then formatted based on the
2113 first few characters */
2114 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2115 DWORD *size)
2117 LPSTR data = NULL;
2118 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2120 if (value[1]=='x')
2122 LPWSTR ptr;
2123 CHAR byte[5];
2124 LPWSTR deformated = NULL;
2125 int count;
2127 deformat_string(package, &value[2], &deformated);
2129 /* binary value type */
2130 ptr = deformated;
2131 *type = REG_BINARY;
2132 if (strlenW(ptr)%2)
2133 *size = (strlenW(ptr)/2)+1;
2134 else
2135 *size = strlenW(ptr)/2;
2137 data = msi_alloc(*size);
2139 byte[0] = '0';
2140 byte[1] = 'x';
2141 byte[4] = 0;
2142 count = 0;
2143 /* if uneven pad with a zero in front */
2144 if (strlenW(ptr)%2)
2146 byte[2]= '0';
2147 byte[3]= *ptr;
2148 ptr++;
2149 data[count] = (BYTE)strtol(byte,NULL,0);
2150 count ++;
2151 TRACE("Uneven byte count\n");
2153 while (*ptr)
2155 byte[2]= *ptr;
2156 ptr++;
2157 byte[3]= *ptr;
2158 ptr++;
2159 data[count] = (BYTE)strtol(byte,NULL,0);
2160 count ++;
2162 msi_free(deformated);
2164 TRACE("Data %i bytes(%i)\n",*size,count);
2166 else
2168 LPWSTR deformated;
2169 LPWSTR p;
2170 DWORD d = 0;
2171 deformat_string(package, &value[1], &deformated);
2173 *type=REG_DWORD;
2174 *size = sizeof(DWORD);
2175 data = msi_alloc(*size);
2176 p = deformated;
2177 if (*p == '-')
2178 p++;
2179 while (*p)
2181 if ( (*p < '0') || (*p > '9') )
2182 break;
2183 d *= 10;
2184 d += (*p - '0');
2185 p++;
2187 if (deformated[0] == '-')
2188 d = -d;
2189 *(LPDWORD)data = d;
2190 TRACE("DWORD %i\n",*(LPDWORD)data);
2192 msi_free(deformated);
2195 else
2197 static const WCHAR szMulti[] = {'[','~',']',0};
2198 LPCWSTR ptr;
2199 *type=REG_SZ;
2201 if (value[0]=='#')
2203 if (value[1]=='%')
2205 ptr = &value[2];
2206 *type=REG_EXPAND_SZ;
2208 else
2209 ptr = &value[1];
2211 else
2212 ptr=value;
2214 if (strstrW(value,szMulti))
2215 *type = REG_MULTI_SZ;
2217 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2219 return data;
2222 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2224 MSIPACKAGE *package = (MSIPACKAGE*)param;
2225 static const WCHAR szHCR[] =
2226 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2227 'R','O','O','T','\\',0};
2228 static const WCHAR szHCU[] =
2229 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2230 'U','S','E','R','\\',0};
2231 static const WCHAR szHLM[] =
2232 {'H','K','E','Y','_','L','O','C','A','L','_',
2233 'M','A','C','H','I','N','E','\\',0};
2234 static const WCHAR szHU[] =
2235 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2237 LPSTR value_data = NULL;
2238 HKEY root_key, hkey;
2239 DWORD type,size;
2240 LPWSTR deformated;
2241 LPCWSTR szRoot, component, name, key, value;
2242 MSICOMPONENT *comp;
2243 MSIRECORD * uirow;
2244 LPWSTR uikey;
2245 INT root;
2246 BOOL check_first = FALSE;
2247 UINT rc;
2249 ui_progress(package,2,0,0,0);
2251 value = NULL;
2252 key = NULL;
2253 uikey = NULL;
2254 name = NULL;
2256 component = MSI_RecordGetString(row, 6);
2257 comp = get_loaded_component(package,component);
2258 if (!comp)
2259 return ERROR_SUCCESS;
2261 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2263 TRACE("Skipping write due to disabled component %s\n",
2264 debugstr_w(component));
2266 comp->Action = comp->Installed;
2268 return ERROR_SUCCESS;
2271 comp->Action = INSTALLSTATE_LOCAL;
2273 name = MSI_RecordGetString(row, 4);
2274 if( MSI_RecordIsNull(row,5) && name )
2276 /* null values can have special meanings */
2277 if (name[0]=='-' && name[1] == 0)
2278 return ERROR_SUCCESS;
2279 else if ((name[0]=='+' && name[1] == 0) ||
2280 (name[0] == '*' && name[1] == 0))
2281 name = NULL;
2282 check_first = TRUE;
2285 root = MSI_RecordGetInteger(row,2);
2286 key = MSI_RecordGetString(row, 3);
2288 /* get the root key */
2289 switch (root)
2291 case -1:
2293 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2294 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2295 if (all_users && all_users[0] == '1')
2297 root_key = HKEY_LOCAL_MACHINE;
2298 szRoot = szHLM;
2300 else
2302 root_key = HKEY_CURRENT_USER;
2303 szRoot = szHCU;
2305 msi_free(all_users);
2307 break;
2308 case 0: root_key = HKEY_CLASSES_ROOT;
2309 szRoot = szHCR;
2310 break;
2311 case 1: root_key = HKEY_CURRENT_USER;
2312 szRoot = szHCU;
2313 break;
2314 case 2: root_key = HKEY_LOCAL_MACHINE;
2315 szRoot = szHLM;
2316 break;
2317 case 3: root_key = HKEY_USERS;
2318 szRoot = szHU;
2319 break;
2320 default:
2321 ERR("Unknown root %i\n",root);
2322 root_key=NULL;
2323 szRoot = NULL;
2324 break;
2326 if (!root_key)
2327 return ERROR_SUCCESS;
2329 deformat_string(package, key , &deformated);
2330 size = strlenW(deformated) + strlenW(szRoot) + 1;
2331 uikey = msi_alloc(size*sizeof(WCHAR));
2332 strcpyW(uikey,szRoot);
2333 strcatW(uikey,deformated);
2335 if (RegCreateKeyW( root_key, deformated, &hkey))
2337 ERR("Could not create key %s\n",debugstr_w(deformated));
2338 msi_free(deformated);
2339 msi_free(uikey);
2340 return ERROR_SUCCESS;
2342 msi_free(deformated);
2344 value = MSI_RecordGetString(row,5);
2345 if (value)
2346 value_data = parse_value(package, value, &type, &size);
2347 else
2349 static const WCHAR szEmpty[] = {0};
2350 value_data = (LPSTR)strdupW(szEmpty);
2351 size = 0;
2352 type = REG_SZ;
2355 deformat_string(package, name, &deformated);
2357 /* get the double nulls to terminate SZ_MULTI */
2358 if (type == REG_MULTI_SZ)
2359 size +=sizeof(WCHAR);
2361 if (!check_first)
2363 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2364 debugstr_w(uikey));
2365 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2367 else
2369 DWORD sz = 0;
2370 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2371 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2373 TRACE("value %s of %s checked already exists\n",
2374 debugstr_w(deformated), debugstr_w(uikey));
2376 else
2378 TRACE("Checked and setting value %s of %s\n",
2379 debugstr_w(deformated), debugstr_w(uikey));
2380 if (deformated || size)
2381 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2384 RegCloseKey(hkey);
2386 uirow = MSI_CreateRecord(3);
2387 MSI_RecordSetStringW(uirow,2,deformated);
2388 MSI_RecordSetStringW(uirow,1,uikey);
2390 if (type == REG_SZ)
2391 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2392 else
2393 MSI_RecordSetStringW(uirow,3,value);
2395 ui_actiondata(package,szWriteRegistryValues,uirow);
2396 msiobj_release( &uirow->hdr );
2398 msi_free(value_data);
2399 msi_free(deformated);
2400 msi_free(uikey);
2402 return ERROR_SUCCESS;
2405 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2407 UINT rc;
2408 MSIQUERY * view;
2409 static const WCHAR ExecSeqQuery[] =
2410 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2411 '`','R','e','g','i','s','t','r','y','`',0 };
2413 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2414 if (rc != ERROR_SUCCESS)
2415 return ERROR_SUCCESS;
2417 /* increment progress bar each time action data is sent */
2418 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2420 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2422 msiobj_release(&view->hdr);
2423 return rc;
2426 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2428 package->script->CurrentlyScripting = TRUE;
2430 return ERROR_SUCCESS;
2434 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2436 MSICOMPONENT *comp;
2437 DWORD progress = 0;
2438 DWORD total = 0;
2439 static const WCHAR q1[]=
2440 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2441 '`','R','e','g','i','s','t','r','y','`',0};
2442 UINT rc;
2443 MSIQUERY * view;
2444 MSIFEATURE *feature;
2445 MSIFILE *file;
2447 TRACE("InstallValidate\n");
2449 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2450 if (rc == ERROR_SUCCESS)
2452 MSI_IterateRecords( view, &progress, NULL, package );
2453 msiobj_release( &view->hdr );
2454 total += progress * REG_PROGRESS_VALUE;
2457 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2458 total += COMPONENT_PROGRESS_VALUE;
2460 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2461 total += file->FileSize;
2463 ui_progress(package,0,total,0,0);
2465 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2467 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2468 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2469 feature->ActionRequest);
2472 return ERROR_SUCCESS;
2475 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2477 MSIPACKAGE* package = (MSIPACKAGE*)param;
2478 LPCWSTR cond = NULL;
2479 LPCWSTR message = NULL;
2480 static const WCHAR title[]=
2481 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2483 cond = MSI_RecordGetString(row,1);
2485 if (MSI_EvaluateConditionW(package,cond) != MSICONDITION_TRUE)
2487 LPWSTR deformated;
2488 message = MSI_RecordGetString(row,2);
2489 deformat_string(package,message,&deformated);
2490 MessageBoxW(NULL,deformated,title,MB_OK);
2491 msi_free(deformated);
2492 return ERROR_FUNCTION_FAILED;
2495 return ERROR_SUCCESS;
2498 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2500 UINT rc;
2501 MSIQUERY * view = NULL;
2502 static const WCHAR ExecSeqQuery[] =
2503 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2504 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2506 TRACE("Checking launch conditions\n");
2508 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2509 if (rc != ERROR_SUCCESS)
2510 return ERROR_SUCCESS;
2512 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2513 msiobj_release(&view->hdr);
2515 return rc;
2518 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2521 if (!cmp->KeyPath)
2522 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2524 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2526 MSIRECORD * row = 0;
2527 UINT root,len;
2528 LPWSTR deformated,buffer,deformated_name;
2529 LPCWSTR key,name;
2530 static const WCHAR ExecSeqQuery[] =
2531 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2532 '`','R','e','g','i','s','t','r','y','`',' ',
2533 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2534 ' ','=',' ' ,'\'','%','s','\'',0 };
2535 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2536 static const WCHAR fmt2[]=
2537 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2539 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2540 if (!row)
2541 return NULL;
2543 root = MSI_RecordGetInteger(row,2);
2544 key = MSI_RecordGetString(row, 3);
2545 name = MSI_RecordGetString(row, 4);
2546 deformat_string(package, key , &deformated);
2547 deformat_string(package, name, &deformated_name);
2549 len = strlenW(deformated) + 6;
2550 if (deformated_name)
2551 len+=strlenW(deformated_name);
2553 buffer = msi_alloc( len *sizeof(WCHAR));
2555 if (deformated_name)
2556 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2557 else
2558 sprintfW(buffer,fmt,root,deformated);
2560 msi_free(deformated);
2561 msi_free(deformated_name);
2562 msiobj_release(&row->hdr);
2564 return buffer;
2566 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2568 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2569 return NULL;
2571 else
2573 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2575 if (file)
2576 return strdupW( file->TargetPath );
2578 return NULL;
2581 static HKEY openSharedDLLsKey(void)
2583 HKEY hkey=0;
2584 static const WCHAR path[] =
2585 {'S','o','f','t','w','a','r','e','\\',
2586 'M','i','c','r','o','s','o','f','t','\\',
2587 'W','i','n','d','o','w','s','\\',
2588 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2589 'S','h','a','r','e','d','D','L','L','s',0};
2591 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2592 return hkey;
2595 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2597 HKEY hkey;
2598 DWORD count=0;
2599 DWORD type;
2600 DWORD sz = sizeof(count);
2601 DWORD rc;
2603 hkey = openSharedDLLsKey();
2604 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2605 if (rc != ERROR_SUCCESS)
2606 count = 0;
2607 RegCloseKey(hkey);
2608 return count;
2611 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2613 HKEY hkey;
2615 hkey = openSharedDLLsKey();
2616 if (count > 0)
2617 msi_reg_set_val_dword( hkey, path, count );
2618 else
2619 RegDeleteValueW(hkey,path);
2620 RegCloseKey(hkey);
2621 return count;
2625 * Return TRUE if the count should be written out and FALSE if not
2627 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2629 MSIFEATURE *feature;
2630 INT count = 0;
2631 BOOL write = FALSE;
2633 /* only refcount DLLs */
2634 if (comp->KeyPath == NULL ||
2635 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2636 comp->Attributes & msidbComponentAttributesODBCDataSource)
2637 write = FALSE;
2638 else
2640 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2641 write = (count > 0);
2643 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2644 write = TRUE;
2647 /* increment counts */
2648 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2650 ComponentList *cl;
2652 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2653 continue;
2655 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2657 if ( cl->component == comp )
2658 count++;
2662 /* decrement counts */
2663 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2665 ComponentList *cl;
2667 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2668 continue;
2670 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2672 if ( cl->component == comp )
2673 count--;
2677 /* ref count all the files in the component */
2678 if (write)
2680 MSIFILE *file;
2682 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2684 if (file->Component == comp)
2685 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2689 /* add a count for permenent */
2690 if (comp->Attributes & msidbComponentAttributesPermanent)
2691 count ++;
2693 comp->RefCount = count;
2695 if (write)
2696 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2700 * Ok further analysis makes me think that this work is
2701 * actually done in the PublishComponents and PublishFeatures
2702 * step, and not here. It appears like the keypath and all that is
2703 * resolved in this step, however actually written in the Publish steps.
2704 * But we will leave it here for now because it is unclear
2706 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2708 WCHAR squished_pc[GUID_SIZE];
2709 WCHAR squished_cc[GUID_SIZE];
2710 UINT rc;
2711 MSICOMPONENT *comp;
2712 HKEY hkey=0,hkey2=0;
2714 /* writes the Component and Features values to the registry */
2716 rc = MSIREG_OpenComponents(&hkey);
2717 if (rc != ERROR_SUCCESS)
2718 return rc;
2720 squash_guid(package->ProductCode,squished_pc);
2721 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2723 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2725 MSIRECORD * uirow;
2727 ui_progress(package,2,0,0,0);
2728 if (!comp->ComponentId)
2729 continue;
2731 squash_guid(comp->ComponentId,squished_cc);
2733 msi_free(comp->FullKeypath);
2734 comp->FullKeypath = resolve_keypath( package, comp );
2736 /* do the refcounting */
2737 ACTION_RefCountComponent( package, comp );
2739 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2740 debugstr_w(comp->Component),
2741 debugstr_w(squished_cc),
2742 debugstr_w(comp->FullKeypath),
2743 comp->RefCount);
2745 * Write the keypath out if the component is to be registered
2746 * and delete the key if the component is to be deregistered
2748 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2750 rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2751 if (rc != ERROR_SUCCESS)
2752 continue;
2754 if (!comp->FullKeypath)
2755 continue;
2757 msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2759 if (comp->Attributes & msidbComponentAttributesPermanent)
2761 static const WCHAR szPermKey[] =
2762 { '0','0','0','0','0','0','0','0','0','0','0','0',
2763 '0','0','0','0','0','0','0','0','0','0','0','0',
2764 '0','0','0','0','0','0','0','0',0 };
2766 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2769 RegCloseKey(hkey2);
2771 else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2773 DWORD res;
2775 rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2776 if (rc != ERROR_SUCCESS)
2777 continue;
2779 RegDeleteValueW(hkey2,squished_pc);
2781 /* if the key is empty delete it */
2782 res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2783 RegCloseKey(hkey2);
2784 if (res == ERROR_NO_MORE_ITEMS)
2785 RegDeleteKeyW(hkey,squished_cc);
2789 /* UI stuff */
2790 uirow = MSI_CreateRecord(3);
2791 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2792 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2793 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2794 ui_actiondata(package,szProcessComponents,uirow);
2795 msiobj_release( &uirow->hdr );
2797 RegCloseKey(hkey);
2798 return rc;
2801 typedef struct {
2802 CLSID clsid;
2803 LPWSTR source;
2805 LPWSTR path;
2806 ITypeLib *ptLib;
2807 } typelib_struct;
2809 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2810 LPWSTR lpszName, LONG_PTR lParam)
2812 TLIBATTR *attr;
2813 typelib_struct *tl_struct = (typelib_struct*) lParam;
2814 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2815 int sz;
2816 HRESULT res;
2818 if (!IS_INTRESOURCE(lpszName))
2820 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2821 return TRUE;
2824 sz = strlenW(tl_struct->source)+4;
2825 sz *= sizeof(WCHAR);
2827 if ((INT_PTR)lpszName == 1)
2828 tl_struct->path = strdupW(tl_struct->source);
2829 else
2831 tl_struct->path = msi_alloc(sz);
2832 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2835 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2836 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2837 if (!SUCCEEDED(res))
2839 msi_free(tl_struct->path);
2840 tl_struct->path = NULL;
2842 return TRUE;
2845 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2846 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2848 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2849 return FALSE;
2852 msi_free(tl_struct->path);
2853 tl_struct->path = NULL;
2855 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2856 ITypeLib_Release(tl_struct->ptLib);
2858 return TRUE;
2861 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2863 MSIPACKAGE* package = (MSIPACKAGE*)param;
2864 LPCWSTR component;
2865 MSICOMPONENT *comp;
2866 MSIFILE *file;
2867 typelib_struct tl_struct;
2868 HMODULE module;
2869 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2871 component = MSI_RecordGetString(row,3);
2872 comp = get_loaded_component(package,component);
2873 if (!comp)
2874 return ERROR_SUCCESS;
2876 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2878 TRACE("Skipping typelib reg due to disabled component\n");
2880 comp->Action = comp->Installed;
2882 return ERROR_SUCCESS;
2885 comp->Action = INSTALLSTATE_LOCAL;
2887 file = get_loaded_file( package, comp->KeyPath );
2888 if (!file)
2889 return ERROR_SUCCESS;
2891 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2892 if (module)
2894 LPCWSTR guid;
2895 guid = MSI_RecordGetString(row,1);
2896 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2897 tl_struct.source = strdupW( file->TargetPath );
2898 tl_struct.path = NULL;
2900 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2901 (LONG_PTR)&tl_struct);
2903 if (tl_struct.path)
2905 LPWSTR help = NULL;
2906 LPCWSTR helpid;
2907 HRESULT res;
2909 helpid = MSI_RecordGetString(row,6);
2911 if (helpid)
2912 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
2913 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2914 msi_free(help);
2916 if (!SUCCEEDED(res))
2917 ERR("Failed to register type library %s\n",
2918 debugstr_w(tl_struct.path));
2919 else
2921 ui_actiondata(package,szRegisterTypeLibraries,row);
2923 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2926 ITypeLib_Release(tl_struct.ptLib);
2927 msi_free(tl_struct.path);
2929 else
2930 ERR("Failed to load type library %s\n",
2931 debugstr_w(tl_struct.source));
2933 FreeLibrary(module);
2934 msi_free(tl_struct.source);
2936 else
2937 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
2939 return ERROR_SUCCESS;
2942 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
2945 * OK this is a bit confusing.. I am given a _Component key and I believe
2946 * that the file that is being registered as a type library is the "key file
2947 * of that component" which I interpret to mean "The file in the KeyPath of
2948 * that component".
2950 UINT rc;
2951 MSIQUERY * view;
2952 static const WCHAR Query[] =
2953 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2954 '`','T','y','p','e','L','i','b','`',0};
2956 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2957 if (rc != ERROR_SUCCESS)
2958 return ERROR_SUCCESS;
2960 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
2961 msiobj_release(&view->hdr);
2962 return rc;
2965 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
2967 MSIPACKAGE *package = (MSIPACKAGE*)param;
2968 LPWSTR target_file, target_folder, filename;
2969 LPCWSTR buffer, extension;
2970 MSICOMPONENT *comp;
2971 static const WCHAR szlnk[]={'.','l','n','k',0};
2972 IShellLinkW *sl = NULL;
2973 IPersistFile *pf = NULL;
2974 HRESULT res;
2976 buffer = MSI_RecordGetString(row,4);
2977 comp = get_loaded_component(package,buffer);
2978 if (!comp)
2979 return ERROR_SUCCESS;
2981 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
2983 TRACE("Skipping shortcut creation due to disabled component\n");
2985 comp->Action = comp->Installed;
2987 return ERROR_SUCCESS;
2990 comp->Action = INSTALLSTATE_LOCAL;
2992 ui_actiondata(package,szCreateShortcuts,row);
2994 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
2995 &IID_IShellLinkW, (LPVOID *) &sl );
2997 if (FAILED( res ))
2999 ERR("CLSID_ShellLink not available\n");
3000 goto err;
3003 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3004 if (FAILED( res ))
3006 ERR("QueryInterface(IID_IPersistFile) failed\n");
3007 goto err;
3010 buffer = MSI_RecordGetString(row,2);
3011 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3013 /* may be needed because of a bug somehwere else */
3014 create_full_pathW(target_folder);
3016 filename = msi_dup_record_field( row, 3 );
3017 reduce_to_longfilename(filename);
3019 extension = strchrW(filename,'.');
3020 if (!extension || strcmpiW(extension,szlnk))
3022 int len = strlenW(filename);
3023 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3024 memcpy(filename + len, szlnk, sizeof(szlnk));
3026 target_file = build_directory_name(2, target_folder, filename);
3027 msi_free(target_folder);
3028 msi_free(filename);
3030 buffer = MSI_RecordGetString(row,5);
3031 if (strchrW(buffer,'['))
3033 LPWSTR deformated;
3034 deformat_string(package,buffer,&deformated);
3035 IShellLinkW_SetPath(sl,deformated);
3036 msi_free(deformated);
3038 else
3040 FIXME("poorly handled shortcut format, advertised shortcut\n");
3041 IShellLinkW_SetPath(sl,comp->FullKeypath);
3044 if (!MSI_RecordIsNull(row,6))
3046 LPWSTR deformated;
3047 buffer = MSI_RecordGetString(row,6);
3048 deformat_string(package,buffer,&deformated);
3049 IShellLinkW_SetArguments(sl,deformated);
3050 msi_free(deformated);
3053 if (!MSI_RecordIsNull(row,7))
3055 buffer = MSI_RecordGetString(row,7);
3056 IShellLinkW_SetDescription(sl,buffer);
3059 if (!MSI_RecordIsNull(row,8))
3060 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3062 if (!MSI_RecordIsNull(row,9))
3064 LPWSTR Path;
3065 INT index;
3067 buffer = MSI_RecordGetString(row,9);
3069 Path = build_icon_path(package,buffer);
3070 index = MSI_RecordGetInteger(row,10);
3072 /* no value means 0 */
3073 if (index == MSI_NULL_INTEGER)
3074 index = 0;
3076 IShellLinkW_SetIconLocation(sl,Path,index);
3077 msi_free(Path);
3080 if (!MSI_RecordIsNull(row,11))
3081 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3083 if (!MSI_RecordIsNull(row,12))
3085 LPWSTR Path;
3086 buffer = MSI_RecordGetString(row,12);
3087 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3088 if (Path)
3089 IShellLinkW_SetWorkingDirectory(sl,Path);
3090 msi_free(Path);
3093 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3094 IPersistFile_Save(pf,target_file,FALSE);
3096 msi_free(target_file);
3098 err:
3099 if (pf)
3100 IPersistFile_Release( pf );
3101 if (sl)
3102 IShellLinkW_Release( sl );
3104 return ERROR_SUCCESS;
3107 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3109 UINT rc;
3110 HRESULT res;
3111 MSIQUERY * view;
3112 static const WCHAR Query[] =
3113 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3114 '`','S','h','o','r','t','c','u','t','`',0};
3116 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3117 if (rc != ERROR_SUCCESS)
3118 return ERROR_SUCCESS;
3120 res = CoInitialize( NULL );
3121 if (FAILED (res))
3123 ERR("CoInitialize failed\n");
3124 return ERROR_FUNCTION_FAILED;
3127 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3128 msiobj_release(&view->hdr);
3130 CoUninitialize();
3132 return rc;
3135 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3137 MSIPACKAGE* package = (MSIPACKAGE*)param;
3138 HANDLE the_file;
3139 LPWSTR FilePath;
3140 LPCWSTR FileName;
3141 CHAR buffer[1024];
3142 DWORD sz;
3143 UINT rc;
3144 MSIRECORD *uirow;
3146 FileName = MSI_RecordGetString(row,1);
3147 if (!FileName)
3149 ERR("Unable to get FileName\n");
3150 return ERROR_SUCCESS;
3153 FilePath = build_icon_path(package,FileName);
3155 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3157 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3158 FILE_ATTRIBUTE_NORMAL, NULL);
3160 if (the_file == INVALID_HANDLE_VALUE)
3162 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3163 msi_free(FilePath);
3164 return ERROR_SUCCESS;
3169 DWORD write;
3170 sz = 1024;
3171 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3172 if (rc != ERROR_SUCCESS)
3174 ERR("Failed to get stream\n");
3175 CloseHandle(the_file);
3176 DeleteFileW(FilePath);
3177 break;
3179 WriteFile(the_file,buffer,sz,&write,NULL);
3180 } while (sz == 1024);
3182 msi_free(FilePath);
3184 CloseHandle(the_file);
3186 uirow = MSI_CreateRecord(1);
3187 MSI_RecordSetStringW(uirow,1,FileName);
3188 ui_actiondata(package,szPublishProduct,uirow);
3189 msiobj_release( &uirow->hdr );
3191 return ERROR_SUCCESS;
3195 * 99% of the work done here is only done for
3196 * advertised installs. However this is where the
3197 * Icon table is processed and written out
3198 * so that is what I am going to do here.
3200 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3202 UINT rc;
3203 MSIQUERY * view;
3204 static const WCHAR Query[]=
3205 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3206 '`','I','c','o','n','`',0};
3207 /* for registry stuff */
3208 HKEY hkey=0;
3209 HKEY hukey=0;
3210 static const WCHAR szProductLanguage[] =
3211 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3212 static const WCHAR szARPProductIcon[] =
3213 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3214 static const WCHAR szProductVersion[] =
3215 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3216 DWORD langid;
3217 LPWSTR buffer;
3218 DWORD size;
3219 MSIHANDLE hDb, hSumInfo;
3221 /* write out icon files */
3223 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3224 if (rc == ERROR_SUCCESS)
3226 MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3227 msiobj_release(&view->hdr);
3230 /* ok there is a lot more done here but i need to figure out what */
3232 rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3233 if (rc != ERROR_SUCCESS)
3234 goto end;
3236 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3237 if (rc != ERROR_SUCCESS)
3238 goto end;
3241 buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3242 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3243 msi_free(buffer);
3245 langid = msi_get_property_int( package, szProductLanguage, 0 );
3246 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3248 buffer = msi_dup_property( package, szARPProductIcon );
3249 if (buffer)
3251 LPWSTR path = build_icon_path(package,buffer);
3252 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3253 msi_free( path );
3255 msi_free(buffer);
3257 buffer = msi_dup_property( package, szProductVersion );
3258 if (buffer)
3260 DWORD verdword = msi_version_str_to_dword(buffer);
3261 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3263 msi_free(buffer);
3265 /* FIXME: Need to write more keys to the user registry */
3267 hDb= alloc_msihandle( &package->db->hdr );
3268 if (!hDb) {
3269 rc = ERROR_NOT_ENOUGH_MEMORY;
3270 goto end;
3272 rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo);
3273 MsiCloseHandle(hDb);
3274 if (rc == ERROR_SUCCESS)
3276 WCHAR guidbuffer[0x200];
3277 size = 0x200;
3278 rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3279 guidbuffer, &size);
3280 if (rc == ERROR_SUCCESS)
3282 WCHAR squashed[GUID_SIZE];
3283 /* for now we only care about the first guid */
3284 LPWSTR ptr = strchrW(guidbuffer,';');
3285 if (ptr) *ptr = 0;
3286 squash_guid(guidbuffer,squashed);
3287 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3289 else
3291 ERR("Unable to query Revision_Number...\n");
3292 rc = ERROR_SUCCESS;
3294 MsiCloseHandle(hSumInfo);
3296 else
3298 ERR("Unable to open Summary Information\n");
3299 rc = ERROR_SUCCESS;
3302 end:
3304 RegCloseKey(hkey);
3305 RegCloseKey(hukey);
3307 return rc;
3310 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3312 MSIPACKAGE *package = (MSIPACKAGE*)param;
3313 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3314 LPWSTR deformated_section, deformated_key, deformated_value;
3315 LPWSTR folder, fullname = NULL;
3316 MSIRECORD * uirow;
3317 INT action;
3318 MSICOMPONENT *comp;
3319 static const WCHAR szWindowsFolder[] =
3320 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3322 component = MSI_RecordGetString(row, 8);
3323 comp = get_loaded_component(package,component);
3325 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3327 TRACE("Skipping ini file due to disabled component %s\n",
3328 debugstr_w(component));
3330 comp->Action = comp->Installed;
3332 return ERROR_SUCCESS;
3335 comp->Action = INSTALLSTATE_LOCAL;
3337 identifier = MSI_RecordGetString(row,1);
3338 filename = MSI_RecordGetString(row,2);
3339 dirproperty = MSI_RecordGetString(row,3);
3340 section = MSI_RecordGetString(row,4);
3341 key = MSI_RecordGetString(row,5);
3342 value = MSI_RecordGetString(row,6);
3343 action = MSI_RecordGetInteger(row,7);
3345 deformat_string(package,section,&deformated_section);
3346 deformat_string(package,key,&deformated_key);
3347 deformat_string(package,value,&deformated_value);
3349 if (dirproperty)
3351 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3352 if (!folder)
3353 folder = msi_dup_property( package, dirproperty );
3355 else
3356 folder = msi_dup_property( package, szWindowsFolder );
3358 if (!folder)
3360 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3361 goto cleanup;
3364 fullname = build_directory_name(2, folder, filename);
3366 if (action == 0)
3368 TRACE("Adding value %s to section %s in %s\n",
3369 debugstr_w(deformated_key), debugstr_w(deformated_section),
3370 debugstr_w(fullname));
3371 WritePrivateProfileStringW(deformated_section, deformated_key,
3372 deformated_value, fullname);
3374 else if (action == 1)
3376 WCHAR returned[10];
3377 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3378 returned, 10, fullname);
3379 if (returned[0] == 0)
3381 TRACE("Adding value %s to section %s in %s\n",
3382 debugstr_w(deformated_key), debugstr_w(deformated_section),
3383 debugstr_w(fullname));
3385 WritePrivateProfileStringW(deformated_section, deformated_key,
3386 deformated_value, fullname);
3389 else if (action == 3)
3390 FIXME("Append to existing section not yet implemented\n");
3392 uirow = MSI_CreateRecord(4);
3393 MSI_RecordSetStringW(uirow,1,identifier);
3394 MSI_RecordSetStringW(uirow,2,deformated_section);
3395 MSI_RecordSetStringW(uirow,3,deformated_key);
3396 MSI_RecordSetStringW(uirow,4,deformated_value);
3397 ui_actiondata(package,szWriteIniValues,uirow);
3398 msiobj_release( &uirow->hdr );
3399 cleanup:
3400 msi_free(fullname);
3401 msi_free(folder);
3402 msi_free(deformated_key);
3403 msi_free(deformated_value);
3404 msi_free(deformated_section);
3405 return ERROR_SUCCESS;
3408 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3410 UINT rc;
3411 MSIQUERY * view;
3412 static const WCHAR ExecSeqQuery[] =
3413 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3414 '`','I','n','i','F','i','l','e','`',0};
3416 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3417 if (rc != ERROR_SUCCESS)
3419 TRACE("no IniFile table\n");
3420 return ERROR_SUCCESS;
3423 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3424 msiobj_release(&view->hdr);
3425 return rc;
3428 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3430 MSIPACKAGE *package = (MSIPACKAGE*)param;
3431 LPCWSTR filename;
3432 LPWSTR FullName;
3433 MSIFILE *file;
3434 DWORD len;
3435 static const WCHAR ExeStr[] =
3436 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3437 static const WCHAR close[] = {'\"',0};
3438 STARTUPINFOW si;
3439 PROCESS_INFORMATION info;
3440 BOOL brc;
3441 MSIRECORD *uirow;
3442 LPWSTR uipath, p;
3444 memset(&si,0,sizeof(STARTUPINFOW));
3446 filename = MSI_RecordGetString(row,1);
3447 file = get_loaded_file( package, filename );
3449 if (!file)
3451 ERR("Unable to find file id %s\n",debugstr_w(filename));
3452 return ERROR_SUCCESS;
3455 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3457 FullName = msi_alloc(len*sizeof(WCHAR));
3458 strcpyW(FullName,ExeStr);
3459 strcatW( FullName, file->TargetPath );
3460 strcatW(FullName,close);
3462 TRACE("Registering %s\n",debugstr_w(FullName));
3463 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3464 &si, &info);
3466 if (brc)
3467 msi_dialog_check_messages(info.hProcess);
3469 msi_free(FullName);
3471 /* the UI chunk */
3472 uirow = MSI_CreateRecord( 2 );
3473 uipath = strdupW( file->TargetPath );
3474 p = strrchrW(uipath,'\\');
3475 if (p)
3476 p[1]=0;
3477 MSI_RecordSetStringW( uirow, 1, &p[2] );
3478 MSI_RecordSetStringW( uirow, 2, uipath);
3479 ui_actiondata( package, szSelfRegModules, uirow);
3480 msiobj_release( &uirow->hdr );
3481 msi_free( uipath );
3482 /* FIXME: call ui_progress? */
3484 return ERROR_SUCCESS;
3487 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3489 UINT rc;
3490 MSIQUERY * view;
3491 static const WCHAR ExecSeqQuery[] =
3492 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3493 '`','S','e','l','f','R','e','g','`',0};
3495 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3496 if (rc != ERROR_SUCCESS)
3498 TRACE("no SelfReg table\n");
3499 return ERROR_SUCCESS;
3502 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3503 msiobj_release(&view->hdr);
3505 return ERROR_SUCCESS;
3508 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3510 MSIFEATURE *feature;
3511 UINT rc;
3512 HKEY hkey=0;
3513 HKEY hukey=0;
3515 rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3516 if (rc != ERROR_SUCCESS)
3517 goto end;
3519 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3520 if (rc != ERROR_SUCCESS)
3521 goto end;
3523 /* here the guids are base 85 encoded */
3524 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3526 ComponentList *cl;
3527 LPWSTR data = NULL;
3528 GUID clsid;
3529 INT size;
3530 BOOL absent = FALSE;
3531 MSIRECORD *uirow;
3533 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3534 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3535 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3536 absent = TRUE;
3538 size = 1;
3539 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3541 size += 21;
3543 if (feature->Feature_Parent)
3544 size += strlenW( feature->Feature_Parent )+2;
3546 data = msi_alloc(size * sizeof(WCHAR));
3548 data[0] = 0;
3549 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3551 MSICOMPONENT* component = cl->component;
3552 WCHAR buf[21];
3554 buf[0] = 0;
3555 if (component->ComponentId)
3557 TRACE("From %s\n",debugstr_w(component->ComponentId));
3558 CLSIDFromString(component->ComponentId, &clsid);
3559 encode_base85_guid(&clsid,buf);
3560 TRACE("to %s\n",debugstr_w(buf));
3561 strcatW(data,buf);
3564 if (feature->Feature_Parent)
3566 static const WCHAR sep[] = {'\2',0};
3567 strcatW(data,sep);
3568 strcatW(data,feature->Feature_Parent);
3571 msi_reg_set_val_str( hkey, feature->Feature, data );
3572 msi_free(data);
3574 size = 0;
3575 if (feature->Feature_Parent)
3576 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3577 if (!absent)
3579 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3580 (LPBYTE)feature->Feature_Parent,size);
3582 else
3584 size += 2*sizeof(WCHAR);
3585 data = msi_alloc(size);
3586 data[0] = 0x6;
3587 data[1] = 0;
3588 if (feature->Feature_Parent)
3589 strcpyW( &data[1], feature->Feature_Parent );
3590 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3591 (LPBYTE)data,size);
3592 msi_free(data);
3595 /* the UI chunk */
3596 uirow = MSI_CreateRecord( 1 );
3597 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3598 ui_actiondata( package, szPublishFeatures, uirow);
3599 msiobj_release( &uirow->hdr );
3600 /* FIXME: call ui_progress? */
3603 end:
3604 RegCloseKey(hkey);
3605 RegCloseKey(hukey);
3606 return rc;
3609 static UINT msi_get_local_package_name( LPWSTR path )
3611 static const WCHAR szInstaller[] = {
3612 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3613 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3614 DWORD time, len, i;
3615 HANDLE handle;
3617 time = GetTickCount();
3618 GetWindowsDirectoryW( path, MAX_PATH );
3619 lstrcatW( path, szInstaller );
3620 CreateDirectoryW( path, NULL );
3622 len = lstrlenW(path);
3623 for (i=0; i<0x10000; i++)
3625 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3626 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3627 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3628 if (handle != INVALID_HANDLE_VALUE)
3630 CloseHandle(handle);
3631 break;
3633 if (GetLastError() != ERROR_FILE_EXISTS &&
3634 GetLastError() != ERROR_SHARING_VIOLATION)
3635 return ERROR_FUNCTION_FAILED;
3638 return ERROR_SUCCESS;
3641 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3643 static const WCHAR szOriginalDatabase[] =
3644 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3645 WCHAR packagefile[MAX_PATH];
3646 LPWSTR msiFilePath;
3647 UINT r;
3649 r = msi_get_local_package_name( packagefile );
3650 if (r != ERROR_SUCCESS)
3651 return r;
3653 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3655 msiFilePath = msi_dup_property( package, szOriginalDatabase );
3656 r = CopyFileW( msiFilePath, packagefile, FALSE);
3657 msi_free( msiFilePath );
3659 if (!r)
3661 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3662 debugstr_w(msiFilePath), debugstr_w(packagefile), GetLastError());
3663 return ERROR_FUNCTION_FAILED;
3666 /* FIXME: maybe set this key in ACTION_RegisterProduct instead */
3667 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3668 return ERROR_SUCCESS;
3671 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3673 LPWSTR prop, val, key;
3674 static const LPCSTR propval[] = {
3675 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3676 "ARPCONTACT", "Contact",
3677 "ARPCOMMENTS", "Comments",
3678 "ProductName", "DisplayName",
3679 "ProductVersion", "DisplayVersion",
3680 "ARPHELPLINK", "HelpLink",
3681 "ARPHELPTELEPHONE", "HelpTelephone",
3682 "ARPINSTALLLOCATION", "InstallLocation",
3683 "SourceDir", "InstallSource",
3684 "Manufacturer", "Publisher",
3685 "ARPREADME", "Readme",
3686 "ARPSIZE", "Size",
3687 "ARPURLINFOABOUT", "URLInfoAbout",
3688 "ARPURLUPDATEINFO", "URLUpdateInfo",
3689 NULL,
3691 const LPCSTR *p = propval;
3693 while( *p )
3695 prop = strdupAtoW( *p++ );
3696 key = strdupAtoW( *p++ );
3697 val = msi_dup_property( package, prop );
3698 msi_reg_set_val_str( hkey, key, val );
3699 msi_free(val);
3700 msi_free(key);
3701 msi_free(prop);
3703 return ERROR_SUCCESS;
3706 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3708 HKEY hkey=0;
3709 LPWSTR buffer = NULL;
3710 UINT rc;
3711 DWORD size, langid;
3712 static const WCHAR szWindowsInstaller[] =
3713 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3714 static const WCHAR szUpgradeCode[] =
3715 {'U','p','g','r','a','d','e','C','o','d','e',0};
3716 static const WCHAR modpath_fmt[] =
3717 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3718 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3719 static const WCHAR szModifyPath[] =
3720 {'M','o','d','i','f','y','P','a','t','h',0};
3721 static const WCHAR szUninstallString[] =
3722 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3723 static const WCHAR szEstimatedSize[] =
3724 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3725 static const WCHAR szProductLanguage[] =
3726 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3727 static const WCHAR szProductVersion[] =
3728 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3730 SYSTEMTIME systime;
3731 static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0};
3732 LPWSTR upgrade_code;
3733 WCHAR szDate[9];
3735 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3736 if (rc != ERROR_SUCCESS)
3737 return rc;
3739 /* dump all the info i can grab */
3740 /* FIXME: Flesh out more information */
3742 msi_write_uninstall_property_vals( package, hkey );
3744 msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
3746 msi_make_package_local( package, hkey );
3748 /* do ModifyPath and UninstallString */
3749 size = deformat_string(package,modpath_fmt,&buffer);
3750 RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3751 RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3752 msi_free(buffer);
3754 /* FIXME: Write real Estimated Size when we have it */
3755 msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
3757 GetLocalTime(&systime);
3758 sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
3759 msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
3761 langid = msi_get_property_int( package, szProductLanguage, 0 );
3762 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3764 buffer = msi_dup_property( package, szProductVersion );
3765 if (buffer)
3767 DWORD verdword = msi_version_str_to_dword(buffer);
3769 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3770 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
3771 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
3773 msi_free(buffer);
3775 /* Handle Upgrade Codes */
3776 upgrade_code = msi_dup_property( package, szUpgradeCode );
3777 if (upgrade_code)
3779 HKEY hkey2;
3780 WCHAR squashed[33];
3781 MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3782 squash_guid(package->ProductCode,squashed);
3783 msi_reg_set_val_str( hkey2, squashed, NULL );
3784 RegCloseKey(hkey2);
3785 MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3786 squash_guid(package->ProductCode,squashed);
3787 msi_reg_set_val_str( hkey2, squashed, NULL );
3788 RegCloseKey(hkey2);
3790 msi_free(upgrade_code);
3793 RegCloseKey(hkey);
3795 /* FIXME: call ui_actiondata */
3797 return ERROR_SUCCESS;
3800 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
3802 return execute_script(package,INSTALL_SCRIPT);
3805 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
3807 UINT rc;
3809 /* turn off scheduling */
3810 package->script->CurrentlyScripting= FALSE;
3812 /* first do the same as an InstallExecute */
3813 rc = ACTION_InstallExecute(package);
3814 if (rc != ERROR_SUCCESS)
3815 return rc;
3817 /* then handle Commit Actions */
3818 rc = execute_script(package,COMMIT_SCRIPT);
3820 return rc;
3823 static UINT ACTION_ForceReboot(MSIPACKAGE *package)
3825 static const WCHAR RunOnce[] = {
3826 'S','o','f','t','w','a','r','e','\\',
3827 'M','i','c','r','o','s','o','f','t','\\',
3828 'W','i','n','d','o','w','s','\\',
3829 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3830 'R','u','n','O','n','c','e',0};
3831 static const WCHAR InstallRunOnce[] = {
3832 'S','o','f','t','w','a','r','e','\\',
3833 'M','i','c','r','o','s','o','f','t','\\',
3834 'W','i','n','d','o','w','s','\\',
3835 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3836 'I','n','s','t','a','l','l','e','r','\\',
3837 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
3839 static const WCHAR msiexec_fmt[] = {
3840 '%','s',
3841 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
3842 '\"','%','s','\"',0};
3843 static const WCHAR install_fmt[] = {
3844 '/','I',' ','\"','%','s','\"',' ',
3845 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
3846 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
3847 WCHAR buffer[256], sysdir[MAX_PATH];
3848 HKEY hkey;
3849 WCHAR squished_pc[100];
3851 squash_guid(package->ProductCode,squished_pc);
3853 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
3854 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
3855 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
3856 squished_pc);
3858 msi_reg_set_val_str( hkey, squished_pc, buffer );
3859 RegCloseKey(hkey);
3861 TRACE("Reboot command %s\n",debugstr_w(buffer));
3863 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
3864 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
3866 msi_reg_set_val_str( hkey, squished_pc, buffer );
3867 RegCloseKey(hkey);
3869 return ERROR_INSTALL_SUSPEND;
3872 static UINT msi_set_sourcedir_props(MSIPACKAGE *package)
3874 LPWSTR p, source;
3875 DWORD len;
3877 p = strrchrW( package->PackagePath, '\\' );
3878 if (!p)
3879 return ERROR_SUCCESS;
3881 len = p - package->PackagePath + 2;
3882 source = msi_alloc( len * sizeof(WCHAR) );
3883 lstrcpynW( source, package->PackagePath, len );
3885 MSI_SetPropertyW( package, cszSourceDir, source );
3886 MSI_SetPropertyW( package, cszSOURCEDIR, source );
3888 msi_free( source );
3890 return ERROR_SUCCESS;
3893 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
3895 DWORD attrib;
3896 UINT rc;
3899 * We are currently doing what should be done here in the top level Install
3900 * however for Administrative and uninstalls this step will be needed
3902 if (!package->PackagePath)
3903 return ERROR_SUCCESS;
3905 msi_set_sourcedir_props(package);
3907 attrib = GetFileAttributesW(package->PackagePath);
3908 if (attrib == INVALID_FILE_ATTRIBUTES)
3910 LPWSTR prompt;
3911 LPWSTR msg;
3912 DWORD size = 0;
3914 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
3915 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
3916 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
3917 if (rc == ERROR_MORE_DATA)
3919 prompt = msi_alloc(size * sizeof(WCHAR));
3920 MsiSourceListGetInfoW(package->ProductCode, NULL,
3921 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
3922 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
3924 else
3925 prompt = strdupW(package->PackagePath);
3927 msg = generate_error_string(package,1302,1,prompt);
3928 while(attrib == INVALID_FILE_ATTRIBUTES)
3930 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
3931 if (rc == IDCANCEL)
3933 rc = ERROR_INSTALL_USEREXIT;
3934 break;
3936 attrib = GetFileAttributesW(package->PackagePath);
3938 msi_free(prompt);
3939 rc = ERROR_SUCCESS;
3941 else
3942 return ERROR_SUCCESS;
3944 return rc;
3947 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
3949 HKEY hkey=0;
3950 LPWSTR buffer;
3951 LPWSTR productid;
3952 UINT rc,i;
3954 static const WCHAR szPropKeys[][80] =
3956 {'P','r','o','d','u','c','t','I','D',0},
3957 {'U','S','E','R','N','A','M','E',0},
3958 {'C','O','M','P','A','N','Y','N','A','M','E',0},
3959 {0},
3962 static const WCHAR szRegKeys[][80] =
3964 {'P','r','o','d','u','c','t','I','D',0},
3965 {'R','e','g','O','w','n','e','r',0},
3966 {'R','e','g','C','o','m','p','a','n','y',0},
3967 {0},
3970 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
3971 if (!productid)
3972 return ERROR_SUCCESS;
3974 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3975 if (rc != ERROR_SUCCESS)
3976 goto end;
3978 for( i = 0; szPropKeys[i][0]; i++ )
3980 buffer = msi_dup_property( package, szPropKeys[i] );
3981 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
3982 msi_free( buffer );
3985 end:
3986 msi_free(productid);
3987 RegCloseKey(hkey);
3989 /* FIXME: call ui_actiondata */
3991 return ERROR_SUCCESS;
3995 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
3997 UINT rc;
3999 package->script->InWhatSequence |= SEQUENCE_EXEC;
4000 rc = ACTION_ProcessExecSequence(package,FALSE);
4001 return rc;
4005 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4007 MSIPACKAGE *package = (MSIPACKAGE*)param;
4008 LPCWSTR compgroupid=NULL;
4009 LPCWSTR feature=NULL;
4010 LPCWSTR text = NULL;
4011 LPCWSTR qualifier = NULL;
4012 LPCWSTR component = NULL;
4013 LPWSTR advertise = NULL;
4014 LPWSTR output = NULL;
4015 HKEY hkey;
4016 UINT rc = ERROR_SUCCESS;
4017 MSICOMPONENT *comp;
4018 DWORD sz = 0;
4019 MSIRECORD *uirow;
4021 component = MSI_RecordGetString(rec,3);
4022 comp = get_loaded_component(package,component);
4024 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4025 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4026 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4028 TRACE("Skipping: Component %s not scheduled for install\n",
4029 debugstr_w(component));
4031 return ERROR_SUCCESS;
4034 compgroupid = MSI_RecordGetString(rec,1);
4035 qualifier = MSI_RecordGetString(rec,2);
4037 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4038 if (rc != ERROR_SUCCESS)
4039 goto end;
4041 text = MSI_RecordGetString(rec,4);
4042 feature = MSI_RecordGetString(rec,5);
4044 advertise = create_component_advertise_string(package, comp, feature);
4046 sz = strlenW(advertise);
4048 if (text)
4049 sz += lstrlenW(text);
4051 sz+=3;
4052 sz *= sizeof(WCHAR);
4054 output = msi_alloc_zero(sz);
4055 strcpyW(output,advertise);
4056 msi_free(advertise);
4058 if (text)
4059 strcatW(output,text);
4061 msi_reg_set_val_multi_str( hkey, qualifier, output );
4063 end:
4064 RegCloseKey(hkey);
4065 msi_free(output);
4067 /* the UI chunk */
4068 uirow = MSI_CreateRecord( 2 );
4069 MSI_RecordSetStringW( uirow, 1, compgroupid );
4070 MSI_RecordSetStringW( uirow, 2, qualifier);
4071 ui_actiondata( package, szPublishComponents, uirow);
4072 msiobj_release( &uirow->hdr );
4073 /* FIXME: call ui_progress? */
4075 return rc;
4079 * At present I am ignorning the advertised components part of this and only
4080 * focusing on the qualified component sets
4082 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4084 UINT rc;
4085 MSIQUERY * view;
4086 static const WCHAR ExecSeqQuery[] =
4087 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4088 '`','P','u','b','l','i','s','h',
4089 'C','o','m','p','o','n','e','n','t','`',0};
4091 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4092 if (rc != ERROR_SUCCESS)
4093 return ERROR_SUCCESS;
4095 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4096 msiobj_release(&view->hdr);
4098 return rc;
4101 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4103 MSIPACKAGE *package = (MSIPACKAGE*)param;
4104 MSIRECORD *row;
4105 MSIFILE *file;
4106 SC_HANDLE hscm, service = NULL;
4107 LPCWSTR name, disp, comp, depends, pass;
4108 LPCWSTR load_order, serv_name, key;
4109 DWORD serv_type, start_type;
4110 DWORD err_control;
4112 static const WCHAR query[] =
4113 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4114 '`','C','o','m','p','o','n','e','n','t','`',' ',
4115 'W','H','E','R','E',' ',
4116 '`','C','o','m','p','o','n','e','n','t','`',' ',
4117 '=','\'','%','s','\'',0};
4119 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4120 if (!hscm)
4122 ERR("Failed to open the SC Manager!\n");
4123 goto done;
4126 start_type = MSI_RecordGetInteger(rec, 5);
4127 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4128 goto done;
4130 depends = MSI_RecordGetString(rec, 8);
4131 if (depends && *depends)
4132 FIXME("Dependency list unhandled!\n");
4134 name = MSI_RecordGetString(rec, 2);
4135 disp = MSI_RecordGetString(rec, 3);
4136 serv_type = MSI_RecordGetInteger(rec, 4);
4137 err_control = MSI_RecordGetInteger(rec, 6);
4138 load_order = MSI_RecordGetString(rec, 7);
4139 serv_name = MSI_RecordGetString(rec, 9);
4140 pass = MSI_RecordGetString(rec, 10);
4141 comp = MSI_RecordGetString(rec, 12);
4143 /* fetch the service path */
4144 row = MSI_QueryGetRecord(package->db, query, comp);
4145 if (!row)
4147 ERR("Control query failed!\n");
4148 goto done;
4151 key = MSI_RecordGetString(row, 6);
4152 msiobj_release(&row->hdr);
4154 file = get_loaded_file(package, key);
4155 if (!file)
4157 ERR("Failed to load the service file\n");
4158 goto done;
4161 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4162 start_type, err_control, file->TargetPath,
4163 load_order, NULL, NULL, serv_name, pass);
4164 if (!service)
4166 if (GetLastError() != ERROR_SERVICE_EXISTS)
4167 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4170 done:
4171 CloseServiceHandle(service);
4172 CloseServiceHandle(hscm);
4174 return ERROR_SUCCESS;
4177 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4179 UINT rc;
4180 MSIQUERY * view;
4181 static const WCHAR ExecSeqQuery[] =
4182 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4183 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4185 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4186 if (rc != ERROR_SUCCESS)
4187 return ERROR_SUCCESS;
4189 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4190 msiobj_release(&view->hdr);
4192 return rc;
4195 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4196 static LPCWSTR *msi_service_args_to_vector(LPCWSTR name, LPWSTR args, DWORD *numargs)
4198 LPCWSTR *vector;
4199 LPWSTR p, q;
4200 DWORD sep_len;
4202 static const WCHAR separator[] = {'[','~',']',0};
4204 *numargs = 0;
4205 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4207 if (!args)
4208 return NULL;
4210 vector = msi_alloc(sizeof(LPWSTR));
4211 if (!vector)
4212 return NULL;
4214 p = args;
4217 (*numargs)++;
4218 vector[*numargs - 1] = p;
4220 if ((q = strstrW(p, separator)))
4222 *q = '\0';
4224 vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4225 if (!vector)
4226 return NULL;
4228 p = q + sep_len;
4230 } while (q);
4232 return vector;
4235 static MSICOMPONENT *msi_find_component( MSIPACKAGE *package, LPCWSTR component )
4237 MSICOMPONENT *comp;
4239 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
4241 if (!lstrcmpW(comp->Component, component))
4242 return comp;
4245 return NULL;
4248 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4250 MSIPACKAGE *package = (MSIPACKAGE *)param;
4251 MSICOMPONENT *comp;
4252 SC_HANDLE scm, service = NULL;
4253 LPCWSTR name, *vector = NULL;
4254 LPWSTR args;
4255 DWORD event, numargs;
4256 UINT r = ERROR_FUNCTION_FAILED;
4258 comp = msi_find_component(package, MSI_RecordGetString(rec, 6));
4259 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4260 return ERROR_SUCCESS;
4262 name = MSI_RecordGetString(rec, 2);
4263 event = MSI_RecordGetInteger(rec, 3);
4264 args = strdupW(MSI_RecordGetString(rec, 4));
4266 if (!(event & msidbServiceControlEventStart))
4267 return ERROR_SUCCESS;
4269 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4270 if (!scm)
4272 ERR("Failed to open the service control manager\n");
4273 goto done;
4276 service = OpenServiceW(scm, name, SERVICE_START);
4277 if (!service)
4279 ERR("Failed to open service %s\n", debugstr_w(name));
4280 goto done;
4283 vector = msi_service_args_to_vector(name, args, &numargs);
4285 if (!StartServiceW(service, numargs, vector))
4287 ERR("Failed to start service %s\n", debugstr_w(name));
4288 goto done;
4291 r = ERROR_SUCCESS;
4293 done:
4294 CloseServiceHandle(service);
4295 CloseServiceHandle(scm);
4297 msi_free(args);
4298 msi_free(vector);
4299 return r;
4302 static UINT ACTION_StartServices( MSIPACKAGE *package )
4304 UINT rc;
4305 MSIQUERY *view;
4307 static const WCHAR query[] = {
4308 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4309 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4311 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4312 if (rc != ERROR_SUCCESS)
4313 return ERROR_SUCCESS;
4315 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4316 msiobj_release(&view->hdr);
4318 return rc;
4321 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4323 MSIFILE *file;
4325 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4327 if (!lstrcmpW(file->File, filename))
4328 return file;
4331 return NULL;
4334 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4336 MSIPACKAGE *package = (MSIPACKAGE*)param;
4337 LPWSTR driver, driver_path, ptr;
4338 WCHAR outpath[MAX_PATH];
4339 MSIFILE *driver_file, *setup_file;
4340 LPCWSTR desc;
4341 DWORD len, usage;
4342 UINT r = ERROR_SUCCESS;
4344 static const WCHAR driver_fmt[] = {
4345 'D','r','i','v','e','r','=','%','s',0};
4346 static const WCHAR setup_fmt[] = {
4347 'S','e','t','u','p','=','%','s',0};
4348 static const WCHAR usage_fmt[] = {
4349 'F','i','l','e','U','s','a','g','e','=','1',0};
4351 desc = MSI_RecordGetString(rec, 3);
4353 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4354 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4356 if (!driver_file || !setup_file)
4358 ERR("ODBC Driver entry not found!\n");
4359 return ERROR_FUNCTION_FAILED;
4362 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4363 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4364 lstrlenW(usage_fmt) + 1;
4365 driver = msi_alloc(len * sizeof(WCHAR));
4366 if (!driver)
4367 return ERROR_OUTOFMEMORY;
4369 ptr = driver;
4370 lstrcpyW(ptr, desc);
4371 ptr += lstrlenW(ptr) + 1;
4373 sprintfW(ptr, driver_fmt, driver_file->FileName);
4374 ptr += lstrlenW(ptr) + 1;
4376 sprintfW(ptr, setup_fmt, setup_file->FileName);
4377 ptr += lstrlenW(ptr) + 1;
4379 lstrcpyW(ptr, usage_fmt);
4380 ptr += lstrlenW(ptr) + 1;
4381 *ptr = '\0';
4383 driver_path = strdupW(driver_file->TargetPath);
4384 ptr = strrchrW(driver_path, '\\');
4385 if (ptr) *ptr = '\0';
4387 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4388 NULL, ODBC_INSTALL_COMPLETE, &usage))
4390 ERR("Failed to install SQL driver!\n");
4391 r = ERROR_FUNCTION_FAILED;
4394 msi_free(driver);
4395 msi_free(driver_path);
4397 return r;
4400 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4402 UINT rc;
4403 MSIQUERY *view;
4405 static const WCHAR query[] = {
4406 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4407 'O','D','B','C','D','r','i','v','e','r',0 };
4409 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4410 if (rc != ERROR_SUCCESS)
4411 return ERROR_SUCCESS;
4413 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4414 msiobj_release(&view->hdr);
4416 return rc;
4419 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
4420 LPCSTR action, LPCWSTR table )
4422 static const WCHAR query[] = {
4423 'S','E','L','E','C','T',' ','*',' ',
4424 'F','R','O','M',' ','`','%','s','`',0 };
4425 MSIQUERY *view = NULL;
4426 DWORD count = 0;
4427 UINT r;
4429 r = MSI_OpenQuery( package->db, &view, query, table );
4430 if (r == ERROR_SUCCESS)
4432 r = MSI_IterateRecords(view, &count, NULL, package);
4433 msiobj_release(&view->hdr);
4436 if (count)
4437 FIXME("%s -> %u ignored %s table values\n",
4438 action, count, debugstr_w(table));
4440 return ERROR_SUCCESS;
4443 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
4445 TRACE("%p\n", package);
4446 return ERROR_SUCCESS;
4449 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4451 static const WCHAR table[] =
4452 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
4453 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
4456 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
4458 static const WCHAR table[] = { 'M','o','v','e','F','i','l','e',0 };
4459 return msi_unimplemented_action_stub( package, "MoveFiles", table );
4462 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
4464 static const WCHAR table[] = { 'P','a','t','c','h',0 };
4465 return msi_unimplemented_action_stub( package, "PatchFiles", table );
4468 static UINT ACTION_BindImage( MSIPACKAGE *package )
4470 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
4471 return msi_unimplemented_action_stub( package, "BindImage", table );
4474 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
4476 static const WCHAR table[] = {
4477 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
4478 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
4481 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
4483 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
4484 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
4487 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4489 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
4490 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
4493 static UINT ACTION_StopServices( MSIPACKAGE *package )
4495 static const WCHAR table[] = {
4496 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4497 return msi_unimplemented_action_stub( package, "StopServices", table );
4500 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
4502 static const WCHAR table[] = {
4503 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4504 return msi_unimplemented_action_stub( package, "DeleteServices", table );
4506 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
4508 static const WCHAR table[] = {
4509 'P','r','o','d','u','c','t','I','D',0 };
4510 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
4513 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
4515 static const WCHAR table[] = {
4516 'E','n','v','i','r','o','n','m','e','n','t',0 };
4517 return msi_unimplemented_action_stub( package, "WriteEnvironmentStrings", table );
4520 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
4522 static const WCHAR table[] = {
4523 'E','n','v','i','r','o','n','m','e','n','t',0 };
4524 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
4527 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
4529 static const WCHAR table[] = {
4530 'M','s','i','A','s','s','e','m','b','l','y',0 };
4531 return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
4534 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
4536 static const WCHAR table[] = {
4537 'M','s','i','A','s','s','e','m','b','l','y',0 };
4538 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
4541 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
4543 static const WCHAR table[] = { 'F','o','n','t',0 };
4544 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
4547 static UINT ACTION_CCPSearch( MSIPACKAGE *package )
4549 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
4550 return msi_unimplemented_action_stub( package, "CCPSearch", table );
4553 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
4555 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
4556 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
4559 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
4561 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
4562 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
4565 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
4567 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
4568 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
4571 static const struct _actions StandardActions[] = {
4572 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
4573 { szAppSearch, ACTION_AppSearch },
4574 { szBindImage, ACTION_BindImage },
4575 { szCCPSearch, ACTION_CCPSearch},
4576 { szCostFinalize, ACTION_CostFinalize },
4577 { szCostInitialize, ACTION_CostInitialize },
4578 { szCreateFolders, ACTION_CreateFolders },
4579 { szCreateShortcuts, ACTION_CreateShortcuts },
4580 { szDeleteServices, ACTION_DeleteServices },
4581 { szDisableRollback, NULL},
4582 { szDuplicateFiles, ACTION_DuplicateFiles },
4583 { szExecuteAction, ACTION_ExecuteAction },
4584 { szFileCost, ACTION_FileCost },
4585 { szFindRelatedProducts, ACTION_FindRelatedProducts },
4586 { szForceReboot, ACTION_ForceReboot },
4587 { szInstallAdminPackage, NULL},
4588 { szInstallExecute, ACTION_InstallExecute },
4589 { szInstallExecuteAgain, ACTION_InstallExecute },
4590 { szInstallFiles, ACTION_InstallFiles},
4591 { szInstallFinalize, ACTION_InstallFinalize },
4592 { szInstallInitialize, ACTION_InstallInitialize },
4593 { szInstallSFPCatalogFile, NULL},
4594 { szInstallValidate, ACTION_InstallValidate },
4595 { szIsolateComponents, ACTION_IsolateComponents },
4596 { szLaunchConditions, ACTION_LaunchConditions },
4597 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
4598 { szMoveFiles, ACTION_MoveFiles },
4599 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
4600 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
4601 { szInstallODBC, ACTION_InstallODBC },
4602 { szInstallServices, ACTION_InstallServices },
4603 { szPatchFiles, ACTION_PatchFiles },
4604 { szProcessComponents, ACTION_ProcessComponents },
4605 { szPublishComponents, ACTION_PublishComponents },
4606 { szPublishFeatures, ACTION_PublishFeatures },
4607 { szPublishProduct, ACTION_PublishProduct },
4608 { szRegisterClassInfo, ACTION_RegisterClassInfo },
4609 { szRegisterComPlus, ACTION_RegisterComPlus},
4610 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
4611 { szRegisterFonts, ACTION_RegisterFonts },
4612 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
4613 { szRegisterProduct, ACTION_RegisterProduct },
4614 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
4615 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
4616 { szRegisterUser, ACTION_RegisterUser},
4617 { szRemoveDuplicateFiles, NULL},
4618 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
4619 { szRemoveExistingProducts, NULL},
4620 { szRemoveFiles, ACTION_RemoveFiles},
4621 { szRemoveFolders, NULL},
4622 { szRemoveIniValues, ACTION_RemoveIniValues },
4623 { szRemoveODBC, NULL},
4624 { szRemoveRegistryValues, NULL},
4625 { szRemoveShortcuts, NULL},
4626 { szResolveSource, ACTION_ResolveSource},
4627 { szRMCCPSearch, ACTION_RMCCPSearch},
4628 { szScheduleReboot, NULL},
4629 { szSelfRegModules, ACTION_SelfRegModules },
4630 { szSelfUnregModules, ACTION_SelfUnregModules },
4631 { szSetODBCFolders, NULL},
4632 { szStartServices, ACTION_StartServices },
4633 { szStopServices, ACTION_StopServices },
4634 { szUnpublishComponents, NULL},
4635 { szUnpublishFeatures, NULL},
4636 { szUnregisterClassInfo, NULL},
4637 { szUnregisterComPlus, ACTION_UnregisterComPlus},
4638 { szUnregisterExtensionInfo, NULL},
4639 { szUnregisterFonts, ACTION_UnregisterFonts },
4640 { szUnregisterMIMEInfo, NULL},
4641 { szUnregisterProgIdInfo, NULL},
4642 { szUnregisterTypeLibraries, NULL},
4643 { szValidateProductID, ACTION_ValidateProductID},
4644 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
4645 { szWriteIniValues, ACTION_WriteIniValues },
4646 { szWriteRegistryValues, ACTION_WriteRegistryValues},
4647 { NULL, NULL},