msi: Run the InstallExecute sequence if the InstallUISequnce table is empty.
[wine/wine64.git] / dlls / msi / action.c
blobf047a2753449967a5b64997eb38992ae693c9c13
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 BOOL ui_sequence_exists( MSIPACKAGE *package )
589 MSIQUERY *view;
590 UINT rc;
592 static const WCHAR ExecSeqQuery [] =
593 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
594 '`','I','n','s','t','a','l','l',
595 'U','I','S','e','q','u','e','n','c','e','`',
596 ' ','W','H','E','R','E',' ',
597 '`','S','e','q','u','e','n','c','e','`',' ',
598 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
599 '`','S','e','q','u','e','n','c','e','`',0};
601 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
602 if (rc == ERROR_SUCCESS)
604 msiobj_release(&view->hdr);
605 return TRUE;
608 return FALSE;
611 /****************************************************
612 * TOP level entry points
613 *****************************************************/
615 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
616 LPCWSTR szCommandLine )
618 UINT rc;
619 BOOL ui = FALSE, ui_exists;
620 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
621 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
622 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
624 MSI_SetPropertyW(package, szAction, szInstall);
626 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
628 package->script->InWhatSequence = SEQUENCE_INSTALL;
630 if (szPackagePath)
632 LPWSTR p, check, path;
634 path = strdupW(szPackagePath);
635 p = strrchrW(path,'\\');
636 if (p)
638 p++;
639 *p=0;
641 else
643 msi_free(path);
644 path = msi_alloc(MAX_PATH*sizeof(WCHAR));
645 GetCurrentDirectoryW(MAX_PATH,path);
646 strcatW(path,cszbs);
649 check = msi_dup_property( package, cszSourceDir );
650 if (!check)
651 MSI_SetPropertyW(package, cszSourceDir, path);
652 msi_free(check);
654 check = msi_dup_property( package, cszSOURCEDIR );
655 if (!check)
656 MSI_SetPropertyW(package, cszSOURCEDIR, path);
658 msi_free( package->PackagePath );
659 package->PackagePath = path;
661 msi_free(check);
664 msi_parse_command_line( package, szCommandLine );
666 msi_apply_transforms( package );
667 msi_apply_patches( package );
669 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
671 package->script->InWhatSequence |= SEQUENCE_UI;
672 rc = ACTION_ProcessUISequence(package);
673 ui = TRUE;
674 ui_exists = ui_sequence_exists(package);
675 if (rc == ERROR_SUCCESS || !ui_exists)
677 package->script->InWhatSequence |= SEQUENCE_EXEC;
678 rc = ACTION_ProcessExecSequence(package,ui_exists);
681 else
682 rc = ACTION_ProcessExecSequence(package,FALSE);
684 if (rc == -1)
686 /* install was halted but should be considered a success */
687 rc = ERROR_SUCCESS;
690 package->script->CurrentlyScripting= FALSE;
692 /* process the ending type action */
693 if (rc == ERROR_SUCCESS)
694 ACTION_PerformActionSequence(package,-1,ui);
695 else if (rc == ERROR_INSTALL_USEREXIT)
696 ACTION_PerformActionSequence(package,-2,ui);
697 else if (rc == ERROR_INSTALL_SUSPEND)
698 ACTION_PerformActionSequence(package,-4,ui);
699 else /* failed */
700 ACTION_PerformActionSequence(package,-3,ui);
702 /* finish up running custom actions */
703 ACTION_FinishCustomActions(package);
705 return rc;
708 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
710 UINT rc = ERROR_SUCCESS;
711 MSIRECORD * row = 0;
712 static const WCHAR ExecSeqQuery[] =
713 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
714 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
715 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
716 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
718 static const WCHAR UISeqQuery[] =
719 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
720 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
721 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
722 ' ', '=',' ','%','i',0};
724 if (UI)
725 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
726 else
727 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
729 if (row)
731 LPCWSTR action, cond;
733 TRACE("Running the actions\n");
735 /* check conditions */
736 cond = MSI_RecordGetString(row,2);
738 /* this is a hack to skip errors in the condition code */
739 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
740 goto end;
742 action = MSI_RecordGetString(row,1);
743 if (!action)
745 ERR("failed to fetch action\n");
746 rc = ERROR_FUNCTION_FAILED;
747 goto end;
750 if (UI)
751 rc = ACTION_PerformUIAction(package,action);
752 else
753 rc = ACTION_PerformAction(package,action,FALSE);
754 end:
755 msiobj_release(&row->hdr);
757 else
758 rc = ERROR_SUCCESS;
760 return rc;
763 typedef struct {
764 MSIPACKAGE* package;
765 BOOL UI;
766 } iterate_action_param;
768 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
770 iterate_action_param *iap= (iterate_action_param*)param;
771 UINT rc;
772 LPCWSTR cond, action;
774 action = MSI_RecordGetString(row,1);
775 if (!action)
777 ERR("Error is retrieving action name\n");
778 return ERROR_FUNCTION_FAILED;
781 /* check conditions */
782 cond = MSI_RecordGetString(row,2);
784 /* this is a hack to skip errors in the condition code */
785 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
787 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
788 return ERROR_SUCCESS;
791 if (iap->UI)
792 rc = ACTION_PerformUIAction(iap->package,action);
793 else
794 rc = ACTION_PerformAction(iap->package,action,FALSE);
796 msi_dialog_check_messages( NULL );
798 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
799 rc = iap->package->CurrentInstallState;
801 if (rc == ERROR_FUNCTION_NOT_CALLED)
802 rc = ERROR_SUCCESS;
804 if (rc != ERROR_SUCCESS)
805 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
807 return rc;
810 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
812 MSIQUERY * view;
813 UINT r;
814 static const WCHAR query[] =
815 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
816 '`','%','s','`',
817 ' ','W','H','E','R','E',' ',
818 '`','S','e','q','u','e','n','c','e','`',' ',
819 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
820 '`','S','e','q','u','e','n','c','e','`',0};
821 iterate_action_param iap;
824 * FIXME: probably should be checking UILevel in the
825 * ACTION_PerformUIAction/ACTION_PerformAction
826 * rather than saving the UI level here. Those
827 * two functions can be merged too.
829 iap.package = package;
830 iap.UI = TRUE;
832 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
834 r = MSI_OpenQuery( package->db, &view, query, szTable );
835 if (r == ERROR_SUCCESS)
837 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
838 msiobj_release(&view->hdr);
841 return r;
844 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
846 MSIQUERY * view;
847 UINT rc;
848 static const WCHAR ExecSeqQuery[] =
849 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
850 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
851 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
852 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
853 'O','R','D','E','R',' ', 'B','Y',' ',
854 '`','S','e','q','u','e','n','c','e','`',0 };
855 MSIRECORD * row = 0;
856 static const WCHAR IVQuery[] =
857 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
858 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
859 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
860 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
861 ' ','\'', 'I','n','s','t','a','l','l',
862 'V','a','l','i','d','a','t','e','\'', 0};
863 INT seq = 0;
864 iterate_action_param iap;
866 iap.package = package;
867 iap.UI = FALSE;
869 if (package->script->ExecuteSequenceRun)
871 TRACE("Execute Sequence already Run\n");
872 return ERROR_SUCCESS;
875 package->script->ExecuteSequenceRun = TRUE;
877 /* get the sequence number */
878 if (UIran)
880 row = MSI_QueryGetRecord(package->db, IVQuery);
881 if( !row )
882 return ERROR_FUNCTION_FAILED;
883 seq = MSI_RecordGetInteger(row,1);
884 msiobj_release(&row->hdr);
887 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
888 if (rc == ERROR_SUCCESS)
890 TRACE("Running the actions\n");
892 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
893 msiobj_release(&view->hdr);
896 return rc;
899 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
901 MSIQUERY * view;
902 UINT rc;
903 static const WCHAR ExecSeqQuery [] =
904 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
905 '`','I','n','s','t','a','l','l',
906 'U','I','S','e','q','u','e','n','c','e','`',
907 ' ','W','H','E','R','E',' ',
908 '`','S','e','q','u','e','n','c','e','`',' ',
909 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
910 '`','S','e','q','u','e','n','c','e','`',0};
911 iterate_action_param iap;
913 iap.package = package;
914 iap.UI = TRUE;
916 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
918 if (rc == ERROR_SUCCESS)
920 TRACE("Running the actions\n");
922 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
923 msiobj_release(&view->hdr);
926 return rc;
929 /********************************************************
930 * ACTION helper functions and functions that perform the actions
931 *******************************************************/
932 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
933 UINT* rc, BOOL force )
935 BOOL ret = FALSE;
936 BOOL run = force;
937 int i;
939 if (!run && !package->script->CurrentlyScripting)
940 run = TRUE;
942 if (!run)
944 if (strcmpW(action,szInstallFinalize) == 0 ||
945 strcmpW(action,szInstallExecute) == 0 ||
946 strcmpW(action,szInstallExecuteAgain) == 0)
947 run = TRUE;
950 i = 0;
951 while (StandardActions[i].action != NULL)
953 if (strcmpW(StandardActions[i].action, action)==0)
955 if (!run)
957 ui_actioninfo(package, action, TRUE, 0);
958 *rc = schedule_action(package,INSTALL_SCRIPT,action);
959 ui_actioninfo(package, action, FALSE, *rc);
961 else
963 ui_actionstart(package, action);
964 if (StandardActions[i].handler)
966 *rc = StandardActions[i].handler(package);
968 else
970 FIXME("unhandled standard action %s\n",debugstr_w(action));
971 *rc = ERROR_SUCCESS;
974 ret = TRUE;
975 break;
977 i++;
979 return ret;
982 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
983 UINT* rc, BOOL force )
985 BOOL ret=FALSE;
986 UINT arc;
988 arc = ACTION_CustomAction(package,action, force);
990 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
992 *rc = arc;
993 ret = TRUE;
995 return ret;
999 * A lot of actions are really important even if they don't do anything
1000 * explicit... Lots of properties are set at the beginning of the installation
1001 * CostFinalize does a bunch of work to translate the directories and such
1003 * But until I get write access to the database that is hard, so I am going to
1004 * hack it to see if I can get something to run.
1006 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, BOOL force)
1008 UINT rc = ERROR_SUCCESS;
1009 BOOL handled;
1011 TRACE("Performing action (%s)\n",debugstr_w(action));
1013 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1015 if (!handled)
1016 handled = ACTION_HandleCustomAction(package, action, &rc, force);
1018 if (!handled)
1020 FIXME("unhandled msi action %s\n",debugstr_w(action));
1021 rc = ERROR_FUNCTION_NOT_CALLED;
1024 return rc;
1027 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action)
1029 UINT rc = ERROR_SUCCESS;
1030 BOOL handled = FALSE;
1032 TRACE("Performing action (%s)\n",debugstr_w(action));
1034 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1036 if (!handled)
1037 handled = ACTION_HandleCustomAction(package, action, &rc, FALSE);
1039 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1040 handled = TRUE;
1042 if (!handled)
1044 FIXME("unhandled msi action %s\n",debugstr_w(action));
1045 rc = ERROR_FUNCTION_NOT_CALLED;
1048 return rc;
1053 * Actual Action Handlers
1056 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1058 MSIPACKAGE *package = (MSIPACKAGE*)param;
1059 LPCWSTR dir;
1060 LPWSTR full_path;
1061 MSIRECORD *uirow;
1062 MSIFOLDER *folder;
1064 dir = MSI_RecordGetString(row,1);
1065 if (!dir)
1067 ERR("Unable to get folder id\n");
1068 return ERROR_SUCCESS;
1071 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1072 if (!full_path)
1074 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1075 return ERROR_SUCCESS;
1078 TRACE("Folder is %s\n",debugstr_w(full_path));
1080 /* UI stuff */
1081 uirow = MSI_CreateRecord(1);
1082 MSI_RecordSetStringW(uirow,1,full_path);
1083 ui_actiondata(package,szCreateFolders,uirow);
1084 msiobj_release( &uirow->hdr );
1086 if (folder->State == 0)
1087 create_full_pathW(full_path);
1089 folder->State = 3;
1091 msi_free(full_path);
1092 return ERROR_SUCCESS;
1095 /* FIXME: probably should merge this with the above function */
1096 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1098 UINT rc = ERROR_SUCCESS;
1099 MSIFOLDER *folder;
1100 LPWSTR install_path;
1102 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1103 if (!install_path)
1104 return ERROR_FUNCTION_FAILED;
1106 /* create the path */
1107 if (folder->State == 0)
1109 create_full_pathW(install_path);
1110 folder->State = 2;
1112 msi_free(install_path);
1114 return rc;
1117 UINT msi_create_component_directories( MSIPACKAGE *package )
1119 MSICOMPONENT *comp;
1121 /* create all the folders required by the components are going to install */
1122 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1124 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1125 continue;
1126 msi_create_directory( package, comp->Directory );
1129 return ERROR_SUCCESS;
1133 * Also we cannot enable/disable components either, so for now I am just going
1134 * to do all the directories for all the components.
1136 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1138 static const WCHAR ExecSeqQuery[] =
1139 {'S','E','L','E','C','T',' ',
1140 '`','D','i','r','e','c','t','o','r','y','_','`',
1141 ' ','F','R','O','M',' ',
1142 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1143 UINT rc;
1144 MSIQUERY *view;
1146 /* create all the empty folders specified in the CreateFolder table */
1147 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1148 if (rc != ERROR_SUCCESS)
1149 return ERROR_SUCCESS;
1151 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1152 msiobj_release(&view->hdr);
1154 msi_create_component_directories( package );
1156 return rc;
1159 static UINT load_component( MSIRECORD *row, LPVOID param )
1161 MSIPACKAGE *package = param;
1162 MSICOMPONENT *comp;
1164 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1165 if (!comp)
1166 return ERROR_FUNCTION_FAILED;
1168 list_add_tail( &package->components, &comp->entry );
1170 /* fill in the data */
1171 comp->Component = msi_dup_record_field( row, 1 );
1173 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1175 comp->ComponentId = msi_dup_record_field( row, 2 );
1176 comp->Directory = msi_dup_record_field( row, 3 );
1177 comp->Attributes = MSI_RecordGetInteger(row,4);
1178 comp->Condition = msi_dup_record_field( row, 5 );
1179 comp->KeyPath = msi_dup_record_field( row, 6 );
1181 comp->Installed = INSTALLSTATE_UNKNOWN;
1182 msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1184 return ERROR_SUCCESS;
1187 static UINT load_all_components( MSIPACKAGE *package )
1189 static const WCHAR query[] = {
1190 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1191 '`','C','o','m','p','o','n','e','n','t','`',0 };
1192 MSIQUERY *view;
1193 UINT r;
1195 if (!list_empty(&package->components))
1196 return ERROR_SUCCESS;
1198 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1199 if (r != ERROR_SUCCESS)
1200 return r;
1202 r = MSI_IterateRecords(view, NULL, load_component, package);
1203 msiobj_release(&view->hdr);
1204 return r;
1207 typedef struct {
1208 MSIPACKAGE *package;
1209 MSIFEATURE *feature;
1210 } _ilfs;
1212 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1214 ComponentList *cl;
1216 cl = msi_alloc( sizeof (*cl) );
1217 if ( !cl )
1218 return ERROR_NOT_ENOUGH_MEMORY;
1219 cl->component = comp;
1220 list_add_tail( &feature->Components, &cl->entry );
1222 return ERROR_SUCCESS;
1225 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1227 FeatureList *fl;
1229 fl = msi_alloc( sizeof(*fl) );
1230 if ( !fl )
1231 return ERROR_NOT_ENOUGH_MEMORY;
1232 fl->feature = child;
1233 list_add_tail( &parent->Children, &fl->entry );
1235 return ERROR_SUCCESS;
1238 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1240 _ilfs* ilfs= (_ilfs*)param;
1241 LPCWSTR component;
1242 MSICOMPONENT *comp;
1244 component = MSI_RecordGetString(row,1);
1246 /* check to see if the component is already loaded */
1247 comp = get_loaded_component( ilfs->package, component );
1248 if (!comp)
1250 ERR("unknown component %s\n", debugstr_w(component));
1251 return ERROR_FUNCTION_FAILED;
1254 add_feature_component( ilfs->feature, comp );
1255 comp->Enabled = TRUE;
1257 return ERROR_SUCCESS;
1260 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1262 MSIFEATURE *feature;
1264 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1266 if ( !lstrcmpW( feature->Feature, name ) )
1267 return feature;
1270 return NULL;
1273 static UINT load_feature(MSIRECORD * row, LPVOID param)
1275 MSIPACKAGE* package = (MSIPACKAGE*)param;
1276 MSIFEATURE* feature;
1277 static const WCHAR Query1[] =
1278 {'S','E','L','E','C','T',' ',
1279 '`','C','o','m','p','o','n','e','n','t','_','`',
1280 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1281 'C','o','m','p','o','n','e','n','t','s','`',' ',
1282 'W','H','E','R','E',' ',
1283 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1284 MSIQUERY * view;
1285 UINT rc;
1286 _ilfs ilfs;
1288 /* fill in the data */
1290 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1291 if (!feature)
1292 return ERROR_NOT_ENOUGH_MEMORY;
1294 list_init( &feature->Children );
1295 list_init( &feature->Components );
1297 feature->Feature = msi_dup_record_field( row, 1 );
1299 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1301 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1302 feature->Title = msi_dup_record_field( row, 3 );
1303 feature->Description = msi_dup_record_field( row, 4 );
1305 if (!MSI_RecordIsNull(row,5))
1306 feature->Display = MSI_RecordGetInteger(row,5);
1308 feature->Level= MSI_RecordGetInteger(row,6);
1309 feature->Directory = msi_dup_record_field( row, 7 );
1310 feature->Attributes = MSI_RecordGetInteger(row,8);
1312 feature->Installed = INSTALLSTATE_UNKNOWN;
1313 msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1315 list_add_tail( &package->features, &feature->entry );
1317 /* load feature components */
1319 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1320 if (rc != ERROR_SUCCESS)
1321 return ERROR_SUCCESS;
1323 ilfs.package = package;
1324 ilfs.feature = feature;
1326 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1327 msiobj_release(&view->hdr);
1329 return ERROR_SUCCESS;
1332 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1334 MSIPACKAGE* package = (MSIPACKAGE*)param;
1335 MSIFEATURE *parent, *child;
1337 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1338 if (!child)
1339 return ERROR_FUNCTION_FAILED;
1341 if (!child->Feature_Parent)
1342 return ERROR_SUCCESS;
1344 parent = find_feature_by_name( package, child->Feature_Parent );
1345 if (!parent)
1346 return ERROR_FUNCTION_FAILED;
1348 add_feature_child( parent, child );
1349 return ERROR_SUCCESS;
1352 static UINT load_all_features( MSIPACKAGE *package )
1354 static const WCHAR query[] = {
1355 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1356 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1357 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1358 MSIQUERY *view;
1359 UINT r;
1361 if (!list_empty(&package->features))
1362 return ERROR_SUCCESS;
1364 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1365 if (r != ERROR_SUCCESS)
1366 return r;
1368 r = MSI_IterateRecords( view, NULL, load_feature, package );
1369 if (r != ERROR_SUCCESS)
1370 return r;
1372 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1373 msiobj_release( &view->hdr );
1375 return r;
1378 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1380 if (!p)
1381 return p;
1382 p = strchrW(p, ch);
1383 if (!p)
1384 return p;
1385 *p = 0;
1386 return p+1;
1389 static UINT load_file(MSIRECORD *row, LPVOID param)
1391 MSIPACKAGE* package = (MSIPACKAGE*)param;
1392 LPCWSTR component;
1393 MSIFILE *file;
1395 /* fill in the data */
1397 file = msi_alloc_zero( sizeof (MSIFILE) );
1398 if (!file)
1399 return ERROR_NOT_ENOUGH_MEMORY;
1401 file->File = msi_dup_record_field( row, 1 );
1403 component = MSI_RecordGetString( row, 2 );
1404 file->Component = get_loaded_component( package, component );
1406 if (!file->Component)
1407 ERR("Unfound Component %s\n",debugstr_w(component));
1409 file->FileName = msi_dup_record_field( row, 3 );
1410 reduce_to_longfilename( file->FileName );
1412 file->ShortName = msi_dup_record_field( row, 3 );
1413 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1415 file->FileSize = MSI_RecordGetInteger( row, 4 );
1416 file->Version = msi_dup_record_field( row, 5 );
1417 file->Language = msi_dup_record_field( row, 6 );
1418 file->Attributes = MSI_RecordGetInteger( row, 7 );
1419 file->Sequence = MSI_RecordGetInteger( row, 8 );
1421 file->state = msifs_invalid;
1423 /* if the compressed bits are not set in the file attributes,
1424 * then read the information from the package word count property
1426 if (file->Attributes & msidbFileAttributesCompressed)
1428 file->IsCompressed = TRUE;
1430 else if (file->Attributes & msidbFileAttributesNoncompressed)
1432 file->IsCompressed = FALSE;
1434 else
1436 file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1439 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1441 list_add_tail( &package->files, &file->entry );
1443 return ERROR_SUCCESS;
1446 static UINT load_all_files(MSIPACKAGE *package)
1448 MSIQUERY * view;
1449 UINT rc;
1450 static const WCHAR Query[] =
1451 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1452 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1453 '`','S','e','q','u','e','n','c','e','`', 0};
1455 if (!list_empty(&package->files))
1456 return ERROR_SUCCESS;
1458 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1459 if (rc != ERROR_SUCCESS)
1460 return ERROR_SUCCESS;
1462 rc = MSI_IterateRecords(view, NULL, load_file, package);
1463 msiobj_release(&view->hdr);
1465 return ERROR_SUCCESS;
1468 static UINT load_folder( MSIRECORD *row, LPVOID param )
1470 MSIPACKAGE *package = param;
1471 static const WCHAR szDot[] = { '.',0 };
1472 static WCHAR szEmpty[] = { 0 };
1473 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1474 MSIFOLDER *folder;
1476 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1477 if (!folder)
1478 return ERROR_NOT_ENOUGH_MEMORY;
1480 folder->Directory = msi_dup_record_field( row, 1 );
1482 TRACE("%s\n", debugstr_w(folder->Directory));
1484 p = msi_dup_record_field(row, 3);
1486 /* split src and target dir */
1487 tgt_short = p;
1488 src_short = folder_split_path( p, ':' );
1490 /* split the long and short paths */
1491 tgt_long = folder_split_path( tgt_short, '|' );
1492 src_long = folder_split_path( src_short, '|' );
1494 /* check for no-op dirs */
1495 if (!lstrcmpW(szDot, tgt_short))
1496 tgt_short = szEmpty;
1497 if (!lstrcmpW(szDot, src_short))
1498 src_short = szEmpty;
1500 if (!tgt_long)
1501 tgt_long = tgt_short;
1503 if (!src_short) {
1504 src_short = tgt_short;
1505 src_long = tgt_long;
1508 if (!src_long)
1509 src_long = src_short;
1511 /* FIXME: use the target short path too */
1512 folder->TargetDefault = strdupW(tgt_long);
1513 folder->SourceShortPath = strdupW(src_short);
1514 folder->SourceLongPath = strdupW(src_long);
1515 msi_free(p);
1517 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1518 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1519 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1521 folder->Parent = msi_dup_record_field( row, 2 );
1523 folder->Property = msi_dup_property( package, folder->Directory );
1525 list_add_tail( &package->folders, &folder->entry );
1527 TRACE("returning %p\n", folder);
1529 return ERROR_SUCCESS;
1532 static UINT load_all_folders( MSIPACKAGE *package )
1534 static const WCHAR query[] = {
1535 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1536 '`','D','i','r','e','c','t','o','r','y','`',0 };
1537 MSIQUERY *view;
1538 UINT r;
1540 if (!list_empty(&package->folders))
1541 return ERROR_SUCCESS;
1543 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1544 if (r != ERROR_SUCCESS)
1545 return r;
1547 r = MSI_IterateRecords(view, NULL, load_folder, package);
1548 msiobj_release(&view->hdr);
1549 return r;
1553 * I am not doing any of the costing functionality yet.
1554 * Mostly looking at doing the Component and Feature loading
1556 * The native MSI does A LOT of modification to tables here. Mostly adding
1557 * a lot of temporary columns to the Feature and Component tables.
1559 * note: Native msi also tracks the short filename. But I am only going to
1560 * track the long ones. Also looking at this directory table
1561 * it appears that the directory table does not get the parents
1562 * resolved base on property only based on their entries in the
1563 * directory table.
1565 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1567 static const WCHAR szCosting[] =
1568 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1569 static const WCHAR szZero[] = { '0', 0 };
1571 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
1572 return ERROR_SUCCESS;
1574 MSI_SetPropertyW(package, szCosting, szZero);
1575 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1577 load_all_components( package );
1578 load_all_features( package );
1579 load_all_files( package );
1580 load_all_folders( package );
1582 return ERROR_SUCCESS;
1585 static UINT execute_script(MSIPACKAGE *package, UINT script )
1587 int i;
1588 UINT rc = ERROR_SUCCESS;
1590 TRACE("Executing Script %i\n",script);
1592 if (!package->script)
1594 ERR("no script!\n");
1595 return ERROR_FUNCTION_FAILED;
1598 for (i = 0; i < package->script->ActionCount[script]; i++)
1600 LPWSTR action;
1601 action = package->script->Actions[script][i];
1602 ui_actionstart(package, action);
1603 TRACE("Executing Action (%s)\n",debugstr_w(action));
1604 rc = ACTION_PerformAction(package, action, TRUE);
1605 if (rc != ERROR_SUCCESS)
1606 break;
1608 msi_free_action_script(package, script);
1609 return rc;
1612 static UINT ACTION_FileCost(MSIPACKAGE *package)
1614 return ERROR_SUCCESS;
1617 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1619 MSICOMPONENT *comp;
1621 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1623 INSTALLSTATE res;
1625 if (!comp->ComponentId)
1626 continue;
1628 res = MsiGetComponentPathW( package->ProductCode,
1629 comp->ComponentId, NULL, NULL);
1630 if (res < 0)
1631 res = INSTALLSTATE_ABSENT;
1632 comp->Installed = res;
1636 /* scan for and update current install states */
1637 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1639 MSICOMPONENT *comp;
1640 MSIFEATURE *feature;
1642 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1644 ComponentList *cl;
1645 INSTALLSTATE res = INSTALLSTATE_ABSENT;
1647 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1649 comp= cl->component;
1651 if (!comp->ComponentId)
1653 res = INSTALLSTATE_ABSENT;
1654 break;
1657 if (res == INSTALLSTATE_ABSENT)
1658 res = comp->Installed;
1659 else
1661 if (res == comp->Installed)
1662 continue;
1664 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1665 res != INSTALLSTATE_SOURCE)
1667 res = INSTALLSTATE_INCOMPLETE;
1671 feature->Installed = res;
1675 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
1676 INSTALLSTATE state)
1678 static const WCHAR all[]={'A','L','L',0};
1679 LPWSTR override;
1680 MSIFEATURE *feature;
1682 override = msi_dup_property( package, property );
1683 if (!override)
1684 return FALSE;
1686 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1688 if (strcmpiW(override,all)==0)
1689 msi_feature_set_state( feature, state );
1690 else
1692 LPWSTR ptr = override;
1693 LPWSTR ptr2 = strchrW(override,',');
1695 while (ptr)
1697 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1698 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1700 msi_feature_set_state( feature, state );
1701 break;
1703 if (ptr2)
1705 ptr=ptr2+1;
1706 ptr2 = strchrW(ptr,',');
1708 else
1709 break;
1713 msi_free(override);
1715 return TRUE;
1718 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1720 int install_level;
1721 static const WCHAR szlevel[] =
1722 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1723 static const WCHAR szAddLocal[] =
1724 {'A','D','D','L','O','C','A','L',0};
1725 static const WCHAR szRemove[] =
1726 {'R','E','M','O','V','E',0};
1727 static const WCHAR szReinstall[] =
1728 {'R','E','I','N','S','T','A','L','L',0};
1729 BOOL override = FALSE;
1730 MSICOMPONENT* component;
1731 MSIFEATURE *feature;
1734 /* I do not know if this is where it should happen.. but */
1736 TRACE("Checking Install Level\n");
1738 install_level = msi_get_property_int( package, szlevel, 1 );
1740 /* ok here is the _real_ rub
1741 * all these activation/deactivation things happen in order and things
1742 * later on the list override things earlier on the list.
1743 * 1) INSTALLLEVEL processing
1744 * 2) ADDLOCAL
1745 * 3) REMOVE
1746 * 4) ADDSOURCE
1747 * 5) ADDDEFAULT
1748 * 6) REINSTALL
1749 * 7) COMPADDLOCAL
1750 * 8) COMPADDSOURCE
1751 * 9) FILEADDLOCAL
1752 * 10) FILEADDSOURCE
1753 * 11) FILEADDDEFAULT
1754 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1755 * ignored for all the features. seems strange, especially since it is not
1756 * documented anywhere, but it is how it works.
1758 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1759 * REMOVE are the big ones, since we don't handle administrative installs
1760 * yet anyway.
1762 override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1763 override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1764 override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1766 if (!override)
1768 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1770 BOOL feature_state = ((feature->Level > 0) &&
1771 (feature->Level <= install_level));
1773 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1775 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1776 msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1777 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1778 msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1779 else
1780 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1784 /* disable child features of unselected parent features */
1785 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1787 FeatureList *fl;
1789 if (feature->Level > 0 && feature->Level <= install_level)
1790 continue;
1792 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1793 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1796 else
1798 /* set the Preselected Property */
1799 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1800 static const WCHAR szOne[] = { '1', 0 };
1802 MSI_SetPropertyW(package,szPreselected,szOne);
1806 * now we want to enable or disable components base on feature
1809 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1811 ComponentList *cl;
1813 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1814 debugstr_w(feature->Feature), feature->Installed, feature->Action);
1816 /* features with components that have compressed files are made local */
1817 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1819 if (cl->component->Enabled &&
1820 cl->component->ForceLocalState &&
1821 feature->Action == INSTALLSTATE_SOURCE)
1823 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1824 break;
1828 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1830 component = cl->component;
1832 if (!component->Enabled)
1833 continue;
1835 switch (feature->Action)
1837 case INSTALLSTATE_ADVERTISED:
1838 component->hasAdvertiseFeature = 1;
1839 break;
1840 case INSTALLSTATE_SOURCE:
1841 component->hasSourceFeature = 1;
1842 break;
1843 case INSTALLSTATE_LOCAL:
1844 component->hasLocalFeature = 1;
1845 break;
1846 case INSTALLSTATE_DEFAULT:
1847 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1848 component->hasAdvertiseFeature = 1;
1849 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1850 component->hasSourceFeature = 1;
1851 else
1852 component->hasLocalFeature = 1;
1853 break;
1854 default:
1855 break;
1860 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1862 /* if the component isn't enabled, leave it alone */
1863 if (!component->Enabled)
1864 continue;
1866 /* check if it's local or source */
1867 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1868 (component->hasLocalFeature || component->hasSourceFeature))
1870 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1871 !component->ForceLocalState)
1872 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1873 else
1874 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1875 continue;
1878 /* if any feature is local, the component must be local too */
1879 if (component->hasLocalFeature)
1881 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1882 continue;
1885 if (component->hasSourceFeature)
1887 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1888 continue;
1891 if (component->hasAdvertiseFeature)
1893 msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1894 continue;
1897 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1900 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1902 if (component->Action == INSTALLSTATE_DEFAULT)
1904 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1905 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1908 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1909 debugstr_w(component->Component), component->Installed, component->Action);
1913 return ERROR_SUCCESS;
1916 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1918 MSIPACKAGE *package = (MSIPACKAGE*)param;
1919 LPCWSTR name;
1920 LPWSTR path;
1922 name = MSI_RecordGetString(row,1);
1924 /* This helper function now does ALL the work */
1925 TRACE("Dir %s ...\n",debugstr_w(name));
1926 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1927 TRACE("resolves to %s\n",debugstr_w(path));
1928 msi_free(path);
1930 return ERROR_SUCCESS;
1933 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1935 MSIPACKAGE *package = (MSIPACKAGE*)param;
1936 LPCWSTR name;
1937 MSIFEATURE *feature;
1939 name = MSI_RecordGetString( row, 1 );
1941 feature = get_loaded_feature( package, name );
1942 if (!feature)
1943 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1944 else
1946 LPCWSTR Condition;
1947 Condition = MSI_RecordGetString(row,3);
1949 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1951 int level = MSI_RecordGetInteger(row,2);
1952 TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
1953 feature->Level = level;
1956 return ERROR_SUCCESS;
1959 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1961 static const WCHAR name_fmt[] =
1962 {'%','u','.','%','u','.','%','u','.','%','u',0};
1963 static WCHAR name[] = {'\\',0};
1964 VS_FIXEDFILEINFO *lpVer;
1965 WCHAR filever[0x100];
1966 LPVOID version;
1967 DWORD versize;
1968 DWORD handle;
1969 UINT sz;
1971 TRACE("%s\n", debugstr_w(filename));
1973 versize = GetFileVersionInfoSizeW( filename, &handle );
1974 if (!versize)
1975 return NULL;
1977 version = msi_alloc( versize );
1978 GetFileVersionInfoW( filename, 0, versize, version );
1980 VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz );
1981 msi_free( version );
1983 sprintfW( filever, name_fmt,
1984 HIWORD(lpVer->dwFileVersionMS),
1985 LOWORD(lpVer->dwFileVersionMS),
1986 HIWORD(lpVer->dwFileVersionLS),
1987 LOWORD(lpVer->dwFileVersionLS));
1989 return strdupW( filever );
1992 static UINT msi_check_file_install_states( MSIPACKAGE *package )
1994 LPWSTR file_version;
1995 MSIFILE *file;
1997 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1999 MSICOMPONENT* comp = file->Component;
2000 LPWSTR p;
2002 if (!comp)
2003 continue;
2005 if (file->IsCompressed)
2006 comp->ForceLocalState = TRUE;
2008 /* calculate target */
2009 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2011 msi_free(file->TargetPath);
2013 TRACE("file %s is named %s\n",
2014 debugstr_w(file->File), debugstr_w(file->FileName));
2016 file->TargetPath = build_directory_name(2, p, file->FileName);
2018 msi_free(p);
2020 TRACE("file %s resolves to %s\n",
2021 debugstr_w(file->File), debugstr_w(file->TargetPath));
2023 /* don't check files of components that aren't installed */
2024 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2025 comp->Installed == INSTALLSTATE_ABSENT)
2027 file->state = msifs_missing; /* assume files are missing */
2028 continue;
2031 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2033 file->state = msifs_missing;
2034 comp->Cost += file->FileSize;
2035 comp->Installed = INSTALLSTATE_INCOMPLETE;
2036 continue;
2039 if (file->Version &&
2040 (file_version = msi_get_disk_file_version( file->TargetPath )))
2042 TRACE("new %s old %s\n", debugstr_w(file->Version),
2043 debugstr_w(file_version));
2044 /* FIXME: seems like a bad way to compare version numbers */
2045 if (lstrcmpiW(file_version, file->Version)<0)
2047 file->state = msifs_overwrite;
2048 comp->Cost += file->FileSize;
2049 comp->Installed = INSTALLSTATE_INCOMPLETE;
2051 else
2052 file->state = msifs_present;
2053 msi_free( file_version );
2055 else
2056 file->state = msifs_present;
2059 return ERROR_SUCCESS;
2063 * A lot is done in this function aside from just the costing.
2064 * The costing needs to be implemented at some point but for now I am going
2065 * to focus on the directory building
2068 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2070 static const WCHAR ExecSeqQuery[] =
2071 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2072 '`','D','i','r','e','c','t','o','r','y','`',0};
2073 static const WCHAR ConditionQuery[] =
2074 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2075 '`','C','o','n','d','i','t','i','o','n','`',0};
2076 static const WCHAR szCosting[] =
2077 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2078 static const WCHAR szlevel[] =
2079 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2080 static const WCHAR szOne[] = { '1', 0 };
2081 MSICOMPONENT *comp;
2082 UINT rc;
2083 MSIQUERY * view;
2084 LPWSTR level;
2086 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2087 return ERROR_SUCCESS;
2089 TRACE("Building Directory properties\n");
2091 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2092 if (rc == ERROR_SUCCESS)
2094 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2095 package);
2096 msiobj_release(&view->hdr);
2099 /* read components states from the registry */
2100 ACTION_GetComponentInstallStates(package);
2102 TRACE("File calculations\n");
2103 msi_check_file_install_states( package );
2105 TRACE("Evaluating Condition Table\n");
2107 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2108 if (rc == ERROR_SUCCESS)
2110 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2111 package);
2112 msiobj_release(&view->hdr);
2115 TRACE("Enabling or Disabling Components\n");
2116 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2118 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2120 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2121 comp->Enabled = FALSE;
2125 MSI_SetPropertyW(package,szCosting,szOne);
2126 /* set default run level if not set */
2127 level = msi_dup_property( package, szlevel );
2128 if (!level)
2129 MSI_SetPropertyW(package,szlevel, szOne);
2130 msi_free(level);
2132 ACTION_UpdateFeatureInstallStates(package);
2134 return MSI_SetFeatureStates(package);
2137 /* OK this value is "interpreted" and then formatted based on the
2138 first few characters */
2139 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2140 DWORD *size)
2142 LPSTR data = NULL;
2143 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2145 if (value[1]=='x')
2147 LPWSTR ptr;
2148 CHAR byte[5];
2149 LPWSTR deformated = NULL;
2150 int count;
2152 deformat_string(package, &value[2], &deformated);
2154 /* binary value type */
2155 ptr = deformated;
2156 *type = REG_BINARY;
2157 if (strlenW(ptr)%2)
2158 *size = (strlenW(ptr)/2)+1;
2159 else
2160 *size = strlenW(ptr)/2;
2162 data = msi_alloc(*size);
2164 byte[0] = '0';
2165 byte[1] = 'x';
2166 byte[4] = 0;
2167 count = 0;
2168 /* if uneven pad with a zero in front */
2169 if (strlenW(ptr)%2)
2171 byte[2]= '0';
2172 byte[3]= *ptr;
2173 ptr++;
2174 data[count] = (BYTE)strtol(byte,NULL,0);
2175 count ++;
2176 TRACE("Uneven byte count\n");
2178 while (*ptr)
2180 byte[2]= *ptr;
2181 ptr++;
2182 byte[3]= *ptr;
2183 ptr++;
2184 data[count] = (BYTE)strtol(byte,NULL,0);
2185 count ++;
2187 msi_free(deformated);
2189 TRACE("Data %i bytes(%i)\n",*size,count);
2191 else
2193 LPWSTR deformated;
2194 LPWSTR p;
2195 DWORD d = 0;
2196 deformat_string(package, &value[1], &deformated);
2198 *type=REG_DWORD;
2199 *size = sizeof(DWORD);
2200 data = msi_alloc(*size);
2201 p = deformated;
2202 if (*p == '-')
2203 p++;
2204 while (*p)
2206 if ( (*p < '0') || (*p > '9') )
2207 break;
2208 d *= 10;
2209 d += (*p - '0');
2210 p++;
2212 if (deformated[0] == '-')
2213 d = -d;
2214 *(LPDWORD)data = d;
2215 TRACE("DWORD %i\n",*(LPDWORD)data);
2217 msi_free(deformated);
2220 else
2222 static const WCHAR szMulti[] = {'[','~',']',0};
2223 LPCWSTR ptr;
2224 *type=REG_SZ;
2226 if (value[0]=='#')
2228 if (value[1]=='%')
2230 ptr = &value[2];
2231 *type=REG_EXPAND_SZ;
2233 else
2234 ptr = &value[1];
2236 else
2237 ptr=value;
2239 if (strstrW(value,szMulti))
2240 *type = REG_MULTI_SZ;
2242 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2244 return data;
2247 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2249 MSIPACKAGE *package = (MSIPACKAGE*)param;
2250 static const WCHAR szHCR[] =
2251 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2252 'R','O','O','T','\\',0};
2253 static const WCHAR szHCU[] =
2254 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2255 'U','S','E','R','\\',0};
2256 static const WCHAR szHLM[] =
2257 {'H','K','E','Y','_','L','O','C','A','L','_',
2258 'M','A','C','H','I','N','E','\\',0};
2259 static const WCHAR szHU[] =
2260 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2262 LPSTR value_data = NULL;
2263 HKEY root_key, hkey;
2264 DWORD type,size;
2265 LPWSTR deformated;
2266 LPCWSTR szRoot, component, name, key, value;
2267 MSICOMPONENT *comp;
2268 MSIRECORD * uirow;
2269 LPWSTR uikey;
2270 INT root;
2271 BOOL check_first = FALSE;
2272 UINT rc;
2274 ui_progress(package,2,0,0,0);
2276 value = NULL;
2277 key = NULL;
2278 uikey = NULL;
2279 name = NULL;
2281 component = MSI_RecordGetString(row, 6);
2282 comp = get_loaded_component(package,component);
2283 if (!comp)
2284 return ERROR_SUCCESS;
2286 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2288 TRACE("Skipping write due to disabled component %s\n",
2289 debugstr_w(component));
2291 comp->Action = comp->Installed;
2293 return ERROR_SUCCESS;
2296 comp->Action = INSTALLSTATE_LOCAL;
2298 name = MSI_RecordGetString(row, 4);
2299 if( MSI_RecordIsNull(row,5) && name )
2301 /* null values can have special meanings */
2302 if (name[0]=='-' && name[1] == 0)
2303 return ERROR_SUCCESS;
2304 else if ((name[0]=='+' && name[1] == 0) ||
2305 (name[0] == '*' && name[1] == 0))
2306 name = NULL;
2307 check_first = TRUE;
2310 root = MSI_RecordGetInteger(row,2);
2311 key = MSI_RecordGetString(row, 3);
2313 /* get the root key */
2314 switch (root)
2316 case -1:
2318 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2319 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2320 if (all_users && all_users[0] == '1')
2322 root_key = HKEY_LOCAL_MACHINE;
2323 szRoot = szHLM;
2325 else
2327 root_key = HKEY_CURRENT_USER;
2328 szRoot = szHCU;
2330 msi_free(all_users);
2332 break;
2333 case 0: root_key = HKEY_CLASSES_ROOT;
2334 szRoot = szHCR;
2335 break;
2336 case 1: root_key = HKEY_CURRENT_USER;
2337 szRoot = szHCU;
2338 break;
2339 case 2: root_key = HKEY_LOCAL_MACHINE;
2340 szRoot = szHLM;
2341 break;
2342 case 3: root_key = HKEY_USERS;
2343 szRoot = szHU;
2344 break;
2345 default:
2346 ERR("Unknown root %i\n",root);
2347 root_key=NULL;
2348 szRoot = NULL;
2349 break;
2351 if (!root_key)
2352 return ERROR_SUCCESS;
2354 deformat_string(package, key , &deformated);
2355 size = strlenW(deformated) + strlenW(szRoot) + 1;
2356 uikey = msi_alloc(size*sizeof(WCHAR));
2357 strcpyW(uikey,szRoot);
2358 strcatW(uikey,deformated);
2360 if (RegCreateKeyW( root_key, deformated, &hkey))
2362 ERR("Could not create key %s\n",debugstr_w(deformated));
2363 msi_free(deformated);
2364 msi_free(uikey);
2365 return ERROR_SUCCESS;
2367 msi_free(deformated);
2369 value = MSI_RecordGetString(row,5);
2370 if (value)
2371 value_data = parse_value(package, value, &type, &size);
2372 else
2374 static const WCHAR szEmpty[] = {0};
2375 value_data = (LPSTR)strdupW(szEmpty);
2376 size = 0;
2377 type = REG_SZ;
2380 deformat_string(package, name, &deformated);
2382 /* get the double nulls to terminate SZ_MULTI */
2383 if (type == REG_MULTI_SZ)
2384 size +=sizeof(WCHAR);
2386 if (!check_first)
2388 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2389 debugstr_w(uikey));
2390 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2392 else
2394 DWORD sz = 0;
2395 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2396 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2398 TRACE("value %s of %s checked already exists\n",
2399 debugstr_w(deformated), debugstr_w(uikey));
2401 else
2403 TRACE("Checked and setting value %s of %s\n",
2404 debugstr_w(deformated), debugstr_w(uikey));
2405 if (deformated || size)
2406 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2409 RegCloseKey(hkey);
2411 uirow = MSI_CreateRecord(3);
2412 MSI_RecordSetStringW(uirow,2,deformated);
2413 MSI_RecordSetStringW(uirow,1,uikey);
2415 if (type == REG_SZ)
2416 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2417 else
2418 MSI_RecordSetStringW(uirow,3,value);
2420 ui_actiondata(package,szWriteRegistryValues,uirow);
2421 msiobj_release( &uirow->hdr );
2423 msi_free(value_data);
2424 msi_free(deformated);
2425 msi_free(uikey);
2427 return ERROR_SUCCESS;
2430 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2432 UINT rc;
2433 MSIQUERY * view;
2434 static const WCHAR ExecSeqQuery[] =
2435 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2436 '`','R','e','g','i','s','t','r','y','`',0 };
2438 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2439 if (rc != ERROR_SUCCESS)
2440 return ERROR_SUCCESS;
2442 /* increment progress bar each time action data is sent */
2443 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2445 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2447 msiobj_release(&view->hdr);
2448 return rc;
2451 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2453 package->script->CurrentlyScripting = TRUE;
2455 return ERROR_SUCCESS;
2459 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2461 MSICOMPONENT *comp;
2462 DWORD progress = 0;
2463 DWORD total = 0;
2464 static const WCHAR q1[]=
2465 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2466 '`','R','e','g','i','s','t','r','y','`',0};
2467 UINT rc;
2468 MSIQUERY * view;
2469 MSIFEATURE *feature;
2470 MSIFILE *file;
2472 TRACE("InstallValidate\n");
2474 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2475 if (rc == ERROR_SUCCESS)
2477 MSI_IterateRecords( view, &progress, NULL, package );
2478 msiobj_release( &view->hdr );
2479 total += progress * REG_PROGRESS_VALUE;
2482 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2483 total += COMPONENT_PROGRESS_VALUE;
2485 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2486 total += file->FileSize;
2488 ui_progress(package,0,total,0,0);
2490 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2492 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2493 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2494 feature->ActionRequest);
2497 return ERROR_SUCCESS;
2500 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2502 MSIPACKAGE* package = (MSIPACKAGE*)param;
2503 LPCWSTR cond = NULL;
2504 LPCWSTR message = NULL;
2505 static const WCHAR title[]=
2506 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2508 cond = MSI_RecordGetString(row,1);
2510 if (MSI_EvaluateConditionW(package,cond) != MSICONDITION_TRUE)
2512 LPWSTR deformated;
2513 message = MSI_RecordGetString(row,2);
2514 deformat_string(package,message,&deformated);
2515 MessageBoxW(NULL,deformated,title,MB_OK);
2516 msi_free(deformated);
2517 return ERROR_FUNCTION_FAILED;
2520 return ERROR_SUCCESS;
2523 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2525 UINT rc;
2526 MSIQUERY * view = NULL;
2527 static const WCHAR ExecSeqQuery[] =
2528 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2529 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2531 TRACE("Checking launch conditions\n");
2533 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2534 if (rc != ERROR_SUCCESS)
2535 return ERROR_SUCCESS;
2537 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2538 msiobj_release(&view->hdr);
2540 return rc;
2543 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2546 if (!cmp->KeyPath)
2547 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2549 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2551 MSIRECORD * row = 0;
2552 UINT root,len;
2553 LPWSTR deformated,buffer,deformated_name;
2554 LPCWSTR key,name;
2555 static const WCHAR ExecSeqQuery[] =
2556 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2557 '`','R','e','g','i','s','t','r','y','`',' ',
2558 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2559 ' ','=',' ' ,'\'','%','s','\'',0 };
2560 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2561 static const WCHAR fmt2[]=
2562 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2564 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2565 if (!row)
2566 return NULL;
2568 root = MSI_RecordGetInteger(row,2);
2569 key = MSI_RecordGetString(row, 3);
2570 name = MSI_RecordGetString(row, 4);
2571 deformat_string(package, key , &deformated);
2572 deformat_string(package, name, &deformated_name);
2574 len = strlenW(deformated) + 6;
2575 if (deformated_name)
2576 len+=strlenW(deformated_name);
2578 buffer = msi_alloc( len *sizeof(WCHAR));
2580 if (deformated_name)
2581 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2582 else
2583 sprintfW(buffer,fmt,root,deformated);
2585 msi_free(deformated);
2586 msi_free(deformated_name);
2587 msiobj_release(&row->hdr);
2589 return buffer;
2591 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2593 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2594 return NULL;
2596 else
2598 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2600 if (file)
2601 return strdupW( file->TargetPath );
2603 return NULL;
2606 static HKEY openSharedDLLsKey(void)
2608 HKEY hkey=0;
2609 static const WCHAR path[] =
2610 {'S','o','f','t','w','a','r','e','\\',
2611 'M','i','c','r','o','s','o','f','t','\\',
2612 'W','i','n','d','o','w','s','\\',
2613 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2614 'S','h','a','r','e','d','D','L','L','s',0};
2616 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2617 return hkey;
2620 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2622 HKEY hkey;
2623 DWORD count=0;
2624 DWORD type;
2625 DWORD sz = sizeof(count);
2626 DWORD rc;
2628 hkey = openSharedDLLsKey();
2629 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2630 if (rc != ERROR_SUCCESS)
2631 count = 0;
2632 RegCloseKey(hkey);
2633 return count;
2636 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2638 HKEY hkey;
2640 hkey = openSharedDLLsKey();
2641 if (count > 0)
2642 msi_reg_set_val_dword( hkey, path, count );
2643 else
2644 RegDeleteValueW(hkey,path);
2645 RegCloseKey(hkey);
2646 return count;
2650 * Return TRUE if the count should be written out and FALSE if not
2652 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2654 MSIFEATURE *feature;
2655 INT count = 0;
2656 BOOL write = FALSE;
2658 /* only refcount DLLs */
2659 if (comp->KeyPath == NULL ||
2660 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2661 comp->Attributes & msidbComponentAttributesODBCDataSource)
2662 write = FALSE;
2663 else
2665 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2666 write = (count > 0);
2668 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2669 write = TRUE;
2672 /* increment counts */
2673 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2675 ComponentList *cl;
2677 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2678 continue;
2680 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2682 if ( cl->component == comp )
2683 count++;
2687 /* decrement counts */
2688 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2690 ComponentList *cl;
2692 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2693 continue;
2695 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2697 if ( cl->component == comp )
2698 count--;
2702 /* ref count all the files in the component */
2703 if (write)
2705 MSIFILE *file;
2707 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2709 if (file->Component == comp)
2710 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2714 /* add a count for permenent */
2715 if (comp->Attributes & msidbComponentAttributesPermanent)
2716 count ++;
2718 comp->RefCount = count;
2720 if (write)
2721 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2725 * Ok further analysis makes me think that this work is
2726 * actually done in the PublishComponents and PublishFeatures
2727 * step, and not here. It appears like the keypath and all that is
2728 * resolved in this step, however actually written in the Publish steps.
2729 * But we will leave it here for now because it is unclear
2731 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2733 WCHAR squished_pc[GUID_SIZE];
2734 WCHAR squished_cc[GUID_SIZE];
2735 UINT rc;
2736 MSICOMPONENT *comp;
2737 HKEY hkey=0,hkey2=0;
2739 /* writes the Component and Features values to the registry */
2741 rc = MSIREG_OpenComponents(&hkey);
2742 if (rc != ERROR_SUCCESS)
2743 return rc;
2745 squash_guid(package->ProductCode,squished_pc);
2746 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2748 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2750 MSIRECORD * uirow;
2752 ui_progress(package,2,0,0,0);
2753 if (!comp->ComponentId)
2754 continue;
2756 squash_guid(comp->ComponentId,squished_cc);
2758 msi_free(comp->FullKeypath);
2759 comp->FullKeypath = resolve_keypath( package, comp );
2761 /* do the refcounting */
2762 ACTION_RefCountComponent( package, comp );
2764 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2765 debugstr_w(comp->Component),
2766 debugstr_w(squished_cc),
2767 debugstr_w(comp->FullKeypath),
2768 comp->RefCount);
2770 * Write the keypath out if the component is to be registered
2771 * and delete the key if the component is to be deregistered
2773 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2775 rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2776 if (rc != ERROR_SUCCESS)
2777 continue;
2779 if (!comp->FullKeypath)
2780 continue;
2782 msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2784 if (comp->Attributes & msidbComponentAttributesPermanent)
2786 static const WCHAR szPermKey[] =
2787 { '0','0','0','0','0','0','0','0','0','0','0','0',
2788 '0','0','0','0','0','0','0','0','0','0','0','0',
2789 '0','0','0','0','0','0','0','0',0 };
2791 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2794 RegCloseKey(hkey2);
2796 else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2798 DWORD res;
2800 rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2801 if (rc != ERROR_SUCCESS)
2802 continue;
2804 RegDeleteValueW(hkey2,squished_pc);
2806 /* if the key is empty delete it */
2807 res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2808 RegCloseKey(hkey2);
2809 if (res == ERROR_NO_MORE_ITEMS)
2810 RegDeleteKeyW(hkey,squished_cc);
2814 /* UI stuff */
2815 uirow = MSI_CreateRecord(3);
2816 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2817 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2818 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2819 ui_actiondata(package,szProcessComponents,uirow);
2820 msiobj_release( &uirow->hdr );
2822 RegCloseKey(hkey);
2823 return rc;
2826 typedef struct {
2827 CLSID clsid;
2828 LPWSTR source;
2830 LPWSTR path;
2831 ITypeLib *ptLib;
2832 } typelib_struct;
2834 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2835 LPWSTR lpszName, LONG_PTR lParam)
2837 TLIBATTR *attr;
2838 typelib_struct *tl_struct = (typelib_struct*) lParam;
2839 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2840 int sz;
2841 HRESULT res;
2843 if (!IS_INTRESOURCE(lpszName))
2845 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2846 return TRUE;
2849 sz = strlenW(tl_struct->source)+4;
2850 sz *= sizeof(WCHAR);
2852 if ((INT_PTR)lpszName == 1)
2853 tl_struct->path = strdupW(tl_struct->source);
2854 else
2856 tl_struct->path = msi_alloc(sz);
2857 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2860 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2861 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2862 if (!SUCCEEDED(res))
2864 msi_free(tl_struct->path);
2865 tl_struct->path = NULL;
2867 return TRUE;
2870 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2871 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2873 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2874 return FALSE;
2877 msi_free(tl_struct->path);
2878 tl_struct->path = NULL;
2880 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2881 ITypeLib_Release(tl_struct->ptLib);
2883 return TRUE;
2886 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2888 MSIPACKAGE* package = (MSIPACKAGE*)param;
2889 LPCWSTR component;
2890 MSICOMPONENT *comp;
2891 MSIFILE *file;
2892 typelib_struct tl_struct;
2893 HMODULE module;
2894 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2896 component = MSI_RecordGetString(row,3);
2897 comp = get_loaded_component(package,component);
2898 if (!comp)
2899 return ERROR_SUCCESS;
2901 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2903 TRACE("Skipping typelib reg due to disabled component\n");
2905 comp->Action = comp->Installed;
2907 return ERROR_SUCCESS;
2910 comp->Action = INSTALLSTATE_LOCAL;
2912 file = get_loaded_file( package, comp->KeyPath );
2913 if (!file)
2914 return ERROR_SUCCESS;
2916 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2917 if (module)
2919 LPCWSTR guid;
2920 guid = MSI_RecordGetString(row,1);
2921 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2922 tl_struct.source = strdupW( file->TargetPath );
2923 tl_struct.path = NULL;
2925 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2926 (LONG_PTR)&tl_struct);
2928 if (tl_struct.path)
2930 LPWSTR help = NULL;
2931 LPCWSTR helpid;
2932 HRESULT res;
2934 helpid = MSI_RecordGetString(row,6);
2936 if (helpid)
2937 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
2938 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2939 msi_free(help);
2941 if (!SUCCEEDED(res))
2942 ERR("Failed to register type library %s\n",
2943 debugstr_w(tl_struct.path));
2944 else
2946 ui_actiondata(package,szRegisterTypeLibraries,row);
2948 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2951 ITypeLib_Release(tl_struct.ptLib);
2952 msi_free(tl_struct.path);
2954 else
2955 ERR("Failed to load type library %s\n",
2956 debugstr_w(tl_struct.source));
2958 FreeLibrary(module);
2959 msi_free(tl_struct.source);
2961 else
2962 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
2964 return ERROR_SUCCESS;
2967 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
2970 * OK this is a bit confusing.. I am given a _Component key and I believe
2971 * that the file that is being registered as a type library is the "key file
2972 * of that component" which I interpret to mean "The file in the KeyPath of
2973 * that component".
2975 UINT rc;
2976 MSIQUERY * view;
2977 static const WCHAR Query[] =
2978 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2979 '`','T','y','p','e','L','i','b','`',0};
2981 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2982 if (rc != ERROR_SUCCESS)
2983 return ERROR_SUCCESS;
2985 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
2986 msiobj_release(&view->hdr);
2987 return rc;
2990 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
2992 MSIPACKAGE *package = (MSIPACKAGE*)param;
2993 LPWSTR target_file, target_folder, filename;
2994 LPCWSTR buffer, extension;
2995 MSICOMPONENT *comp;
2996 static const WCHAR szlnk[]={'.','l','n','k',0};
2997 IShellLinkW *sl = NULL;
2998 IPersistFile *pf = NULL;
2999 HRESULT res;
3001 buffer = MSI_RecordGetString(row,4);
3002 comp = get_loaded_component(package,buffer);
3003 if (!comp)
3004 return ERROR_SUCCESS;
3006 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3008 TRACE("Skipping shortcut creation due to disabled component\n");
3010 comp->Action = comp->Installed;
3012 return ERROR_SUCCESS;
3015 comp->Action = INSTALLSTATE_LOCAL;
3017 ui_actiondata(package,szCreateShortcuts,row);
3019 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3020 &IID_IShellLinkW, (LPVOID *) &sl );
3022 if (FAILED( res ))
3024 ERR("CLSID_ShellLink not available\n");
3025 goto err;
3028 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3029 if (FAILED( res ))
3031 ERR("QueryInterface(IID_IPersistFile) failed\n");
3032 goto err;
3035 buffer = MSI_RecordGetString(row,2);
3036 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3038 /* may be needed because of a bug somehwere else */
3039 create_full_pathW(target_folder);
3041 filename = msi_dup_record_field( row, 3 );
3042 reduce_to_longfilename(filename);
3044 extension = strchrW(filename,'.');
3045 if (!extension || strcmpiW(extension,szlnk))
3047 int len = strlenW(filename);
3048 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3049 memcpy(filename + len, szlnk, sizeof(szlnk));
3051 target_file = build_directory_name(2, target_folder, filename);
3052 msi_free(target_folder);
3053 msi_free(filename);
3055 buffer = MSI_RecordGetString(row,5);
3056 if (strchrW(buffer,'['))
3058 LPWSTR deformated;
3059 deformat_string(package,buffer,&deformated);
3060 IShellLinkW_SetPath(sl,deformated);
3061 msi_free(deformated);
3063 else
3065 FIXME("poorly handled shortcut format, advertised shortcut\n");
3066 IShellLinkW_SetPath(sl,comp->FullKeypath);
3069 if (!MSI_RecordIsNull(row,6))
3071 LPWSTR deformated;
3072 buffer = MSI_RecordGetString(row,6);
3073 deformat_string(package,buffer,&deformated);
3074 IShellLinkW_SetArguments(sl,deformated);
3075 msi_free(deformated);
3078 if (!MSI_RecordIsNull(row,7))
3080 buffer = MSI_RecordGetString(row,7);
3081 IShellLinkW_SetDescription(sl,buffer);
3084 if (!MSI_RecordIsNull(row,8))
3085 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3087 if (!MSI_RecordIsNull(row,9))
3089 LPWSTR Path;
3090 INT index;
3092 buffer = MSI_RecordGetString(row,9);
3094 Path = build_icon_path(package,buffer);
3095 index = MSI_RecordGetInteger(row,10);
3097 /* no value means 0 */
3098 if (index == MSI_NULL_INTEGER)
3099 index = 0;
3101 IShellLinkW_SetIconLocation(sl,Path,index);
3102 msi_free(Path);
3105 if (!MSI_RecordIsNull(row,11))
3106 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3108 if (!MSI_RecordIsNull(row,12))
3110 LPWSTR Path;
3111 buffer = MSI_RecordGetString(row,12);
3112 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3113 if (Path)
3114 IShellLinkW_SetWorkingDirectory(sl,Path);
3115 msi_free(Path);
3118 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3119 IPersistFile_Save(pf,target_file,FALSE);
3121 msi_free(target_file);
3123 err:
3124 if (pf)
3125 IPersistFile_Release( pf );
3126 if (sl)
3127 IShellLinkW_Release( sl );
3129 return ERROR_SUCCESS;
3132 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3134 UINT rc;
3135 HRESULT res;
3136 MSIQUERY * view;
3137 static const WCHAR Query[] =
3138 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3139 '`','S','h','o','r','t','c','u','t','`',0};
3141 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3142 if (rc != ERROR_SUCCESS)
3143 return ERROR_SUCCESS;
3145 res = CoInitialize( NULL );
3146 if (FAILED (res))
3148 ERR("CoInitialize failed\n");
3149 return ERROR_FUNCTION_FAILED;
3152 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3153 msiobj_release(&view->hdr);
3155 CoUninitialize();
3157 return rc;
3160 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3162 MSIPACKAGE* package = (MSIPACKAGE*)param;
3163 HANDLE the_file;
3164 LPWSTR FilePath;
3165 LPCWSTR FileName;
3166 CHAR buffer[1024];
3167 DWORD sz;
3168 UINT rc;
3169 MSIRECORD *uirow;
3171 FileName = MSI_RecordGetString(row,1);
3172 if (!FileName)
3174 ERR("Unable to get FileName\n");
3175 return ERROR_SUCCESS;
3178 FilePath = build_icon_path(package,FileName);
3180 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3182 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3183 FILE_ATTRIBUTE_NORMAL, NULL);
3185 if (the_file == INVALID_HANDLE_VALUE)
3187 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3188 msi_free(FilePath);
3189 return ERROR_SUCCESS;
3194 DWORD write;
3195 sz = 1024;
3196 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3197 if (rc != ERROR_SUCCESS)
3199 ERR("Failed to get stream\n");
3200 CloseHandle(the_file);
3201 DeleteFileW(FilePath);
3202 break;
3204 WriteFile(the_file,buffer,sz,&write,NULL);
3205 } while (sz == 1024);
3207 msi_free(FilePath);
3209 CloseHandle(the_file);
3211 uirow = MSI_CreateRecord(1);
3212 MSI_RecordSetStringW(uirow,1,FileName);
3213 ui_actiondata(package,szPublishProduct,uirow);
3214 msiobj_release( &uirow->hdr );
3216 return ERROR_SUCCESS;
3220 * 99% of the work done here is only done for
3221 * advertised installs. However this is where the
3222 * Icon table is processed and written out
3223 * so that is what I am going to do here.
3225 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3227 UINT rc;
3228 MSIQUERY * view;
3229 static const WCHAR Query[]=
3230 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3231 '`','I','c','o','n','`',0};
3232 /* for registry stuff */
3233 HKEY hkey=0;
3234 HKEY hukey=0;
3235 static const WCHAR szProductLanguage[] =
3236 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3237 static const WCHAR szARPProductIcon[] =
3238 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3239 static const WCHAR szProductVersion[] =
3240 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3241 DWORD langid;
3242 LPWSTR buffer;
3243 DWORD size;
3244 MSIHANDLE hDb, hSumInfo;
3246 /* write out icon files */
3248 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3249 if (rc == ERROR_SUCCESS)
3251 MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3252 msiobj_release(&view->hdr);
3255 /* ok there is a lot more done here but i need to figure out what */
3257 rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3258 if (rc != ERROR_SUCCESS)
3259 goto end;
3261 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3262 if (rc != ERROR_SUCCESS)
3263 goto end;
3266 buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3267 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3268 msi_free(buffer);
3270 langid = msi_get_property_int( package, szProductLanguage, 0 );
3271 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3273 buffer = msi_dup_property( package, szARPProductIcon );
3274 if (buffer)
3276 LPWSTR path = build_icon_path(package,buffer);
3277 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3278 msi_free( path );
3280 msi_free(buffer);
3282 buffer = msi_dup_property( package, szProductVersion );
3283 if (buffer)
3285 DWORD verdword = msi_version_str_to_dword(buffer);
3286 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3288 msi_free(buffer);
3290 /* FIXME: Need to write more keys to the user registry */
3292 hDb= alloc_msihandle( &package->db->hdr );
3293 if (!hDb) {
3294 rc = ERROR_NOT_ENOUGH_MEMORY;
3295 goto end;
3297 rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo);
3298 MsiCloseHandle(hDb);
3299 if (rc == ERROR_SUCCESS)
3301 WCHAR guidbuffer[0x200];
3302 size = 0x200;
3303 rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3304 guidbuffer, &size);
3305 if (rc == ERROR_SUCCESS)
3307 WCHAR squashed[GUID_SIZE];
3308 /* for now we only care about the first guid */
3309 LPWSTR ptr = strchrW(guidbuffer,';');
3310 if (ptr) *ptr = 0;
3311 squash_guid(guidbuffer,squashed);
3312 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3314 else
3316 ERR("Unable to query Revision_Number...\n");
3317 rc = ERROR_SUCCESS;
3319 MsiCloseHandle(hSumInfo);
3321 else
3323 ERR("Unable to open Summary Information\n");
3324 rc = ERROR_SUCCESS;
3327 end:
3329 RegCloseKey(hkey);
3330 RegCloseKey(hukey);
3332 return rc;
3335 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3337 MSIPACKAGE *package = (MSIPACKAGE*)param;
3338 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3339 LPWSTR deformated_section, deformated_key, deformated_value;
3340 LPWSTR folder, fullname = NULL;
3341 MSIRECORD * uirow;
3342 INT action;
3343 MSICOMPONENT *comp;
3344 static const WCHAR szWindowsFolder[] =
3345 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3347 component = MSI_RecordGetString(row, 8);
3348 comp = get_loaded_component(package,component);
3350 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3352 TRACE("Skipping ini file due to disabled component %s\n",
3353 debugstr_w(component));
3355 comp->Action = comp->Installed;
3357 return ERROR_SUCCESS;
3360 comp->Action = INSTALLSTATE_LOCAL;
3362 identifier = MSI_RecordGetString(row,1);
3363 filename = MSI_RecordGetString(row,2);
3364 dirproperty = MSI_RecordGetString(row,3);
3365 section = MSI_RecordGetString(row,4);
3366 key = MSI_RecordGetString(row,5);
3367 value = MSI_RecordGetString(row,6);
3368 action = MSI_RecordGetInteger(row,7);
3370 deformat_string(package,section,&deformated_section);
3371 deformat_string(package,key,&deformated_key);
3372 deformat_string(package,value,&deformated_value);
3374 if (dirproperty)
3376 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3377 if (!folder)
3378 folder = msi_dup_property( package, dirproperty );
3380 else
3381 folder = msi_dup_property( package, szWindowsFolder );
3383 if (!folder)
3385 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3386 goto cleanup;
3389 fullname = build_directory_name(2, folder, filename);
3391 if (action == 0)
3393 TRACE("Adding value %s to section %s in %s\n",
3394 debugstr_w(deformated_key), debugstr_w(deformated_section),
3395 debugstr_w(fullname));
3396 WritePrivateProfileStringW(deformated_section, deformated_key,
3397 deformated_value, fullname);
3399 else if (action == 1)
3401 WCHAR returned[10];
3402 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3403 returned, 10, fullname);
3404 if (returned[0] == 0)
3406 TRACE("Adding value %s to section %s in %s\n",
3407 debugstr_w(deformated_key), debugstr_w(deformated_section),
3408 debugstr_w(fullname));
3410 WritePrivateProfileStringW(deformated_section, deformated_key,
3411 deformated_value, fullname);
3414 else if (action == 3)
3415 FIXME("Append to existing section not yet implemented\n");
3417 uirow = MSI_CreateRecord(4);
3418 MSI_RecordSetStringW(uirow,1,identifier);
3419 MSI_RecordSetStringW(uirow,2,deformated_section);
3420 MSI_RecordSetStringW(uirow,3,deformated_key);
3421 MSI_RecordSetStringW(uirow,4,deformated_value);
3422 ui_actiondata(package,szWriteIniValues,uirow);
3423 msiobj_release( &uirow->hdr );
3424 cleanup:
3425 msi_free(fullname);
3426 msi_free(folder);
3427 msi_free(deformated_key);
3428 msi_free(deformated_value);
3429 msi_free(deformated_section);
3430 return ERROR_SUCCESS;
3433 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3435 UINT rc;
3436 MSIQUERY * view;
3437 static const WCHAR ExecSeqQuery[] =
3438 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3439 '`','I','n','i','F','i','l','e','`',0};
3441 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3442 if (rc != ERROR_SUCCESS)
3444 TRACE("no IniFile table\n");
3445 return ERROR_SUCCESS;
3448 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3449 msiobj_release(&view->hdr);
3450 return rc;
3453 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3455 MSIPACKAGE *package = (MSIPACKAGE*)param;
3456 LPCWSTR filename;
3457 LPWSTR FullName;
3458 MSIFILE *file;
3459 DWORD len;
3460 static const WCHAR ExeStr[] =
3461 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3462 static const WCHAR close[] = {'\"',0};
3463 STARTUPINFOW si;
3464 PROCESS_INFORMATION info;
3465 BOOL brc;
3466 MSIRECORD *uirow;
3467 LPWSTR uipath, p;
3469 memset(&si,0,sizeof(STARTUPINFOW));
3471 filename = MSI_RecordGetString(row,1);
3472 file = get_loaded_file( package, filename );
3474 if (!file)
3476 ERR("Unable to find file id %s\n",debugstr_w(filename));
3477 return ERROR_SUCCESS;
3480 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3482 FullName = msi_alloc(len*sizeof(WCHAR));
3483 strcpyW(FullName,ExeStr);
3484 strcatW( FullName, file->TargetPath );
3485 strcatW(FullName,close);
3487 TRACE("Registering %s\n",debugstr_w(FullName));
3488 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3489 &si, &info);
3491 if (brc)
3492 msi_dialog_check_messages(info.hProcess);
3494 msi_free(FullName);
3496 /* the UI chunk */
3497 uirow = MSI_CreateRecord( 2 );
3498 uipath = strdupW( file->TargetPath );
3499 p = strrchrW(uipath,'\\');
3500 if (p)
3501 p[1]=0;
3502 MSI_RecordSetStringW( uirow, 1, &p[2] );
3503 MSI_RecordSetStringW( uirow, 2, uipath);
3504 ui_actiondata( package, szSelfRegModules, uirow);
3505 msiobj_release( &uirow->hdr );
3506 msi_free( uipath );
3507 /* FIXME: call ui_progress? */
3509 return ERROR_SUCCESS;
3512 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3514 UINT rc;
3515 MSIQUERY * view;
3516 static const WCHAR ExecSeqQuery[] =
3517 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3518 '`','S','e','l','f','R','e','g','`',0};
3520 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3521 if (rc != ERROR_SUCCESS)
3523 TRACE("no SelfReg table\n");
3524 return ERROR_SUCCESS;
3527 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3528 msiobj_release(&view->hdr);
3530 return ERROR_SUCCESS;
3533 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3535 MSIFEATURE *feature;
3536 UINT rc;
3537 HKEY hkey=0;
3538 HKEY hukey=0;
3540 rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3541 if (rc != ERROR_SUCCESS)
3542 goto end;
3544 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3545 if (rc != ERROR_SUCCESS)
3546 goto end;
3548 /* here the guids are base 85 encoded */
3549 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3551 ComponentList *cl;
3552 LPWSTR data = NULL;
3553 GUID clsid;
3554 INT size;
3555 BOOL absent = FALSE;
3556 MSIRECORD *uirow;
3558 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3559 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3560 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3561 absent = TRUE;
3563 size = 1;
3564 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3566 size += 21;
3568 if (feature->Feature_Parent)
3569 size += strlenW( feature->Feature_Parent )+2;
3571 data = msi_alloc(size * sizeof(WCHAR));
3573 data[0] = 0;
3574 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3576 MSICOMPONENT* component = cl->component;
3577 WCHAR buf[21];
3579 buf[0] = 0;
3580 if (component->ComponentId)
3582 TRACE("From %s\n",debugstr_w(component->ComponentId));
3583 CLSIDFromString(component->ComponentId, &clsid);
3584 encode_base85_guid(&clsid,buf);
3585 TRACE("to %s\n",debugstr_w(buf));
3586 strcatW(data,buf);
3589 if (feature->Feature_Parent)
3591 static const WCHAR sep[] = {'\2',0};
3592 strcatW(data,sep);
3593 strcatW(data,feature->Feature_Parent);
3596 msi_reg_set_val_str( hkey, feature->Feature, data );
3597 msi_free(data);
3599 size = 0;
3600 if (feature->Feature_Parent)
3601 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3602 if (!absent)
3604 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3605 (LPBYTE)feature->Feature_Parent,size);
3607 else
3609 size += 2*sizeof(WCHAR);
3610 data = msi_alloc(size);
3611 data[0] = 0x6;
3612 data[1] = 0;
3613 if (feature->Feature_Parent)
3614 strcpyW( &data[1], feature->Feature_Parent );
3615 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3616 (LPBYTE)data,size);
3617 msi_free(data);
3620 /* the UI chunk */
3621 uirow = MSI_CreateRecord( 1 );
3622 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3623 ui_actiondata( package, szPublishFeatures, uirow);
3624 msiobj_release( &uirow->hdr );
3625 /* FIXME: call ui_progress? */
3628 end:
3629 RegCloseKey(hkey);
3630 RegCloseKey(hukey);
3631 return rc;
3634 static UINT msi_get_local_package_name( LPWSTR path )
3636 static const WCHAR szInstaller[] = {
3637 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3638 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3639 DWORD time, len, i;
3640 HANDLE handle;
3642 time = GetTickCount();
3643 GetWindowsDirectoryW( path, MAX_PATH );
3644 lstrcatW( path, szInstaller );
3645 CreateDirectoryW( path, NULL );
3647 len = lstrlenW(path);
3648 for (i=0; i<0x10000; i++)
3650 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3651 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3652 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3653 if (handle != INVALID_HANDLE_VALUE)
3655 CloseHandle(handle);
3656 break;
3658 if (GetLastError() != ERROR_FILE_EXISTS &&
3659 GetLastError() != ERROR_SHARING_VIOLATION)
3660 return ERROR_FUNCTION_FAILED;
3663 return ERROR_SUCCESS;
3666 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3668 static const WCHAR szOriginalDatabase[] =
3669 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3670 WCHAR packagefile[MAX_PATH];
3671 LPWSTR msiFilePath;
3672 UINT r;
3674 r = msi_get_local_package_name( packagefile );
3675 if (r != ERROR_SUCCESS)
3676 return r;
3678 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3680 msiFilePath = msi_dup_property( package, szOriginalDatabase );
3681 r = CopyFileW( msiFilePath, packagefile, FALSE);
3682 msi_free( msiFilePath );
3684 if (!r)
3686 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3687 debugstr_w(msiFilePath), debugstr_w(packagefile), GetLastError());
3688 return ERROR_FUNCTION_FAILED;
3691 /* FIXME: maybe set this key in ACTION_RegisterProduct instead */
3692 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3693 return ERROR_SUCCESS;
3696 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3698 LPWSTR prop, val, key;
3699 static const LPCSTR propval[] = {
3700 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3701 "ARPCONTACT", "Contact",
3702 "ARPCOMMENTS", "Comments",
3703 "ProductName", "DisplayName",
3704 "ProductVersion", "DisplayVersion",
3705 "ARPHELPLINK", "HelpLink",
3706 "ARPHELPTELEPHONE", "HelpTelephone",
3707 "ARPINSTALLLOCATION", "InstallLocation",
3708 "SourceDir", "InstallSource",
3709 "Manufacturer", "Publisher",
3710 "ARPREADME", "Readme",
3711 "ARPSIZE", "Size",
3712 "ARPURLINFOABOUT", "URLInfoAbout",
3713 "ARPURLUPDATEINFO", "URLUpdateInfo",
3714 NULL,
3716 const LPCSTR *p = propval;
3718 while( *p )
3720 prop = strdupAtoW( *p++ );
3721 key = strdupAtoW( *p++ );
3722 val = msi_dup_property( package, prop );
3723 msi_reg_set_val_str( hkey, key, val );
3724 msi_free(val);
3725 msi_free(key);
3726 msi_free(prop);
3728 return ERROR_SUCCESS;
3731 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3733 HKEY hkey=0;
3734 LPWSTR buffer = NULL;
3735 UINT rc;
3736 DWORD size, langid;
3737 static const WCHAR szWindowsInstaller[] =
3738 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3739 static const WCHAR szUpgradeCode[] =
3740 {'U','p','g','r','a','d','e','C','o','d','e',0};
3741 static const WCHAR modpath_fmt[] =
3742 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3743 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3744 static const WCHAR szModifyPath[] =
3745 {'M','o','d','i','f','y','P','a','t','h',0};
3746 static const WCHAR szUninstallString[] =
3747 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3748 static const WCHAR szEstimatedSize[] =
3749 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3750 static const WCHAR szProductLanguage[] =
3751 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3752 static const WCHAR szProductVersion[] =
3753 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3755 SYSTEMTIME systime;
3756 static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0};
3757 LPWSTR upgrade_code;
3758 WCHAR szDate[9];
3760 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3761 if (rc != ERROR_SUCCESS)
3762 return rc;
3764 /* dump all the info i can grab */
3765 /* FIXME: Flesh out more information */
3767 msi_write_uninstall_property_vals( package, hkey );
3769 msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
3771 msi_make_package_local( package, hkey );
3773 /* do ModifyPath and UninstallString */
3774 size = deformat_string(package,modpath_fmt,&buffer);
3775 RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3776 RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3777 msi_free(buffer);
3779 /* FIXME: Write real Estimated Size when we have it */
3780 msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
3782 GetLocalTime(&systime);
3783 sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
3784 msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
3786 langid = msi_get_property_int( package, szProductLanguage, 0 );
3787 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3789 buffer = msi_dup_property( package, szProductVersion );
3790 if (buffer)
3792 DWORD verdword = msi_version_str_to_dword(buffer);
3794 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3795 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
3796 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
3798 msi_free(buffer);
3800 /* Handle Upgrade Codes */
3801 upgrade_code = msi_dup_property( package, szUpgradeCode );
3802 if (upgrade_code)
3804 HKEY hkey2;
3805 WCHAR squashed[33];
3806 MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3807 squash_guid(package->ProductCode,squashed);
3808 msi_reg_set_val_str( hkey2, squashed, NULL );
3809 RegCloseKey(hkey2);
3810 MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3811 squash_guid(package->ProductCode,squashed);
3812 msi_reg_set_val_str( hkey2, squashed, NULL );
3813 RegCloseKey(hkey2);
3815 msi_free(upgrade_code);
3818 RegCloseKey(hkey);
3820 /* FIXME: call ui_actiondata */
3822 return ERROR_SUCCESS;
3825 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
3827 return execute_script(package,INSTALL_SCRIPT);
3830 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
3832 UINT rc;
3834 /* turn off scheduling */
3835 package->script->CurrentlyScripting= FALSE;
3837 /* first do the same as an InstallExecute */
3838 rc = ACTION_InstallExecute(package);
3839 if (rc != ERROR_SUCCESS)
3840 return rc;
3842 /* then handle Commit Actions */
3843 rc = execute_script(package,COMMIT_SCRIPT);
3845 return rc;
3848 static UINT ACTION_ForceReboot(MSIPACKAGE *package)
3850 static const WCHAR RunOnce[] = {
3851 'S','o','f','t','w','a','r','e','\\',
3852 'M','i','c','r','o','s','o','f','t','\\',
3853 'W','i','n','d','o','w','s','\\',
3854 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3855 'R','u','n','O','n','c','e',0};
3856 static const WCHAR InstallRunOnce[] = {
3857 'S','o','f','t','w','a','r','e','\\',
3858 'M','i','c','r','o','s','o','f','t','\\',
3859 'W','i','n','d','o','w','s','\\',
3860 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3861 'I','n','s','t','a','l','l','e','r','\\',
3862 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
3864 static const WCHAR msiexec_fmt[] = {
3865 '%','s',
3866 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
3867 '\"','%','s','\"',0};
3868 static const WCHAR install_fmt[] = {
3869 '/','I',' ','\"','%','s','\"',' ',
3870 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
3871 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
3872 WCHAR buffer[256], sysdir[MAX_PATH];
3873 HKEY hkey;
3874 WCHAR squished_pc[100];
3876 squash_guid(package->ProductCode,squished_pc);
3878 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
3879 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
3880 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
3881 squished_pc);
3883 msi_reg_set_val_str( hkey, squished_pc, buffer );
3884 RegCloseKey(hkey);
3886 TRACE("Reboot command %s\n",debugstr_w(buffer));
3888 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
3889 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
3891 msi_reg_set_val_str( hkey, squished_pc, buffer );
3892 RegCloseKey(hkey);
3894 return ERROR_INSTALL_SUSPEND;
3897 static UINT msi_set_sourcedir_props(MSIPACKAGE *package)
3899 LPWSTR p, source;
3900 DWORD len;
3902 p = strrchrW( package->PackagePath, '\\' );
3903 if (!p)
3904 return ERROR_SUCCESS;
3906 len = p - package->PackagePath + 2;
3907 source = msi_alloc( len * sizeof(WCHAR) );
3908 lstrcpynW( source, package->PackagePath, len );
3910 MSI_SetPropertyW( package, cszSourceDir, source );
3911 MSI_SetPropertyW( package, cszSOURCEDIR, source );
3913 msi_free( source );
3915 return ERROR_SUCCESS;
3918 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
3920 DWORD attrib;
3921 UINT rc;
3924 * We are currently doing what should be done here in the top level Install
3925 * however for Administrative and uninstalls this step will be needed
3927 if (!package->PackagePath)
3928 return ERROR_SUCCESS;
3930 msi_set_sourcedir_props(package);
3932 attrib = GetFileAttributesW(package->PackagePath);
3933 if (attrib == INVALID_FILE_ATTRIBUTES)
3935 LPWSTR prompt;
3936 LPWSTR msg;
3937 DWORD size = 0;
3939 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
3940 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
3941 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
3942 if (rc == ERROR_MORE_DATA)
3944 prompt = msi_alloc(size * sizeof(WCHAR));
3945 MsiSourceListGetInfoW(package->ProductCode, NULL,
3946 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
3947 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
3949 else
3950 prompt = strdupW(package->PackagePath);
3952 msg = generate_error_string(package,1302,1,prompt);
3953 while(attrib == INVALID_FILE_ATTRIBUTES)
3955 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
3956 if (rc == IDCANCEL)
3958 rc = ERROR_INSTALL_USEREXIT;
3959 break;
3961 attrib = GetFileAttributesW(package->PackagePath);
3963 msi_free(prompt);
3964 rc = ERROR_SUCCESS;
3966 else
3967 return ERROR_SUCCESS;
3969 return rc;
3972 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
3974 HKEY hkey=0;
3975 LPWSTR buffer;
3976 LPWSTR productid;
3977 UINT rc,i;
3979 static const WCHAR szPropKeys[][80] =
3981 {'P','r','o','d','u','c','t','I','D',0},
3982 {'U','S','E','R','N','A','M','E',0},
3983 {'C','O','M','P','A','N','Y','N','A','M','E',0},
3984 {0},
3987 static const WCHAR szRegKeys[][80] =
3989 {'P','r','o','d','u','c','t','I','D',0},
3990 {'R','e','g','O','w','n','e','r',0},
3991 {'R','e','g','C','o','m','p','a','n','y',0},
3992 {0},
3995 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
3996 if (!productid)
3997 return ERROR_SUCCESS;
3999 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4000 if (rc != ERROR_SUCCESS)
4001 goto end;
4003 for( i = 0; szPropKeys[i][0]; i++ )
4005 buffer = msi_dup_property( package, szPropKeys[i] );
4006 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4007 msi_free( buffer );
4010 end:
4011 msi_free(productid);
4012 RegCloseKey(hkey);
4014 /* FIXME: call ui_actiondata */
4016 return ERROR_SUCCESS;
4020 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4022 UINT rc;
4024 package->script->InWhatSequence |= SEQUENCE_EXEC;
4025 rc = ACTION_ProcessExecSequence(package,FALSE);
4026 return rc;
4030 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4032 MSIPACKAGE *package = (MSIPACKAGE*)param;
4033 LPCWSTR compgroupid=NULL;
4034 LPCWSTR feature=NULL;
4035 LPCWSTR text = NULL;
4036 LPCWSTR qualifier = NULL;
4037 LPCWSTR component = NULL;
4038 LPWSTR advertise = NULL;
4039 LPWSTR output = NULL;
4040 HKEY hkey;
4041 UINT rc = ERROR_SUCCESS;
4042 MSICOMPONENT *comp;
4043 DWORD sz = 0;
4044 MSIRECORD *uirow;
4046 component = MSI_RecordGetString(rec,3);
4047 comp = get_loaded_component(package,component);
4049 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4050 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4051 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4053 TRACE("Skipping: Component %s not scheduled for install\n",
4054 debugstr_w(component));
4056 return ERROR_SUCCESS;
4059 compgroupid = MSI_RecordGetString(rec,1);
4060 qualifier = MSI_RecordGetString(rec,2);
4062 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4063 if (rc != ERROR_SUCCESS)
4064 goto end;
4066 text = MSI_RecordGetString(rec,4);
4067 feature = MSI_RecordGetString(rec,5);
4069 advertise = create_component_advertise_string(package, comp, feature);
4071 sz = strlenW(advertise);
4073 if (text)
4074 sz += lstrlenW(text);
4076 sz+=3;
4077 sz *= sizeof(WCHAR);
4079 output = msi_alloc_zero(sz);
4080 strcpyW(output,advertise);
4081 msi_free(advertise);
4083 if (text)
4084 strcatW(output,text);
4086 msi_reg_set_val_multi_str( hkey, qualifier, output );
4088 end:
4089 RegCloseKey(hkey);
4090 msi_free(output);
4092 /* the UI chunk */
4093 uirow = MSI_CreateRecord( 2 );
4094 MSI_RecordSetStringW( uirow, 1, compgroupid );
4095 MSI_RecordSetStringW( uirow, 2, qualifier);
4096 ui_actiondata( package, szPublishComponents, uirow);
4097 msiobj_release( &uirow->hdr );
4098 /* FIXME: call ui_progress? */
4100 return rc;
4104 * At present I am ignorning the advertised components part of this and only
4105 * focusing on the qualified component sets
4107 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4109 UINT rc;
4110 MSIQUERY * view;
4111 static const WCHAR ExecSeqQuery[] =
4112 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4113 '`','P','u','b','l','i','s','h',
4114 'C','o','m','p','o','n','e','n','t','`',0};
4116 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4117 if (rc != ERROR_SUCCESS)
4118 return ERROR_SUCCESS;
4120 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4121 msiobj_release(&view->hdr);
4123 return rc;
4126 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4128 MSIPACKAGE *package = (MSIPACKAGE*)param;
4129 MSIRECORD *row;
4130 MSIFILE *file;
4131 SC_HANDLE hscm, service = NULL;
4132 LPCWSTR name, disp, comp, depends, pass;
4133 LPCWSTR load_order, serv_name, key;
4134 DWORD serv_type, start_type;
4135 DWORD err_control;
4137 static const WCHAR query[] =
4138 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4139 '`','C','o','m','p','o','n','e','n','t','`',' ',
4140 'W','H','E','R','E',' ',
4141 '`','C','o','m','p','o','n','e','n','t','`',' ',
4142 '=','\'','%','s','\'',0};
4144 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4145 if (!hscm)
4147 ERR("Failed to open the SC Manager!\n");
4148 goto done;
4151 start_type = MSI_RecordGetInteger(rec, 5);
4152 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4153 goto done;
4155 depends = MSI_RecordGetString(rec, 8);
4156 if (depends && *depends)
4157 FIXME("Dependency list unhandled!\n");
4159 name = MSI_RecordGetString(rec, 2);
4160 disp = MSI_RecordGetString(rec, 3);
4161 serv_type = MSI_RecordGetInteger(rec, 4);
4162 err_control = MSI_RecordGetInteger(rec, 6);
4163 load_order = MSI_RecordGetString(rec, 7);
4164 serv_name = MSI_RecordGetString(rec, 9);
4165 pass = MSI_RecordGetString(rec, 10);
4166 comp = MSI_RecordGetString(rec, 12);
4168 /* fetch the service path */
4169 row = MSI_QueryGetRecord(package->db, query, comp);
4170 if (!row)
4172 ERR("Control query failed!\n");
4173 goto done;
4176 key = MSI_RecordGetString(row, 6);
4177 msiobj_release(&row->hdr);
4179 file = get_loaded_file(package, key);
4180 if (!file)
4182 ERR("Failed to load the service file\n");
4183 goto done;
4186 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4187 start_type, err_control, file->TargetPath,
4188 load_order, NULL, NULL, serv_name, pass);
4189 if (!service)
4191 if (GetLastError() != ERROR_SERVICE_EXISTS)
4192 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4195 done:
4196 CloseServiceHandle(service);
4197 CloseServiceHandle(hscm);
4199 return ERROR_SUCCESS;
4202 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4204 UINT rc;
4205 MSIQUERY * view;
4206 static const WCHAR ExecSeqQuery[] =
4207 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4208 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4210 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4211 if (rc != ERROR_SUCCESS)
4212 return ERROR_SUCCESS;
4214 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4215 msiobj_release(&view->hdr);
4217 return rc;
4220 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4221 static LPCWSTR *msi_service_args_to_vector(LPCWSTR name, LPWSTR args, DWORD *numargs)
4223 LPCWSTR *vector;
4224 LPWSTR p, q;
4225 DWORD sep_len;
4227 static const WCHAR separator[] = {'[','~',']',0};
4229 *numargs = 0;
4230 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4232 if (!args)
4233 return NULL;
4235 vector = msi_alloc(sizeof(LPWSTR));
4236 if (!vector)
4237 return NULL;
4239 p = args;
4242 (*numargs)++;
4243 vector[*numargs - 1] = p;
4245 if ((q = strstrW(p, separator)))
4247 *q = '\0';
4249 vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4250 if (!vector)
4251 return NULL;
4253 p = q + sep_len;
4255 } while (q);
4257 return vector;
4260 static MSICOMPONENT *msi_find_component( MSIPACKAGE *package, LPCWSTR component )
4262 MSICOMPONENT *comp;
4264 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
4266 if (!lstrcmpW(comp->Component, component))
4267 return comp;
4270 return NULL;
4273 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4275 MSIPACKAGE *package = (MSIPACKAGE *)param;
4276 MSICOMPONENT *comp;
4277 SC_HANDLE scm, service = NULL;
4278 LPCWSTR name, *vector = NULL;
4279 LPWSTR args;
4280 DWORD event, numargs;
4281 UINT r = ERROR_FUNCTION_FAILED;
4283 comp = msi_find_component(package, MSI_RecordGetString(rec, 6));
4284 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4285 return ERROR_SUCCESS;
4287 name = MSI_RecordGetString(rec, 2);
4288 event = MSI_RecordGetInteger(rec, 3);
4289 args = strdupW(MSI_RecordGetString(rec, 4));
4291 if (!(event & msidbServiceControlEventStart))
4292 return ERROR_SUCCESS;
4294 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4295 if (!scm)
4297 ERR("Failed to open the service control manager\n");
4298 goto done;
4301 service = OpenServiceW(scm, name, SERVICE_START);
4302 if (!service)
4304 ERR("Failed to open service %s\n", debugstr_w(name));
4305 goto done;
4308 vector = msi_service_args_to_vector(name, args, &numargs);
4310 if (!StartServiceW(service, numargs, vector))
4312 ERR("Failed to start service %s\n", debugstr_w(name));
4313 goto done;
4316 r = ERROR_SUCCESS;
4318 done:
4319 CloseServiceHandle(service);
4320 CloseServiceHandle(scm);
4322 msi_free(args);
4323 msi_free(vector);
4324 return r;
4327 static UINT ACTION_StartServices( MSIPACKAGE *package )
4329 UINT rc;
4330 MSIQUERY *view;
4332 static const WCHAR query[] = {
4333 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4334 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4336 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4337 if (rc != ERROR_SUCCESS)
4338 return ERROR_SUCCESS;
4340 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4341 msiobj_release(&view->hdr);
4343 return rc;
4346 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4348 MSIFILE *file;
4350 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4352 if (!lstrcmpW(file->File, filename))
4353 return file;
4356 return NULL;
4359 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4361 MSIPACKAGE *package = (MSIPACKAGE*)param;
4362 LPWSTR driver, driver_path, ptr;
4363 WCHAR outpath[MAX_PATH];
4364 MSIFILE *driver_file, *setup_file;
4365 LPCWSTR desc;
4366 DWORD len, usage;
4367 UINT r = ERROR_SUCCESS;
4369 static const WCHAR driver_fmt[] = {
4370 'D','r','i','v','e','r','=','%','s',0};
4371 static const WCHAR setup_fmt[] = {
4372 'S','e','t','u','p','=','%','s',0};
4373 static const WCHAR usage_fmt[] = {
4374 'F','i','l','e','U','s','a','g','e','=','1',0};
4376 desc = MSI_RecordGetString(rec, 3);
4378 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4379 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4381 if (!driver_file || !setup_file)
4383 ERR("ODBC Driver entry not found!\n");
4384 return ERROR_FUNCTION_FAILED;
4387 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4388 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4389 lstrlenW(usage_fmt) + 1;
4390 driver = msi_alloc(len * sizeof(WCHAR));
4391 if (!driver)
4392 return ERROR_OUTOFMEMORY;
4394 ptr = driver;
4395 lstrcpyW(ptr, desc);
4396 ptr += lstrlenW(ptr) + 1;
4398 sprintfW(ptr, driver_fmt, driver_file->FileName);
4399 ptr += lstrlenW(ptr) + 1;
4401 sprintfW(ptr, setup_fmt, setup_file->FileName);
4402 ptr += lstrlenW(ptr) + 1;
4404 lstrcpyW(ptr, usage_fmt);
4405 ptr += lstrlenW(ptr) + 1;
4406 *ptr = '\0';
4408 driver_path = strdupW(driver_file->TargetPath);
4409 ptr = strrchrW(driver_path, '\\');
4410 if (ptr) *ptr = '\0';
4412 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4413 NULL, ODBC_INSTALL_COMPLETE, &usage))
4415 ERR("Failed to install SQL driver!\n");
4416 r = ERROR_FUNCTION_FAILED;
4419 msi_free(driver);
4420 msi_free(driver_path);
4422 return r;
4425 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4427 UINT rc;
4428 MSIQUERY *view;
4430 static const WCHAR query[] = {
4431 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4432 'O','D','B','C','D','r','i','v','e','r',0 };
4434 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4435 if (rc != ERROR_SUCCESS)
4436 return ERROR_SUCCESS;
4438 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4439 msiobj_release(&view->hdr);
4441 return rc;
4444 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
4445 LPCSTR action, LPCWSTR table )
4447 static const WCHAR query[] = {
4448 'S','E','L','E','C','T',' ','*',' ',
4449 'F','R','O','M',' ','`','%','s','`',0 };
4450 MSIQUERY *view = NULL;
4451 DWORD count = 0;
4452 UINT r;
4454 r = MSI_OpenQuery( package->db, &view, query, table );
4455 if (r == ERROR_SUCCESS)
4457 r = MSI_IterateRecords(view, &count, NULL, package);
4458 msiobj_release(&view->hdr);
4461 if (count)
4462 FIXME("%s -> %u ignored %s table values\n",
4463 action, count, debugstr_w(table));
4465 return ERROR_SUCCESS;
4468 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
4470 TRACE("%p\n", package);
4471 return ERROR_SUCCESS;
4474 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4476 static const WCHAR table[] =
4477 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
4478 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
4481 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
4483 static const WCHAR table[] = { 'M','o','v','e','F','i','l','e',0 };
4484 return msi_unimplemented_action_stub( package, "MoveFiles", table );
4487 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
4489 static const WCHAR table[] = { 'P','a','t','c','h',0 };
4490 return msi_unimplemented_action_stub( package, "PatchFiles", table );
4493 static UINT ACTION_BindImage( MSIPACKAGE *package )
4495 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
4496 return msi_unimplemented_action_stub( package, "BindImage", table );
4499 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
4501 static const WCHAR table[] = {
4502 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
4503 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
4506 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
4508 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
4509 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
4512 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4514 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
4515 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
4518 static UINT ACTION_StopServices( MSIPACKAGE *package )
4520 static const WCHAR table[] = {
4521 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4522 return msi_unimplemented_action_stub( package, "StopServices", table );
4525 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
4527 static const WCHAR table[] = {
4528 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4529 return msi_unimplemented_action_stub( package, "DeleteServices", table );
4531 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
4533 static const WCHAR table[] = {
4534 'P','r','o','d','u','c','t','I','D',0 };
4535 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
4538 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
4540 static const WCHAR table[] = {
4541 'E','n','v','i','r','o','n','m','e','n','t',0 };
4542 return msi_unimplemented_action_stub( package, "WriteEnvironmentStrings", table );
4545 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
4547 static const WCHAR table[] = {
4548 'E','n','v','i','r','o','n','m','e','n','t',0 };
4549 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
4552 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
4554 static const WCHAR table[] = {
4555 'M','s','i','A','s','s','e','m','b','l','y',0 };
4556 return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
4559 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
4561 static const WCHAR table[] = {
4562 'M','s','i','A','s','s','e','m','b','l','y',0 };
4563 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
4566 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
4568 static const WCHAR table[] = { 'F','o','n','t',0 };
4569 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
4572 static UINT ACTION_CCPSearch( MSIPACKAGE *package )
4574 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
4575 return msi_unimplemented_action_stub( package, "CCPSearch", table );
4578 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
4580 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
4581 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
4584 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
4586 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
4587 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
4590 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
4592 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
4593 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
4596 static const struct _actions StandardActions[] = {
4597 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
4598 { szAppSearch, ACTION_AppSearch },
4599 { szBindImage, ACTION_BindImage },
4600 { szCCPSearch, ACTION_CCPSearch},
4601 { szCostFinalize, ACTION_CostFinalize },
4602 { szCostInitialize, ACTION_CostInitialize },
4603 { szCreateFolders, ACTION_CreateFolders },
4604 { szCreateShortcuts, ACTION_CreateShortcuts },
4605 { szDeleteServices, ACTION_DeleteServices },
4606 { szDisableRollback, NULL},
4607 { szDuplicateFiles, ACTION_DuplicateFiles },
4608 { szExecuteAction, ACTION_ExecuteAction },
4609 { szFileCost, ACTION_FileCost },
4610 { szFindRelatedProducts, ACTION_FindRelatedProducts },
4611 { szForceReboot, ACTION_ForceReboot },
4612 { szInstallAdminPackage, NULL},
4613 { szInstallExecute, ACTION_InstallExecute },
4614 { szInstallExecuteAgain, ACTION_InstallExecute },
4615 { szInstallFiles, ACTION_InstallFiles},
4616 { szInstallFinalize, ACTION_InstallFinalize },
4617 { szInstallInitialize, ACTION_InstallInitialize },
4618 { szInstallSFPCatalogFile, NULL},
4619 { szInstallValidate, ACTION_InstallValidate },
4620 { szIsolateComponents, ACTION_IsolateComponents },
4621 { szLaunchConditions, ACTION_LaunchConditions },
4622 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
4623 { szMoveFiles, ACTION_MoveFiles },
4624 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
4625 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
4626 { szInstallODBC, ACTION_InstallODBC },
4627 { szInstallServices, ACTION_InstallServices },
4628 { szPatchFiles, ACTION_PatchFiles },
4629 { szProcessComponents, ACTION_ProcessComponents },
4630 { szPublishComponents, ACTION_PublishComponents },
4631 { szPublishFeatures, ACTION_PublishFeatures },
4632 { szPublishProduct, ACTION_PublishProduct },
4633 { szRegisterClassInfo, ACTION_RegisterClassInfo },
4634 { szRegisterComPlus, ACTION_RegisterComPlus},
4635 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
4636 { szRegisterFonts, ACTION_RegisterFonts },
4637 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
4638 { szRegisterProduct, ACTION_RegisterProduct },
4639 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
4640 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
4641 { szRegisterUser, ACTION_RegisterUser},
4642 { szRemoveDuplicateFiles, NULL},
4643 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
4644 { szRemoveExistingProducts, NULL},
4645 { szRemoveFiles, ACTION_RemoveFiles},
4646 { szRemoveFolders, NULL},
4647 { szRemoveIniValues, ACTION_RemoveIniValues },
4648 { szRemoveODBC, NULL},
4649 { szRemoveRegistryValues, NULL},
4650 { szRemoveShortcuts, NULL},
4651 { szResolveSource, ACTION_ResolveSource},
4652 { szRMCCPSearch, ACTION_RMCCPSearch},
4653 { szScheduleReboot, NULL},
4654 { szSelfRegModules, ACTION_SelfRegModules },
4655 { szSelfUnregModules, ACTION_SelfUnregModules },
4656 { szSetODBCFolders, NULL},
4657 { szStartServices, ACTION_StartServices },
4658 { szStopServices, ACTION_StopServices },
4659 { szUnpublishComponents, NULL},
4660 { szUnpublishFeatures, NULL},
4661 { szUnregisterClassInfo, NULL},
4662 { szUnregisterComPlus, ACTION_UnregisterComPlus},
4663 { szUnregisterExtensionInfo, NULL},
4664 { szUnregisterFonts, ACTION_UnregisterFonts },
4665 { szUnregisterMIMEInfo, NULL},
4666 { szUnregisterProgIdInfo, NULL},
4667 { szUnregisterTypeLibraries, NULL},
4668 { szValidateProductID, ACTION_ValidateProductID},
4669 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
4670 { szWriteIniValues, ACTION_WriteIniValues },
4671 { szWriteRegistryValues, ACTION_WriteRegistryValues},
4672 { NULL, NULL},