Changes in crossover-wine-src-6.1.0 except for configure
[wine/hacks.git] / dlls / msi / action.c
blob5b0110cbfe528a7d53353e22ecac0b90a5f68b4e
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 * Pages I need
24 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
26 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
29 #include <stdarg.h>
31 #define COBJMACROS
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winerror.h"
36 #include "winreg.h"
37 #include "winsvc.h"
38 #include "odbcinst.h"
39 #include "wine/debug.h"
40 #include "msidefs.h"
41 #include "msipriv.h"
42 #include "winuser.h"
43 #include "shlobj.h"
44 #include "wine/unicode.h"
45 #include "winver.h"
47 #define REG_PROGRESS_VALUE 13200
48 #define COMPONENT_PROGRESS_VALUE 24000
50 WINE_DEFAULT_DEBUG_CHANNEL(msi);
53 * Prototypes
55 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
56 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
57 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
60 * consts and values used
62 static const WCHAR c_colon[] = {'C',':','\\',0};
64 static const WCHAR szCreateFolders[] =
65 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
66 static const WCHAR szCostFinalize[] =
67 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
68 const WCHAR szInstallFiles[] =
69 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
70 const WCHAR szDuplicateFiles[] =
71 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
72 static const WCHAR szWriteRegistryValues[] =
73 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
74 'V','a','l','u','e','s',0};
75 static const WCHAR szCostInitialize[] =
76 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
77 static const WCHAR szFileCost[] =
78 {'F','i','l','e','C','o','s','t',0};
79 static const WCHAR szInstallInitialize[] =
80 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
81 static const WCHAR szInstallValidate[] =
82 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
83 static const WCHAR szLaunchConditions[] =
84 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
85 static const WCHAR szProcessComponents[] =
86 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
87 static const WCHAR szRegisterTypeLibraries[] =
88 {'R','e','g','i','s','t','e','r','T','y','p','e',
89 'L','i','b','r','a','r','i','e','s',0};
90 const WCHAR szRegisterClassInfo[] =
91 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
92 const WCHAR szRegisterProgIdInfo[] =
93 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
94 static const WCHAR szCreateShortcuts[] =
95 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
96 static const WCHAR szPublishProduct[] =
97 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
98 static const WCHAR szWriteIniValues[] =
99 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
100 static const WCHAR szSelfRegModules[] =
101 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
102 static const WCHAR szPublishFeatures[] =
103 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
104 static const WCHAR szRegisterProduct[] =
105 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
106 static const WCHAR szInstallExecute[] =
107 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
108 static const WCHAR szInstallExecuteAgain[] =
109 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
110 'A','g','a','i','n',0};
111 static const WCHAR szInstallFinalize[] =
112 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
113 static const WCHAR szForceReboot[] =
114 {'F','o','r','c','e','R','e','b','o','o','t',0};
115 static const WCHAR szResolveSource[] =
116 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
117 static const WCHAR szAppSearch[] =
118 {'A','p','p','S','e','a','r','c','h',0};
119 static const WCHAR szAllocateRegistrySpace[] =
120 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
121 'S','p','a','c','e',0};
122 static const WCHAR szBindImage[] =
123 {'B','i','n','d','I','m','a','g','e',0};
124 static const WCHAR szCCPSearch[] =
125 {'C','C','P','S','e','a','r','c','h',0};
126 static const WCHAR szDeleteServices[] =
127 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
128 static const WCHAR szDisableRollback[] =
129 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
130 static const WCHAR szExecuteAction[] =
131 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
132 const WCHAR szFindRelatedProducts[] =
133 {'F','i','n','d','R','e','l','a','t','e','d',
134 'P','r','o','d','u','c','t','s',0};
135 static const WCHAR szInstallAdminPackage[] =
136 {'I','n','s','t','a','l','l','A','d','m','i','n',
137 'P','a','c','k','a','g','e',0};
138 static const WCHAR szInstallSFPCatalogFile[] =
139 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
140 'F','i','l','e',0};
141 static const WCHAR szIsolateComponents[] =
142 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
143 const WCHAR szMigrateFeatureStates[] =
144 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
145 'S','t','a','t','e','s',0};
146 const WCHAR szMoveFiles[] =
147 {'M','o','v','e','F','i','l','e','s',0};
148 static const WCHAR szMsiPublishAssemblies[] =
149 {'M','s','i','P','u','b','l','i','s','h',
150 'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szMsiUnpublishAssemblies[] =
152 {'M','s','i','U','n','p','u','b','l','i','s','h',
153 'A','s','s','e','m','b','l','i','e','s',0};
154 static const WCHAR szInstallODBC[] =
155 {'I','n','s','t','a','l','l','O','D','B','C',0};
156 static const WCHAR szInstallServices[] =
157 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
158 const WCHAR szPatchFiles[] =
159 {'P','a','t','c','h','F','i','l','e','s',0};
160 static const WCHAR szPublishComponents[] =
161 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
162 static const WCHAR szRegisterComPlus[] =
163 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
164 const WCHAR szRegisterExtensionInfo[] =
165 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
166 'I','n','f','o',0};
167 static const WCHAR szRegisterFonts[] =
168 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
169 const WCHAR szRegisterMIMEInfo[] =
170 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
171 static const WCHAR szRegisterUser[] =
172 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
173 const WCHAR szRemoveDuplicateFiles[] =
174 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
175 'F','i','l','e','s',0};
176 static const WCHAR szRemoveEnvironmentStrings[] =
177 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
178 'S','t','r','i','n','g','s',0};
179 const WCHAR szRemoveExistingProducts[] =
180 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
181 'P','r','o','d','u','c','t','s',0};
182 const WCHAR szRemoveFiles[] =
183 {'R','e','m','o','v','e','F','i','l','e','s',0};
184 static const WCHAR szRemoveFolders[] =
185 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
186 static const WCHAR szRemoveIniValues[] =
187 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
188 static const WCHAR szRemoveODBC[] =
189 {'R','e','m','o','v','e','O','D','B','C',0};
190 static const WCHAR szRemoveRegistryValues[] =
191 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
192 'V','a','l','u','e','s',0};
193 static const WCHAR szRemoveShortcuts[] =
194 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
195 static const WCHAR szRMCCPSearch[] =
196 {'R','M','C','C','P','S','e','a','r','c','h',0};
197 static const WCHAR szScheduleReboot[] =
198 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
199 static const WCHAR szSelfUnregModules[] =
200 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
201 static const WCHAR szSetODBCFolders[] =
202 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
203 static const WCHAR szStartServices[] =
204 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
205 static const WCHAR szStopServices[] =
206 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
207 static const WCHAR szUnpublishComponents[] =
208 {'U','n','p','u','b','l','i','s','h',
209 'C','o','m','p','o','n','e','n','t','s',0};
210 static const WCHAR szUnpublishFeatures[] =
211 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
212 const WCHAR szUnregisterClassInfo[] =
213 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
214 'I','n','f','o',0};
215 static const WCHAR szUnregisterComPlus[] =
216 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
217 const WCHAR szUnregisterExtensionInfo[] =
218 {'U','n','r','e','g','i','s','t','e','r',
219 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
220 static const WCHAR szUnregisterFonts[] =
221 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
222 const WCHAR szUnregisterMIMEInfo[] =
223 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
224 const WCHAR szUnregisterProgIdInfo[] =
225 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
226 'I','n','f','o',0};
227 static const WCHAR szUnregisterTypeLibraries[] =
228 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
229 'L','i','b','r','a','r','i','e','s',0};
230 static const WCHAR szValidateProductID[] =
231 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
232 static const WCHAR szWriteEnvironmentStrings[] =
233 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
234 'S','t','r','i','n','g','s',0};
236 /* action handlers */
237 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
239 struct _actions {
240 LPCWSTR action;
241 STANDARDACTIONHANDLER handler;
244 static const struct _actions StandardActions[];
247 /********************************************************
248 * helper functions
249 ********************************************************/
251 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
253 static const WCHAR Query_t[] =
254 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
255 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
256 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
257 ' ','\'','%','s','\'',0};
258 MSIRECORD * row;
260 row = MSI_QueryGetRecord( package->db, Query_t, action );
261 if (!row)
262 return;
263 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
264 msiobj_release(&row->hdr);
267 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
268 UINT rc)
270 MSIRECORD * row;
271 static const WCHAR template_s[]=
272 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
273 '%','s', '.',0};
274 static const WCHAR template_e[]=
275 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
276 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
277 '%','i','.',0};
278 static const WCHAR format[] =
279 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
280 WCHAR message[1024];
281 WCHAR timet[0x100];
283 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
284 if (start)
285 sprintfW(message,template_s,timet,action);
286 else
287 sprintfW(message,template_e,timet,action,rc);
289 row = MSI_CreateRecord(1);
290 MSI_RecordSetStringW(row,1,message);
292 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
293 msiobj_release(&row->hdr);
296 static UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine )
298 LPCWSTR ptr,ptr2;
299 BOOL quote;
300 DWORD len;
301 LPWSTR prop = NULL, val = NULL;
303 if (!szCommandLine)
304 return ERROR_SUCCESS;
306 ptr = szCommandLine;
308 while (*ptr)
310 if (*ptr==' ')
312 ptr++;
313 continue;
316 TRACE("Looking at %s\n",debugstr_w(ptr));
318 ptr2 = strchrW(ptr,'=');
319 if (!ptr2)
321 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
322 break;
325 quote = FALSE;
327 len = ptr2-ptr;
328 prop = msi_alloc((len+1)*sizeof(WCHAR));
329 memcpy(prop,ptr,len*sizeof(WCHAR));
330 prop[len]=0;
331 ptr2++;
333 len = 0;
334 ptr = ptr2;
335 while (*ptr && (quote || (!quote && *ptr!=' ')))
337 if (*ptr == '"')
338 quote = !quote;
339 ptr++;
340 len++;
343 if (*ptr2=='"')
345 ptr2++;
346 len -= 2;
348 val = msi_alloc((len+1)*sizeof(WCHAR));
349 memcpy(val,ptr2,len*sizeof(WCHAR));
350 val[len] = 0;
352 if (lstrlenW(prop) > 0)
354 TRACE("Found commandline property (%s) = (%s)\n",
355 debugstr_w(prop), debugstr_w(val));
356 MSI_SetPropertyW(package,prop,val);
358 msi_free(val);
359 msi_free(prop);
362 return ERROR_SUCCESS;
366 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
368 LPCWSTR pc;
369 LPWSTR p, *ret = NULL;
370 UINT count = 0;
372 if (!str)
373 return ret;
375 /* count the number of substrings */
376 for ( pc = str, count = 0; pc; count++ )
378 pc = strchrW( pc, sep );
379 if (pc)
380 pc++;
383 /* allocate space for an array of substring pointers and the substrings */
384 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
385 (lstrlenW(str)+1) * sizeof(WCHAR) );
386 if (!ret)
387 return ret;
389 /* copy the string and set the pointers */
390 p = (LPWSTR) &ret[count+1];
391 lstrcpyW( p, str );
392 for( count = 0; (ret[count] = p); count++ )
394 p = strchrW( p, sep );
395 if (p)
396 *p++ = 0;
399 return ret;
402 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
404 WCHAR szProductCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
405 LPWSTR prod_code, patch_product;
406 UINT ret;
408 prod_code = msi_dup_property( package, szProductCode );
409 patch_product = msi_get_suminfo_product( patch );
411 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
413 if ( strstrW( patch_product, prod_code ) )
414 ret = ERROR_SUCCESS;
415 else
416 ret = ERROR_FUNCTION_FAILED;
418 msi_free( patch_product );
419 msi_free( prod_code );
421 return ret;
424 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
425 MSIDATABASE *patch_db, LPCWSTR name )
427 UINT ret = ERROR_FUNCTION_FAILED;
428 IStorage *stg = NULL;
429 HRESULT r;
431 TRACE("%p %s\n", package, debugstr_w(name) );
433 if (*name++ != ':')
435 ERR("expected a colon in %s\n", debugstr_w(name));
436 return ERROR_FUNCTION_FAILED;
439 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
440 if (SUCCEEDED(r))
442 ret = msi_check_transform_applicable( package, stg );
443 if (ret == ERROR_SUCCESS)
444 msi_table_apply_transform( package->db, stg );
445 else
446 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
447 IStorage_Release( stg );
449 else
450 ERR("failed to open substorage %s\n", debugstr_w(name));
452 return ERROR_SUCCESS;
455 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
457 static const WCHAR szProdID[] = { 'P','r','o','d','u','c','t','I','D',0 };
458 LPWSTR guid_list, *guids, product_id;
459 UINT i, ret = ERROR_FUNCTION_FAILED;
461 product_id = msi_dup_property( package, szProdID );
462 if (!product_id)
464 /* FIXME: the property ProductID should be written into the DB somewhere */
465 ERR("no product ID to check\n");
466 return ERROR_SUCCESS;
469 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
470 guids = msi_split_string( guid_list, ';' );
471 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
473 if (!lstrcmpW( guids[i], product_id ))
474 ret = ERROR_SUCCESS;
476 msi_free( guids );
477 msi_free( guid_list );
478 msi_free( product_id );
480 return ret;
483 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
485 MSISUMMARYINFO *si;
486 LPWSTR str, *substorage;
487 UINT i, r = ERROR_SUCCESS;
489 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
490 if (!si)
491 return ERROR_FUNCTION_FAILED;
493 msi_check_patch_applicable( package, si );
495 /* enumerate the substorage */
496 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
497 substorage = msi_split_string( str, ';' );
498 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
499 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
500 msi_free( substorage );
501 msi_free( str );
503 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
505 msiobj_release( &si->hdr );
507 return r;
510 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
512 MSIDATABASE *patch_db = NULL;
513 UINT r;
515 TRACE("%p %s\n", package, debugstr_w( file ) );
517 /* FIXME:
518 * We probably want to make sure we only open a patch collection here.
519 * Patch collections (.msp) and databases (.msi) have different GUIDs
520 * but currently MSI_OpenDatabaseW will accept both.
522 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
523 if ( r != ERROR_SUCCESS )
525 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
526 return r;
529 msi_parse_patch_summary( package, patch_db );
532 * There might be a CAB file in the patch package,
533 * so append it to the list of storage to search for streams.
535 append_storage_to_db( package->db, patch_db->storage );
537 msiobj_release( &patch_db->hdr );
539 return ERROR_SUCCESS;
542 /* get the PATCH property, and apply all the patches it specifies */
543 static UINT msi_apply_patches( MSIPACKAGE *package )
545 static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
546 LPWSTR patch_list, *patches;
547 UINT i, r = ERROR_SUCCESS;
549 patch_list = msi_dup_property( package, szPatch );
551 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
553 patches = msi_split_string( patch_list, ';' );
554 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
555 r = msi_apply_patch_package( package, patches[i] );
557 msi_free( patches );
558 msi_free( patch_list );
560 return r;
563 static UINT msi_apply_transforms( MSIPACKAGE *package )
565 static const WCHAR szTransforms[] = {
566 'T','R','A','N','S','F','O','R','M','S',0 };
567 LPWSTR xform_list, *xforms;
568 UINT i, r = ERROR_SUCCESS;
570 xform_list = msi_dup_property( package, szTransforms );
571 xforms = msi_split_string( xform_list, ';' );
573 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
575 if (xforms[i][0] == ':')
576 r = msi_apply_substorage_transform( package, package->db, &xforms[i][1] );
577 else
578 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
581 msi_free( xforms );
582 msi_free( xform_list );
584 return r;
587 /****************************************************
588 * TOP level entry points
589 *****************************************************/
591 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
592 LPCWSTR szCommandLine )
594 UINT rc;
595 BOOL ui = FALSE;
596 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
597 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
598 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
600 MSI_SetPropertyW(package, szAction, szInstall);
602 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
604 package->script->InWhatSequence = SEQUENCE_INSTALL;
606 if (szPackagePath)
608 LPWSTR p, check, path;
610 path = strdupW(szPackagePath);
611 p = strrchrW(path,'\\');
612 if (p)
614 p++;
615 *p=0;
617 else
619 msi_free(path);
620 path = msi_alloc(MAX_PATH*sizeof(WCHAR));
621 GetCurrentDirectoryW(MAX_PATH,path);
622 strcatW(path,cszbs);
625 check = msi_dup_property( package, cszSourceDir );
626 if (!check)
627 MSI_SetPropertyW(package, cszSourceDir, path);
628 msi_free(check);
630 check = msi_dup_property( package, cszSOURCEDIR );
631 if (!check)
632 MSI_SetPropertyW(package, cszSOURCEDIR, path);
634 msi_free( package->PackagePath );
635 package->PackagePath = path;
637 msi_free(check);
640 msi_parse_command_line( package, szCommandLine );
643 static const WCHAR szcdcache[] = { 'C','D','C','A','C','H','E',0 };
644 static const WCHAR szzero[] = { '0',0 };
645 static const WCHAR szprodname[] = { 'P','r','o','d','u','c','t','N','a','m','e',0 };
646 static const WCHAR szword2003[] = {
647 'M','i','c','r','o','s','o','f','t',' ','O','f','f','i','c','e',' ',
648 'W','o','r','d',' ','V','i','e','w','e','r',' ','2','0','0','3',0};
649 LPWSTR prod;
651 prod = msi_dup_property( package, szprodname );
652 if (!lstrcmpW( prod, szword2003 ))
653 MSI_SetPropertyW( package, szcdcache, szzero );
654 msi_free( prod );
657 msi_apply_transforms( package );
658 msi_apply_patches( package );
660 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
662 package->script->InWhatSequence |= SEQUENCE_UI;
663 rc = ACTION_ProcessUISequence(package);
664 ui = TRUE;
665 if (rc == ERROR_SUCCESS)
667 package->script->InWhatSequence |= SEQUENCE_EXEC;
668 rc = ACTION_ProcessExecSequence(package,TRUE);
671 else
672 rc = ACTION_ProcessExecSequence(package,FALSE);
674 if (rc == -1)
676 /* install was halted but should be considered a success */
677 rc = ERROR_SUCCESS;
680 package->script->CurrentlyScripting= FALSE;
682 /* process the ending type action */
683 if (rc == ERROR_SUCCESS)
684 ACTION_PerformActionSequence(package,-1,ui);
685 else if (rc == ERROR_INSTALL_USEREXIT)
686 ACTION_PerformActionSequence(package,-2,ui);
687 else if (rc == ERROR_INSTALL_SUSPEND)
688 ACTION_PerformActionSequence(package,-4,ui);
689 else /* failed */
690 ACTION_PerformActionSequence(package,-3,ui);
692 /* finish up running custom actions */
693 ACTION_FinishCustomActions(package);
695 return rc;
698 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
700 UINT rc = ERROR_SUCCESS;
701 MSIRECORD * row = 0;
702 static const WCHAR ExecSeqQuery[] =
703 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
704 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
705 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
706 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
708 static const WCHAR UISeqQuery[] =
709 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
710 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
711 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
712 ' ', '=',' ','%','i',0};
714 if (UI)
715 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
716 else
717 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
719 if (row)
721 LPCWSTR action, cond;
723 TRACE("Running the actions\n");
725 /* check conditions */
726 cond = MSI_RecordGetString(row,2);
728 /* this is a hack to skip errors in the condition code */
729 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
730 goto end;
732 action = MSI_RecordGetString(row,1);
733 if (!action)
735 ERR("failed to fetch action\n");
736 rc = ERROR_FUNCTION_FAILED;
737 goto end;
740 if (UI)
741 rc = ACTION_PerformUIAction(package,action);
742 else
743 rc = ACTION_PerformAction(package,action,FALSE);
744 end:
745 msiobj_release(&row->hdr);
747 else
748 rc = ERROR_SUCCESS;
750 return rc;
753 typedef struct {
754 MSIPACKAGE* package;
755 BOOL UI;
756 } iterate_action_param;
758 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
760 iterate_action_param *iap= (iterate_action_param*)param;
761 UINT rc;
762 LPCWSTR cond, action;
764 action = MSI_RecordGetString(row,1);
765 if (!action)
767 ERR("Error is retrieving action name\n");
768 return ERROR_FUNCTION_FAILED;
771 /* check conditions */
772 cond = MSI_RecordGetString(row,2);
774 /* this is a hack to skip errors in the condition code */
775 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
777 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
778 return ERROR_SUCCESS;
781 if (iap->UI)
782 rc = ACTION_PerformUIAction(iap->package,action);
783 else
784 rc = ACTION_PerformAction(iap->package,action,FALSE);
786 msi_dialog_check_messages( NULL );
788 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
789 rc = iap->package->CurrentInstallState;
791 if (rc == ERROR_FUNCTION_NOT_CALLED)
792 rc = ERROR_SUCCESS;
794 if (rc != ERROR_SUCCESS)
795 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
797 return rc;
800 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
802 MSIQUERY * view;
803 UINT r;
804 static const WCHAR query[] =
805 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
806 '`','%','s','`',
807 ' ','W','H','E','R','E',' ',
808 '`','S','e','q','u','e','n','c','e','`',' ',
809 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
810 '`','S','e','q','u','e','n','c','e','`',0};
811 iterate_action_param iap;
814 * FIXME: probably should be checking UILevel in the
815 * ACTION_PerformUIAction/ACTION_PerformAction
816 * rather than saving the UI level here. Those
817 * two functions can be merged too.
819 iap.package = package;
820 iap.UI = TRUE;
822 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
824 r = MSI_OpenQuery( package->db, &view, query, szTable );
825 if (r == ERROR_SUCCESS)
827 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
828 msiobj_release(&view->hdr);
831 return r;
834 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
836 MSIQUERY * view;
837 UINT rc;
838 static const WCHAR ExecSeqQuery[] =
839 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
840 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
841 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
842 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
843 'O','R','D','E','R',' ', 'B','Y',' ',
844 '`','S','e','q','u','e','n','c','e','`',0 };
845 MSIRECORD * row = 0;
846 static const WCHAR IVQuery[] =
847 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
848 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
849 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
850 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
851 ' ','\'', 'I','n','s','t','a','l','l',
852 'V','a','l','i','d','a','t','e','\'', 0};
853 INT seq = 0;
854 iterate_action_param iap;
856 iap.package = package;
857 iap.UI = FALSE;
859 if (package->script->ExecuteSequenceRun)
861 TRACE("Execute Sequence already Run\n");
862 return ERROR_SUCCESS;
865 package->script->ExecuteSequenceRun = TRUE;
867 /* get the sequence number */
868 if (UIran)
870 row = MSI_QueryGetRecord(package->db, IVQuery);
871 if( !row )
872 return ERROR_FUNCTION_FAILED;
873 seq = MSI_RecordGetInteger(row,1);
874 msiobj_release(&row->hdr);
877 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
878 if (rc == ERROR_SUCCESS)
880 TRACE("Running the actions\n");
882 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
883 msiobj_release(&view->hdr);
886 return rc;
889 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
891 MSIQUERY * view;
892 UINT rc;
893 static const WCHAR ExecSeqQuery [] =
894 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
895 '`','I','n','s','t','a','l','l',
896 'U','I','S','e','q','u','e','n','c','e','`',
897 ' ','W','H','E','R','E',' ',
898 '`','S','e','q','u','e','n','c','e','`',' ',
899 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
900 '`','S','e','q','u','e','n','c','e','`',0};
901 iterate_action_param iap;
903 iap.package = package;
904 iap.UI = TRUE;
906 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
908 if (rc == ERROR_SUCCESS)
910 TRACE("Running the actions\n");
912 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
913 msiobj_release(&view->hdr);
916 return rc;
919 /********************************************************
920 * ACTION helper functions and functions that perform the actions
921 *******************************************************/
922 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
923 UINT* rc, BOOL force )
925 BOOL ret = FALSE;
926 BOOL run = force;
927 int i;
929 if (!run && !package->script->CurrentlyScripting)
930 run = TRUE;
932 if (!run)
934 if (strcmpW(action,szInstallFinalize) == 0 ||
935 strcmpW(action,szInstallExecute) == 0 ||
936 strcmpW(action,szInstallExecuteAgain) == 0)
937 run = TRUE;
940 i = 0;
941 while (StandardActions[i].action != NULL)
943 if (strcmpW(StandardActions[i].action, action)==0)
945 if (!run)
947 ui_actioninfo(package, action, TRUE, 0);
948 *rc = schedule_action(package,INSTALL_SCRIPT,action);
949 ui_actioninfo(package, action, FALSE, *rc);
951 else
953 ui_actionstart(package, action);
954 if (StandardActions[i].handler)
956 *rc = StandardActions[i].handler(package);
958 else
960 FIXME("unhandled standard action %s\n",debugstr_w(action));
961 *rc = ERROR_SUCCESS;
964 ret = TRUE;
965 break;
967 i++;
969 return ret;
972 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
973 UINT* rc, BOOL force )
975 BOOL ret=FALSE;
976 UINT arc;
978 arc = ACTION_CustomAction(package,action, force);
980 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
982 *rc = arc;
983 ret = TRUE;
985 return ret;
989 * A lot of actions are really important even if they don't do anything
990 * explicit... Lots of properties are set at the beginning of the installation
991 * CostFinalize does a bunch of work to translate the directories and such
993 * But until I get write access to the database that is hard, so I am going to
994 * hack it to see if I can get something to run.
996 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, BOOL force)
998 UINT rc = ERROR_SUCCESS;
999 BOOL handled;
1001 TRACE("Performing action (%s)\n",debugstr_w(action));
1003 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1005 if (!handled)
1006 handled = ACTION_HandleCustomAction(package, action, &rc, force);
1008 if (!handled)
1010 FIXME("unhandled msi action %s\n",debugstr_w(action));
1011 rc = ERROR_FUNCTION_NOT_CALLED;
1014 return rc;
1017 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action)
1019 UINT rc = ERROR_SUCCESS;
1020 BOOL handled = FALSE;
1022 TRACE("Performing action (%s)\n",debugstr_w(action));
1024 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1026 if (!handled)
1027 handled = ACTION_HandleCustomAction(package, action, &rc, FALSE);
1029 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1030 handled = TRUE;
1032 if (!handled)
1034 FIXME("unhandled msi action %s\n",debugstr_w(action));
1035 rc = ERROR_FUNCTION_NOT_CALLED;
1038 return rc;
1043 * Actual Action Handlers
1046 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1048 MSIPACKAGE *package = (MSIPACKAGE*)param;
1049 LPCWSTR dir;
1050 LPWSTR full_path;
1051 MSIRECORD *uirow;
1052 MSIFOLDER *folder;
1054 dir = MSI_RecordGetString(row,1);
1055 if (!dir)
1057 ERR("Unable to get folder id\n");
1058 return ERROR_SUCCESS;
1061 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1062 if (!full_path)
1064 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1065 return ERROR_SUCCESS;
1068 TRACE("Folder is %s\n",debugstr_w(full_path));
1070 /* UI stuff */
1071 uirow = MSI_CreateRecord(1);
1072 MSI_RecordSetStringW(uirow,1,full_path);
1073 ui_actiondata(package,szCreateFolders,uirow);
1074 msiobj_release( &uirow->hdr );
1076 if (folder->State == 0)
1077 create_full_pathW(full_path);
1079 folder->State = 3;
1081 msi_free(full_path);
1082 return ERROR_SUCCESS;
1085 /* FIXME: probably should merge this with the above function */
1086 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1088 UINT rc = ERROR_SUCCESS;
1089 MSIFOLDER *folder;
1090 LPWSTR install_path;
1092 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1093 if (!install_path)
1094 return ERROR_FUNCTION_FAILED;
1096 /* create the path */
1097 if (folder->State == 0)
1099 create_full_pathW(install_path);
1100 folder->State = 2;
1102 msi_free(install_path);
1104 return rc;
1107 UINT msi_create_component_directories( MSIPACKAGE *package )
1109 MSICOMPONENT *comp;
1111 /* create all the folders required by the components are going to install */
1112 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1114 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1115 continue;
1116 msi_create_directory( package, comp->Directory );
1119 return ERROR_SUCCESS;
1123 * Also we cannot enable/disable components either, so for now I am just going
1124 * to do all the directories for all the components.
1126 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1128 static const WCHAR ExecSeqQuery[] =
1129 {'S','E','L','E','C','T',' ',
1130 '`','D','i','r','e','c','t','o','r','y','_','`',
1131 ' ','F','R','O','M',' ',
1132 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1133 UINT rc;
1134 MSIQUERY *view;
1136 /* create all the empty folders specified in the CreateFolder table */
1137 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1138 if (rc != ERROR_SUCCESS)
1139 return ERROR_SUCCESS;
1141 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1142 msiobj_release(&view->hdr);
1144 msi_create_component_directories( package );
1146 return rc;
1149 static UINT load_component( MSIRECORD *row, LPVOID param )
1151 MSIPACKAGE *package = param;
1152 MSICOMPONENT *comp;
1154 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1155 if (!comp)
1156 return ERROR_FUNCTION_FAILED;
1158 list_add_tail( &package->components, &comp->entry );
1160 /* fill in the data */
1161 comp->Component = msi_dup_record_field( row, 1 );
1163 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1165 comp->ComponentId = msi_dup_record_field( row, 2 );
1166 comp->Directory = msi_dup_record_field( row, 3 );
1167 comp->Attributes = MSI_RecordGetInteger(row,4);
1168 comp->Condition = msi_dup_record_field( row, 5 );
1169 comp->KeyPath = msi_dup_record_field( row, 6 );
1171 comp->Installed = INSTALLSTATE_UNKNOWN;
1172 msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1174 return ERROR_SUCCESS;
1177 static UINT load_all_components( MSIPACKAGE *package )
1179 static const WCHAR query[] = {
1180 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1181 '`','C','o','m','p','o','n','e','n','t','`',0 };
1182 MSIQUERY *view;
1183 UINT r;
1185 if (!list_empty(&package->components))
1186 return ERROR_SUCCESS;
1188 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1189 if (r != ERROR_SUCCESS)
1190 return r;
1192 r = MSI_IterateRecords(view, NULL, load_component, package);
1193 msiobj_release(&view->hdr);
1194 return r;
1197 typedef struct {
1198 MSIPACKAGE *package;
1199 MSIFEATURE *feature;
1200 } _ilfs;
1202 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1204 ComponentList *cl;
1206 cl = msi_alloc( sizeof (*cl) );
1207 if ( !cl )
1208 return ERROR_NOT_ENOUGH_MEMORY;
1209 cl->component = comp;
1210 list_add_tail( &feature->Components, &cl->entry );
1212 return ERROR_SUCCESS;
1215 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1217 FeatureList *fl;
1219 fl = msi_alloc( sizeof(*fl) );
1220 if ( !fl )
1221 return ERROR_NOT_ENOUGH_MEMORY;
1222 fl->feature = child;
1223 list_add_tail( &parent->Children, &fl->entry );
1225 return ERROR_SUCCESS;
1228 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1230 _ilfs* ilfs= (_ilfs*)param;
1231 LPCWSTR component;
1232 MSICOMPONENT *comp;
1234 component = MSI_RecordGetString(row,1);
1236 /* check to see if the component is already loaded */
1237 comp = get_loaded_component( ilfs->package, component );
1238 if (!comp)
1240 ERR("unknown component %s\n", debugstr_w(component));
1241 return ERROR_FUNCTION_FAILED;
1244 add_feature_component( ilfs->feature, comp );
1245 comp->Enabled = TRUE;
1247 return ERROR_SUCCESS;
1250 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1252 MSIFEATURE *feature;
1254 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1256 if ( !lstrcmpW( feature->Feature, name ) )
1257 return feature;
1260 return NULL;
1263 static UINT load_feature(MSIRECORD * row, LPVOID param)
1265 MSIPACKAGE* package = (MSIPACKAGE*)param;
1266 MSIFEATURE* feature;
1267 static const WCHAR Query1[] =
1268 {'S','E','L','E','C','T',' ',
1269 '`','C','o','m','p','o','n','e','n','t','_','`',
1270 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1271 'C','o','m','p','o','n','e','n','t','s','`',' ',
1272 'W','H','E','R','E',' ',
1273 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1274 MSIQUERY * view;
1275 UINT rc;
1276 _ilfs ilfs;
1278 /* fill in the data */
1280 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1281 if (!feature)
1282 return ERROR_NOT_ENOUGH_MEMORY;
1284 list_init( &feature->Children );
1285 list_init( &feature->Components );
1287 feature->Feature = msi_dup_record_field( row, 1 );
1289 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1291 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1292 feature->Title = msi_dup_record_field( row, 3 );
1293 feature->Description = msi_dup_record_field( row, 4 );
1295 if (!MSI_RecordIsNull(row,5))
1296 feature->Display = MSI_RecordGetInteger(row,5);
1298 feature->Level= MSI_RecordGetInteger(row,6);
1299 feature->Directory = msi_dup_record_field( row, 7 );
1300 feature->Attributes = MSI_RecordGetInteger(row,8);
1302 feature->Installed = INSTALLSTATE_UNKNOWN;
1303 msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1305 list_add_tail( &package->features, &feature->entry );
1307 /* load feature components */
1309 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1310 if (rc != ERROR_SUCCESS)
1311 return ERROR_SUCCESS;
1313 ilfs.package = package;
1314 ilfs.feature = feature;
1316 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1317 msiobj_release(&view->hdr);
1319 return ERROR_SUCCESS;
1322 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1324 MSIPACKAGE* package = (MSIPACKAGE*)param;
1325 MSIFEATURE *parent, *child;
1327 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1328 if (!child)
1329 return ERROR_FUNCTION_FAILED;
1331 if (!child->Feature_Parent)
1332 return ERROR_SUCCESS;
1334 parent = find_feature_by_name( package, child->Feature_Parent );
1335 if (!parent)
1336 return ERROR_FUNCTION_FAILED;
1338 add_feature_child( parent, child );
1339 return ERROR_SUCCESS;
1342 static UINT load_all_features( MSIPACKAGE *package )
1344 static const WCHAR query[] = {
1345 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1346 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1347 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1348 MSIQUERY *view;
1349 UINT r;
1351 if (!list_empty(&package->features))
1352 return ERROR_SUCCESS;
1354 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1355 if (r != ERROR_SUCCESS)
1356 return r;
1358 r = MSI_IterateRecords( view, NULL, load_feature, package );
1359 if (r != ERROR_SUCCESS)
1360 return r;
1362 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1363 msiobj_release( &view->hdr );
1365 return r;
1368 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1370 if (!p)
1371 return p;
1372 p = strchrW(p, ch);
1373 if (!p)
1374 return p;
1375 *p = 0;
1376 return p+1;
1379 static UINT load_file(MSIRECORD *row, LPVOID param)
1381 MSIPACKAGE* package = (MSIPACKAGE*)param;
1382 LPCWSTR component;
1383 MSIFILE *file;
1385 /* fill in the data */
1387 file = msi_alloc_zero( sizeof (MSIFILE) );
1388 if (!file)
1389 return ERROR_NOT_ENOUGH_MEMORY;
1391 file->File = msi_dup_record_field( row, 1 );
1393 component = MSI_RecordGetString( row, 2 );
1394 file->Component = get_loaded_component( package, component );
1396 if (!file->Component)
1397 ERR("Unfound Component %s\n",debugstr_w(component));
1399 file->FileName = msi_dup_record_field( row, 3 );
1400 reduce_to_longfilename( file->FileName );
1402 file->ShortName = msi_dup_record_field( row, 3 );
1403 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1405 file->FileSize = MSI_RecordGetInteger( row, 4 );
1406 file->Version = msi_dup_record_field( row, 5 );
1407 file->Language = msi_dup_record_field( row, 6 );
1408 file->Attributes = MSI_RecordGetInteger( row, 7 );
1409 file->Sequence = MSI_RecordGetInteger( row, 8 );
1411 file->state = msifs_invalid;
1413 /* if the compressed bits are not set in the file attributes,
1414 * then read the information from the package word count property
1416 if (file->Attributes & msidbFileAttributesCompressed)
1418 file->IsCompressed = TRUE;
1420 else if (file->Attributes & msidbFileAttributesNoncompressed)
1422 file->IsCompressed = FALSE;
1424 else
1426 file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1429 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1431 list_add_tail( &package->files, &file->entry );
1433 return ERROR_SUCCESS;
1436 static UINT load_all_files(MSIPACKAGE *package)
1438 MSIQUERY * view;
1439 UINT rc;
1440 static const WCHAR Query[] =
1441 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1442 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1443 '`','S','e','q','u','e','n','c','e','`', 0};
1445 if (!list_empty(&package->files))
1446 return ERROR_SUCCESS;
1448 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1449 if (rc != ERROR_SUCCESS)
1450 return ERROR_SUCCESS;
1452 rc = MSI_IterateRecords(view, NULL, load_file, package);
1453 msiobj_release(&view->hdr);
1455 return ERROR_SUCCESS;
1458 static UINT load_folder( MSIRECORD *row, LPVOID param )
1460 MSIPACKAGE *package = param;
1461 static const WCHAR szDot[] = { '.',0 };
1462 static WCHAR szEmpty[] = { 0 };
1463 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1464 MSIFOLDER *folder;
1466 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1467 if (!folder)
1468 return ERROR_NOT_ENOUGH_MEMORY;
1470 folder->Directory = msi_dup_record_field( row, 1 );
1472 TRACE("%s\n", debugstr_w(folder->Directory));
1474 p = msi_dup_record_field(row, 3);
1476 /* split src and target dir */
1477 tgt_short = p;
1478 src_short = folder_split_path( p, ':' );
1480 /* split the long and short paths */
1481 tgt_long = folder_split_path( tgt_short, '|' );
1482 src_long = folder_split_path( src_short, '|' );
1484 /* check for no-op dirs */
1485 if (!lstrcmpW(szDot, tgt_short))
1486 tgt_short = szEmpty;
1487 if (!lstrcmpW(szDot, src_short))
1488 src_short = szEmpty;
1490 if (!tgt_long)
1491 tgt_long = tgt_short;
1493 if (!src_short) {
1494 src_short = tgt_short;
1495 src_long = tgt_long;
1498 if (!src_long)
1499 src_long = src_short;
1501 /* FIXME: use the target short path too */
1502 folder->TargetDefault = strdupW(tgt_long);
1503 folder->SourceShortPath = strdupW(src_short);
1504 folder->SourceLongPath = strdupW(src_long);
1505 msi_free(p);
1507 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1508 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1509 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1511 folder->Parent = msi_dup_record_field( row, 2 );
1513 folder->Property = msi_dup_property( package, folder->Directory );
1515 list_add_tail( &package->folders, &folder->entry );
1517 TRACE("returning %p\n", folder);
1519 return ERROR_SUCCESS;
1522 static UINT load_all_folders( MSIPACKAGE *package )
1524 static const WCHAR query[] = {
1525 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1526 '`','D','i','r','e','c','t','o','r','y','`',0 };
1527 MSIQUERY *view;
1528 UINT r;
1530 if (!list_empty(&package->folders))
1531 return ERROR_SUCCESS;
1533 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1534 if (r != ERROR_SUCCESS)
1535 return r;
1537 r = MSI_IterateRecords(view, NULL, load_folder, package);
1538 msiobj_release(&view->hdr);
1539 return r;
1543 * I am not doing any of the costing functionality yet.
1544 * Mostly looking at doing the Component and Feature loading
1546 * The native MSI does A LOT of modification to tables here. Mostly adding
1547 * a lot of temporary columns to the Feature and Component tables.
1549 * note: Native msi also tracks the short filename. But I am only going to
1550 * track the long ones. Also looking at this directory table
1551 * it appears that the directory table does not get the parents
1552 * resolved base on property only based on their entries in the
1553 * directory table.
1555 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1557 static const WCHAR szCosting[] =
1558 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1559 static const WCHAR szZero[] = { '0', 0 };
1561 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
1562 return ERROR_SUCCESS;
1564 MSI_SetPropertyW(package, szCosting, szZero);
1565 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1567 load_all_components( package );
1568 load_all_features( package );
1569 load_all_files( package );
1570 load_all_folders( package );
1572 return ERROR_SUCCESS;
1575 static UINT execute_script(MSIPACKAGE *package, UINT script )
1577 int i;
1578 UINT rc = ERROR_SUCCESS;
1580 TRACE("Executing Script %i\n",script);
1582 if (!package->script)
1584 ERR("no script!\n");
1585 return ERROR_FUNCTION_FAILED;
1588 for (i = 0; i < package->script->ActionCount[script]; i++)
1590 LPWSTR action;
1591 action = package->script->Actions[script][i];
1592 ui_actionstart(package, action);
1593 TRACE("Executing Action (%s)\n",debugstr_w(action));
1594 rc = ACTION_PerformAction(package, action, TRUE);
1595 if (rc != ERROR_SUCCESS)
1596 break;
1598 msi_free_action_script(package, script);
1599 return rc;
1602 static UINT ACTION_FileCost(MSIPACKAGE *package)
1604 return ERROR_SUCCESS;
1607 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1609 MSICOMPONENT *comp;
1611 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1613 INSTALLSTATE res;
1615 if (!comp->ComponentId)
1616 continue;
1618 res = MsiGetComponentPathW( package->ProductCode,
1619 comp->ComponentId, NULL, NULL);
1620 if (res < 0)
1621 res = INSTALLSTATE_ABSENT;
1622 comp->Installed = res;
1626 /* scan for and update current install states */
1627 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1629 MSICOMPONENT *comp;
1630 MSIFEATURE *feature;
1632 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1634 ComponentList *cl;
1635 INSTALLSTATE res = INSTALLSTATE_ABSENT;
1637 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1639 comp= cl->component;
1641 if (!comp->ComponentId)
1643 res = INSTALLSTATE_ABSENT;
1644 break;
1647 if (res == INSTALLSTATE_ABSENT)
1648 res = comp->Installed;
1649 else
1651 if (res == comp->Installed)
1652 continue;
1654 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1655 res != INSTALLSTATE_SOURCE)
1657 res = INSTALLSTATE_INCOMPLETE;
1661 feature->Installed = res;
1665 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
1666 INSTALLSTATE state)
1668 static const WCHAR all[]={'A','L','L',0};
1669 LPWSTR override;
1670 MSIFEATURE *feature;
1672 override = msi_dup_property( package, property );
1673 if (!override)
1674 return FALSE;
1676 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1678 if (strcmpiW(override,all)==0)
1679 msi_feature_set_state( feature, state );
1680 else
1682 LPWSTR ptr = override;
1683 LPWSTR ptr2 = strchrW(override,',');
1685 while (ptr)
1687 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1688 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1690 msi_feature_set_state( feature, state );
1691 break;
1693 if (ptr2)
1695 ptr=ptr2+1;
1696 ptr2 = strchrW(ptr,',');
1698 else
1699 break;
1703 msi_free(override);
1705 return TRUE;
1708 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1710 int install_level;
1711 static const WCHAR szlevel[] =
1712 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1713 static const WCHAR szAddLocal[] =
1714 {'A','D','D','L','O','C','A','L',0};
1715 static const WCHAR szRemove[] =
1716 {'R','E','M','O','V','E',0};
1717 static const WCHAR szReinstall[] =
1718 {'R','E','I','N','S','T','A','L','L',0};
1719 BOOL override = FALSE;
1720 MSICOMPONENT* component;
1721 MSIFEATURE *feature;
1724 /* I do not know if this is where it should happen.. but */
1726 TRACE("Checking Install Level\n");
1728 install_level = msi_get_property_int( package, szlevel, 1 );
1730 /* ok here is the _real_ rub
1731 * all these activation/deactivation things happen in order and things
1732 * later on the list override things earlier on the list.
1733 * 1) INSTALLLEVEL processing
1734 * 2) ADDLOCAL
1735 * 3) REMOVE
1736 * 4) ADDSOURCE
1737 * 5) ADDDEFAULT
1738 * 6) REINSTALL
1739 * 7) COMPADDLOCAL
1740 * 8) COMPADDSOURCE
1741 * 9) FILEADDLOCAL
1742 * 10) FILEADDSOURCE
1743 * 11) FILEADDDEFAULT
1744 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1745 * ignored for all the features. seems strange, especially since it is not
1746 * documented anywhere, but it is how it works.
1748 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1749 * REMOVE are the big ones, since we don't handle administrative installs
1750 * yet anyway.
1752 override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1753 override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1754 override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1756 if (!override)
1758 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1760 BOOL feature_state = ((feature->Level > 0) &&
1761 (feature->Level <= install_level));
1763 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1765 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1766 msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1767 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1768 msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1769 else
1770 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1774 /* disable child features of unselected parent features */
1775 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1777 FeatureList *fl;
1779 if (feature->Level > 0 && feature->Level <= install_level)
1780 continue;
1782 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1783 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1786 else
1788 /* set the Preselected Property */
1789 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1790 static const WCHAR szOne[] = { '1', 0 };
1792 MSI_SetPropertyW(package,szPreselected,szOne);
1796 * now we want to enable or disable components base on feature
1799 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1801 ComponentList *cl;
1803 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1804 debugstr_w(feature->Feature), feature->Installed, feature->Action);
1806 /* features with components that have compressed files are made local */
1807 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1809 if (cl->component->Enabled &&
1810 cl->component->ForceLocalState &&
1811 feature->Action == INSTALLSTATE_SOURCE)
1813 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1814 break;
1818 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1820 component = cl->component;
1822 if (!component->Enabled)
1823 continue;
1825 switch (feature->Action)
1827 case INSTALLSTATE_ADVERTISED:
1828 component->hasAdvertiseFeature = 1;
1829 break;
1830 case INSTALLSTATE_SOURCE:
1831 component->hasSourceFeature = 1;
1832 break;
1833 case INSTALLSTATE_LOCAL:
1834 component->hasLocalFeature = 1;
1835 break;
1836 case INSTALLSTATE_DEFAULT:
1837 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1838 component->hasAdvertiseFeature = 1;
1839 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1840 component->hasSourceFeature = 1;
1841 else
1842 component->hasLocalFeature = 1;
1843 break;
1844 default:
1845 break;
1850 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1852 /* if the component isn't enabled, leave it alone */
1853 if (!component->Enabled)
1854 continue;
1856 /* check if it's local or source */
1857 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1858 (component->hasLocalFeature || component->hasSourceFeature))
1860 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1861 !component->ForceLocalState)
1862 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1863 else
1864 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1865 continue;
1868 /* if any feature is local, the component must be local too */
1869 if (component->hasLocalFeature)
1871 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1872 continue;
1875 if (component->hasSourceFeature)
1877 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1878 continue;
1881 if (component->hasAdvertiseFeature)
1883 msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1884 continue;
1887 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1890 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1892 if (component->Action == INSTALLSTATE_DEFAULT)
1894 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1895 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1898 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1899 debugstr_w(component->Component), component->Installed, component->Action);
1903 return ERROR_SUCCESS;
1906 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1908 MSIPACKAGE *package = (MSIPACKAGE*)param;
1909 LPCWSTR name;
1910 LPWSTR path;
1912 name = MSI_RecordGetString(row,1);
1914 /* This helper function now does ALL the work */
1915 TRACE("Dir %s ...\n",debugstr_w(name));
1916 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1917 TRACE("resolves to %s\n",debugstr_w(path));
1918 msi_free(path);
1920 return ERROR_SUCCESS;
1923 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1925 MSIPACKAGE *package = (MSIPACKAGE*)param;
1926 LPCWSTR name;
1927 MSIFEATURE *feature;
1929 name = MSI_RecordGetString( row, 1 );
1931 feature = get_loaded_feature( package, name );
1932 if (!feature)
1933 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1934 else
1936 LPCWSTR Condition;
1937 Condition = MSI_RecordGetString(row,3);
1939 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1941 int level = MSI_RecordGetInteger(row,2);
1942 TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
1943 feature->Level = level;
1946 return ERROR_SUCCESS;
1949 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1951 static const WCHAR name_fmt[] =
1952 {'%','u','.','%','u','.','%','u','.','%','u',0};
1953 static WCHAR name[] = {'\\',0};
1954 VS_FIXEDFILEINFO *lpVer;
1955 WCHAR filever[0x100];
1956 LPVOID version;
1957 DWORD versize;
1958 DWORD handle;
1959 UINT sz;
1961 TRACE("%s\n", debugstr_w(filename));
1963 versize = GetFileVersionInfoSizeW( filename, &handle );
1964 if (!versize)
1965 return NULL;
1967 version = msi_alloc( versize );
1968 GetFileVersionInfoW( filename, 0, versize, version );
1970 VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz );
1971 msi_free( version );
1973 sprintfW( filever, name_fmt,
1974 HIWORD(lpVer->dwFileVersionMS),
1975 LOWORD(lpVer->dwFileVersionMS),
1976 HIWORD(lpVer->dwFileVersionLS),
1977 LOWORD(lpVer->dwFileVersionLS));
1979 return strdupW( filever );
1982 static UINT msi_check_file_install_states( MSIPACKAGE *package )
1984 LPWSTR file_version;
1985 MSIFILE *file;
1987 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1989 MSICOMPONENT* comp = file->Component;
1990 LPWSTR p;
1992 if (!comp)
1993 continue;
1995 if (file->IsCompressed)
1996 comp->ForceLocalState = TRUE;
1998 /* calculate target */
1999 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2001 msi_free(file->TargetPath);
2003 TRACE("file %s is named %s\n",
2004 debugstr_w(file->File), debugstr_w(file->FileName));
2006 file->TargetPath = build_directory_name(2, p, file->FileName);
2008 msi_free(p);
2010 TRACE("file %s resolves to %s\n",
2011 debugstr_w(file->File), debugstr_w(file->TargetPath));
2013 /* don't check files of components that aren't installed */
2014 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2015 comp->Installed == INSTALLSTATE_ABSENT)
2017 file->state = msifs_missing; /* assume files are missing */
2018 continue;
2021 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2023 file->state = msifs_missing;
2024 comp->Cost += file->FileSize;
2025 comp->Installed = INSTALLSTATE_INCOMPLETE;
2026 continue;
2029 if (file->Version &&
2030 (file_version = msi_get_disk_file_version( file->TargetPath )))
2032 TRACE("new %s old %s\n", debugstr_w(file->Version),
2033 debugstr_w(file_version));
2034 /* FIXME: seems like a bad way to compare version numbers */
2035 if (lstrcmpiW(file_version, file->Version)<0)
2037 file->state = msifs_overwrite;
2038 comp->Cost += file->FileSize;
2039 comp->Installed = INSTALLSTATE_INCOMPLETE;
2041 else
2042 file->state = msifs_present;
2043 msi_free( file_version );
2045 else
2046 file->state = msifs_present;
2049 return ERROR_SUCCESS;
2053 * A lot is done in this function aside from just the costing.
2054 * The costing needs to be implemented at some point but for now I am going
2055 * to focus on the directory building
2058 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2060 static const WCHAR ExecSeqQuery[] =
2061 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2062 '`','D','i','r','e','c','t','o','r','y','`',0};
2063 static const WCHAR ConditionQuery[] =
2064 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2065 '`','C','o','n','d','i','t','i','o','n','`',0};
2066 static const WCHAR szCosting[] =
2067 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2068 static const WCHAR szlevel[] =
2069 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2070 static const WCHAR szOne[] = { '1', 0 };
2071 MSICOMPONENT *comp;
2072 UINT rc;
2073 MSIQUERY * view;
2074 LPWSTR level;
2076 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2077 return ERROR_SUCCESS;
2079 TRACE("Building Directory properties\n");
2081 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2082 if (rc == ERROR_SUCCESS)
2084 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2085 package);
2086 msiobj_release(&view->hdr);
2089 /* read components states from the registry */
2090 ACTION_GetComponentInstallStates(package);
2092 TRACE("File calculations\n");
2093 msi_check_file_install_states( package );
2095 TRACE("Evaluating Condition Table\n");
2097 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2098 if (rc == ERROR_SUCCESS)
2100 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2101 package);
2102 msiobj_release(&view->hdr);
2105 TRACE("Enabling or Disabling Components\n");
2106 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2108 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2110 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2111 comp->Enabled = FALSE;
2115 MSI_SetPropertyW(package,szCosting,szOne);
2116 /* set default run level if not set */
2117 level = msi_dup_property( package, szlevel );
2118 if (!level)
2119 MSI_SetPropertyW(package,szlevel, szOne);
2120 msi_free(level);
2122 ACTION_UpdateFeatureInstallStates(package);
2124 return MSI_SetFeatureStates(package);
2127 /* OK this value is "interpreted" and then formatted based on the
2128 first few characters */
2129 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2130 DWORD *size)
2132 LPSTR data = NULL;
2133 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2135 if (value[1]=='x')
2137 LPWSTR ptr;
2138 CHAR byte[5];
2139 LPWSTR deformated = NULL;
2140 int count;
2142 deformat_string(package, &value[2], &deformated);
2144 /* binary value type */
2145 ptr = deformated;
2146 *type = REG_BINARY;
2147 if (strlenW(ptr)%2)
2148 *size = (strlenW(ptr)/2)+1;
2149 else
2150 *size = strlenW(ptr)/2;
2152 data = msi_alloc(*size);
2154 byte[0] = '0';
2155 byte[1] = 'x';
2156 byte[4] = 0;
2157 count = 0;
2158 /* if uneven pad with a zero in front */
2159 if (strlenW(ptr)%2)
2161 byte[2]= '0';
2162 byte[3]= *ptr;
2163 ptr++;
2164 data[count] = (BYTE)strtol(byte,NULL,0);
2165 count ++;
2166 TRACE("Uneven byte count\n");
2168 while (*ptr)
2170 byte[2]= *ptr;
2171 ptr++;
2172 byte[3]= *ptr;
2173 ptr++;
2174 data[count] = (BYTE)strtol(byte,NULL,0);
2175 count ++;
2177 msi_free(deformated);
2179 TRACE("Data %i bytes(%i)\n",*size,count);
2181 else
2183 LPWSTR deformated;
2184 LPWSTR p;
2185 DWORD d = 0;
2186 deformat_string(package, &value[1], &deformated);
2188 *type=REG_DWORD;
2189 *size = sizeof(DWORD);
2190 data = msi_alloc(*size);
2191 p = deformated;
2192 if (*p == '-')
2193 p++;
2194 while (*p)
2196 if ( (*p < '0') || (*p > '9') )
2197 break;
2198 d *= 10;
2199 d += (*p - '0');
2200 p++;
2202 if (deformated[0] == '-')
2203 d = -d;
2204 *(LPDWORD)data = d;
2205 TRACE("DWORD %i\n",*(LPDWORD)data);
2207 msi_free(deformated);
2210 else
2212 static const WCHAR szMulti[] = {'[','~',']',0};
2213 LPCWSTR ptr;
2214 *type=REG_SZ;
2216 if (value[0]=='#')
2218 if (value[1]=='%')
2220 ptr = &value[2];
2221 *type=REG_EXPAND_SZ;
2223 else
2224 ptr = &value[1];
2226 else
2227 ptr=value;
2229 if (strstrW(value,szMulti))
2230 *type = REG_MULTI_SZ;
2232 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2234 return data;
2237 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2239 MSIPACKAGE *package = (MSIPACKAGE*)param;
2240 static const WCHAR szHCR[] =
2241 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2242 'R','O','O','T','\\',0};
2243 static const WCHAR szHCU[] =
2244 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2245 'U','S','E','R','\\',0};
2246 static const WCHAR szHLM[] =
2247 {'H','K','E','Y','_','L','O','C','A','L','_',
2248 'M','A','C','H','I','N','E','\\',0};
2249 static const WCHAR szHU[] =
2250 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2252 LPSTR value_data = NULL;
2253 HKEY root_key, hkey;
2254 DWORD type,size;
2255 LPWSTR deformated;
2256 LPCWSTR szRoot, component, name, key, value;
2257 MSICOMPONENT *comp;
2258 MSIRECORD * uirow;
2259 LPWSTR uikey;
2260 INT root;
2261 BOOL check_first = FALSE;
2262 UINT rc;
2264 ui_progress(package,2,0,0,0);
2266 value = NULL;
2267 key = NULL;
2268 uikey = NULL;
2269 name = NULL;
2271 component = MSI_RecordGetString(row, 6);
2272 comp = get_loaded_component(package,component);
2273 if (!comp)
2274 return ERROR_SUCCESS;
2276 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2278 TRACE("Skipping write due to disabled component %s\n",
2279 debugstr_w(component));
2281 comp->Action = comp->Installed;
2283 return ERROR_SUCCESS;
2286 comp->Action = INSTALLSTATE_LOCAL;
2288 name = MSI_RecordGetString(row, 4);
2289 if( MSI_RecordIsNull(row,5) && name )
2291 /* null values can have special meanings */
2292 if (name[0]=='-' && name[1] == 0)
2293 return ERROR_SUCCESS;
2294 else if ((name[0]=='+' && name[1] == 0) ||
2295 (name[0] == '*' && name[1] == 0))
2296 name = NULL;
2297 check_first = TRUE;
2300 root = MSI_RecordGetInteger(row,2);
2301 key = MSI_RecordGetString(row, 3);
2303 /* get the root key */
2304 switch (root)
2306 case -1:
2308 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2309 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2310 if (all_users && all_users[0] == '1')
2312 root_key = HKEY_LOCAL_MACHINE;
2313 szRoot = szHLM;
2315 else
2317 root_key = HKEY_CURRENT_USER;
2318 szRoot = szHCU;
2320 msi_free(all_users);
2322 break;
2323 case 0: root_key = HKEY_CLASSES_ROOT;
2324 szRoot = szHCR;
2325 break;
2326 case 1: root_key = HKEY_CURRENT_USER;
2327 szRoot = szHCU;
2328 break;
2329 case 2: root_key = HKEY_LOCAL_MACHINE;
2330 szRoot = szHLM;
2331 break;
2332 case 3: root_key = HKEY_USERS;
2333 szRoot = szHU;
2334 break;
2335 default:
2336 ERR("Unknown root %i\n",root);
2337 root_key=NULL;
2338 szRoot = NULL;
2339 break;
2341 if (!root_key)
2342 return ERROR_SUCCESS;
2344 deformat_string(package, key , &deformated);
2345 size = strlenW(deformated) + strlenW(szRoot) + 1;
2346 uikey = msi_alloc(size*sizeof(WCHAR));
2347 strcpyW(uikey,szRoot);
2348 strcatW(uikey,deformated);
2350 if (RegCreateKeyW( root_key, deformated, &hkey))
2352 ERR("Could not create key %s\n",debugstr_w(deformated));
2353 msi_free(deformated);
2354 msi_free(uikey);
2355 return ERROR_SUCCESS;
2357 msi_free(deformated);
2359 value = MSI_RecordGetString(row,5);
2360 if (value)
2361 value_data = parse_value(package, value, &type, &size);
2362 else
2364 static const WCHAR szEmpty[] = {0};
2365 value_data = (LPSTR)strdupW(szEmpty);
2366 size = 0;
2367 type = REG_SZ;
2370 deformat_string(package, name, &deformated);
2372 /* get the double nulls to terminate SZ_MULTI */
2373 if (type == REG_MULTI_SZ)
2374 size +=sizeof(WCHAR);
2376 if (!check_first)
2378 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2379 debugstr_w(uikey));
2380 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2382 else
2384 DWORD sz = 0;
2385 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2386 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2388 TRACE("value %s of %s checked already exists\n",
2389 debugstr_w(deformated), debugstr_w(uikey));
2391 else
2393 TRACE("Checked and setting value %s of %s\n",
2394 debugstr_w(deformated), debugstr_w(uikey));
2395 if (deformated || size)
2396 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2399 RegCloseKey(hkey);
2401 uirow = MSI_CreateRecord(3);
2402 MSI_RecordSetStringW(uirow,2,deformated);
2403 MSI_RecordSetStringW(uirow,1,uikey);
2405 if (type == REG_SZ)
2406 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2407 else
2408 MSI_RecordSetStringW(uirow,3,value);
2410 ui_actiondata(package,szWriteRegistryValues,uirow);
2411 msiobj_release( &uirow->hdr );
2413 msi_free(value_data);
2414 msi_free(deformated);
2415 msi_free(uikey);
2417 return ERROR_SUCCESS;
2420 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2422 UINT rc;
2423 MSIQUERY * view;
2424 static const WCHAR ExecSeqQuery[] =
2425 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2426 '`','R','e','g','i','s','t','r','y','`',0 };
2428 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2429 if (rc != ERROR_SUCCESS)
2430 return ERROR_SUCCESS;
2432 /* increment progress bar each time action data is sent */
2433 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2435 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2437 msiobj_release(&view->hdr);
2438 return rc;
2441 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2443 package->script->CurrentlyScripting = TRUE;
2445 return ERROR_SUCCESS;
2449 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2451 MSICOMPONENT *comp;
2452 DWORD progress = 0;
2453 DWORD total = 0;
2454 static const WCHAR q1[]=
2455 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2456 '`','R','e','g','i','s','t','r','y','`',0};
2457 UINT rc;
2458 MSIQUERY * view;
2459 MSIFEATURE *feature;
2460 MSIFILE *file;
2462 TRACE("InstallValidate\n");
2464 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2465 if (rc == ERROR_SUCCESS)
2467 MSI_IterateRecords( view, &progress, NULL, package );
2468 msiobj_release( &view->hdr );
2469 total += progress * REG_PROGRESS_VALUE;
2472 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2473 total += COMPONENT_PROGRESS_VALUE;
2475 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2476 total += file->FileSize;
2478 ui_progress(package,0,total,0,0);
2480 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2482 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2483 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2484 feature->ActionRequest);
2487 return ERROR_SUCCESS;
2490 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2492 MSIPACKAGE* package = (MSIPACKAGE*)param;
2493 LPCWSTR cond = NULL;
2494 LPCWSTR message = NULL;
2495 static const WCHAR title[]=
2496 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2498 cond = MSI_RecordGetString(row,1);
2500 if (MSI_EvaluateConditionW(package,cond) != MSICONDITION_TRUE)
2502 LPWSTR deformated;
2503 message = MSI_RecordGetString(row,2);
2504 deformat_string(package,message,&deformated);
2505 MessageBoxW(NULL,deformated,title,MB_OK);
2506 msi_free(deformated);
2507 return ERROR_FUNCTION_FAILED;
2510 return ERROR_SUCCESS;
2513 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2515 UINT rc;
2516 MSIQUERY * view = NULL;
2517 static const WCHAR ExecSeqQuery[] =
2518 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2519 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2521 TRACE("Checking launch conditions\n");
2523 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2524 if (rc != ERROR_SUCCESS)
2525 return ERROR_SUCCESS;
2527 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2528 msiobj_release(&view->hdr);
2530 return rc;
2533 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2536 if (!cmp->KeyPath)
2537 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2539 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2541 MSIRECORD * row = 0;
2542 UINT root,len;
2543 LPWSTR deformated,buffer,deformated_name;
2544 LPCWSTR key,name;
2545 static const WCHAR ExecSeqQuery[] =
2546 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2547 '`','R','e','g','i','s','t','r','y','`',' ',
2548 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2549 ' ','=',' ' ,'\'','%','s','\'',0 };
2550 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2551 static const WCHAR fmt2[]=
2552 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2554 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2555 if (!row)
2556 return NULL;
2558 root = MSI_RecordGetInteger(row,2);
2559 key = MSI_RecordGetString(row, 3);
2560 name = MSI_RecordGetString(row, 4);
2561 deformat_string(package, key , &deformated);
2562 deformat_string(package, name, &deformated_name);
2564 len = strlenW(deformated) + 6;
2565 if (deformated_name)
2566 len+=strlenW(deformated_name);
2568 buffer = msi_alloc( len *sizeof(WCHAR));
2570 if (deformated_name)
2571 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2572 else
2573 sprintfW(buffer,fmt,root,deformated);
2575 msi_free(deformated);
2576 msi_free(deformated_name);
2577 msiobj_release(&row->hdr);
2579 return buffer;
2581 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2583 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2584 return NULL;
2586 else
2588 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2590 if (file)
2591 return strdupW( file->TargetPath );
2593 return NULL;
2596 static HKEY openSharedDLLsKey(void)
2598 HKEY hkey=0;
2599 static const WCHAR path[] =
2600 {'S','o','f','t','w','a','r','e','\\',
2601 'M','i','c','r','o','s','o','f','t','\\',
2602 'W','i','n','d','o','w','s','\\',
2603 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2604 'S','h','a','r','e','d','D','L','L','s',0};
2606 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2607 return hkey;
2610 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2612 HKEY hkey;
2613 DWORD count=0;
2614 DWORD type;
2615 DWORD sz = sizeof(count);
2616 DWORD rc;
2618 hkey = openSharedDLLsKey();
2619 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2620 if (rc != ERROR_SUCCESS)
2621 count = 0;
2622 RegCloseKey(hkey);
2623 return count;
2626 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2628 HKEY hkey;
2630 hkey = openSharedDLLsKey();
2631 if (count > 0)
2632 msi_reg_set_val_dword( hkey, path, count );
2633 else
2634 RegDeleteValueW(hkey,path);
2635 RegCloseKey(hkey);
2636 return count;
2640 * Return TRUE if the count should be written out and FALSE if not
2642 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2644 MSIFEATURE *feature;
2645 INT count = 0;
2646 BOOL write = FALSE;
2648 /* only refcount DLLs */
2649 if (comp->KeyPath == NULL ||
2650 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2651 comp->Attributes & msidbComponentAttributesODBCDataSource)
2652 write = FALSE;
2653 else
2655 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2656 write = (count > 0);
2658 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2659 write = TRUE;
2662 /* increment counts */
2663 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2665 ComponentList *cl;
2667 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2668 continue;
2670 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2672 if ( cl->component == comp )
2673 count++;
2677 /* decrement counts */
2678 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2680 ComponentList *cl;
2682 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2683 continue;
2685 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2687 if ( cl->component == comp )
2688 count--;
2692 /* ref count all the files in the component */
2693 if (write)
2695 MSIFILE *file;
2697 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2699 if (file->Component == comp)
2700 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2704 /* add a count for permenent */
2705 if (comp->Attributes & msidbComponentAttributesPermanent)
2706 count ++;
2708 comp->RefCount = count;
2710 if (write)
2711 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2715 * Ok further analysis makes me think that this work is
2716 * actually done in the PublishComponents and PublishFeatures
2717 * step, and not here. It appears like the keypath and all that is
2718 * resolved in this step, however actually written in the Publish steps.
2719 * But we will leave it here for now because it is unclear
2721 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2723 WCHAR squished_pc[GUID_SIZE];
2724 WCHAR squished_cc[GUID_SIZE];
2725 UINT rc;
2726 MSICOMPONENT *comp;
2727 HKEY hkey=0,hkey2=0;
2729 /* writes the Component and Features values to the registry */
2731 rc = MSIREG_OpenComponents(&hkey);
2732 if (rc != ERROR_SUCCESS)
2733 return rc;
2735 squash_guid(package->ProductCode,squished_pc);
2736 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2738 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2740 MSIRECORD * uirow;
2742 ui_progress(package,2,0,0,0);
2743 if (!comp->ComponentId)
2744 continue;
2746 squash_guid(comp->ComponentId,squished_cc);
2748 msi_free(comp->FullKeypath);
2749 comp->FullKeypath = resolve_keypath( package, comp );
2751 /* do the refcounting */
2752 ACTION_RefCountComponent( package, comp );
2754 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2755 debugstr_w(comp->Component),
2756 debugstr_w(squished_cc),
2757 debugstr_w(comp->FullKeypath),
2758 comp->RefCount);
2760 * Write the keypath out if the component is to be registered
2761 * and delete the key if the component is to be deregistered
2763 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2765 rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2766 if (rc != ERROR_SUCCESS)
2767 continue;
2769 if (!comp->FullKeypath)
2770 continue;
2772 msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2774 if (comp->Attributes & msidbComponentAttributesPermanent)
2776 static const WCHAR szPermKey[] =
2777 { '0','0','0','0','0','0','0','0','0','0','0','0',
2778 '0','0','0','0','0','0','0','0','0','0','0','0',
2779 '0','0','0','0','0','0','0','0',0 };
2781 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2784 RegCloseKey(hkey2);
2786 else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2788 DWORD res;
2790 rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2791 if (rc != ERROR_SUCCESS)
2792 continue;
2794 RegDeleteValueW(hkey2,squished_pc);
2796 /* if the key is empty delete it */
2797 res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2798 RegCloseKey(hkey2);
2799 if (res == ERROR_NO_MORE_ITEMS)
2800 RegDeleteKeyW(hkey,squished_cc);
2804 /* UI stuff */
2805 uirow = MSI_CreateRecord(3);
2806 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2807 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2808 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2809 ui_actiondata(package,szProcessComponents,uirow);
2810 msiobj_release( &uirow->hdr );
2812 RegCloseKey(hkey);
2813 return rc;
2816 typedef struct {
2817 CLSID clsid;
2818 LPWSTR source;
2820 LPWSTR path;
2821 ITypeLib *ptLib;
2822 } typelib_struct;
2824 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2825 LPWSTR lpszName, LONG_PTR lParam)
2827 TLIBATTR *attr;
2828 typelib_struct *tl_struct = (typelib_struct*) lParam;
2829 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2830 int sz;
2831 HRESULT res;
2833 if (!IS_INTRESOURCE(lpszName))
2835 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2836 return TRUE;
2839 sz = strlenW(tl_struct->source)+4;
2840 sz *= sizeof(WCHAR);
2842 if ((INT_PTR)lpszName == 1)
2843 tl_struct->path = strdupW(tl_struct->source);
2844 else
2846 tl_struct->path = msi_alloc(sz);
2847 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2850 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2851 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2852 if (!SUCCEEDED(res))
2854 msi_free(tl_struct->path);
2855 tl_struct->path = NULL;
2857 return TRUE;
2860 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2861 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2863 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2864 return FALSE;
2867 msi_free(tl_struct->path);
2868 tl_struct->path = NULL;
2870 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2871 ITypeLib_Release(tl_struct->ptLib);
2873 return TRUE;
2876 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2878 MSIPACKAGE* package = (MSIPACKAGE*)param;
2879 LPCWSTR component;
2880 MSICOMPONENT *comp;
2881 MSIFILE *file;
2882 typelib_struct tl_struct;
2883 HMODULE module;
2884 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2886 component = MSI_RecordGetString(row,3);
2887 comp = get_loaded_component(package,component);
2888 if (!comp)
2889 return ERROR_SUCCESS;
2891 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2893 TRACE("Skipping typelib reg due to disabled component\n");
2895 comp->Action = comp->Installed;
2897 return ERROR_SUCCESS;
2900 comp->Action = INSTALLSTATE_LOCAL;
2902 file = get_loaded_file( package, comp->KeyPath );
2903 if (!file)
2904 return ERROR_SUCCESS;
2906 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2907 if (module)
2909 LPCWSTR guid;
2910 guid = MSI_RecordGetString(row,1);
2911 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2912 tl_struct.source = strdupW( file->TargetPath );
2913 tl_struct.path = NULL;
2915 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2916 (LONG_PTR)&tl_struct);
2918 if (tl_struct.path)
2920 LPWSTR help = NULL;
2921 LPCWSTR helpid;
2922 HRESULT res;
2924 helpid = MSI_RecordGetString(row,6);
2926 if (helpid)
2927 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
2928 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2929 msi_free(help);
2931 if (!SUCCEEDED(res))
2932 ERR("Failed to register type library %s\n",
2933 debugstr_w(tl_struct.path));
2934 else
2936 ui_actiondata(package,szRegisterTypeLibraries,row);
2938 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2941 ITypeLib_Release(tl_struct.ptLib);
2942 msi_free(tl_struct.path);
2944 else
2945 ERR("Failed to load type library %s\n",
2946 debugstr_w(tl_struct.source));
2948 FreeLibrary(module);
2949 msi_free(tl_struct.source);
2951 else
2952 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
2954 return ERROR_SUCCESS;
2957 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
2960 * OK this is a bit confusing.. I am given a _Component key and I believe
2961 * that the file that is being registered as a type library is the "key file
2962 * of that component" which I interpret to mean "The file in the KeyPath of
2963 * that component".
2965 UINT rc;
2966 MSIQUERY * view;
2967 static const WCHAR Query[] =
2968 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2969 '`','T','y','p','e','L','i','b','`',0};
2971 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2972 if (rc != ERROR_SUCCESS)
2973 return ERROR_SUCCESS;
2975 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
2976 msiobj_release(&view->hdr);
2977 return rc;
2980 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
2982 MSIPACKAGE *package = (MSIPACKAGE*)param;
2983 LPWSTR target_file, target_folder, filename;
2984 LPCWSTR buffer, extension;
2985 MSICOMPONENT *comp;
2986 static const WCHAR szlnk[]={'.','l','n','k',0};
2987 IShellLinkW *sl = NULL;
2988 IPersistFile *pf = NULL;
2989 HRESULT res;
2991 buffer = MSI_RecordGetString(row,4);
2992 comp = get_loaded_component(package,buffer);
2993 if (!comp)
2994 return ERROR_SUCCESS;
2996 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
2998 TRACE("Skipping shortcut creation due to disabled component\n");
3000 comp->Action = comp->Installed;
3002 return ERROR_SUCCESS;
3005 comp->Action = INSTALLSTATE_LOCAL;
3007 ui_actiondata(package,szCreateShortcuts,row);
3009 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3010 &IID_IShellLinkW, (LPVOID *) &sl );
3012 if (FAILED( res ))
3014 ERR("CLSID_ShellLink not available\n");
3015 goto err;
3018 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3019 if (FAILED( res ))
3021 ERR("QueryInterface(IID_IPersistFile) failed\n");
3022 goto err;
3025 buffer = MSI_RecordGetString(row,2);
3026 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3028 /* may be needed because of a bug somehwere else */
3029 create_full_pathW(target_folder);
3031 filename = msi_dup_record_field( row, 3 );
3032 reduce_to_longfilename(filename);
3034 extension = strchrW(filename,'.');
3035 if (!extension || strcmpiW(extension,szlnk))
3037 int len = strlenW(filename);
3038 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3039 memcpy(filename + len, szlnk, sizeof(szlnk));
3041 target_file = build_directory_name(2, target_folder, filename);
3042 msi_free(target_folder);
3043 msi_free(filename);
3045 buffer = MSI_RecordGetString(row,5);
3046 if (strchrW(buffer,'['))
3048 LPWSTR deformated;
3049 deformat_string(package,buffer,&deformated);
3050 IShellLinkW_SetPath(sl,deformated);
3051 msi_free(deformated);
3053 else
3055 FIXME("poorly handled shortcut format, advertised shortcut\n");
3056 IShellLinkW_SetPath(sl,comp->FullKeypath);
3059 if (!MSI_RecordIsNull(row,6))
3061 LPWSTR deformated;
3062 buffer = MSI_RecordGetString(row,6);
3063 deformat_string(package,buffer,&deformated);
3064 IShellLinkW_SetArguments(sl,deformated);
3065 msi_free(deformated);
3068 if (!MSI_RecordIsNull(row,7))
3070 buffer = MSI_RecordGetString(row,7);
3071 IShellLinkW_SetDescription(sl,buffer);
3074 if (!MSI_RecordIsNull(row,8))
3075 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3077 if (!MSI_RecordIsNull(row,9))
3079 LPWSTR Path;
3080 INT index;
3082 buffer = MSI_RecordGetString(row,9);
3084 Path = build_icon_path(package,buffer);
3085 index = MSI_RecordGetInteger(row,10);
3087 /* no value means 0 */
3088 if (index == MSI_NULL_INTEGER)
3089 index = 0;
3091 IShellLinkW_SetIconLocation(sl,Path,index);
3092 msi_free(Path);
3095 if (!MSI_RecordIsNull(row,11))
3096 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3098 if (!MSI_RecordIsNull(row,12))
3100 LPWSTR Path;
3101 buffer = MSI_RecordGetString(row,12);
3102 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3103 if (Path)
3104 IShellLinkW_SetWorkingDirectory(sl,Path);
3105 msi_free(Path);
3108 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3109 IPersistFile_Save(pf,target_file,FALSE);
3111 msi_free(target_file);
3113 err:
3114 if (pf)
3115 IPersistFile_Release( pf );
3116 if (sl)
3117 IShellLinkW_Release( sl );
3119 return ERROR_SUCCESS;
3122 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3124 UINT rc;
3125 HRESULT res;
3126 MSIQUERY * view;
3127 static const WCHAR Query[] =
3128 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3129 '`','S','h','o','r','t','c','u','t','`',0};
3131 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3132 if (rc != ERROR_SUCCESS)
3133 return ERROR_SUCCESS;
3135 res = CoInitialize( NULL );
3136 if (FAILED (res))
3138 ERR("CoInitialize failed\n");
3139 return ERROR_FUNCTION_FAILED;
3142 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3143 msiobj_release(&view->hdr);
3145 CoUninitialize();
3147 return rc;
3150 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3152 MSIPACKAGE* package = (MSIPACKAGE*)param;
3153 HANDLE the_file;
3154 LPWSTR FilePath;
3155 LPCWSTR FileName;
3156 CHAR buffer[1024];
3157 DWORD sz;
3158 UINT rc;
3159 MSIRECORD *uirow;
3161 FileName = MSI_RecordGetString(row,1);
3162 if (!FileName)
3164 ERR("Unable to get FileName\n");
3165 return ERROR_SUCCESS;
3168 FilePath = build_icon_path(package,FileName);
3170 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3172 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3173 FILE_ATTRIBUTE_NORMAL, NULL);
3175 if (the_file == INVALID_HANDLE_VALUE)
3177 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3178 msi_free(FilePath);
3179 return ERROR_SUCCESS;
3184 DWORD write;
3185 sz = 1024;
3186 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3187 if (rc != ERROR_SUCCESS)
3189 ERR("Failed to get stream\n");
3190 CloseHandle(the_file);
3191 DeleteFileW(FilePath);
3192 break;
3194 WriteFile(the_file,buffer,sz,&write,NULL);
3195 } while (sz == 1024);
3197 msi_free(FilePath);
3199 CloseHandle(the_file);
3201 uirow = MSI_CreateRecord(1);
3202 MSI_RecordSetStringW(uirow,1,FileName);
3203 ui_actiondata(package,szPublishProduct,uirow);
3204 msiobj_release( &uirow->hdr );
3206 return ERROR_SUCCESS;
3210 * 99% of the work done here is only done for
3211 * advertised installs. However this is where the
3212 * Icon table is processed and written out
3213 * so that is what I am going to do here.
3215 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3217 UINT rc;
3218 MSIQUERY * view;
3219 static const WCHAR Query[]=
3220 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3221 '`','I','c','o','n','`',0};
3222 /* for registry stuff */
3223 HKEY hkey=0;
3224 HKEY hukey=0;
3225 static const WCHAR szProductLanguage[] =
3226 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3227 static const WCHAR szARPProductIcon[] =
3228 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3229 static const WCHAR szProductVersion[] =
3230 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3231 DWORD langid;
3232 LPWSTR buffer;
3233 DWORD size;
3234 MSIHANDLE hDb, hSumInfo;
3236 /* write out icon files */
3238 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3239 if (rc == ERROR_SUCCESS)
3241 MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3242 msiobj_release(&view->hdr);
3245 /* ok there is a lot more done here but i need to figure out what */
3247 rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3248 if (rc != ERROR_SUCCESS)
3249 goto end;
3251 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3252 if (rc != ERROR_SUCCESS)
3253 goto end;
3256 buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3257 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3258 msi_free(buffer);
3260 langid = msi_get_property_int( package, szProductLanguage, 0 );
3261 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3263 buffer = msi_dup_property( package, szARPProductIcon );
3264 if (buffer)
3266 LPWSTR path = build_icon_path(package,buffer);
3267 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3268 msi_free( path );
3270 msi_free(buffer);
3272 buffer = msi_dup_property( package, szProductVersion );
3273 if (buffer)
3275 DWORD verdword = msi_version_str_to_dword(buffer);
3276 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3278 msi_free(buffer);
3280 /* FIXME: Need to write more keys to the user registry */
3282 hDb= alloc_msihandle( &package->db->hdr );
3283 if (!hDb) {
3284 rc = ERROR_NOT_ENOUGH_MEMORY;
3285 goto end;
3287 rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo);
3288 MsiCloseHandle(hDb);
3289 if (rc == ERROR_SUCCESS)
3291 WCHAR guidbuffer[0x200];
3292 size = 0x200;
3293 rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3294 guidbuffer, &size);
3295 if (rc == ERROR_SUCCESS)
3297 WCHAR squashed[GUID_SIZE];
3298 /* for now we only care about the first guid */
3299 LPWSTR ptr = strchrW(guidbuffer,';');
3300 if (ptr) *ptr = 0;
3301 squash_guid(guidbuffer,squashed);
3302 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3304 else
3306 ERR("Unable to query Revision_Number...\n");
3307 rc = ERROR_SUCCESS;
3309 MsiCloseHandle(hSumInfo);
3311 else
3313 ERR("Unable to open Summary Information\n");
3314 rc = ERROR_SUCCESS;
3317 end:
3319 RegCloseKey(hkey);
3320 RegCloseKey(hukey);
3322 return rc;
3325 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3327 MSIPACKAGE *package = (MSIPACKAGE*)param;
3328 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3329 LPWSTR deformated_section, deformated_key, deformated_value;
3330 LPWSTR folder, fullname = NULL;
3331 MSIRECORD * uirow;
3332 INT action;
3333 MSICOMPONENT *comp;
3334 static const WCHAR szWindowsFolder[] =
3335 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3337 component = MSI_RecordGetString(row, 8);
3338 comp = get_loaded_component(package,component);
3340 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3342 TRACE("Skipping ini file due to disabled component %s\n",
3343 debugstr_w(component));
3345 comp->Action = comp->Installed;
3347 return ERROR_SUCCESS;
3350 comp->Action = INSTALLSTATE_LOCAL;
3352 identifier = MSI_RecordGetString(row,1);
3353 filename = MSI_RecordGetString(row,2);
3354 dirproperty = MSI_RecordGetString(row,3);
3355 section = MSI_RecordGetString(row,4);
3356 key = MSI_RecordGetString(row,5);
3357 value = MSI_RecordGetString(row,6);
3358 action = MSI_RecordGetInteger(row,7);
3360 deformat_string(package,section,&deformated_section);
3361 deformat_string(package,key,&deformated_key);
3362 deformat_string(package,value,&deformated_value);
3364 if (dirproperty)
3366 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3367 if (!folder)
3368 folder = msi_dup_property( package, dirproperty );
3370 else
3371 folder = msi_dup_property( package, szWindowsFolder );
3373 if (!folder)
3375 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3376 goto cleanup;
3379 fullname = build_directory_name(2, folder, filename);
3381 if (action == 0)
3383 TRACE("Adding value %s to section %s in %s\n",
3384 debugstr_w(deformated_key), debugstr_w(deformated_section),
3385 debugstr_w(fullname));
3386 WritePrivateProfileStringW(deformated_section, deformated_key,
3387 deformated_value, fullname);
3389 else if (action == 1)
3391 WCHAR returned[10];
3392 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3393 returned, 10, fullname);
3394 if (returned[0] == 0)
3396 TRACE("Adding value %s to section %s in %s\n",
3397 debugstr_w(deformated_key), debugstr_w(deformated_section),
3398 debugstr_w(fullname));
3400 WritePrivateProfileStringW(deformated_section, deformated_key,
3401 deformated_value, fullname);
3404 else if (action == 3)
3405 FIXME("Append to existing section not yet implemented\n");
3407 uirow = MSI_CreateRecord(4);
3408 MSI_RecordSetStringW(uirow,1,identifier);
3409 MSI_RecordSetStringW(uirow,2,deformated_section);
3410 MSI_RecordSetStringW(uirow,3,deformated_key);
3411 MSI_RecordSetStringW(uirow,4,deformated_value);
3412 ui_actiondata(package,szWriteIniValues,uirow);
3413 msiobj_release( &uirow->hdr );
3414 cleanup:
3415 msi_free(fullname);
3416 msi_free(folder);
3417 msi_free(deformated_key);
3418 msi_free(deformated_value);
3419 msi_free(deformated_section);
3420 return ERROR_SUCCESS;
3423 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3425 UINT rc;
3426 MSIQUERY * view;
3427 static const WCHAR ExecSeqQuery[] =
3428 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3429 '`','I','n','i','F','i','l','e','`',0};
3431 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3432 if (rc != ERROR_SUCCESS)
3434 TRACE("no IniFile table\n");
3435 return ERROR_SUCCESS;
3438 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3439 msiobj_release(&view->hdr);
3440 return rc;
3443 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3445 MSIPACKAGE *package = (MSIPACKAGE*)param;
3446 LPCWSTR filename;
3447 LPWSTR FullName;
3448 MSIFILE *file;
3449 DWORD len;
3450 static const WCHAR ExeStr[] =
3451 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3452 static const WCHAR close[] = {'\"',0};
3453 STARTUPINFOW si;
3454 PROCESS_INFORMATION info;
3455 BOOL brc;
3456 MSIRECORD *uirow;
3457 LPWSTR uipath, p;
3459 memset(&si,0,sizeof(STARTUPINFOW));
3461 filename = MSI_RecordGetString(row,1);
3462 file = get_loaded_file( package, filename );
3464 if (!file)
3466 ERR("Unable to find file id %s\n",debugstr_w(filename));
3467 return ERROR_SUCCESS;
3470 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3472 FullName = msi_alloc(len*sizeof(WCHAR));
3473 strcpyW(FullName,ExeStr);
3474 strcatW( FullName, file->TargetPath );
3475 strcatW(FullName,close);
3477 TRACE("Registering %s\n",debugstr_w(FullName));
3478 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3479 &si, &info);
3481 if (brc)
3482 msi_dialog_check_messages(info.hProcess);
3484 msi_free(FullName);
3486 /* the UI chunk */
3487 uirow = MSI_CreateRecord( 2 );
3488 uipath = strdupW( file->TargetPath );
3489 p = strrchrW(uipath,'\\');
3490 if (p)
3491 p[1]=0;
3492 MSI_RecordSetStringW( uirow, 1, &p[2] );
3493 MSI_RecordSetStringW( uirow, 2, uipath);
3494 ui_actiondata( package, szSelfRegModules, uirow);
3495 msiobj_release( &uirow->hdr );
3496 msi_free( uipath );
3497 /* FIXME: call ui_progress? */
3499 return ERROR_SUCCESS;
3502 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3504 UINT rc;
3505 MSIQUERY * view;
3506 static const WCHAR ExecSeqQuery[] =
3507 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3508 '`','S','e','l','f','R','e','g','`',0};
3510 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3511 if (rc != ERROR_SUCCESS)
3513 TRACE("no SelfReg table\n");
3514 return ERROR_SUCCESS;
3517 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3518 msiobj_release(&view->hdr);
3520 return ERROR_SUCCESS;
3523 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3525 MSIFEATURE *feature;
3526 UINT rc;
3527 HKEY hkey=0;
3528 HKEY hukey=0;
3530 rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3531 if (rc != ERROR_SUCCESS)
3532 goto end;
3534 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3535 if (rc != ERROR_SUCCESS)
3536 goto end;
3538 /* here the guids are base 85 encoded */
3539 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3541 ComponentList *cl;
3542 LPWSTR data = NULL;
3543 GUID clsid;
3544 INT size;
3545 BOOL absent = FALSE;
3546 MSIRECORD *uirow;
3548 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3549 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3550 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3551 absent = TRUE;
3553 size = 1;
3554 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3556 size += 21;
3558 if (feature->Feature_Parent)
3559 size += strlenW( feature->Feature_Parent )+2;
3561 data = msi_alloc(size * sizeof(WCHAR));
3563 data[0] = 0;
3564 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3566 MSICOMPONENT* component = cl->component;
3567 WCHAR buf[21];
3569 buf[0] = 0;
3570 if (component->ComponentId)
3572 TRACE("From %s\n",debugstr_w(component->ComponentId));
3573 CLSIDFromString(component->ComponentId, &clsid);
3574 encode_base85_guid(&clsid,buf);
3575 TRACE("to %s\n",debugstr_w(buf));
3576 strcatW(data,buf);
3579 if (feature->Feature_Parent)
3581 static const WCHAR sep[] = {'\2',0};
3582 strcatW(data,sep);
3583 strcatW(data,feature->Feature_Parent);
3586 msi_reg_set_val_str( hkey, feature->Feature, data );
3587 msi_free(data);
3589 size = 0;
3590 if (feature->Feature_Parent)
3591 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3592 if (!absent)
3594 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3595 (LPBYTE)feature->Feature_Parent,size);
3597 else
3599 size += 2*sizeof(WCHAR);
3600 data = msi_alloc(size);
3601 data[0] = 0x6;
3602 data[1] = 0;
3603 if (feature->Feature_Parent)
3604 strcpyW( &data[1], feature->Feature_Parent );
3605 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3606 (LPBYTE)data,size);
3607 msi_free(data);
3610 /* the UI chunk */
3611 uirow = MSI_CreateRecord( 1 );
3612 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3613 ui_actiondata( package, szPublishFeatures, uirow);
3614 msiobj_release( &uirow->hdr );
3615 /* FIXME: call ui_progress? */
3618 end:
3619 RegCloseKey(hkey);
3620 RegCloseKey(hukey);
3621 return rc;
3624 static UINT msi_get_local_package_name( LPWSTR path )
3626 static const WCHAR szInstaller[] = {
3627 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3628 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3629 DWORD time, len, i;
3630 HANDLE handle;
3632 time = GetTickCount();
3633 GetWindowsDirectoryW( path, MAX_PATH );
3634 lstrcatW( path, szInstaller );
3635 CreateDirectoryW( path, NULL );
3637 len = lstrlenW(path);
3638 for (i=0; i<0x10000; i++)
3640 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3641 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3642 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3643 if (handle != INVALID_HANDLE_VALUE)
3645 CloseHandle(handle);
3646 break;
3648 if (GetLastError() != ERROR_FILE_EXISTS &&
3649 GetLastError() != ERROR_SHARING_VIOLATION)
3650 return ERROR_FUNCTION_FAILED;
3653 return ERROR_SUCCESS;
3656 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3658 static const WCHAR szOriginalDatabase[] =
3659 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3660 WCHAR packagefile[MAX_PATH];
3661 LPWSTR msiFilePath;
3662 UINT r;
3664 r = msi_get_local_package_name( packagefile );
3665 if (r != ERROR_SUCCESS)
3666 return r;
3668 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3670 msiFilePath = msi_dup_property( package, szOriginalDatabase );
3671 r = CopyFileW( msiFilePath, packagefile, FALSE);
3672 msi_free( msiFilePath );
3674 if (!r)
3676 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3677 debugstr_w(msiFilePath), debugstr_w(packagefile), GetLastError());
3678 return ERROR_FUNCTION_FAILED;
3681 /* FIXME: maybe set this key in ACTION_RegisterProduct instead */
3682 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3683 return ERROR_SUCCESS;
3686 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3688 LPWSTR prop, val, key;
3689 static const LPCSTR propval[] = {
3690 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3691 "ARPCONTACT", "Contact",
3692 "ARPCOMMENTS", "Comments",
3693 "ProductName", "DisplayName",
3694 "ProductVersion", "DisplayVersion",
3695 "ARPHELPLINK", "HelpLink",
3696 "ARPHELPTELEPHONE", "HelpTelephone",
3697 "ARPINSTALLLOCATION", "InstallLocation",
3698 "SourceDir", "InstallSource",
3699 "Manufacturer", "Publisher",
3700 "ARPREADME", "Readme",
3701 "ARPSIZE", "Size",
3702 "ARPURLINFOABOUT", "URLInfoAbout",
3703 "ARPURLUPDATEINFO", "URLUpdateInfo",
3704 NULL,
3706 const LPCSTR *p = propval;
3708 while( *p )
3710 prop = strdupAtoW( *p++ );
3711 key = strdupAtoW( *p++ );
3712 val = msi_dup_property( package, prop );
3713 msi_reg_set_val_str( hkey, key, val );
3714 msi_free(val);
3715 msi_free(key);
3716 msi_free(prop);
3718 return ERROR_SUCCESS;
3721 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3723 HKEY hkey=0;
3724 LPWSTR buffer = NULL;
3725 UINT rc;
3726 DWORD size, langid;
3727 static const WCHAR szWindowsInstaller[] =
3728 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3729 static const WCHAR szUpgradeCode[] =
3730 {'U','p','g','r','a','d','e','C','o','d','e',0};
3731 static const WCHAR modpath_fmt[] =
3732 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3733 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3734 static const WCHAR szModifyPath[] =
3735 {'M','o','d','i','f','y','P','a','t','h',0};
3736 static const WCHAR szUninstallString[] =
3737 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3738 static const WCHAR szEstimatedSize[] =
3739 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3740 static const WCHAR szProductLanguage[] =
3741 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3742 static const WCHAR szProductVersion[] =
3743 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3745 SYSTEMTIME systime;
3746 static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0};
3747 LPWSTR upgrade_code;
3748 WCHAR szDate[9];
3750 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3751 if (rc != ERROR_SUCCESS)
3752 return rc;
3754 /* dump all the info i can grab */
3755 /* FIXME: Flesh out more information */
3757 msi_write_uninstall_property_vals( package, hkey );
3759 msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
3761 msi_make_package_local( package, hkey );
3763 /* do ModifyPath and UninstallString */
3764 size = deformat_string(package,modpath_fmt,&buffer);
3765 RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3766 RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3767 msi_free(buffer);
3769 /* FIXME: Write real Estimated Size when we have it */
3770 msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
3772 GetLocalTime(&systime);
3773 sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
3774 msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
3776 langid = msi_get_property_int( package, szProductLanguage, 0 );
3777 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3779 buffer = msi_dup_property( package, szProductVersion );
3780 if (buffer)
3782 DWORD verdword = msi_version_str_to_dword(buffer);
3784 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3785 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
3786 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
3788 msi_free(buffer);
3790 /* Handle Upgrade Codes */
3791 upgrade_code = msi_dup_property( package, szUpgradeCode );
3792 if (upgrade_code)
3794 HKEY hkey2;
3795 WCHAR squashed[33];
3796 MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3797 squash_guid(package->ProductCode,squashed);
3798 msi_reg_set_val_str( hkey2, squashed, NULL );
3799 RegCloseKey(hkey2);
3800 MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3801 squash_guid(package->ProductCode,squashed);
3802 msi_reg_set_val_str( hkey2, squashed, NULL );
3803 RegCloseKey(hkey2);
3805 msi_free(upgrade_code);
3808 RegCloseKey(hkey);
3810 /* FIXME: call ui_actiondata */
3812 return ERROR_SUCCESS;
3815 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
3817 return execute_script(package,INSTALL_SCRIPT);
3820 int global_deferred=0;
3821 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
3823 UINT rc;
3825 /* turn off scheduling */
3826 package->script->CurrentlyScripting= FALSE;
3828 /* first do the same as an InstallExecute */
3829 rc = ACTION_InstallExecute(package);
3830 if (rc != ERROR_SUCCESS)
3831 return rc;
3833 /* then handle Commit Actions */
3834 TRACE("setting global_deferred=1\n");
3835 global_deferred=1;
3836 rc = execute_script(package,COMMIT_SCRIPT);
3837 global_deferred=0;
3838 TRACE("setting global_deferred=0\n");
3840 return rc;
3843 static UINT ACTION_ForceReboot(MSIPACKAGE *package)
3845 static const WCHAR RunOnce[] = {
3846 'S','o','f','t','w','a','r','e','\\',
3847 'M','i','c','r','o','s','o','f','t','\\',
3848 'W','i','n','d','o','w','s','\\',
3849 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3850 'R','u','n','O','n','c','e',0};
3851 static const WCHAR InstallRunOnce[] = {
3852 'S','o','f','t','w','a','r','e','\\',
3853 'M','i','c','r','o','s','o','f','t','\\',
3854 'W','i','n','d','o','w','s','\\',
3855 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3856 'I','n','s','t','a','l','l','e','r','\\',
3857 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
3859 static const WCHAR msiexec_fmt[] = {
3860 '%','s',
3861 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
3862 '\"','%','s','\"',0};
3863 static const WCHAR install_fmt[] = {
3864 '/','I',' ','\"','%','s','\"',' ',
3865 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
3866 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
3867 WCHAR buffer[256], sysdir[MAX_PATH];
3868 HKEY hkey;
3869 WCHAR squished_pc[100];
3871 squash_guid(package->ProductCode,squished_pc);
3873 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
3874 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
3875 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
3876 squished_pc);
3878 msi_reg_set_val_str( hkey, squished_pc, buffer );
3879 RegCloseKey(hkey);
3881 TRACE("Reboot command %s\n",debugstr_w(buffer));
3883 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
3884 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
3886 msi_reg_set_val_str( hkey, squished_pc, buffer );
3887 RegCloseKey(hkey);
3889 return ERROR_INSTALL_SUSPEND;
3892 static UINT msi_set_sourcedir_props(MSIPACKAGE *package)
3894 LPWSTR p, source;
3895 DWORD len;
3897 p = strrchrW( package->PackagePath, '\\' );
3898 if (!p)
3899 return ERROR_SUCCESS;
3901 len = p - package->PackagePath + 2;
3902 source = msi_alloc( len * sizeof(WCHAR) );
3903 lstrcpynW( source, package->PackagePath, len );
3905 MSI_SetPropertyW( package, cszSourceDir, source );
3906 MSI_SetPropertyW( package, cszSOURCEDIR, source );
3908 msi_free( source );
3910 return ERROR_SUCCESS;
3913 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
3915 DWORD attrib;
3916 UINT rc;
3919 * We are currently doing what should be done here in the top level Install
3920 * however for Administrative and uninstalls this step will be needed
3922 if (!package->PackagePath)
3923 return ERROR_SUCCESS;
3925 msi_set_sourcedir_props(package);
3927 attrib = GetFileAttributesW(package->PackagePath);
3928 if (attrib == INVALID_FILE_ATTRIBUTES)
3930 LPWSTR prompt;
3931 LPWSTR msg;
3932 DWORD size = 0;
3934 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
3935 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
3936 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
3937 if (rc == ERROR_MORE_DATA)
3939 prompt = msi_alloc(size * sizeof(WCHAR));
3940 MsiSourceListGetInfoW(package->ProductCode, NULL,
3941 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
3942 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
3944 else
3945 prompt = strdupW(package->PackagePath);
3947 msg = generate_error_string(package,1302,1,prompt);
3948 while(attrib == INVALID_FILE_ATTRIBUTES)
3950 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
3951 if (rc == IDCANCEL)
3953 rc = ERROR_INSTALL_USEREXIT;
3954 break;
3956 attrib = GetFileAttributesW(package->PackagePath);
3958 msi_free(prompt);
3959 rc = ERROR_SUCCESS;
3961 else
3962 return ERROR_SUCCESS;
3964 return rc;
3967 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
3969 HKEY hkey=0;
3970 LPWSTR buffer;
3971 LPWSTR productid;
3972 UINT rc,i;
3974 static const WCHAR szPropKeys[][80] =
3976 {'P','r','o','d','u','c','t','I','D',0},
3977 {'U','S','E','R','N','A','M','E',0},
3978 {'C','O','M','P','A','N','Y','N','A','M','E',0},
3979 {0},
3982 static const WCHAR szRegKeys[][80] =
3984 {'P','r','o','d','u','c','t','I','D',0},
3985 {'R','e','g','O','w','n','e','r',0},
3986 {'R','e','g','C','o','m','p','a','n','y',0},
3987 {0},
3990 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
3991 if (!productid)
3992 return ERROR_SUCCESS;
3994 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3995 if (rc != ERROR_SUCCESS)
3996 goto end;
3998 for( i = 0; szPropKeys[i][0]; i++ )
4000 buffer = msi_dup_property( package, szPropKeys[i] );
4001 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4002 msi_free( buffer );
4005 end:
4006 msi_free(productid);
4007 RegCloseKey(hkey);
4009 /* FIXME: call ui_actiondata */
4011 return ERROR_SUCCESS;
4015 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4017 UINT rc;
4019 package->script->InWhatSequence |= SEQUENCE_EXEC;
4020 rc = ACTION_ProcessExecSequence(package,FALSE);
4021 return rc;
4025 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4027 MSIPACKAGE *package = (MSIPACKAGE*)param;
4028 LPCWSTR compgroupid=NULL;
4029 LPCWSTR feature=NULL;
4030 LPCWSTR text = NULL;
4031 LPCWSTR qualifier = NULL;
4032 LPCWSTR component = NULL;
4033 LPWSTR advertise = NULL;
4034 LPWSTR output = NULL;
4035 HKEY hkey;
4036 UINT rc = ERROR_SUCCESS;
4037 MSICOMPONENT *comp;
4038 DWORD sz = 0;
4039 MSIRECORD *uirow;
4041 component = MSI_RecordGetString(rec,3);
4042 comp = get_loaded_component(package,component);
4044 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4045 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4046 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4048 TRACE("Skipping: Component %s not scheduled for install\n",
4049 debugstr_w(component));
4051 return ERROR_SUCCESS;
4054 compgroupid = MSI_RecordGetString(rec,1);
4055 qualifier = MSI_RecordGetString(rec,2);
4057 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4058 if (rc != ERROR_SUCCESS)
4059 goto end;
4061 text = MSI_RecordGetString(rec,4);
4062 feature = MSI_RecordGetString(rec,5);
4064 advertise = create_component_advertise_string(package, comp, feature);
4066 sz = strlenW(advertise);
4068 if (text)
4069 sz += lstrlenW(text);
4071 sz+=3;
4072 sz *= sizeof(WCHAR);
4074 output = msi_alloc_zero(sz);
4075 strcpyW(output,advertise);
4076 msi_free(advertise);
4078 if (text)
4079 strcatW(output,text);
4081 msi_reg_set_val_multi_str( hkey, qualifier, output );
4083 end:
4084 RegCloseKey(hkey);
4085 msi_free(output);
4087 /* the UI chunk */
4088 uirow = MSI_CreateRecord( 2 );
4089 MSI_RecordSetStringW( uirow, 1, compgroupid );
4090 MSI_RecordSetStringW( uirow, 2, qualifier);
4091 ui_actiondata( package, szPublishComponents, uirow);
4092 msiobj_release( &uirow->hdr );
4093 /* FIXME: call ui_progress? */
4095 return rc;
4099 * At present I am ignorning the advertised components part of this and only
4100 * focusing on the qualified component sets
4102 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4104 UINT rc;
4105 MSIQUERY * view;
4106 static const WCHAR ExecSeqQuery[] =
4107 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4108 '`','P','u','b','l','i','s','h',
4109 'C','o','m','p','o','n','e','n','t','`',0};
4111 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4112 if (rc != ERROR_SUCCESS)
4113 return ERROR_SUCCESS;
4115 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4116 msiobj_release(&view->hdr);
4118 return rc;
4121 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4123 MSIPACKAGE *package = (MSIPACKAGE*)param;
4124 MSIRECORD *row;
4125 MSIFILE *file;
4126 SC_HANDLE hscm, service = NULL;
4127 LPCWSTR name, disp, comp, depends, pass;
4128 LPCWSTR load_order, serv_name, key;
4129 DWORD serv_type, start_type;
4130 DWORD err_control;
4132 static const WCHAR query[] =
4133 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4134 '`','C','o','m','p','o','n','e','n','t','`',' ',
4135 'W','H','E','R','E',' ',
4136 '`','C','o','m','p','o','n','e','n','t','`',' ',
4137 '=','\'','%','s','\'',0};
4139 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4140 if (!hscm)
4142 ERR("Failed to open the SC Manager!\n");
4143 goto done;
4146 start_type = MSI_RecordGetInteger(rec, 5);
4147 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4148 goto done;
4150 depends = MSI_RecordGetString(rec, 8);
4151 if (depends && *depends)
4152 FIXME("Dependency list unhandled!\n");
4154 name = MSI_RecordGetString(rec, 2);
4155 disp = MSI_RecordGetString(rec, 3);
4156 serv_type = MSI_RecordGetInteger(rec, 4);
4157 err_control = MSI_RecordGetInteger(rec, 6);
4158 load_order = MSI_RecordGetString(rec, 7);
4159 serv_name = MSI_RecordGetString(rec, 9);
4160 pass = MSI_RecordGetString(rec, 10);
4161 comp = MSI_RecordGetString(rec, 12);
4163 /* fetch the service path */
4164 row = MSI_QueryGetRecord(package->db, query, comp);
4165 if (!row)
4167 ERR("Control query failed!\n");
4168 goto done;
4171 key = MSI_RecordGetString(row, 6);
4172 msiobj_release(&row->hdr);
4174 file = get_loaded_file(package, key);
4175 if (!file)
4177 ERR("Failed to load the service file\n");
4178 goto done;
4181 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4182 start_type, err_control, file->TargetPath,
4183 load_order, NULL, NULL, serv_name, pass);
4184 if (!service)
4186 if (GetLastError() != ERROR_SERVICE_EXISTS)
4187 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4190 done:
4191 CloseServiceHandle(service);
4192 CloseServiceHandle(hscm);
4194 return ERROR_SUCCESS;
4197 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4199 UINT rc;
4200 MSIQUERY * view;
4201 static const WCHAR ExecSeqQuery[] =
4202 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4203 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4205 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4206 if (rc != ERROR_SUCCESS)
4207 return ERROR_SUCCESS;
4209 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4210 msiobj_release(&view->hdr);
4212 return rc;
4215 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4216 static LPCWSTR *msi_service_args_to_vector(LPCWSTR name, LPWSTR args, DWORD *numargs)
4218 LPCWSTR *vector;
4219 LPWSTR p, q;
4220 DWORD sep_len;
4222 static const WCHAR separator[] = {'[','~',']',0};
4224 *numargs = 0;
4225 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4227 if (!args)
4228 return NULL;
4230 vector = msi_alloc(sizeof(LPWSTR));
4231 if (!vector)
4232 return NULL;
4234 p = args;
4237 (*numargs)++;
4238 vector[*numargs - 1] = p;
4240 if ((q = strstrW(p, separator)))
4242 *q = '\0';
4244 vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4245 if (!vector)
4246 return NULL;
4248 p = q + sep_len;
4250 } while (q);
4252 return vector;
4255 static MSICOMPONENT *msi_find_component( MSIPACKAGE *package, LPCWSTR component )
4257 MSICOMPONENT *comp;
4259 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
4261 if (!lstrcmpW(comp->Component, component))
4262 return comp;
4265 return NULL;
4268 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4270 MSIPACKAGE *package = (MSIPACKAGE *)param;
4271 MSICOMPONENT *comp;
4272 SC_HANDLE scm, service = NULL;
4273 LPCWSTR name, *vector = NULL;
4274 LPWSTR args;
4275 DWORD event, numargs;
4276 UINT r = ERROR_FUNCTION_FAILED;
4278 comp = msi_find_component(package, MSI_RecordGetString(rec, 6));
4279 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4280 return ERROR_SUCCESS;
4282 name = MSI_RecordGetString(rec, 2);
4283 event = MSI_RecordGetInteger(rec, 3);
4284 args = strdupW(MSI_RecordGetString(rec, 4));
4286 if (!(event & msidbServiceControlEventStart))
4287 return ERROR_SUCCESS;
4289 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4290 if (!scm)
4292 ERR("Failed to open the service control manager\n");
4293 goto done;
4296 service = OpenServiceW(scm, name, SERVICE_START);
4297 if (!service)
4299 ERR("Failed to open service %s\n", debugstr_w(name));
4300 goto done;
4303 vector = msi_service_args_to_vector(name, args, &numargs);
4305 if (!StartServiceW(service, numargs, vector))
4307 ERR("Failed to start service %s\n", debugstr_w(name));
4308 goto done;
4311 r = ERROR_SUCCESS;
4313 done:
4314 CloseServiceHandle(service);
4315 CloseServiceHandle(scm);
4317 msi_free(args);
4318 msi_free(vector);
4319 return r;
4322 static UINT ACTION_StartServices( MSIPACKAGE *package )
4324 UINT rc;
4325 MSIQUERY *view;
4327 static const WCHAR query[] = {
4328 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4329 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4331 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4332 if (rc != ERROR_SUCCESS)
4333 return ERROR_SUCCESS;
4335 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4336 msiobj_release(&view->hdr);
4338 return rc;
4341 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4343 MSIFILE *file;
4345 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4347 if (!lstrcmpW(file->File, filename))
4348 return file;
4351 return NULL;
4354 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4356 MSIPACKAGE *package = (MSIPACKAGE*)param;
4357 LPWSTR driver, driver_path, ptr;
4358 WCHAR outpath[MAX_PATH];
4359 MSIFILE *driver_file, *setup_file;
4360 LPCWSTR desc;
4361 DWORD len, usage;
4362 UINT r = ERROR_SUCCESS;
4364 static const WCHAR driver_fmt[] = {
4365 'D','r','i','v','e','r','=','%','s',0};
4366 static const WCHAR setup_fmt[] = {
4367 'S','e','t','u','p','=','%','s',0};
4368 static const WCHAR usage_fmt[] = {
4369 'F','i','l','e','U','s','a','g','e','=','1',0};
4371 desc = MSI_RecordGetString(rec, 3);
4373 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4374 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4376 if (!driver_file || !setup_file)
4378 ERR("ODBC Driver entry not found!\n");
4379 return ERROR_FUNCTION_FAILED;
4382 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4383 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4384 lstrlenW(usage_fmt) + 1;
4385 driver = msi_alloc(len * sizeof(WCHAR));
4386 if (!driver)
4387 return ERROR_OUTOFMEMORY;
4389 ptr = driver;
4390 lstrcpyW(ptr, desc);
4391 ptr += lstrlenW(ptr) + 1;
4393 sprintfW(ptr, driver_fmt, driver_file->FileName);
4394 ptr += lstrlenW(ptr) + 1;
4396 sprintfW(ptr, setup_fmt, setup_file->FileName);
4397 ptr += lstrlenW(ptr) + 1;
4399 lstrcpyW(ptr, usage_fmt);
4400 ptr += lstrlenW(ptr) + 1;
4401 *ptr = '\0';
4403 driver_path = strdupW(driver_file->TargetPath);
4404 ptr = strrchrW(driver_path, '\\');
4405 if (ptr) *ptr = '\0';
4407 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4408 NULL, ODBC_INSTALL_COMPLETE, &usage))
4410 ERR("Failed to install SQL driver!\n");
4411 r = ERROR_FUNCTION_FAILED;
4414 msi_free(driver);
4415 msi_free(driver_path);
4417 return r;
4420 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4422 UINT rc;
4423 MSIQUERY *view;
4425 static const WCHAR query[] = {
4426 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4427 'O','D','B','C','D','r','i','v','e','r',0 };
4429 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4430 if (rc != ERROR_SUCCESS)
4431 return ERROR_SUCCESS;
4433 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4434 msiobj_release(&view->hdr);
4436 return rc;
4439 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
4440 LPCSTR action, LPCWSTR table )
4442 static const WCHAR query[] = {
4443 'S','E','L','E','C','T',' ','*',' ',
4444 'F','R','O','M',' ','`','%','s','`',0 };
4445 MSIQUERY *view = NULL;
4446 DWORD count = 0;
4447 UINT r;
4449 r = MSI_OpenQuery( package->db, &view, query, table );
4450 if (r == ERROR_SUCCESS)
4452 r = MSI_IterateRecords(view, &count, NULL, package);
4453 msiobj_release(&view->hdr);
4456 if (count)
4457 FIXME("%s -> %u ignored %s table values\n",
4458 action, count, debugstr_w(table));
4460 return ERROR_SUCCESS;
4463 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
4465 TRACE("%p\n", package);
4466 return ERROR_SUCCESS;
4469 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4471 static const WCHAR table[] =
4472 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
4473 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
4476 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
4478 static const WCHAR table[] = { 'M','o','v','e','F','i','l','e',0 };
4479 return msi_unimplemented_action_stub( package, "MoveFiles", table );
4482 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
4484 static const WCHAR table[] = { 'P','a','t','c','h',0 };
4485 return msi_unimplemented_action_stub( package, "PatchFiles", table );
4488 static UINT ACTION_BindImage( MSIPACKAGE *package )
4490 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
4491 return msi_unimplemented_action_stub( package, "BindImage", table );
4494 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
4496 static const WCHAR table[] = {
4497 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
4498 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
4501 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
4503 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
4504 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
4507 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4509 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
4510 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
4513 static UINT ACTION_StopServices( MSIPACKAGE *package )
4515 static const WCHAR table[] = {
4516 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4517 return msi_unimplemented_action_stub( package, "StopServices", table );
4520 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
4522 static const WCHAR table[] = {
4523 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4524 return msi_unimplemented_action_stub( package, "DeleteServices", table );
4526 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
4528 static const WCHAR table[] = {
4529 'P','r','o','d','u','c','t','I','D',0 };
4530 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
4533 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
4535 static const WCHAR table[] = {
4536 'E','n','v','i','r','o','n','m','e','n','t',0 };
4537 return msi_unimplemented_action_stub( package, "WriteEnvironmentStrings", table );
4540 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
4542 static const WCHAR table[] = {
4543 'E','n','v','i','r','o','n','m','e','n','t',0 };
4544 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
4547 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
4549 static const WCHAR table[] = {
4550 'M','s','i','A','s','s','e','m','b','l','y',0 };
4551 return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
4554 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
4556 static const WCHAR table[] = {
4557 'M','s','i','A','s','s','e','m','b','l','y',0 };
4558 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
4561 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
4563 static const WCHAR table[] = { 'F','o','n','t',0 };
4564 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
4567 static UINT ACTION_CCPSearch( MSIPACKAGE *package )
4569 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
4570 return msi_unimplemented_action_stub( package, "CCPSearch", table );
4573 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
4575 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
4576 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
4579 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
4581 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
4582 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
4585 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
4587 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
4588 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
4591 static const struct _actions StandardActions[] = {
4592 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
4593 { szAppSearch, ACTION_AppSearch },
4594 { szBindImage, ACTION_BindImage },
4595 { szCCPSearch, ACTION_CCPSearch},
4596 { szCostFinalize, ACTION_CostFinalize },
4597 { szCostInitialize, ACTION_CostInitialize },
4598 { szCreateFolders, ACTION_CreateFolders },
4599 { szCreateShortcuts, ACTION_CreateShortcuts },
4600 { szDeleteServices, ACTION_DeleteServices },
4601 { szDisableRollback, NULL},
4602 { szDuplicateFiles, ACTION_DuplicateFiles },
4603 { szExecuteAction, ACTION_ExecuteAction },
4604 { szFileCost, ACTION_FileCost },
4605 { szFindRelatedProducts, ACTION_FindRelatedProducts },
4606 { szForceReboot, ACTION_ForceReboot },
4607 { szInstallAdminPackage, NULL},
4608 { szInstallExecute, ACTION_InstallExecute },
4609 { szInstallExecuteAgain, ACTION_InstallExecute },
4610 { szInstallFiles, ACTION_InstallFiles},
4611 { szInstallFinalize, ACTION_InstallFinalize },
4612 { szInstallInitialize, ACTION_InstallInitialize },
4613 { szInstallSFPCatalogFile, NULL},
4614 { szInstallValidate, ACTION_InstallValidate },
4615 { szIsolateComponents, ACTION_IsolateComponents },
4616 { szLaunchConditions, ACTION_LaunchConditions },
4617 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
4618 { szMoveFiles, ACTION_MoveFiles },
4619 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
4620 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
4621 { szInstallODBC, ACTION_InstallODBC },
4622 { szInstallServices, ACTION_InstallServices },
4623 { szPatchFiles, ACTION_PatchFiles },
4624 { szProcessComponents, ACTION_ProcessComponents },
4625 { szPublishComponents, ACTION_PublishComponents },
4626 { szPublishFeatures, ACTION_PublishFeatures },
4627 { szPublishProduct, ACTION_PublishProduct },
4628 { szRegisterClassInfo, ACTION_RegisterClassInfo },
4629 { szRegisterComPlus, ACTION_RegisterComPlus},
4630 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
4631 { szRegisterFonts, ACTION_RegisterFonts },
4632 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
4633 { szRegisterProduct, ACTION_RegisterProduct },
4634 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
4635 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
4636 { szRegisterUser, ACTION_RegisterUser},
4637 { szRemoveDuplicateFiles, NULL},
4638 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
4639 { szRemoveExistingProducts, NULL},
4640 { szRemoveFiles, ACTION_RemoveFiles},
4641 { szRemoveFolders, NULL},
4642 { szRemoveIniValues, ACTION_RemoveIniValues },
4643 { szRemoveODBC, NULL},
4644 { szRemoveRegistryValues, NULL},
4645 { szRemoveShortcuts, NULL},
4646 { szResolveSource, ACTION_ResolveSource},
4647 { szRMCCPSearch, ACTION_RMCCPSearch},
4648 { szScheduleReboot, NULL},
4649 { szSelfRegModules, ACTION_SelfRegModules },
4650 { szSelfUnregModules, ACTION_SelfUnregModules },
4651 { szSetODBCFolders, NULL},
4652 { szStartServices, ACTION_StartServices },
4653 { szStopServices, ACTION_StopServices },
4654 { szUnpublishComponents, NULL},
4655 { szUnpublishFeatures, NULL},
4656 { szUnregisterClassInfo, NULL},
4657 { szUnregisterComPlus, ACTION_UnregisterComPlus},
4658 { szUnregisterExtensionInfo, NULL},
4659 { szUnregisterFonts, ACTION_UnregisterFonts },
4660 { szUnregisterMIMEInfo, NULL},
4661 { szUnregisterProgIdInfo, NULL},
4662 { szUnregisterTypeLibraries, NULL},
4663 { szValidateProductID, ACTION_ValidateProductID},
4664 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
4665 { szWriteIniValues, ACTION_WriteIniValues },
4666 { szWriteRegistryValues, ACTION_WriteRegistryValues},
4667 { NULL, NULL},