msi: Fix the size allocated for the deferred custom action string.
[wine/gsoc_dplay.git] / dlls / msi / action.c
blob953c65b0716ed475f5cf8bf7a0eb38510a2b70f5
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 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 szProdCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
458 LPWSTR guid_list, *guids, product_code;
459 UINT i, ret = ERROR_FUNCTION_FAILED;
461 product_code = msi_dup_property( package, szProdCode );
462 if (!product_code)
464 /* FIXME: the property ProductCode should be written into the DB somewhere */
465 ERR("no product code 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_code ))
474 ret = ERROR_SUCCESS;
476 msi_free( guids );
477 msi_free( guid_list );
478 msi_free( product_code );
480 return ret;
483 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
485 MSISUMMARYINFO *si;
486 LPWSTR str, *substorage;
487 UINT i, r = ERROR_SUCCESS;
489 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
490 if (!si)
491 return ERROR_FUNCTION_FAILED;
493 msi_check_patch_applicable( package, si );
495 /* enumerate the substorage */
496 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
497 substorage = msi_split_string( str, ';' );
498 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
499 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
500 msi_free( substorage );
501 msi_free( str );
503 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
505 msiobj_release( &si->hdr );
507 return r;
510 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
512 MSIDATABASE *patch_db = NULL;
513 UINT r;
515 TRACE("%p %s\n", package, debugstr_w( file ) );
517 /* FIXME:
518 * We probably want to make sure we only open a patch collection here.
519 * Patch collections (.msp) and databases (.msi) have different GUIDs
520 * but currently MSI_OpenDatabaseW will accept both.
522 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
523 if ( r != ERROR_SUCCESS )
525 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
526 return r;
529 msi_parse_patch_summary( package, patch_db );
532 * There might be a CAB file in the patch package,
533 * so append it to the list of storage to search for streams.
535 append_storage_to_db( package->db, patch_db->storage );
537 msiobj_release( &patch_db->hdr );
539 return ERROR_SUCCESS;
542 /* get the PATCH property, and apply all the patches it specifies */
543 static UINT msi_apply_patches( MSIPACKAGE *package )
545 static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
546 LPWSTR patch_list, *patches;
547 UINT i, r = ERROR_SUCCESS;
549 patch_list = msi_dup_property( package, szPatch );
551 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
553 patches = msi_split_string( patch_list, ';' );
554 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
555 r = msi_apply_patch_package( package, patches[i] );
557 msi_free( patches );
558 msi_free( patch_list );
560 return r;
563 static UINT msi_apply_transforms( MSIPACKAGE *package )
565 static const WCHAR szTransforms[] = {
566 'T','R','A','N','S','F','O','R','M','S',0 };
567 LPWSTR xform_list, *xforms;
568 UINT i, r = ERROR_SUCCESS;
570 xform_list = msi_dup_property( package, szTransforms );
571 xforms = msi_split_string( xform_list, ';' );
573 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
575 if (xforms[i][0] == ':')
576 r = msi_apply_substorage_transform( package, package->db, &xforms[i][1] );
577 else
578 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
581 msi_free( xforms );
582 msi_free( xform_list );
584 return r;
587 BOOL ui_sequence_exists( MSIPACKAGE *package )
589 MSIQUERY *view;
590 UINT rc;
592 static const WCHAR ExecSeqQuery [] =
593 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
594 '`','I','n','s','t','a','l','l',
595 'U','I','S','e','q','u','e','n','c','e','`',
596 ' ','W','H','E','R','E',' ',
597 '`','S','e','q','u','e','n','c','e','`',' ',
598 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
599 '`','S','e','q','u','e','n','c','e','`',0};
601 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
602 if (rc == ERROR_SUCCESS)
604 msiobj_release(&view->hdr);
605 return TRUE;
608 return FALSE;
611 /****************************************************
612 * TOP level entry points
613 *****************************************************/
615 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
616 LPCWSTR szCommandLine )
618 UINT rc;
619 BOOL ui = FALSE, ui_exists;
620 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
621 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
622 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
624 MSI_SetPropertyW(package, szAction, szInstall);
626 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
628 package->script->InWhatSequence = SEQUENCE_INSTALL;
630 if (szPackagePath)
632 LPWSTR p, check, dir;
633 LPCWSTR file;
635 dir = strdupW(szPackagePath);
636 p = strrchrW(dir, '\\');
637 if (p)
639 *(++p) = 0;
640 file = szPackagePath + (p - dir);
642 else
644 msi_free(dir);
645 dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
646 GetCurrentDirectoryW(MAX_PATH, dir);
647 lstrcatW(dir, cszbs);
648 file = szPackagePath;
651 msi_free( package->PackagePath );
652 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
653 if (!package->PackagePath)
655 msi_free(dir);
656 return ERROR_OUTOFMEMORY;
659 lstrcpyW(package->PackagePath, dir);
660 lstrcatW(package->PackagePath, file);
662 check = msi_dup_property( package, cszSourceDir );
663 if (!check)
664 MSI_SetPropertyW(package, cszSourceDir, dir);
665 msi_free(check);
667 check = msi_dup_property( package, cszSOURCEDIR );
668 if (!check)
669 MSI_SetPropertyW(package, cszSOURCEDIR, dir);
671 msi_free(dir);
672 msi_free(check);
675 msi_parse_command_line( package, szCommandLine );
677 msi_apply_transforms( package );
678 msi_apply_patches( package );
680 /* properties may have been added by a transform */
681 msi_clone_properties( package );
683 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
685 package->script->InWhatSequence |= SEQUENCE_UI;
686 rc = ACTION_ProcessUISequence(package);
687 ui = TRUE;
688 ui_exists = ui_sequence_exists(package);
689 if (rc == ERROR_SUCCESS || !ui_exists)
691 package->script->InWhatSequence |= SEQUENCE_EXEC;
692 rc = ACTION_ProcessExecSequence(package,ui_exists);
695 else
696 rc = ACTION_ProcessExecSequence(package,FALSE);
698 if (rc == -1)
700 /* install was halted but should be considered a success */
701 rc = ERROR_SUCCESS;
704 package->script->CurrentlyScripting= FALSE;
706 /* process the ending type action */
707 if (rc == ERROR_SUCCESS)
708 ACTION_PerformActionSequence(package,-1,ui);
709 else if (rc == ERROR_INSTALL_USEREXIT)
710 ACTION_PerformActionSequence(package,-2,ui);
711 else if (rc == ERROR_INSTALL_SUSPEND)
712 ACTION_PerformActionSequence(package,-4,ui);
713 else /* failed */
714 ACTION_PerformActionSequence(package,-3,ui);
716 /* finish up running custom actions */
717 ACTION_FinishCustomActions(package);
719 return rc;
722 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
724 UINT rc = ERROR_SUCCESS;
725 MSIRECORD * row = 0;
726 static const WCHAR ExecSeqQuery[] =
727 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
728 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
729 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
730 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
732 static const WCHAR UISeqQuery[] =
733 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
734 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
735 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
736 ' ', '=',' ','%','i',0};
738 if (UI)
739 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
740 else
741 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
743 if (row)
745 LPCWSTR action, cond;
747 TRACE("Running the actions\n");
749 /* check conditions */
750 cond = MSI_RecordGetString(row,2);
752 /* this is a hack to skip errors in the condition code */
753 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
754 goto end;
756 action = MSI_RecordGetString(row,1);
757 if (!action)
759 ERR("failed to fetch action\n");
760 rc = ERROR_FUNCTION_FAILED;
761 goto end;
764 if (UI)
765 rc = ACTION_PerformUIAction(package,action,-1);
766 else
767 rc = ACTION_PerformAction(package,action,-1,FALSE);
768 end:
769 msiobj_release(&row->hdr);
771 else
772 rc = ERROR_SUCCESS;
774 return rc;
777 typedef struct {
778 MSIPACKAGE* package;
779 BOOL UI;
780 } iterate_action_param;
782 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
784 iterate_action_param *iap= (iterate_action_param*)param;
785 UINT rc;
786 LPCWSTR cond, action;
788 action = MSI_RecordGetString(row,1);
789 if (!action)
791 ERR("Error is retrieving action name\n");
792 return ERROR_FUNCTION_FAILED;
795 /* check conditions */
796 cond = MSI_RecordGetString(row,2);
798 /* this is a hack to skip errors in the condition code */
799 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
801 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
802 return ERROR_SUCCESS;
805 if (iap->UI)
806 rc = ACTION_PerformUIAction(iap->package,action,-1);
807 else
808 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
810 msi_dialog_check_messages( NULL );
812 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
813 rc = iap->package->CurrentInstallState;
815 if (rc == ERROR_FUNCTION_NOT_CALLED)
816 rc = ERROR_SUCCESS;
818 if (rc != ERROR_SUCCESS)
819 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
821 return rc;
824 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
826 MSIQUERY * view;
827 UINT r;
828 static const WCHAR query[] =
829 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
830 '`','%','s','`',
831 ' ','W','H','E','R','E',' ',
832 '`','S','e','q','u','e','n','c','e','`',' ',
833 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
834 '`','S','e','q','u','e','n','c','e','`',0};
835 iterate_action_param iap;
838 * FIXME: probably should be checking UILevel in the
839 * ACTION_PerformUIAction/ACTION_PerformAction
840 * rather than saving the UI level here. Those
841 * two functions can be merged too.
843 iap.package = package;
844 iap.UI = TRUE;
846 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
848 r = MSI_OpenQuery( package->db, &view, query, szTable );
849 if (r == ERROR_SUCCESS)
851 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
852 msiobj_release(&view->hdr);
855 return r;
858 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
860 MSIQUERY * view;
861 UINT rc;
862 static const WCHAR ExecSeqQuery[] =
863 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
864 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
865 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
866 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
867 'O','R','D','E','R',' ', 'B','Y',' ',
868 '`','S','e','q','u','e','n','c','e','`',0 };
869 MSIRECORD * row = 0;
870 static const WCHAR IVQuery[] =
871 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
872 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
873 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
874 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
875 ' ','\'', 'I','n','s','t','a','l','l',
876 'V','a','l','i','d','a','t','e','\'', 0};
877 INT seq = 0;
878 iterate_action_param iap;
880 iap.package = package;
881 iap.UI = FALSE;
883 if (package->script->ExecuteSequenceRun)
885 TRACE("Execute Sequence already Run\n");
886 return ERROR_SUCCESS;
889 package->script->ExecuteSequenceRun = TRUE;
891 /* get the sequence number */
892 if (UIran)
894 row = MSI_QueryGetRecord(package->db, IVQuery);
895 if( !row )
896 return ERROR_FUNCTION_FAILED;
897 seq = MSI_RecordGetInteger(row,1);
898 msiobj_release(&row->hdr);
901 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
902 if (rc == ERROR_SUCCESS)
904 TRACE("Running the actions\n");
906 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
907 msiobj_release(&view->hdr);
910 return rc;
913 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
915 MSIQUERY * view;
916 UINT rc;
917 static const WCHAR ExecSeqQuery [] =
918 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
919 '`','I','n','s','t','a','l','l',
920 'U','I','S','e','q','u','e','n','c','e','`',
921 ' ','W','H','E','R','E',' ',
922 '`','S','e','q','u','e','n','c','e','`',' ',
923 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
924 '`','S','e','q','u','e','n','c','e','`',0};
925 iterate_action_param iap;
927 iap.package = package;
928 iap.UI = TRUE;
930 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
932 if (rc == ERROR_SUCCESS)
934 TRACE("Running the actions\n");
936 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
937 msiobj_release(&view->hdr);
940 return rc;
943 /********************************************************
944 * ACTION helper functions and functions that perform the actions
945 *******************************************************/
946 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
947 UINT* rc, BOOL force )
949 BOOL ret = FALSE;
950 BOOL run = force;
951 int i;
953 if (!run && !package->script->CurrentlyScripting)
954 run = TRUE;
956 if (!run)
958 if (strcmpW(action,szInstallFinalize) == 0 ||
959 strcmpW(action,szInstallExecute) == 0 ||
960 strcmpW(action,szInstallExecuteAgain) == 0)
961 run = TRUE;
964 i = 0;
965 while (StandardActions[i].action != NULL)
967 if (strcmpW(StandardActions[i].action, action)==0)
969 if (!run)
971 ui_actioninfo(package, action, TRUE, 0);
972 *rc = schedule_action(package,INSTALL_SCRIPT,action);
973 ui_actioninfo(package, action, FALSE, *rc);
975 else
977 ui_actionstart(package, action);
978 if (StandardActions[i].handler)
980 *rc = StandardActions[i].handler(package);
982 else
984 FIXME("unhandled standard action %s\n",debugstr_w(action));
985 *rc = ERROR_SUCCESS;
988 ret = TRUE;
989 break;
991 i++;
993 return ret;
996 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
997 UINT* rc, UINT script, BOOL force )
999 BOOL ret=FALSE;
1000 UINT arc;
1002 arc = ACTION_CustomAction(package, action, script, force);
1004 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1006 *rc = arc;
1007 ret = TRUE;
1009 return ret;
1013 * A lot of actions are really important even if they don't do anything
1014 * explicit... Lots of properties are set at the beginning of the installation
1015 * CostFinalize does a bunch of work to translate the directories and such
1017 * But until I get write access to the database that is hard, so I am going to
1018 * hack it to see if I can get something to run.
1020 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1022 UINT rc = ERROR_SUCCESS;
1023 BOOL handled;
1025 TRACE("Performing action (%s)\n",debugstr_w(action));
1027 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1029 if (!handled)
1030 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1032 if (!handled)
1034 FIXME("unhandled msi action %s\n",debugstr_w(action));
1035 rc = ERROR_FUNCTION_NOT_CALLED;
1038 return rc;
1041 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1043 UINT rc = ERROR_SUCCESS;
1044 BOOL handled = FALSE;
1046 TRACE("Performing action (%s)\n",debugstr_w(action));
1048 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1050 if (!handled)
1051 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1053 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1054 handled = TRUE;
1056 if (!handled)
1058 FIXME("unhandled msi action %s\n",debugstr_w(action));
1059 rc = ERROR_FUNCTION_NOT_CALLED;
1062 return rc;
1067 * Actual Action Handlers
1070 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1072 MSIPACKAGE *package = (MSIPACKAGE*)param;
1073 LPCWSTR dir;
1074 LPWSTR full_path;
1075 MSIRECORD *uirow;
1076 MSIFOLDER *folder;
1078 dir = MSI_RecordGetString(row,1);
1079 if (!dir)
1081 ERR("Unable to get folder id\n");
1082 return ERROR_SUCCESS;
1085 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1086 if (!full_path)
1088 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1089 return ERROR_SUCCESS;
1092 TRACE("Folder is %s\n",debugstr_w(full_path));
1094 /* UI stuff */
1095 uirow = MSI_CreateRecord(1);
1096 MSI_RecordSetStringW(uirow,1,full_path);
1097 ui_actiondata(package,szCreateFolders,uirow);
1098 msiobj_release( &uirow->hdr );
1100 if (folder->State == 0)
1101 create_full_pathW(full_path);
1103 folder->State = 3;
1105 msi_free(full_path);
1106 return ERROR_SUCCESS;
1109 /* FIXME: probably should merge this with the above function */
1110 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1112 UINT rc = ERROR_SUCCESS;
1113 MSIFOLDER *folder;
1114 LPWSTR install_path;
1116 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1117 if (!install_path)
1118 return ERROR_FUNCTION_FAILED;
1120 /* create the path */
1121 if (folder->State == 0)
1123 create_full_pathW(install_path);
1124 folder->State = 2;
1126 msi_free(install_path);
1128 return rc;
1131 UINT msi_create_component_directories( MSIPACKAGE *package )
1133 MSICOMPONENT *comp;
1135 /* create all the folders required by the components are going to install */
1136 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1138 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1139 continue;
1140 msi_create_directory( package, comp->Directory );
1143 return ERROR_SUCCESS;
1147 * Also we cannot enable/disable components either, so for now I am just going
1148 * to do all the directories for all the components.
1150 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1152 static const WCHAR ExecSeqQuery[] =
1153 {'S','E','L','E','C','T',' ',
1154 '`','D','i','r','e','c','t','o','r','y','_','`',
1155 ' ','F','R','O','M',' ',
1156 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1157 UINT rc;
1158 MSIQUERY *view;
1160 /* create all the empty folders specified in the CreateFolder table */
1161 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1162 if (rc != ERROR_SUCCESS)
1163 return ERROR_SUCCESS;
1165 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1166 msiobj_release(&view->hdr);
1168 msi_create_component_directories( package );
1170 return rc;
1173 static UINT load_component( MSIRECORD *row, LPVOID param )
1175 MSIPACKAGE *package = param;
1176 MSICOMPONENT *comp;
1178 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1179 if (!comp)
1180 return ERROR_FUNCTION_FAILED;
1182 list_add_tail( &package->components, &comp->entry );
1184 /* fill in the data */
1185 comp->Component = msi_dup_record_field( row, 1 );
1187 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1189 comp->ComponentId = msi_dup_record_field( row, 2 );
1190 comp->Directory = msi_dup_record_field( row, 3 );
1191 comp->Attributes = MSI_RecordGetInteger(row,4);
1192 comp->Condition = msi_dup_record_field( row, 5 );
1193 comp->KeyPath = msi_dup_record_field( row, 6 );
1195 comp->Installed = INSTALLSTATE_UNKNOWN;
1196 msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1198 return ERROR_SUCCESS;
1201 static UINT load_all_components( MSIPACKAGE *package )
1203 static const WCHAR query[] = {
1204 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1205 '`','C','o','m','p','o','n','e','n','t','`',0 };
1206 MSIQUERY *view;
1207 UINT r;
1209 if (!list_empty(&package->components))
1210 return ERROR_SUCCESS;
1212 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1213 if (r != ERROR_SUCCESS)
1214 return r;
1216 r = MSI_IterateRecords(view, NULL, load_component, package);
1217 msiobj_release(&view->hdr);
1218 return r;
1221 typedef struct {
1222 MSIPACKAGE *package;
1223 MSIFEATURE *feature;
1224 } _ilfs;
1226 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1228 ComponentList *cl;
1230 cl = msi_alloc( sizeof (*cl) );
1231 if ( !cl )
1232 return ERROR_NOT_ENOUGH_MEMORY;
1233 cl->component = comp;
1234 list_add_tail( &feature->Components, &cl->entry );
1236 return ERROR_SUCCESS;
1239 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1241 FeatureList *fl;
1243 fl = msi_alloc( sizeof(*fl) );
1244 if ( !fl )
1245 return ERROR_NOT_ENOUGH_MEMORY;
1246 fl->feature = child;
1247 list_add_tail( &parent->Children, &fl->entry );
1249 return ERROR_SUCCESS;
1252 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1254 _ilfs* ilfs= (_ilfs*)param;
1255 LPCWSTR component;
1256 MSICOMPONENT *comp;
1258 component = MSI_RecordGetString(row,1);
1260 /* check to see if the component is already loaded */
1261 comp = get_loaded_component( ilfs->package, component );
1262 if (!comp)
1264 ERR("unknown component %s\n", debugstr_w(component));
1265 return ERROR_FUNCTION_FAILED;
1268 add_feature_component( ilfs->feature, comp );
1269 comp->Enabled = TRUE;
1271 return ERROR_SUCCESS;
1274 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1276 MSIFEATURE *feature;
1278 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1280 if ( !lstrcmpW( feature->Feature, name ) )
1281 return feature;
1284 return NULL;
1287 static UINT load_feature(MSIRECORD * row, LPVOID param)
1289 MSIPACKAGE* package = (MSIPACKAGE*)param;
1290 MSIFEATURE* feature;
1291 static const WCHAR Query1[] =
1292 {'S','E','L','E','C','T',' ',
1293 '`','C','o','m','p','o','n','e','n','t','_','`',
1294 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1295 'C','o','m','p','o','n','e','n','t','s','`',' ',
1296 'W','H','E','R','E',' ',
1297 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1298 MSIQUERY * view;
1299 UINT rc;
1300 _ilfs ilfs;
1302 /* fill in the data */
1304 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1305 if (!feature)
1306 return ERROR_NOT_ENOUGH_MEMORY;
1308 list_init( &feature->Children );
1309 list_init( &feature->Components );
1311 feature->Feature = msi_dup_record_field( row, 1 );
1313 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1315 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1316 feature->Title = msi_dup_record_field( row, 3 );
1317 feature->Description = msi_dup_record_field( row, 4 );
1319 if (!MSI_RecordIsNull(row,5))
1320 feature->Display = MSI_RecordGetInteger(row,5);
1322 feature->Level= MSI_RecordGetInteger(row,6);
1323 feature->Directory = msi_dup_record_field( row, 7 );
1324 feature->Attributes = MSI_RecordGetInteger(row,8);
1326 feature->Installed = INSTALLSTATE_UNKNOWN;
1327 msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1329 list_add_tail( &package->features, &feature->entry );
1331 /* load feature components */
1333 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1334 if (rc != ERROR_SUCCESS)
1335 return ERROR_SUCCESS;
1337 ilfs.package = package;
1338 ilfs.feature = feature;
1340 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1341 msiobj_release(&view->hdr);
1343 return ERROR_SUCCESS;
1346 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1348 MSIPACKAGE* package = (MSIPACKAGE*)param;
1349 MSIFEATURE *parent, *child;
1351 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1352 if (!child)
1353 return ERROR_FUNCTION_FAILED;
1355 if (!child->Feature_Parent)
1356 return ERROR_SUCCESS;
1358 parent = find_feature_by_name( package, child->Feature_Parent );
1359 if (!parent)
1360 return ERROR_FUNCTION_FAILED;
1362 add_feature_child( parent, child );
1363 return ERROR_SUCCESS;
1366 static UINT load_all_features( MSIPACKAGE *package )
1368 static const WCHAR query[] = {
1369 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1370 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1371 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1372 MSIQUERY *view;
1373 UINT r;
1375 if (!list_empty(&package->features))
1376 return ERROR_SUCCESS;
1378 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1379 if (r != ERROR_SUCCESS)
1380 return r;
1382 r = MSI_IterateRecords( view, NULL, load_feature, package );
1383 if (r != ERROR_SUCCESS)
1384 return r;
1386 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1387 msiobj_release( &view->hdr );
1389 return r;
1392 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1394 if (!p)
1395 return p;
1396 p = strchrW(p, ch);
1397 if (!p)
1398 return p;
1399 *p = 0;
1400 return p+1;
1403 static UINT load_file(MSIRECORD *row, LPVOID param)
1405 MSIPACKAGE* package = (MSIPACKAGE*)param;
1406 LPCWSTR component;
1407 MSIFILE *file;
1409 /* fill in the data */
1411 file = msi_alloc_zero( sizeof (MSIFILE) );
1412 if (!file)
1413 return ERROR_NOT_ENOUGH_MEMORY;
1415 file->File = msi_dup_record_field( row, 1 );
1417 component = MSI_RecordGetString( row, 2 );
1418 file->Component = get_loaded_component( package, component );
1420 if (!file->Component)
1421 ERR("Unfound Component %s\n",debugstr_w(component));
1423 file->FileName = msi_dup_record_field( row, 3 );
1424 reduce_to_longfilename( file->FileName );
1426 file->ShortName = msi_dup_record_field( row, 3 );
1427 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1429 file->FileSize = MSI_RecordGetInteger( row, 4 );
1430 file->Version = msi_dup_record_field( row, 5 );
1431 file->Language = msi_dup_record_field( row, 6 );
1432 file->Attributes = MSI_RecordGetInteger( row, 7 );
1433 file->Sequence = MSI_RecordGetInteger( row, 8 );
1435 file->state = msifs_invalid;
1437 /* if the compressed bits are not set in the file attributes,
1438 * then read the information from the package word count property
1440 if (file->Attributes & msidbFileAttributesCompressed)
1442 file->IsCompressed = TRUE;
1444 else if (file->Attributes & msidbFileAttributesNoncompressed)
1446 file->IsCompressed = FALSE;
1448 else
1450 file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1453 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1455 list_add_tail( &package->files, &file->entry );
1457 return ERROR_SUCCESS;
1460 static UINT load_all_files(MSIPACKAGE *package)
1462 MSIQUERY * view;
1463 UINT rc;
1464 static const WCHAR Query[] =
1465 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1466 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1467 '`','S','e','q','u','e','n','c','e','`', 0};
1469 if (!list_empty(&package->files))
1470 return ERROR_SUCCESS;
1472 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1473 if (rc != ERROR_SUCCESS)
1474 return ERROR_SUCCESS;
1476 rc = MSI_IterateRecords(view, NULL, load_file, package);
1477 msiobj_release(&view->hdr);
1479 return ERROR_SUCCESS;
1482 static UINT load_folder( MSIRECORD *row, LPVOID param )
1484 MSIPACKAGE *package = param;
1485 static const WCHAR szDot[] = { '.',0 };
1486 static WCHAR szEmpty[] = { 0 };
1487 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1488 MSIFOLDER *folder;
1490 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1491 if (!folder)
1492 return ERROR_NOT_ENOUGH_MEMORY;
1494 folder->Directory = msi_dup_record_field( row, 1 );
1496 TRACE("%s\n", debugstr_w(folder->Directory));
1498 p = msi_dup_record_field(row, 3);
1500 /* split src and target dir */
1501 tgt_short = p;
1502 src_short = folder_split_path( p, ':' );
1504 /* split the long and short paths */
1505 tgt_long = folder_split_path( tgt_short, '|' );
1506 src_long = folder_split_path( src_short, '|' );
1508 /* check for no-op dirs */
1509 if (!lstrcmpW(szDot, tgt_short))
1510 tgt_short = szEmpty;
1511 if (!lstrcmpW(szDot, src_short))
1512 src_short = szEmpty;
1514 if (!tgt_long)
1515 tgt_long = tgt_short;
1517 if (!src_short) {
1518 src_short = tgt_short;
1519 src_long = tgt_long;
1522 if (!src_long)
1523 src_long = src_short;
1525 /* FIXME: use the target short path too */
1526 folder->TargetDefault = strdupW(tgt_long);
1527 folder->SourceShortPath = strdupW(src_short);
1528 folder->SourceLongPath = strdupW(src_long);
1529 msi_free(p);
1531 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1532 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1533 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1535 folder->Parent = msi_dup_record_field( row, 2 );
1537 folder->Property = msi_dup_property( package, folder->Directory );
1539 list_add_tail( &package->folders, &folder->entry );
1541 TRACE("returning %p\n", folder);
1543 return ERROR_SUCCESS;
1546 static UINT load_all_folders( MSIPACKAGE *package )
1548 static const WCHAR query[] = {
1549 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1550 '`','D','i','r','e','c','t','o','r','y','`',0 };
1551 MSIQUERY *view;
1552 UINT r;
1554 if (!list_empty(&package->folders))
1555 return ERROR_SUCCESS;
1557 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1558 if (r != ERROR_SUCCESS)
1559 return r;
1561 r = MSI_IterateRecords(view, NULL, load_folder, package);
1562 msiobj_release(&view->hdr);
1563 return r;
1567 * I am not doing any of the costing functionality yet.
1568 * Mostly looking at doing the Component and Feature loading
1570 * The native MSI does A LOT of modification to tables here. Mostly adding
1571 * a lot of temporary columns to the Feature and Component tables.
1573 * note: Native msi also tracks the short filename. But I am only going to
1574 * track the long ones. Also looking at this directory table
1575 * it appears that the directory table does not get the parents
1576 * resolved base on property only based on their entries in the
1577 * directory table.
1579 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1581 static const WCHAR szCosting[] =
1582 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1583 static const WCHAR szZero[] = { '0', 0 };
1585 MSI_SetPropertyW(package, szCosting, szZero);
1586 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1588 load_all_components( package );
1589 load_all_features( package );
1590 load_all_files( package );
1591 load_all_folders( package );
1593 return ERROR_SUCCESS;
1596 static UINT execute_script(MSIPACKAGE *package, UINT script )
1598 int i;
1599 UINT rc = ERROR_SUCCESS;
1601 TRACE("Executing Script %i\n",script);
1603 if (!package->script)
1605 ERR("no script!\n");
1606 return ERROR_FUNCTION_FAILED;
1609 for (i = 0; i < package->script->ActionCount[script]; i++)
1611 LPWSTR action;
1612 action = package->script->Actions[script][i];
1613 ui_actionstart(package, action);
1614 TRACE("Executing Action (%s)\n",debugstr_w(action));
1615 rc = ACTION_PerformAction(package, action, script, TRUE);
1616 if (rc != ERROR_SUCCESS)
1617 break;
1619 msi_free_action_script(package, script);
1620 return rc;
1623 static UINT ACTION_FileCost(MSIPACKAGE *package)
1625 return ERROR_SUCCESS;
1628 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1630 MSICOMPONENT *comp;
1632 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1634 INSTALLSTATE res;
1636 if (!comp->ComponentId)
1637 continue;
1639 res = MsiGetComponentPathW( package->ProductCode,
1640 comp->ComponentId, NULL, NULL);
1641 if (res < 0)
1642 res = INSTALLSTATE_ABSENT;
1643 comp->Installed = res;
1647 /* scan for and update current install states */
1648 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1650 MSICOMPONENT *comp;
1651 MSIFEATURE *feature;
1653 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1655 ComponentList *cl;
1656 INSTALLSTATE res = INSTALLSTATE_ABSENT;
1658 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1660 comp= cl->component;
1662 if (!comp->ComponentId)
1664 res = INSTALLSTATE_ABSENT;
1665 break;
1668 if (res == INSTALLSTATE_ABSENT)
1669 res = comp->Installed;
1670 else
1672 if (res == comp->Installed)
1673 continue;
1675 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1676 res != INSTALLSTATE_SOURCE)
1678 res = INSTALLSTATE_INCOMPLETE;
1682 feature->Installed = res;
1686 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
1687 INSTALLSTATE state)
1689 static const WCHAR all[]={'A','L','L',0};
1690 LPWSTR override;
1691 MSIFEATURE *feature;
1693 override = msi_dup_property( package, property );
1694 if (!override)
1695 return FALSE;
1697 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1699 if (strcmpiW(override,all)==0)
1700 msi_feature_set_state( feature, state );
1701 else
1703 LPWSTR ptr = override;
1704 LPWSTR ptr2 = strchrW(override,',');
1706 while (ptr)
1708 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1709 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1711 msi_feature_set_state( feature, state );
1712 break;
1714 if (ptr2)
1716 ptr=ptr2+1;
1717 ptr2 = strchrW(ptr,',');
1719 else
1720 break;
1724 msi_free(override);
1726 return TRUE;
1729 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1731 int install_level;
1732 static const WCHAR szlevel[] =
1733 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1734 static const WCHAR szAddLocal[] =
1735 {'A','D','D','L','O','C','A','L',0};
1736 static const WCHAR szRemove[] =
1737 {'R','E','M','O','V','E',0};
1738 static const WCHAR szReinstall[] =
1739 {'R','E','I','N','S','T','A','L','L',0};
1740 BOOL override = FALSE;
1741 MSICOMPONENT* component;
1742 MSIFEATURE *feature;
1745 /* I do not know if this is where it should happen.. but */
1747 TRACE("Checking Install Level\n");
1749 install_level = msi_get_property_int( package, szlevel, 1 );
1751 /* ok here is the _real_ rub
1752 * all these activation/deactivation things happen in order and things
1753 * later on the list override things earlier on the list.
1754 * 1) INSTALLLEVEL processing
1755 * 2) ADDLOCAL
1756 * 3) REMOVE
1757 * 4) ADDSOURCE
1758 * 5) ADDDEFAULT
1759 * 6) REINSTALL
1760 * 7) COMPADDLOCAL
1761 * 8) COMPADDSOURCE
1762 * 9) FILEADDLOCAL
1763 * 10) FILEADDSOURCE
1764 * 11) FILEADDDEFAULT
1765 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1766 * ignored for all the features. seems strange, especially since it is not
1767 * documented anywhere, but it is how it works.
1769 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1770 * REMOVE are the big ones, since we don't handle administrative installs
1771 * yet anyway.
1773 override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1774 override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1775 override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1777 if (!override)
1779 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1781 BOOL feature_state = ((feature->Level > 0) &&
1782 (feature->Level <= install_level));
1784 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1786 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1787 msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1788 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1789 msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1790 else
1791 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1795 /* disable child features of unselected parent features */
1796 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1798 FeatureList *fl;
1800 if (feature->Level > 0 && feature->Level <= install_level)
1801 continue;
1803 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1804 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1807 else
1809 /* set the Preselected Property */
1810 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1811 static const WCHAR szOne[] = { '1', 0 };
1813 MSI_SetPropertyW(package,szPreselected,szOne);
1817 * now we want to enable or disable components base on feature
1820 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1822 ComponentList *cl;
1824 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1825 debugstr_w(feature->Feature), feature->Installed, feature->Action);
1827 /* features with components that have compressed files are made local */
1828 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1830 if (cl->component->Enabled &&
1831 cl->component->ForceLocalState &&
1832 feature->Action == INSTALLSTATE_SOURCE)
1834 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1835 break;
1839 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1841 component = cl->component;
1843 if (!component->Enabled)
1844 continue;
1846 switch (feature->Action)
1848 case INSTALLSTATE_ADVERTISED:
1849 component->hasAdvertiseFeature = 1;
1850 break;
1851 case INSTALLSTATE_SOURCE:
1852 component->hasSourceFeature = 1;
1853 break;
1854 case INSTALLSTATE_LOCAL:
1855 component->hasLocalFeature = 1;
1856 break;
1857 case INSTALLSTATE_DEFAULT:
1858 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1859 component->hasAdvertiseFeature = 1;
1860 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1861 component->hasSourceFeature = 1;
1862 else
1863 component->hasLocalFeature = 1;
1864 break;
1865 default:
1866 break;
1871 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1873 /* if the component isn't enabled, leave it alone */
1874 if (!component->Enabled)
1875 continue;
1877 /* check if it's local or source */
1878 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1879 (component->hasLocalFeature || component->hasSourceFeature))
1881 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1882 !component->ForceLocalState)
1883 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1884 else
1885 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1886 continue;
1889 /* if any feature is local, the component must be local too */
1890 if (component->hasLocalFeature)
1892 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1893 continue;
1896 if (component->hasSourceFeature)
1898 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1899 continue;
1902 if (component->hasAdvertiseFeature)
1904 msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1905 continue;
1908 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1911 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1913 if (component->Action == INSTALLSTATE_DEFAULT)
1915 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1916 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1919 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1920 debugstr_w(component->Component), component->Installed, component->Action);
1924 return ERROR_SUCCESS;
1927 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1929 MSIPACKAGE *package = (MSIPACKAGE*)param;
1930 LPCWSTR name;
1931 LPWSTR path;
1932 MSIFOLDER *f;
1934 name = MSI_RecordGetString(row,1);
1936 f = get_loaded_folder(package, name);
1937 if (!f) return ERROR_SUCCESS;
1939 /* reset the ResolvedTarget */
1940 msi_free(f->ResolvedTarget);
1941 f->ResolvedTarget = NULL;
1943 /* This helper function now does ALL the work */
1944 TRACE("Dir %s ...\n",debugstr_w(name));
1945 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1946 TRACE("resolves to %s\n",debugstr_w(path));
1947 msi_free(path);
1949 return ERROR_SUCCESS;
1952 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1954 MSIPACKAGE *package = (MSIPACKAGE*)param;
1955 LPCWSTR name;
1956 MSIFEATURE *feature;
1958 name = MSI_RecordGetString( row, 1 );
1960 feature = get_loaded_feature( package, name );
1961 if (!feature)
1962 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1963 else
1965 LPCWSTR Condition;
1966 Condition = MSI_RecordGetString(row,3);
1968 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1970 int level = MSI_RecordGetInteger(row,2);
1971 TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
1972 feature->Level = level;
1975 return ERROR_SUCCESS;
1978 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1980 static const WCHAR name_fmt[] =
1981 {'%','u','.','%','u','.','%','u','.','%','u',0};
1982 static WCHAR name[] = {'\\',0};
1983 VS_FIXEDFILEINFO *lpVer;
1984 WCHAR filever[0x100];
1985 LPVOID version;
1986 DWORD versize;
1987 DWORD handle;
1988 UINT sz;
1990 TRACE("%s\n", debugstr_w(filename));
1992 versize = GetFileVersionInfoSizeW( filename, &handle );
1993 if (!versize)
1994 return NULL;
1996 version = msi_alloc( versize );
1997 GetFileVersionInfoW( filename, 0, versize, version );
1999 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2001 msi_free( version );
2002 return NULL;
2005 sprintfW( filever, name_fmt,
2006 HIWORD(lpVer->dwFileVersionMS),
2007 LOWORD(lpVer->dwFileVersionMS),
2008 HIWORD(lpVer->dwFileVersionLS),
2009 LOWORD(lpVer->dwFileVersionLS));
2011 msi_free( version );
2013 return strdupW( filever );
2016 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2018 LPWSTR file_version;
2019 MSIFILE *file;
2021 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2023 MSICOMPONENT* comp = file->Component;
2024 LPWSTR p;
2026 if (!comp)
2027 continue;
2029 if (file->IsCompressed)
2030 comp->ForceLocalState = TRUE;
2032 /* calculate target */
2033 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2035 msi_free(file->TargetPath);
2037 TRACE("file %s is named %s\n",
2038 debugstr_w(file->File), debugstr_w(file->FileName));
2040 file->TargetPath = build_directory_name(2, p, file->FileName);
2042 msi_free(p);
2044 TRACE("file %s resolves to %s\n",
2045 debugstr_w(file->File), debugstr_w(file->TargetPath));
2047 /* don't check files of components that aren't installed */
2048 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2049 comp->Installed == INSTALLSTATE_ABSENT)
2051 file->state = msifs_missing; /* assume files are missing */
2052 continue;
2055 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2057 file->state = msifs_missing;
2058 comp->Cost += file->FileSize;
2059 comp->Installed = INSTALLSTATE_INCOMPLETE;
2060 continue;
2063 if (file->Version &&
2064 (file_version = msi_get_disk_file_version( file->TargetPath )))
2066 TRACE("new %s old %s\n", debugstr_w(file->Version),
2067 debugstr_w(file_version));
2068 /* FIXME: seems like a bad way to compare version numbers */
2069 if (lstrcmpiW(file_version, file->Version)<0)
2071 file->state = msifs_overwrite;
2072 comp->Cost += file->FileSize;
2073 comp->Installed = INSTALLSTATE_INCOMPLETE;
2075 else
2076 file->state = msifs_present;
2077 msi_free( file_version );
2079 else
2080 file->state = msifs_present;
2083 return ERROR_SUCCESS;
2087 * A lot is done in this function aside from just the costing.
2088 * The costing needs to be implemented at some point but for now I am going
2089 * to focus on the directory building
2092 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2094 static const WCHAR ExecSeqQuery[] =
2095 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2096 '`','D','i','r','e','c','t','o','r','y','`',0};
2097 static const WCHAR ConditionQuery[] =
2098 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2099 '`','C','o','n','d','i','t','i','o','n','`',0};
2100 static const WCHAR szCosting[] =
2101 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2102 static const WCHAR szlevel[] =
2103 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2104 static const WCHAR szOne[] = { '1', 0 };
2105 MSICOMPONENT *comp;
2106 UINT rc;
2107 MSIQUERY * view;
2108 LPWSTR level;
2110 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2111 return ERROR_SUCCESS;
2113 TRACE("Building Directory properties\n");
2115 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2116 if (rc == ERROR_SUCCESS)
2118 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2119 package);
2120 msiobj_release(&view->hdr);
2123 /* read components states from the registry */
2124 ACTION_GetComponentInstallStates(package);
2126 TRACE("File calculations\n");
2127 msi_check_file_install_states( package );
2129 TRACE("Evaluating Condition Table\n");
2131 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2132 if (rc == ERROR_SUCCESS)
2134 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2135 package);
2136 msiobj_release(&view->hdr);
2139 TRACE("Enabling or Disabling Components\n");
2140 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2142 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2144 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2145 comp->Enabled = FALSE;
2149 MSI_SetPropertyW(package,szCosting,szOne);
2150 /* set default run level if not set */
2151 level = msi_dup_property( package, szlevel );
2152 if (!level)
2153 MSI_SetPropertyW(package,szlevel, szOne);
2154 msi_free(level);
2156 ACTION_UpdateFeatureInstallStates(package);
2158 return MSI_SetFeatureStates(package);
2161 /* OK this value is "interpreted" and then formatted based on the
2162 first few characters */
2163 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2164 DWORD *size)
2166 LPSTR data = NULL;
2167 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2169 if (value[1]=='x')
2171 LPWSTR ptr;
2172 CHAR byte[5];
2173 LPWSTR deformated = NULL;
2174 int count;
2176 deformat_string(package, &value[2], &deformated);
2178 /* binary value type */
2179 ptr = deformated;
2180 *type = REG_BINARY;
2181 if (strlenW(ptr)%2)
2182 *size = (strlenW(ptr)/2)+1;
2183 else
2184 *size = strlenW(ptr)/2;
2186 data = msi_alloc(*size);
2188 byte[0] = '0';
2189 byte[1] = 'x';
2190 byte[4] = 0;
2191 count = 0;
2192 /* if uneven pad with a zero in front */
2193 if (strlenW(ptr)%2)
2195 byte[2]= '0';
2196 byte[3]= *ptr;
2197 ptr++;
2198 data[count] = (BYTE)strtol(byte,NULL,0);
2199 count ++;
2200 TRACE("Uneven byte count\n");
2202 while (*ptr)
2204 byte[2]= *ptr;
2205 ptr++;
2206 byte[3]= *ptr;
2207 ptr++;
2208 data[count] = (BYTE)strtol(byte,NULL,0);
2209 count ++;
2211 msi_free(deformated);
2213 TRACE("Data %i bytes(%i)\n",*size,count);
2215 else
2217 LPWSTR deformated;
2218 LPWSTR p;
2219 DWORD d = 0;
2220 deformat_string(package, &value[1], &deformated);
2222 *type=REG_DWORD;
2223 *size = sizeof(DWORD);
2224 data = msi_alloc(*size);
2225 p = deformated;
2226 if (*p == '-')
2227 p++;
2228 while (*p)
2230 if ( (*p < '0') || (*p > '9') )
2231 break;
2232 d *= 10;
2233 d += (*p - '0');
2234 p++;
2236 if (deformated[0] == '-')
2237 d = -d;
2238 *(LPDWORD)data = d;
2239 TRACE("DWORD %i\n",*(LPDWORD)data);
2241 msi_free(deformated);
2244 else
2246 static const WCHAR szMulti[] = {'[','~',']',0};
2247 LPCWSTR ptr;
2248 *type=REG_SZ;
2250 if (value[0]=='#')
2252 if (value[1]=='%')
2254 ptr = &value[2];
2255 *type=REG_EXPAND_SZ;
2257 else
2258 ptr = &value[1];
2260 else
2261 ptr=value;
2263 if (strstrW(value,szMulti))
2264 *type = REG_MULTI_SZ;
2266 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2268 return data;
2271 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2273 MSIPACKAGE *package = (MSIPACKAGE*)param;
2274 static const WCHAR szHCR[] =
2275 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2276 'R','O','O','T','\\',0};
2277 static const WCHAR szHCU[] =
2278 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2279 'U','S','E','R','\\',0};
2280 static const WCHAR szHLM[] =
2281 {'H','K','E','Y','_','L','O','C','A','L','_',
2282 'M','A','C','H','I','N','E','\\',0};
2283 static const WCHAR szHU[] =
2284 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2286 LPSTR value_data = NULL;
2287 HKEY root_key, hkey;
2288 DWORD type,size;
2289 LPWSTR deformated;
2290 LPCWSTR szRoot, component, name, key, value;
2291 MSICOMPONENT *comp;
2292 MSIRECORD * uirow;
2293 LPWSTR uikey;
2294 INT root;
2295 BOOL check_first = FALSE;
2296 UINT rc;
2298 ui_progress(package,2,0,0,0);
2300 value = NULL;
2301 key = NULL;
2302 uikey = NULL;
2303 name = NULL;
2305 component = MSI_RecordGetString(row, 6);
2306 comp = get_loaded_component(package,component);
2307 if (!comp)
2308 return ERROR_SUCCESS;
2310 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2312 TRACE("Skipping write due to disabled component %s\n",
2313 debugstr_w(component));
2315 comp->Action = comp->Installed;
2317 return ERROR_SUCCESS;
2320 comp->Action = INSTALLSTATE_LOCAL;
2322 name = MSI_RecordGetString(row, 4);
2323 if( MSI_RecordIsNull(row,5) && name )
2325 /* null values can have special meanings */
2326 if (name[0]=='-' && name[1] == 0)
2327 return ERROR_SUCCESS;
2328 else if ((name[0]=='+' && name[1] == 0) ||
2329 (name[0] == '*' && name[1] == 0))
2330 name = NULL;
2331 check_first = TRUE;
2334 root = MSI_RecordGetInteger(row,2);
2335 key = MSI_RecordGetString(row, 3);
2337 /* get the root key */
2338 switch (root)
2340 case -1:
2342 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2343 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2344 if (all_users && all_users[0] == '1')
2346 root_key = HKEY_LOCAL_MACHINE;
2347 szRoot = szHLM;
2349 else
2351 root_key = HKEY_CURRENT_USER;
2352 szRoot = szHCU;
2354 msi_free(all_users);
2356 break;
2357 case 0: root_key = HKEY_CLASSES_ROOT;
2358 szRoot = szHCR;
2359 break;
2360 case 1: root_key = HKEY_CURRENT_USER;
2361 szRoot = szHCU;
2362 break;
2363 case 2: root_key = HKEY_LOCAL_MACHINE;
2364 szRoot = szHLM;
2365 break;
2366 case 3: root_key = HKEY_USERS;
2367 szRoot = szHU;
2368 break;
2369 default:
2370 ERR("Unknown root %i\n",root);
2371 root_key=NULL;
2372 szRoot = NULL;
2373 break;
2375 if (!root_key)
2376 return ERROR_SUCCESS;
2378 deformat_string(package, key , &deformated);
2379 size = strlenW(deformated) + strlenW(szRoot) + 1;
2380 uikey = msi_alloc(size*sizeof(WCHAR));
2381 strcpyW(uikey,szRoot);
2382 strcatW(uikey,deformated);
2384 if (RegCreateKeyW( root_key, deformated, &hkey))
2386 ERR("Could not create key %s\n",debugstr_w(deformated));
2387 msi_free(deformated);
2388 msi_free(uikey);
2389 return ERROR_SUCCESS;
2391 msi_free(deformated);
2393 value = MSI_RecordGetString(row,5);
2394 if (value)
2395 value_data = parse_value(package, value, &type, &size);
2396 else
2398 static const WCHAR szEmpty[] = {0};
2399 value_data = (LPSTR)strdupW(szEmpty);
2400 size = 0;
2401 type = REG_SZ;
2404 deformat_string(package, name, &deformated);
2406 /* get the double nulls to terminate SZ_MULTI */
2407 if (type == REG_MULTI_SZ)
2408 size +=sizeof(WCHAR);
2410 if (!check_first)
2412 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2413 debugstr_w(uikey));
2414 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2416 else
2418 DWORD sz = 0;
2419 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2420 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2422 TRACE("value %s of %s checked already exists\n",
2423 debugstr_w(deformated), debugstr_w(uikey));
2425 else
2427 TRACE("Checked and setting value %s of %s\n",
2428 debugstr_w(deformated), debugstr_w(uikey));
2429 if (deformated || size)
2430 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2433 RegCloseKey(hkey);
2435 uirow = MSI_CreateRecord(3);
2436 MSI_RecordSetStringW(uirow,2,deformated);
2437 MSI_RecordSetStringW(uirow,1,uikey);
2439 if (type == REG_SZ)
2440 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2441 else
2442 MSI_RecordSetStringW(uirow,3,value);
2444 ui_actiondata(package,szWriteRegistryValues,uirow);
2445 msiobj_release( &uirow->hdr );
2447 msi_free(value_data);
2448 msi_free(deformated);
2449 msi_free(uikey);
2451 return ERROR_SUCCESS;
2454 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2456 UINT rc;
2457 MSIQUERY * view;
2458 static const WCHAR ExecSeqQuery[] =
2459 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2460 '`','R','e','g','i','s','t','r','y','`',0 };
2462 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2463 if (rc != ERROR_SUCCESS)
2464 return ERROR_SUCCESS;
2466 /* increment progress bar each time action data is sent */
2467 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2469 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2471 msiobj_release(&view->hdr);
2472 return rc;
2475 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2477 package->script->CurrentlyScripting = TRUE;
2479 return ERROR_SUCCESS;
2483 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2485 MSICOMPONENT *comp;
2486 DWORD progress = 0;
2487 DWORD total = 0;
2488 static const WCHAR q1[]=
2489 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2490 '`','R','e','g','i','s','t','r','y','`',0};
2491 UINT rc;
2492 MSIQUERY * view;
2493 MSIFEATURE *feature;
2494 MSIFILE *file;
2496 TRACE("InstallValidate\n");
2498 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2499 if (rc == ERROR_SUCCESS)
2501 MSI_IterateRecords( view, &progress, NULL, package );
2502 msiobj_release( &view->hdr );
2503 total += progress * REG_PROGRESS_VALUE;
2506 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2507 total += COMPONENT_PROGRESS_VALUE;
2509 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2510 total += file->FileSize;
2512 ui_progress(package,0,total,0,0);
2514 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2516 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2517 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2518 feature->ActionRequest);
2521 return ERROR_SUCCESS;
2524 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2526 MSIPACKAGE* package = (MSIPACKAGE*)param;
2527 LPCWSTR cond = NULL;
2528 LPCWSTR message = NULL;
2529 UINT r;
2531 static const WCHAR title[]=
2532 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2534 cond = MSI_RecordGetString(row,1);
2536 r = MSI_EvaluateConditionW(package,cond);
2537 if (r == MSICONDITION_FALSE)
2539 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2541 LPWSTR deformated;
2542 message = MSI_RecordGetString(row,2);
2543 deformat_string(package,message,&deformated);
2544 MessageBoxW(NULL,deformated,title,MB_OK);
2545 msi_free(deformated);
2548 return ERROR_INSTALL_FAILURE;
2551 return ERROR_SUCCESS;
2554 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2556 UINT rc;
2557 MSIQUERY * view = NULL;
2558 static const WCHAR ExecSeqQuery[] =
2559 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2560 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2562 TRACE("Checking launch conditions\n");
2564 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2565 if (rc != ERROR_SUCCESS)
2566 return ERROR_SUCCESS;
2568 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2569 msiobj_release(&view->hdr);
2571 return rc;
2574 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2577 if (!cmp->KeyPath)
2578 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2580 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2582 MSIRECORD * row = 0;
2583 UINT root,len;
2584 LPWSTR deformated,buffer,deformated_name;
2585 LPCWSTR key,name;
2586 static const WCHAR ExecSeqQuery[] =
2587 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2588 '`','R','e','g','i','s','t','r','y','`',' ',
2589 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2590 ' ','=',' ' ,'\'','%','s','\'',0 };
2591 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2592 static const WCHAR fmt2[]=
2593 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2595 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2596 if (!row)
2597 return NULL;
2599 root = MSI_RecordGetInteger(row,2);
2600 key = MSI_RecordGetString(row, 3);
2601 name = MSI_RecordGetString(row, 4);
2602 deformat_string(package, key , &deformated);
2603 deformat_string(package, name, &deformated_name);
2605 len = strlenW(deformated) + 6;
2606 if (deformated_name)
2607 len+=strlenW(deformated_name);
2609 buffer = msi_alloc( len *sizeof(WCHAR));
2611 if (deformated_name)
2612 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2613 else
2614 sprintfW(buffer,fmt,root,deformated);
2616 msi_free(deformated);
2617 msi_free(deformated_name);
2618 msiobj_release(&row->hdr);
2620 return buffer;
2622 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2624 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2625 return NULL;
2627 else
2629 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2631 if (file)
2632 return strdupW( file->TargetPath );
2634 return NULL;
2637 static HKEY openSharedDLLsKey(void)
2639 HKEY hkey=0;
2640 static const WCHAR path[] =
2641 {'S','o','f','t','w','a','r','e','\\',
2642 'M','i','c','r','o','s','o','f','t','\\',
2643 'W','i','n','d','o','w','s','\\',
2644 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2645 'S','h','a','r','e','d','D','L','L','s',0};
2647 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2648 return hkey;
2651 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2653 HKEY hkey;
2654 DWORD count=0;
2655 DWORD type;
2656 DWORD sz = sizeof(count);
2657 DWORD rc;
2659 hkey = openSharedDLLsKey();
2660 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2661 if (rc != ERROR_SUCCESS)
2662 count = 0;
2663 RegCloseKey(hkey);
2664 return count;
2667 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2669 HKEY hkey;
2671 hkey = openSharedDLLsKey();
2672 if (count > 0)
2673 msi_reg_set_val_dword( hkey, path, count );
2674 else
2675 RegDeleteValueW(hkey,path);
2676 RegCloseKey(hkey);
2677 return count;
2681 * Return TRUE if the count should be written out and FALSE if not
2683 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2685 MSIFEATURE *feature;
2686 INT count = 0;
2687 BOOL write = FALSE;
2689 /* only refcount DLLs */
2690 if (comp->KeyPath == NULL ||
2691 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2692 comp->Attributes & msidbComponentAttributesODBCDataSource)
2693 write = FALSE;
2694 else
2696 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2697 write = (count > 0);
2699 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2700 write = TRUE;
2703 /* increment counts */
2704 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2706 ComponentList *cl;
2708 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2709 continue;
2711 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2713 if ( cl->component == comp )
2714 count++;
2718 /* decrement counts */
2719 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2721 ComponentList *cl;
2723 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2724 continue;
2726 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2728 if ( cl->component == comp )
2729 count--;
2733 /* ref count all the files in the component */
2734 if (write)
2736 MSIFILE *file;
2738 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2740 if (file->Component == comp)
2741 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2745 /* add a count for permenent */
2746 if (comp->Attributes & msidbComponentAttributesPermanent)
2747 count ++;
2749 comp->RefCount = count;
2751 if (write)
2752 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2756 * Ok further analysis makes me think that this work is
2757 * actually done in the PublishComponents and PublishFeatures
2758 * step, and not here. It appears like the keypath and all that is
2759 * resolved in this step, however actually written in the Publish steps.
2760 * But we will leave it here for now because it is unclear
2762 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2764 WCHAR squished_pc[GUID_SIZE];
2765 WCHAR squished_cc[GUID_SIZE];
2766 UINT rc;
2767 MSICOMPONENT *comp;
2768 HKEY hkey=0,hkey2=0;
2770 /* writes the Component and Features values to the registry */
2772 rc = MSIREG_OpenComponents(&hkey);
2773 if (rc != ERROR_SUCCESS)
2774 return rc;
2776 squash_guid(package->ProductCode,squished_pc);
2777 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2779 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2781 MSIRECORD * uirow;
2783 ui_progress(package,2,0,0,0);
2784 if (!comp->ComponentId)
2785 continue;
2787 squash_guid(comp->ComponentId,squished_cc);
2789 msi_free(comp->FullKeypath);
2790 comp->FullKeypath = resolve_keypath( package, comp );
2792 /* do the refcounting */
2793 ACTION_RefCountComponent( package, comp );
2795 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2796 debugstr_w(comp->Component),
2797 debugstr_w(squished_cc),
2798 debugstr_w(comp->FullKeypath),
2799 comp->RefCount);
2801 * Write the keypath out if the component is to be registered
2802 * and delete the key if the component is to be deregistered
2804 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2806 rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2807 if (rc != ERROR_SUCCESS)
2808 continue;
2810 if (!comp->FullKeypath)
2811 continue;
2813 msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2815 if (comp->Attributes & msidbComponentAttributesPermanent)
2817 static const WCHAR szPermKey[] =
2818 { '0','0','0','0','0','0','0','0','0','0','0','0',
2819 '0','0','0','0','0','0','0','0','0','0','0','0',
2820 '0','0','0','0','0','0','0','0',0 };
2822 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2825 RegCloseKey(hkey2);
2827 else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2829 DWORD res;
2831 rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2832 if (rc != ERROR_SUCCESS)
2833 continue;
2835 RegDeleteValueW(hkey2,squished_pc);
2837 /* if the key is empty delete it */
2838 res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2839 RegCloseKey(hkey2);
2840 if (res == ERROR_NO_MORE_ITEMS)
2841 RegDeleteKeyW(hkey,squished_cc);
2845 /* UI stuff */
2846 uirow = MSI_CreateRecord(3);
2847 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2848 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2849 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2850 ui_actiondata(package,szProcessComponents,uirow);
2851 msiobj_release( &uirow->hdr );
2853 RegCloseKey(hkey);
2854 return rc;
2857 typedef struct {
2858 CLSID clsid;
2859 LPWSTR source;
2861 LPWSTR path;
2862 ITypeLib *ptLib;
2863 } typelib_struct;
2865 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2866 LPWSTR lpszName, LONG_PTR lParam)
2868 TLIBATTR *attr;
2869 typelib_struct *tl_struct = (typelib_struct*) lParam;
2870 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2871 int sz;
2872 HRESULT res;
2874 if (!IS_INTRESOURCE(lpszName))
2876 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2877 return TRUE;
2880 sz = strlenW(tl_struct->source)+4;
2881 sz *= sizeof(WCHAR);
2883 if ((INT_PTR)lpszName == 1)
2884 tl_struct->path = strdupW(tl_struct->source);
2885 else
2887 tl_struct->path = msi_alloc(sz);
2888 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2891 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2892 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2893 if (!SUCCEEDED(res))
2895 msi_free(tl_struct->path);
2896 tl_struct->path = NULL;
2898 return TRUE;
2901 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2902 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2904 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2905 return FALSE;
2908 msi_free(tl_struct->path);
2909 tl_struct->path = NULL;
2911 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2912 ITypeLib_Release(tl_struct->ptLib);
2914 return TRUE;
2917 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2919 MSIPACKAGE* package = (MSIPACKAGE*)param;
2920 LPCWSTR component;
2921 MSICOMPONENT *comp;
2922 MSIFILE *file;
2923 typelib_struct tl_struct;
2924 HMODULE module;
2925 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2927 component = MSI_RecordGetString(row,3);
2928 comp = get_loaded_component(package,component);
2929 if (!comp)
2930 return ERROR_SUCCESS;
2932 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2934 TRACE("Skipping typelib reg due to disabled component\n");
2936 comp->Action = comp->Installed;
2938 return ERROR_SUCCESS;
2941 comp->Action = INSTALLSTATE_LOCAL;
2943 file = get_loaded_file( package, comp->KeyPath );
2944 if (!file)
2945 return ERROR_SUCCESS;
2947 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2948 if (module)
2950 LPCWSTR guid;
2951 guid = MSI_RecordGetString(row,1);
2952 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2953 tl_struct.source = strdupW( file->TargetPath );
2954 tl_struct.path = NULL;
2956 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2957 (LONG_PTR)&tl_struct);
2959 if (tl_struct.path)
2961 LPWSTR help = NULL;
2962 LPCWSTR helpid;
2963 HRESULT res;
2965 helpid = MSI_RecordGetString(row,6);
2967 if (helpid)
2968 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
2969 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2970 msi_free(help);
2972 if (!SUCCEEDED(res))
2973 ERR("Failed to register type library %s\n",
2974 debugstr_w(tl_struct.path));
2975 else
2977 ui_actiondata(package,szRegisterTypeLibraries,row);
2979 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2982 ITypeLib_Release(tl_struct.ptLib);
2983 msi_free(tl_struct.path);
2985 else
2986 ERR("Failed to load type library %s\n",
2987 debugstr_w(tl_struct.source));
2989 FreeLibrary(module);
2990 msi_free(tl_struct.source);
2992 else
2993 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
2995 return ERROR_SUCCESS;
2998 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3001 * OK this is a bit confusing.. I am given a _Component key and I believe
3002 * that the file that is being registered as a type library is the "key file
3003 * of that component" which I interpret to mean "The file in the KeyPath of
3004 * that component".
3006 UINT rc;
3007 MSIQUERY * view;
3008 static const WCHAR Query[] =
3009 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3010 '`','T','y','p','e','L','i','b','`',0};
3012 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3013 if (rc != ERROR_SUCCESS)
3014 return ERROR_SUCCESS;
3016 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3017 msiobj_release(&view->hdr);
3018 return rc;
3021 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3023 MSIPACKAGE *package = (MSIPACKAGE*)param;
3024 LPWSTR target_file, target_folder, filename;
3025 LPCWSTR buffer, extension;
3026 MSICOMPONENT *comp;
3027 static const WCHAR szlnk[]={'.','l','n','k',0};
3028 IShellLinkW *sl = NULL;
3029 IPersistFile *pf = NULL;
3030 HRESULT res;
3032 buffer = MSI_RecordGetString(row,4);
3033 comp = get_loaded_component(package,buffer);
3034 if (!comp)
3035 return ERROR_SUCCESS;
3037 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3039 TRACE("Skipping shortcut creation due to disabled component\n");
3041 comp->Action = comp->Installed;
3043 return ERROR_SUCCESS;
3046 comp->Action = INSTALLSTATE_LOCAL;
3048 ui_actiondata(package,szCreateShortcuts,row);
3050 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3051 &IID_IShellLinkW, (LPVOID *) &sl );
3053 if (FAILED( res ))
3055 ERR("CLSID_ShellLink not available\n");
3056 goto err;
3059 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3060 if (FAILED( res ))
3062 ERR("QueryInterface(IID_IPersistFile) failed\n");
3063 goto err;
3066 buffer = MSI_RecordGetString(row,2);
3067 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3069 /* may be needed because of a bug somehwere else */
3070 create_full_pathW(target_folder);
3072 filename = msi_dup_record_field( row, 3 );
3073 reduce_to_longfilename(filename);
3075 extension = strchrW(filename,'.');
3076 if (!extension || strcmpiW(extension,szlnk))
3078 int len = strlenW(filename);
3079 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3080 memcpy(filename + len, szlnk, sizeof(szlnk));
3082 target_file = build_directory_name(2, target_folder, filename);
3083 msi_free(target_folder);
3084 msi_free(filename);
3086 buffer = MSI_RecordGetString(row,5);
3087 if (strchrW(buffer,'['))
3089 LPWSTR deformated;
3090 deformat_string(package,buffer,&deformated);
3091 IShellLinkW_SetPath(sl,deformated);
3092 msi_free(deformated);
3094 else
3096 FIXME("poorly handled shortcut format, advertised shortcut\n");
3097 IShellLinkW_SetPath(sl,comp->FullKeypath);
3100 if (!MSI_RecordIsNull(row,6))
3102 LPWSTR deformated;
3103 buffer = MSI_RecordGetString(row,6);
3104 deformat_string(package,buffer,&deformated);
3105 IShellLinkW_SetArguments(sl,deformated);
3106 msi_free(deformated);
3109 if (!MSI_RecordIsNull(row,7))
3111 buffer = MSI_RecordGetString(row,7);
3112 IShellLinkW_SetDescription(sl,buffer);
3115 if (!MSI_RecordIsNull(row,8))
3116 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3118 if (!MSI_RecordIsNull(row,9))
3120 LPWSTR Path;
3121 INT index;
3123 buffer = MSI_RecordGetString(row,9);
3125 Path = build_icon_path(package,buffer);
3126 index = MSI_RecordGetInteger(row,10);
3128 /* no value means 0 */
3129 if (index == MSI_NULL_INTEGER)
3130 index = 0;
3132 IShellLinkW_SetIconLocation(sl,Path,index);
3133 msi_free(Path);
3136 if (!MSI_RecordIsNull(row,11))
3137 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3139 if (!MSI_RecordIsNull(row,12))
3141 LPWSTR Path;
3142 buffer = MSI_RecordGetString(row,12);
3143 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3144 if (Path)
3145 IShellLinkW_SetWorkingDirectory(sl,Path);
3146 msi_free(Path);
3149 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3150 IPersistFile_Save(pf,target_file,FALSE);
3152 msi_free(target_file);
3154 err:
3155 if (pf)
3156 IPersistFile_Release( pf );
3157 if (sl)
3158 IShellLinkW_Release( sl );
3160 return ERROR_SUCCESS;
3163 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3165 UINT rc;
3166 HRESULT res;
3167 MSIQUERY * view;
3168 static const WCHAR Query[] =
3169 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3170 '`','S','h','o','r','t','c','u','t','`',0};
3172 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3173 if (rc != ERROR_SUCCESS)
3174 return ERROR_SUCCESS;
3176 res = CoInitialize( NULL );
3177 if (FAILED (res))
3179 ERR("CoInitialize failed\n");
3180 return ERROR_FUNCTION_FAILED;
3183 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3184 msiobj_release(&view->hdr);
3186 CoUninitialize();
3188 return rc;
3191 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3193 MSIPACKAGE* package = (MSIPACKAGE*)param;
3194 HANDLE the_file;
3195 LPWSTR FilePath;
3196 LPCWSTR FileName;
3197 CHAR buffer[1024];
3198 DWORD sz;
3199 UINT rc;
3200 MSIRECORD *uirow;
3202 FileName = MSI_RecordGetString(row,1);
3203 if (!FileName)
3205 ERR("Unable to get FileName\n");
3206 return ERROR_SUCCESS;
3209 FilePath = build_icon_path(package,FileName);
3211 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3213 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3214 FILE_ATTRIBUTE_NORMAL, NULL);
3216 if (the_file == INVALID_HANDLE_VALUE)
3218 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3219 msi_free(FilePath);
3220 return ERROR_SUCCESS;
3225 DWORD write;
3226 sz = 1024;
3227 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3228 if (rc != ERROR_SUCCESS)
3230 ERR("Failed to get stream\n");
3231 CloseHandle(the_file);
3232 DeleteFileW(FilePath);
3233 break;
3235 WriteFile(the_file,buffer,sz,&write,NULL);
3236 } while (sz == 1024);
3238 msi_free(FilePath);
3240 CloseHandle(the_file);
3242 uirow = MSI_CreateRecord(1);
3243 MSI_RecordSetStringW(uirow,1,FileName);
3244 ui_actiondata(package,szPublishProduct,uirow);
3245 msiobj_release( &uirow->hdr );
3247 return ERROR_SUCCESS;
3250 static BOOL msi_check_publish(MSIPACKAGE *package)
3252 MSIFEATURE *feature;
3254 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3256 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3257 return TRUE;
3260 return FALSE;
3264 * 99% of the work done here is only done for
3265 * advertised installs. However this is where the
3266 * Icon table is processed and written out
3267 * so that is what I am going to do here.
3269 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3271 UINT rc;
3272 MSIQUERY * view;
3273 MSISOURCELISTINFO *info;
3274 MSIMEDIADISK *disk;
3275 static const WCHAR Query[]=
3276 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3277 '`','I','c','o','n','`',0};
3278 /* for registry stuff */
3279 HKEY hkey=0;
3280 HKEY hukey=0;
3281 HKEY hudkey=0, props=0;
3282 static const WCHAR szProductLanguage[] =
3283 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3284 static const WCHAR szARPProductIcon[] =
3285 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3286 static const WCHAR szProductVersion[] =
3287 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3288 DWORD langid;
3289 LPWSTR buffer;
3290 DWORD size;
3291 MSIHANDLE hDb, hSumInfo;
3293 /* FIXME: also need to publish if the product is in advertise mode */
3294 if (!msi_check_publish(package))
3295 return ERROR_SUCCESS;
3297 /* write out icon files */
3299 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3300 if (rc == ERROR_SUCCESS)
3302 MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3303 msiobj_release(&view->hdr);
3306 /* ok there is a lot more done here but i need to figure out what */
3308 rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3309 if (rc != ERROR_SUCCESS)
3310 goto end;
3312 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3313 if (rc != ERROR_SUCCESS)
3314 goto end;
3316 rc = MSIREG_OpenUserDataProductKey(package->ProductCode,&hudkey,TRUE);
3317 if (rc != ERROR_SUCCESS)
3318 goto end;
3320 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode,&props,TRUE);
3321 if (rc != ERROR_SUCCESS)
3322 goto end;
3324 buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3325 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3326 msi_free(buffer);
3328 langid = msi_get_property_int( package, szProductLanguage, 0 );
3329 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3331 buffer = msi_dup_property( package, szARPProductIcon );
3332 if (buffer)
3334 LPWSTR path = build_icon_path(package,buffer);
3335 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3336 msi_free( path );
3338 msi_free(buffer);
3340 buffer = msi_dup_property( package, szProductVersion );
3341 if (buffer)
3343 DWORD verdword = msi_version_str_to_dword(buffer);
3344 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3346 msi_free(buffer);
3348 /* FIXME: Need to write more keys to the user registry */
3350 hDb= alloc_msihandle( &package->db->hdr );
3351 if (!hDb) {
3352 rc = ERROR_NOT_ENOUGH_MEMORY;
3353 goto end;
3355 rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo);
3356 MsiCloseHandle(hDb);
3357 if (rc == ERROR_SUCCESS)
3359 WCHAR guidbuffer[0x200];
3360 size = 0x200;
3361 rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3362 guidbuffer, &size);
3363 if (rc == ERROR_SUCCESS)
3365 WCHAR squashed[GUID_SIZE];
3366 /* for now we only care about the first guid */
3367 LPWSTR ptr = strchrW(guidbuffer,';');
3368 if (ptr) *ptr = 0;
3369 squash_guid(guidbuffer,squashed);
3370 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3372 else
3374 ERR("Unable to query Revision_Number...\n");
3375 rc = ERROR_SUCCESS;
3377 MsiCloseHandle(hSumInfo);
3379 else
3381 ERR("Unable to open Summary Information\n");
3382 rc = ERROR_SUCCESS;
3385 /* publish the SourceList info */
3386 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3388 MsiSourceListSetInfoW(package->ProductCode, NULL,
3389 info->context, info->options,
3390 info->property, info->value);
3393 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3395 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3396 disk->context, disk->options,
3397 disk->disk_id, disk->volume_label, disk->disk_prompt);
3400 end:
3401 RegCloseKey(hkey);
3402 RegCloseKey(hukey);
3403 RegCloseKey(hudkey);
3404 RegCloseKey(props);
3406 return rc;
3409 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3411 MSIPACKAGE *package = (MSIPACKAGE*)param;
3412 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3413 LPWSTR deformated_section, deformated_key, deformated_value;
3414 LPWSTR folder, fullname = NULL;
3415 MSIRECORD * uirow;
3416 INT action;
3417 MSICOMPONENT *comp;
3418 static const WCHAR szWindowsFolder[] =
3419 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3421 component = MSI_RecordGetString(row, 8);
3422 comp = get_loaded_component(package,component);
3424 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3426 TRACE("Skipping ini file due to disabled component %s\n",
3427 debugstr_w(component));
3429 comp->Action = comp->Installed;
3431 return ERROR_SUCCESS;
3434 comp->Action = INSTALLSTATE_LOCAL;
3436 identifier = MSI_RecordGetString(row,1);
3437 filename = MSI_RecordGetString(row,2);
3438 dirproperty = MSI_RecordGetString(row,3);
3439 section = MSI_RecordGetString(row,4);
3440 key = MSI_RecordGetString(row,5);
3441 value = MSI_RecordGetString(row,6);
3442 action = MSI_RecordGetInteger(row,7);
3444 deformat_string(package,section,&deformated_section);
3445 deformat_string(package,key,&deformated_key);
3446 deformat_string(package,value,&deformated_value);
3448 if (dirproperty)
3450 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3451 if (!folder)
3452 folder = msi_dup_property( package, dirproperty );
3454 else
3455 folder = msi_dup_property( package, szWindowsFolder );
3457 if (!folder)
3459 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3460 goto cleanup;
3463 fullname = build_directory_name(2, folder, filename);
3465 if (action == 0)
3467 TRACE("Adding value %s to section %s in %s\n",
3468 debugstr_w(deformated_key), debugstr_w(deformated_section),
3469 debugstr_w(fullname));
3470 WritePrivateProfileStringW(deformated_section, deformated_key,
3471 deformated_value, fullname);
3473 else if (action == 1)
3475 WCHAR returned[10];
3476 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3477 returned, 10, fullname);
3478 if (returned[0] == 0)
3480 TRACE("Adding value %s to section %s in %s\n",
3481 debugstr_w(deformated_key), debugstr_w(deformated_section),
3482 debugstr_w(fullname));
3484 WritePrivateProfileStringW(deformated_section, deformated_key,
3485 deformated_value, fullname);
3488 else if (action == 3)
3489 FIXME("Append to existing section not yet implemented\n");
3491 uirow = MSI_CreateRecord(4);
3492 MSI_RecordSetStringW(uirow,1,identifier);
3493 MSI_RecordSetStringW(uirow,2,deformated_section);
3494 MSI_RecordSetStringW(uirow,3,deformated_key);
3495 MSI_RecordSetStringW(uirow,4,deformated_value);
3496 ui_actiondata(package,szWriteIniValues,uirow);
3497 msiobj_release( &uirow->hdr );
3498 cleanup:
3499 msi_free(fullname);
3500 msi_free(folder);
3501 msi_free(deformated_key);
3502 msi_free(deformated_value);
3503 msi_free(deformated_section);
3504 return ERROR_SUCCESS;
3507 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3509 UINT rc;
3510 MSIQUERY * view;
3511 static const WCHAR ExecSeqQuery[] =
3512 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3513 '`','I','n','i','F','i','l','e','`',0};
3515 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3516 if (rc != ERROR_SUCCESS)
3518 TRACE("no IniFile table\n");
3519 return ERROR_SUCCESS;
3522 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3523 msiobj_release(&view->hdr);
3524 return rc;
3527 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3529 MSIPACKAGE *package = (MSIPACKAGE*)param;
3530 LPCWSTR filename;
3531 LPWSTR FullName;
3532 MSIFILE *file;
3533 DWORD len;
3534 static const WCHAR ExeStr[] =
3535 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3536 static const WCHAR close[] = {'\"',0};
3537 STARTUPINFOW si;
3538 PROCESS_INFORMATION info;
3539 BOOL brc;
3540 MSIRECORD *uirow;
3541 LPWSTR uipath, p;
3543 memset(&si,0,sizeof(STARTUPINFOW));
3545 filename = MSI_RecordGetString(row,1);
3546 file = get_loaded_file( package, filename );
3548 if (!file)
3550 ERR("Unable to find file id %s\n",debugstr_w(filename));
3551 return ERROR_SUCCESS;
3554 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3556 FullName = msi_alloc(len*sizeof(WCHAR));
3557 strcpyW(FullName,ExeStr);
3558 strcatW( FullName, file->TargetPath );
3559 strcatW(FullName,close);
3561 TRACE("Registering %s\n",debugstr_w(FullName));
3562 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3563 &si, &info);
3565 if (brc)
3566 msi_dialog_check_messages(info.hProcess);
3568 msi_free(FullName);
3570 /* the UI chunk */
3571 uirow = MSI_CreateRecord( 2 );
3572 uipath = strdupW( file->TargetPath );
3573 p = strrchrW(uipath,'\\');
3574 if (p)
3575 p[0]=0;
3576 MSI_RecordSetStringW( uirow, 1, &p[1] );
3577 MSI_RecordSetStringW( uirow, 2, uipath);
3578 ui_actiondata( package, szSelfRegModules, uirow);
3579 msiobj_release( &uirow->hdr );
3580 msi_free( uipath );
3581 /* FIXME: call ui_progress? */
3583 return ERROR_SUCCESS;
3586 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3588 UINT rc;
3589 MSIQUERY * view;
3590 static const WCHAR ExecSeqQuery[] =
3591 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3592 '`','S','e','l','f','R','e','g','`',0};
3594 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3595 if (rc != ERROR_SUCCESS)
3597 TRACE("no SelfReg table\n");
3598 return ERROR_SUCCESS;
3601 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3602 msiobj_release(&view->hdr);
3604 return ERROR_SUCCESS;
3607 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3609 MSIFEATURE *feature;
3610 UINT rc;
3611 HKEY hkey=0;
3612 HKEY hukey=0;
3614 rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3615 if (rc != ERROR_SUCCESS)
3616 goto end;
3618 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3619 if (rc != ERROR_SUCCESS)
3620 goto end;
3622 /* here the guids are base 85 encoded */
3623 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3625 ComponentList *cl;
3626 LPWSTR data = NULL;
3627 GUID clsid;
3628 INT size;
3629 BOOL absent = FALSE;
3630 MSIRECORD *uirow;
3632 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3633 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3634 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3635 absent = TRUE;
3637 size = 1;
3638 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3640 size += 21;
3642 if (feature->Feature_Parent)
3643 size += strlenW( feature->Feature_Parent )+2;
3645 data = msi_alloc(size * sizeof(WCHAR));
3647 data[0] = 0;
3648 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3650 MSICOMPONENT* component = cl->component;
3651 WCHAR buf[21];
3653 buf[0] = 0;
3654 if (component->ComponentId)
3656 TRACE("From %s\n",debugstr_w(component->ComponentId));
3657 CLSIDFromString(component->ComponentId, &clsid);
3658 encode_base85_guid(&clsid,buf);
3659 TRACE("to %s\n",debugstr_w(buf));
3660 strcatW(data,buf);
3663 if (feature->Feature_Parent)
3665 static const WCHAR sep[] = {'\2',0};
3666 strcatW(data,sep);
3667 strcatW(data,feature->Feature_Parent);
3670 msi_reg_set_val_str( hkey, feature->Feature, data );
3671 msi_free(data);
3673 size = 0;
3674 if (feature->Feature_Parent)
3675 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3676 if (!absent)
3678 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3679 (LPBYTE)feature->Feature_Parent,size);
3681 else
3683 size += 2*sizeof(WCHAR);
3684 data = msi_alloc(size);
3685 data[0] = 0x6;
3686 data[1] = 0;
3687 if (feature->Feature_Parent)
3688 strcpyW( &data[1], feature->Feature_Parent );
3689 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3690 (LPBYTE)data,size);
3691 msi_free(data);
3694 /* the UI chunk */
3695 uirow = MSI_CreateRecord( 1 );
3696 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3697 ui_actiondata( package, szPublishFeatures, uirow);
3698 msiobj_release( &uirow->hdr );
3699 /* FIXME: call ui_progress? */
3702 end:
3703 RegCloseKey(hkey);
3704 RegCloseKey(hukey);
3705 return rc;
3708 static UINT msi_get_local_package_name( LPWSTR path )
3710 static const WCHAR szInstaller[] = {
3711 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3712 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3713 DWORD time, len, i;
3714 HANDLE handle;
3716 time = GetTickCount();
3717 GetWindowsDirectoryW( path, MAX_PATH );
3718 lstrcatW( path, szInstaller );
3719 CreateDirectoryW( path, NULL );
3721 len = lstrlenW(path);
3722 for (i=0; i<0x10000; i++)
3724 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3725 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3726 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3727 if (handle != INVALID_HANDLE_VALUE)
3729 CloseHandle(handle);
3730 break;
3732 if (GetLastError() != ERROR_FILE_EXISTS &&
3733 GetLastError() != ERROR_SHARING_VIOLATION)
3734 return ERROR_FUNCTION_FAILED;
3737 return ERROR_SUCCESS;
3740 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3742 static const WCHAR szOriginalDatabase[] =
3743 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3744 WCHAR packagefile[MAX_PATH];
3745 LPWSTR msiFilePath;
3746 UINT r;
3748 r = msi_get_local_package_name( packagefile );
3749 if (r != ERROR_SUCCESS)
3750 return r;
3752 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3754 msiFilePath = msi_dup_property( package, szOriginalDatabase );
3755 r = CopyFileW( msiFilePath, packagefile, FALSE);
3757 if (!r)
3759 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3760 debugstr_w(msiFilePath), debugstr_w(packagefile), GetLastError());
3761 msi_free( msiFilePath );
3762 return ERROR_FUNCTION_FAILED;
3764 msi_free( msiFilePath );
3766 /* FIXME: maybe set this key in ACTION_RegisterProduct instead */
3767 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3768 return ERROR_SUCCESS;
3771 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3773 LPWSTR prop, val, key;
3774 static const LPCSTR propval[] = {
3775 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3776 "ARPCONTACT", "Contact",
3777 "ARPCOMMENTS", "Comments",
3778 "ProductName", "DisplayName",
3779 "ProductVersion", "DisplayVersion",
3780 "ARPHELPLINK", "HelpLink",
3781 "ARPHELPTELEPHONE", "HelpTelephone",
3782 "ARPINSTALLLOCATION", "InstallLocation",
3783 "SourceDir", "InstallSource",
3784 "Manufacturer", "Publisher",
3785 "ARPREADME", "Readme",
3786 "ARPSIZE", "Size",
3787 "ARPURLINFOABOUT", "URLInfoAbout",
3788 "ARPURLUPDATEINFO", "URLUpdateInfo",
3789 NULL,
3791 const LPCSTR *p = propval;
3793 while( *p )
3795 prop = strdupAtoW( *p++ );
3796 key = strdupAtoW( *p++ );
3797 val = msi_dup_property( package, prop );
3798 msi_reg_set_val_str( hkey, key, val );
3799 msi_free(val);
3800 msi_free(key);
3801 msi_free(prop);
3803 return ERROR_SUCCESS;
3806 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3808 HKEY hkey=0;
3809 HKEY hudkey=0, props=0;
3810 LPWSTR buffer = NULL;
3811 UINT rc;
3812 DWORD size, langid;
3813 static const WCHAR szWindowsInstaller[] =
3814 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3815 static const WCHAR szUpgradeCode[] =
3816 {'U','p','g','r','a','d','e','C','o','d','e',0};
3817 static const WCHAR modpath_fmt[] =
3818 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3819 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3820 static const WCHAR szModifyPath[] =
3821 {'M','o','d','i','f','y','P','a','t','h',0};
3822 static const WCHAR szUninstallString[] =
3823 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3824 static const WCHAR szEstimatedSize[] =
3825 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3826 static const WCHAR szProductLanguage[] =
3827 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3828 static const WCHAR szProductVersion[] =
3829 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3831 SYSTEMTIME systime;
3832 static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0};
3833 LPWSTR upgrade_code;
3834 WCHAR szDate[9];
3836 /* FIXME: also need to publish if the product is in advertise mode */
3837 if (!msi_check_publish(package))
3838 return ERROR_SUCCESS;
3840 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3841 if (rc != ERROR_SUCCESS)
3842 return rc;
3844 /* dump all the info i can grab */
3845 /* FIXME: Flesh out more information */
3847 msi_write_uninstall_property_vals( package, hkey );
3849 msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
3851 msi_make_package_local( package, hkey );
3853 /* do ModifyPath and UninstallString */
3854 size = deformat_string(package,modpath_fmt,&buffer);
3855 RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3856 RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3857 msi_free(buffer);
3859 /* FIXME: Write real Estimated Size when we have it */
3860 msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
3862 GetLocalTime(&systime);
3863 sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
3864 msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
3866 langid = msi_get_property_int( package, szProductLanguage, 0 );
3867 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3869 buffer = msi_dup_property( package, szProductVersion );
3870 if (buffer)
3872 DWORD verdword = msi_version_str_to_dword(buffer);
3874 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3875 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
3876 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
3878 msi_free(buffer);
3880 /* Handle Upgrade Codes */
3881 upgrade_code = msi_dup_property( package, szUpgradeCode );
3882 if (upgrade_code)
3884 HKEY hkey2;
3885 WCHAR squashed[33];
3886 MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3887 squash_guid(package->ProductCode,squashed);
3888 msi_reg_set_val_str( hkey2, squashed, NULL );
3889 RegCloseKey(hkey2);
3890 MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3891 squash_guid(package->ProductCode,squashed);
3892 msi_reg_set_val_str( hkey2, squashed, NULL );
3893 RegCloseKey(hkey2);
3895 msi_free(upgrade_code);
3898 RegCloseKey(hkey);
3900 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3901 if (rc != ERROR_SUCCESS)
3902 return rc;
3904 RegCloseKey(hudkey);
3906 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
3907 if (rc != ERROR_SUCCESS)
3908 return rc;
3910 msi_reg_set_val_dword( props, szWindowsInstaller, 1 );
3911 RegCloseKey(props);
3913 return ERROR_SUCCESS;
3916 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
3918 return execute_script(package,INSTALL_SCRIPT);
3921 static UINT msi_unpublish_product(MSIPACKAGE *package)
3923 LPWSTR remove = NULL;
3924 LPWSTR *features = NULL;
3925 BOOL full_uninstall = TRUE;
3926 MSIFEATURE *feature;
3928 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
3929 static const WCHAR szAll[] = {'A','L','L',0};
3931 remove = msi_dup_property(package, szRemove);
3932 if (!remove)
3933 return ERROR_SUCCESS;
3935 features = msi_split_string(remove, ',');
3936 if (!features)
3938 msi_free(remove);
3939 ERR("REMOVE feature list is empty!\n");
3940 return ERROR_FUNCTION_FAILED;
3943 if (!lstrcmpW(features[0], szAll))
3944 full_uninstall = TRUE;
3945 else
3947 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3949 if (feature->Action != INSTALLSTATE_ABSENT)
3950 full_uninstall = FALSE;
3954 if (!full_uninstall)
3955 goto done;
3957 MSIREG_DeleteProductKey(package->ProductCode);
3958 MSIREG_DeleteUserProductKey(package->ProductCode);
3959 MSIREG_DeleteUserDataProductKey(package->ProductCode);
3961 done:
3962 msi_free(remove);
3963 msi_free(features);
3964 return ERROR_SUCCESS;
3967 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
3969 UINT rc;
3971 rc = msi_unpublish_product(package);
3972 if (rc != ERROR_SUCCESS)
3973 return rc;
3975 /* turn off scheduling */
3976 package->script->CurrentlyScripting= FALSE;
3978 /* first do the same as an InstallExecute */
3979 rc = ACTION_InstallExecute(package);
3980 if (rc != ERROR_SUCCESS)
3981 return rc;
3983 /* then handle Commit Actions */
3984 rc = execute_script(package,COMMIT_SCRIPT);
3986 return rc;
3989 UINT ACTION_ForceReboot(MSIPACKAGE *package)
3991 static const WCHAR RunOnce[] = {
3992 'S','o','f','t','w','a','r','e','\\',
3993 'M','i','c','r','o','s','o','f','t','\\',
3994 'W','i','n','d','o','w','s','\\',
3995 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3996 'R','u','n','O','n','c','e',0};
3997 static const WCHAR InstallRunOnce[] = {
3998 'S','o','f','t','w','a','r','e','\\',
3999 'M','i','c','r','o','s','o','f','t','\\',
4000 'W','i','n','d','o','w','s','\\',
4001 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4002 'I','n','s','t','a','l','l','e','r','\\',
4003 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4005 static const WCHAR msiexec_fmt[] = {
4006 '%','s',
4007 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4008 '\"','%','s','\"',0};
4009 static const WCHAR install_fmt[] = {
4010 '/','I',' ','\"','%','s','\"',' ',
4011 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4012 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4013 WCHAR buffer[256], sysdir[MAX_PATH];
4014 HKEY hkey;
4015 WCHAR squished_pc[100];
4017 squash_guid(package->ProductCode,squished_pc);
4019 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4020 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4021 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4022 squished_pc);
4024 msi_reg_set_val_str( hkey, squished_pc, buffer );
4025 RegCloseKey(hkey);
4027 TRACE("Reboot command %s\n",debugstr_w(buffer));
4029 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4030 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4032 msi_reg_set_val_str( hkey, squished_pc, buffer );
4033 RegCloseKey(hkey);
4035 return ERROR_INSTALL_SUSPEND;
4038 static UINT msi_set_sourcedir_props(MSIPACKAGE *package)
4040 LPWSTR p, source;
4041 DWORD len;
4043 p = strrchrW( package->PackagePath, '\\' );
4044 if (!p)
4045 return ERROR_SUCCESS;
4047 len = p - package->PackagePath + 2;
4048 source = msi_alloc( len * sizeof(WCHAR) );
4049 lstrcpynW( source, package->PackagePath, len );
4051 MSI_SetPropertyW( package, cszSourceDir, source );
4052 MSI_SetPropertyW( package, cszSOURCEDIR, source );
4054 msi_free( source );
4056 return ERROR_SUCCESS;
4059 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4061 DWORD attrib;
4062 UINT rc;
4065 * We are currently doing what should be done here in the top level Install
4066 * however for Administrative and uninstalls this step will be needed
4068 if (!package->PackagePath)
4069 return ERROR_SUCCESS;
4071 msi_set_sourcedir_props(package);
4073 attrib = GetFileAttributesW(package->PackagePath);
4074 if (attrib == INVALID_FILE_ATTRIBUTES)
4076 LPWSTR prompt;
4077 LPWSTR msg;
4078 DWORD size = 0;
4080 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4081 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4082 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4083 if (rc == ERROR_MORE_DATA)
4085 prompt = msi_alloc(size * sizeof(WCHAR));
4086 MsiSourceListGetInfoW(package->ProductCode, NULL,
4087 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4088 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4090 else
4091 prompt = strdupW(package->PackagePath);
4093 msg = generate_error_string(package,1302,1,prompt);
4094 while(attrib == INVALID_FILE_ATTRIBUTES)
4096 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4097 if (rc == IDCANCEL)
4099 rc = ERROR_INSTALL_USEREXIT;
4100 break;
4102 attrib = GetFileAttributesW(package->PackagePath);
4104 msi_free(prompt);
4105 rc = ERROR_SUCCESS;
4107 else
4108 return ERROR_SUCCESS;
4110 return rc;
4113 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4115 HKEY hkey=0;
4116 LPWSTR buffer;
4117 LPWSTR productid;
4118 UINT rc,i;
4120 static const WCHAR szPropKeys[][80] =
4122 {'P','r','o','d','u','c','t','I','D',0},
4123 {'U','S','E','R','N','A','M','E',0},
4124 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4125 {0},
4128 static const WCHAR szRegKeys[][80] =
4130 {'P','r','o','d','u','c','t','I','D',0},
4131 {'R','e','g','O','w','n','e','r',0},
4132 {'R','e','g','C','o','m','p','a','n','y',0},
4133 {0},
4136 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4137 if (!productid)
4138 return ERROR_SUCCESS;
4140 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4141 if (rc != ERROR_SUCCESS)
4142 goto end;
4144 for( i = 0; szPropKeys[i][0]; i++ )
4146 buffer = msi_dup_property( package, szPropKeys[i] );
4147 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4148 msi_free( buffer );
4151 end:
4152 msi_free(productid);
4153 RegCloseKey(hkey);
4155 /* FIXME: call ui_actiondata */
4157 return ERROR_SUCCESS;
4161 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4163 UINT rc;
4165 package->script->InWhatSequence |= SEQUENCE_EXEC;
4166 rc = ACTION_ProcessExecSequence(package,FALSE);
4167 return rc;
4171 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4173 MSIPACKAGE *package = (MSIPACKAGE*)param;
4174 LPCWSTR compgroupid=NULL;
4175 LPCWSTR feature=NULL;
4176 LPCWSTR text = NULL;
4177 LPCWSTR qualifier = NULL;
4178 LPCWSTR component = NULL;
4179 LPWSTR advertise = NULL;
4180 LPWSTR output = NULL;
4181 HKEY hkey;
4182 UINT rc = ERROR_SUCCESS;
4183 MSICOMPONENT *comp;
4184 DWORD sz = 0;
4185 MSIRECORD *uirow;
4187 component = MSI_RecordGetString(rec,3);
4188 comp = get_loaded_component(package,component);
4190 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4191 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4192 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4194 TRACE("Skipping: Component %s not scheduled for install\n",
4195 debugstr_w(component));
4197 return ERROR_SUCCESS;
4200 compgroupid = MSI_RecordGetString(rec,1);
4201 qualifier = MSI_RecordGetString(rec,2);
4203 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4204 if (rc != ERROR_SUCCESS)
4205 goto end;
4207 text = MSI_RecordGetString(rec,4);
4208 feature = MSI_RecordGetString(rec,5);
4210 advertise = create_component_advertise_string(package, comp, feature);
4212 sz = strlenW(advertise);
4214 if (text)
4215 sz += lstrlenW(text);
4217 sz+=3;
4218 sz *= sizeof(WCHAR);
4220 output = msi_alloc_zero(sz);
4221 strcpyW(output,advertise);
4222 msi_free(advertise);
4224 if (text)
4225 strcatW(output,text);
4227 msi_reg_set_val_multi_str( hkey, qualifier, output );
4229 end:
4230 RegCloseKey(hkey);
4231 msi_free(output);
4233 /* the UI chunk */
4234 uirow = MSI_CreateRecord( 2 );
4235 MSI_RecordSetStringW( uirow, 1, compgroupid );
4236 MSI_RecordSetStringW( uirow, 2, qualifier);
4237 ui_actiondata( package, szPublishComponents, uirow);
4238 msiobj_release( &uirow->hdr );
4239 /* FIXME: call ui_progress? */
4241 return rc;
4245 * At present I am ignorning the advertised components part of this and only
4246 * focusing on the qualified component sets
4248 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4250 UINT rc;
4251 MSIQUERY * view;
4252 static const WCHAR ExecSeqQuery[] =
4253 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4254 '`','P','u','b','l','i','s','h',
4255 'C','o','m','p','o','n','e','n','t','`',0};
4257 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4258 if (rc != ERROR_SUCCESS)
4259 return ERROR_SUCCESS;
4261 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4262 msiobj_release(&view->hdr);
4264 return rc;
4267 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4269 MSIPACKAGE *package = (MSIPACKAGE*)param;
4270 MSIRECORD *row;
4271 MSIFILE *file;
4272 SC_HANDLE hscm, service = NULL;
4273 LPCWSTR name, disp, comp, depends, pass;
4274 LPCWSTR load_order, serv_name, key;
4275 DWORD serv_type, start_type;
4276 DWORD err_control;
4278 static const WCHAR query[] =
4279 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4280 '`','C','o','m','p','o','n','e','n','t','`',' ',
4281 'W','H','E','R','E',' ',
4282 '`','C','o','m','p','o','n','e','n','t','`',' ',
4283 '=','\'','%','s','\'',0};
4285 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4286 if (!hscm)
4288 ERR("Failed to open the SC Manager!\n");
4289 goto done;
4292 start_type = MSI_RecordGetInteger(rec, 5);
4293 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4294 goto done;
4296 depends = MSI_RecordGetString(rec, 8);
4297 if (depends && *depends)
4298 FIXME("Dependency list unhandled!\n");
4300 name = MSI_RecordGetString(rec, 2);
4301 disp = MSI_RecordGetString(rec, 3);
4302 serv_type = MSI_RecordGetInteger(rec, 4);
4303 err_control = MSI_RecordGetInteger(rec, 6);
4304 load_order = MSI_RecordGetString(rec, 7);
4305 serv_name = MSI_RecordGetString(rec, 9);
4306 pass = MSI_RecordGetString(rec, 10);
4307 comp = MSI_RecordGetString(rec, 12);
4309 /* fetch the service path */
4310 row = MSI_QueryGetRecord(package->db, query, comp);
4311 if (!row)
4313 ERR("Control query failed!\n");
4314 goto done;
4317 key = MSI_RecordGetString(row, 6);
4318 msiobj_release(&row->hdr);
4320 file = get_loaded_file(package, key);
4321 if (!file)
4323 ERR("Failed to load the service file\n");
4324 goto done;
4327 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4328 start_type, err_control, file->TargetPath,
4329 load_order, NULL, NULL, serv_name, pass);
4330 if (!service)
4332 if (GetLastError() != ERROR_SERVICE_EXISTS)
4333 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4336 done:
4337 CloseServiceHandle(service);
4338 CloseServiceHandle(hscm);
4340 return ERROR_SUCCESS;
4343 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4345 UINT rc;
4346 MSIQUERY * view;
4347 static const WCHAR ExecSeqQuery[] =
4348 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4349 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4351 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4352 if (rc != ERROR_SUCCESS)
4353 return ERROR_SUCCESS;
4355 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4356 msiobj_release(&view->hdr);
4358 return rc;
4361 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4362 static LPCWSTR *msi_service_args_to_vector(LPCWSTR name, LPWSTR args, DWORD *numargs)
4364 LPCWSTR *vector;
4365 LPWSTR p, q;
4366 DWORD sep_len;
4368 static const WCHAR separator[] = {'[','~',']',0};
4370 *numargs = 0;
4371 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4373 if (!args)
4374 return NULL;
4376 vector = msi_alloc(sizeof(LPWSTR));
4377 if (!vector)
4378 return NULL;
4380 p = args;
4383 (*numargs)++;
4384 vector[*numargs - 1] = p;
4386 if ((q = strstrW(p, separator)))
4388 *q = '\0';
4390 vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4391 if (!vector)
4392 return NULL;
4394 p = q + sep_len;
4396 } while (q);
4398 return vector;
4401 static MSICOMPONENT *msi_find_component( MSIPACKAGE *package, LPCWSTR component )
4403 MSICOMPONENT *comp;
4405 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
4407 if (!lstrcmpW(comp->Component, component))
4408 return comp;
4411 return NULL;
4414 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4416 MSIPACKAGE *package = (MSIPACKAGE *)param;
4417 MSICOMPONENT *comp;
4418 SC_HANDLE scm, service = NULL;
4419 LPCWSTR name, *vector = NULL;
4420 LPWSTR args;
4421 DWORD event, numargs;
4422 UINT r = ERROR_FUNCTION_FAILED;
4424 comp = msi_find_component(package, MSI_RecordGetString(rec, 6));
4425 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4426 return ERROR_SUCCESS;
4428 name = MSI_RecordGetString(rec, 2);
4429 event = MSI_RecordGetInteger(rec, 3);
4430 args = strdupW(MSI_RecordGetString(rec, 4));
4432 if (!(event & msidbServiceControlEventStart))
4433 return ERROR_SUCCESS;
4435 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4436 if (!scm)
4438 ERR("Failed to open the service control manager\n");
4439 goto done;
4442 service = OpenServiceW(scm, name, SERVICE_START);
4443 if (!service)
4445 ERR("Failed to open service %s\n", debugstr_w(name));
4446 goto done;
4449 vector = msi_service_args_to_vector(name, args, &numargs);
4451 if (!StartServiceW(service, numargs, vector))
4453 ERR("Failed to start service %s\n", debugstr_w(name));
4454 goto done;
4457 r = ERROR_SUCCESS;
4459 done:
4460 CloseServiceHandle(service);
4461 CloseServiceHandle(scm);
4463 msi_free(args);
4464 msi_free(vector);
4465 return r;
4468 static UINT ACTION_StartServices( MSIPACKAGE *package )
4470 UINT rc;
4471 MSIQUERY *view;
4473 static const WCHAR query[] = {
4474 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4475 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4477 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4478 if (rc != ERROR_SUCCESS)
4479 return ERROR_SUCCESS;
4481 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4482 msiobj_release(&view->hdr);
4484 return rc;
4487 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4489 MSIFILE *file;
4491 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4493 if (!lstrcmpW(file->File, filename))
4494 return file;
4497 return NULL;
4500 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4502 MSIPACKAGE *package = (MSIPACKAGE*)param;
4503 LPWSTR driver, driver_path, ptr;
4504 WCHAR outpath[MAX_PATH];
4505 MSIFILE *driver_file, *setup_file;
4506 LPCWSTR desc;
4507 DWORD len, usage;
4508 UINT r = ERROR_SUCCESS;
4510 static const WCHAR driver_fmt[] = {
4511 'D','r','i','v','e','r','=','%','s',0};
4512 static const WCHAR setup_fmt[] = {
4513 'S','e','t','u','p','=','%','s',0};
4514 static const WCHAR usage_fmt[] = {
4515 'F','i','l','e','U','s','a','g','e','=','1',0};
4517 desc = MSI_RecordGetString(rec, 3);
4519 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4520 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4522 if (!driver_file || !setup_file)
4524 ERR("ODBC Driver entry not found!\n");
4525 return ERROR_FUNCTION_FAILED;
4528 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4529 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4530 lstrlenW(usage_fmt) + 1;
4531 driver = msi_alloc(len * sizeof(WCHAR));
4532 if (!driver)
4533 return ERROR_OUTOFMEMORY;
4535 ptr = driver;
4536 lstrcpyW(ptr, desc);
4537 ptr += lstrlenW(ptr) + 1;
4539 sprintfW(ptr, driver_fmt, driver_file->FileName);
4540 ptr += lstrlenW(ptr) + 1;
4542 sprintfW(ptr, setup_fmt, setup_file->FileName);
4543 ptr += lstrlenW(ptr) + 1;
4545 lstrcpyW(ptr, usage_fmt);
4546 ptr += lstrlenW(ptr) + 1;
4547 *ptr = '\0';
4549 driver_path = strdupW(driver_file->TargetPath);
4550 ptr = strrchrW(driver_path, '\\');
4551 if (ptr) *ptr = '\0';
4553 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4554 NULL, ODBC_INSTALL_COMPLETE, &usage))
4556 ERR("Failed to install SQL driver!\n");
4557 r = ERROR_FUNCTION_FAILED;
4560 msi_free(driver);
4561 msi_free(driver_path);
4563 return r;
4566 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4568 MSIPACKAGE *package = (MSIPACKAGE*)param;
4569 LPWSTR translator, translator_path, ptr;
4570 WCHAR outpath[MAX_PATH];
4571 MSIFILE *translator_file, *setup_file;
4572 LPCWSTR desc;
4573 DWORD len, usage;
4574 UINT r = ERROR_SUCCESS;
4576 static const WCHAR translator_fmt[] = {
4577 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4578 static const WCHAR setup_fmt[] = {
4579 'S','e','t','u','p','=','%','s',0};
4581 desc = MSI_RecordGetString(rec, 3);
4583 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4584 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4586 if (!translator_file || !setup_file)
4588 ERR("ODBC Translator entry not found!\n");
4589 return ERROR_FUNCTION_FAILED;
4592 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4593 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4594 translator = msi_alloc(len * sizeof(WCHAR));
4595 if (!translator)
4596 return ERROR_OUTOFMEMORY;
4598 ptr = translator;
4599 lstrcpyW(ptr, desc);
4600 ptr += lstrlenW(ptr) + 1;
4602 sprintfW(ptr, translator_fmt, translator_file->FileName);
4603 ptr += lstrlenW(ptr) + 1;
4605 sprintfW(ptr, setup_fmt, setup_file->FileName);
4606 ptr += lstrlenW(ptr) + 1;
4607 *ptr = '\0';
4609 translator_path = strdupW(translator_file->TargetPath);
4610 ptr = strrchrW(translator_path, '\\');
4611 if (ptr) *ptr = '\0';
4613 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4614 NULL, ODBC_INSTALL_COMPLETE, &usage))
4616 ERR("Failed to install SQL translator!\n");
4617 r = ERROR_FUNCTION_FAILED;
4620 msi_free(translator);
4621 msi_free(translator_path);
4623 return r;
4626 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4628 LPWSTR attrs;
4629 LPCWSTR desc, driver;
4630 WORD request = ODBC_ADD_SYS_DSN;
4631 INT registration;
4632 DWORD len;
4633 UINT r = ERROR_SUCCESS;
4635 static const WCHAR attrs_fmt[] = {
4636 'D','S','N','=','%','s',0 };
4638 desc = MSI_RecordGetString(rec, 3);
4639 driver = MSI_RecordGetString(rec, 4);
4640 registration = MSI_RecordGetInteger(rec, 5);
4642 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4643 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4645 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4646 attrs = msi_alloc(len * sizeof(WCHAR));
4647 if (!attrs)
4648 return ERROR_OUTOFMEMORY;
4650 sprintfW(attrs, attrs_fmt, desc);
4651 attrs[len - 1] = '\0';
4653 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4655 ERR("Failed to install SQL data source!\n");
4656 r = ERROR_FUNCTION_FAILED;
4659 msi_free(attrs);
4661 return r;
4664 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4666 UINT rc;
4667 MSIQUERY *view;
4669 static const WCHAR driver_query[] = {
4670 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4671 'O','D','B','C','D','r','i','v','e','r',0 };
4673 static const WCHAR translator_query[] = {
4674 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4675 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4677 static const WCHAR source_query[] = {
4678 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4679 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4681 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4682 if (rc != ERROR_SUCCESS)
4683 return ERROR_SUCCESS;
4685 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4686 msiobj_release(&view->hdr);
4688 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4689 if (rc != ERROR_SUCCESS)
4690 return ERROR_SUCCESS;
4692 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4693 msiobj_release(&view->hdr);
4695 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4696 if (rc != ERROR_SUCCESS)
4697 return ERROR_SUCCESS;
4699 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4700 msiobj_release(&view->hdr);
4702 return rc;
4705 #define ENV_ACT_SETALWAYS 0x1
4706 #define ENV_ACT_SETABSENT 0x2
4707 #define ENV_ACT_REMOVE 0x4
4708 #define ENV_ACT_REMOVEMATCH 0x8
4710 #define ENV_MOD_MACHINE 0x20000000
4711 #define ENV_MOD_APPEND 0x40000000
4712 #define ENV_MOD_PREFIX 0x80000000
4713 #define ENV_MOD_MASK 0xC0000000
4715 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4717 static LONG env_set_flags( LPCWSTR *name, LPWSTR *value, DWORD *flags )
4719 LPCWSTR cptr = *name;
4720 LPWSTR ptr = *value;
4722 static const WCHAR prefix[] = {'[','~',']',0};
4724 *flags = 0;
4725 while (*cptr)
4727 if (*cptr == '=')
4728 *flags |= ENV_ACT_SETALWAYS;
4729 else if (*cptr == '+')
4730 *flags |= ENV_ACT_SETABSENT;
4731 else if (*cptr == '-')
4732 *flags |= ENV_ACT_REMOVE;
4733 else if (*cptr == '!')
4734 *flags |= ENV_ACT_REMOVEMATCH;
4735 else if (*cptr == '*')
4736 *flags |= ENV_MOD_MACHINE;
4737 else
4738 break;
4740 cptr++;
4741 (*name)++;
4744 if (!*cptr)
4746 ERR("Missing environment variable\n");
4747 return ERROR_FUNCTION_FAILED;
4750 if (!strncmpW(ptr, prefix, lstrlenW(prefix)))
4752 *flags |= ENV_MOD_PREFIX;
4753 *value += lstrlenW(prefix);
4755 else
4757 ptr += lstrlenW(ptr) - lstrlenW(prefix) - 1;
4758 if (!lstrcmpW(ptr, prefix))
4760 *flags |= ENV_MOD_APPEND;
4761 *ptr = '\0';
4765 if (!*flags ||
4766 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
4767 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
4768 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
4769 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
4771 ERR("Invalid flags: %08x\n", *flags);
4772 return ERROR_FUNCTION_FAILED;
4775 return ERROR_SUCCESS;
4778 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
4780 MSIPACKAGE *package = param;
4781 LPCWSTR name, value, comp;
4782 LPWSTR data = NULL, newval = NULL;
4783 LPWSTR deformatted, ptr;
4784 DWORD flags, type, size;
4785 LONG res;
4786 HKEY env = NULL, root = HKEY_CURRENT_USER;
4788 static const WCHAR environment[] =
4789 {'S','y','s','t','e','m','\\',
4790 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4791 'C','o','n','t','r','o','l','\\',
4792 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4793 'E','n','v','i','r','o','n','m','e','n','t',0};
4794 static const WCHAR semicolon[] = {';',0};
4796 name = MSI_RecordGetString(rec, 2);
4797 value = MSI_RecordGetString(rec, 3);
4798 comp = MSI_RecordGetString(rec, 4);
4800 deformat_string(package, value, &deformatted);
4801 if (!deformatted)
4802 return ERROR_OUTOFMEMORY;
4804 res = env_set_flags(&name, &deformatted, &flags);
4805 if (res != ERROR_SUCCESS)
4806 goto done;
4808 value = deformatted;
4810 if (flags & ENV_MOD_MACHINE)
4811 root = HKEY_LOCAL_MACHINE;
4813 res = RegOpenKeyExW(root, environment, 0, KEY_ALL_ACCESS, &env);
4814 if (res != ERROR_SUCCESS)
4815 goto done;
4817 if (flags & ENV_ACT_REMOVE)
4818 FIXME("Not removing environment variable on uninstall!\n");
4820 size = 0;
4821 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
4822 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
4823 (res == ERROR_SUCCESS && type != REG_SZ))
4824 goto done;
4826 if (res != ERROR_FILE_NOT_FOUND)
4828 if (flags & ENV_ACT_SETABSENT)
4830 res = ERROR_SUCCESS;
4831 goto done;
4834 data = msi_alloc(size);
4835 if (!data)
4837 RegCloseKey(env);
4838 return ERROR_OUTOFMEMORY;
4841 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
4842 if (res != ERROR_SUCCESS)
4843 goto done;
4845 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
4847 res = RegDeleteKeyW(env, name);
4848 goto done;
4851 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
4852 newval = msi_alloc(size);
4853 ptr = newval;
4854 if (!newval)
4856 res = ERROR_OUTOFMEMORY;
4857 goto done;
4860 if (!(flags & ENV_MOD_MASK))
4861 lstrcpyW(newval, value);
4862 else
4864 if (flags & ENV_MOD_PREFIX)
4866 lstrcpyW(newval, value);
4867 lstrcatW(newval, semicolon);
4868 ptr = newval + lstrlenW(value) + 1;
4871 lstrcpyW(ptr, data);
4873 if (flags & ENV_MOD_APPEND)
4875 lstrcatW(newval, semicolon);
4876 lstrcatW(newval, value);
4880 else
4882 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
4883 newval = msi_alloc(size);
4884 if (!newval)
4886 res = ERROR_OUTOFMEMORY;
4887 goto done;
4890 lstrcpyW(newval, value);
4893 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
4894 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
4896 done:
4897 if (env) RegCloseKey(env);
4898 msi_free(deformatted);
4899 msi_free(data);
4900 msi_free(newval);
4901 return res;
4904 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
4906 UINT rc;
4907 MSIQUERY * view;
4908 static const WCHAR ExecSeqQuery[] =
4909 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4910 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
4911 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4912 if (rc != ERROR_SUCCESS)
4913 return ERROR_SUCCESS;
4915 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
4916 msiobj_release(&view->hdr);
4918 return rc;
4921 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
4922 LPCSTR action, LPCWSTR table )
4924 static const WCHAR query[] = {
4925 'S','E','L','E','C','T',' ','*',' ',
4926 'F','R','O','M',' ','`','%','s','`',0 };
4927 MSIQUERY *view = NULL;
4928 DWORD count = 0;
4929 UINT r;
4931 r = MSI_OpenQuery( package->db, &view, query, table );
4932 if (r == ERROR_SUCCESS)
4934 r = MSI_IterateRecords(view, &count, NULL, package);
4935 msiobj_release(&view->hdr);
4938 if (count)
4939 FIXME("%s -> %u ignored %s table values\n",
4940 action, count, debugstr_w(table));
4942 return ERROR_SUCCESS;
4945 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
4947 TRACE("%p\n", package);
4948 return ERROR_SUCCESS;
4951 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4953 static const WCHAR table[] =
4954 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
4955 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
4958 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
4960 static const WCHAR table[] = { 'M','o','v','e','F','i','l','e',0 };
4961 return msi_unimplemented_action_stub( package, "MoveFiles", table );
4964 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
4966 static const WCHAR table[] = { 'P','a','t','c','h',0 };
4967 return msi_unimplemented_action_stub( package, "PatchFiles", table );
4970 static UINT ACTION_BindImage( MSIPACKAGE *package )
4972 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
4973 return msi_unimplemented_action_stub( package, "BindImage", table );
4976 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
4978 static const WCHAR table[] = {
4979 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
4980 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
4983 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
4985 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
4986 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
4989 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4991 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
4992 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
4995 static UINT ACTION_StopServices( MSIPACKAGE *package )
4997 static const WCHAR table[] = {
4998 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4999 return msi_unimplemented_action_stub( package, "StopServices", table );
5002 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5004 static const WCHAR table[] = {
5005 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5006 return msi_unimplemented_action_stub( package, "DeleteServices", table );
5008 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5010 static const WCHAR table[] = {
5011 'P','r','o','d','u','c','t','I','D',0 };
5012 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5015 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5017 static const WCHAR table[] = {
5018 'E','n','v','i','r','o','n','m','e','n','t',0 };
5019 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5022 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5024 static const WCHAR table[] = {
5025 'M','s','i','A','s','s','e','m','b','l','y',0 };
5026 return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
5029 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5031 static const WCHAR table[] = {
5032 'M','s','i','A','s','s','e','m','b','l','y',0 };
5033 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5036 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5038 static const WCHAR table[] = { 'F','o','n','t',0 };
5039 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5042 static UINT ACTION_CCPSearch( MSIPACKAGE *package )
5044 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5045 return msi_unimplemented_action_stub( package, "CCPSearch", table );
5048 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5050 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5051 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5054 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5056 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5057 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5060 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5062 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5063 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5066 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5068 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5069 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5072 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5074 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5075 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5078 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5080 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5081 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5084 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5086 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5087 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5090 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5092 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5093 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5096 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5098 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5099 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5102 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5104 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5105 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5108 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5110 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5111 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5114 static UINT ACTION_UnpublishFeatures( MSIPACKAGE *package )
5116 static const WCHAR table[] = { 'F','e','a','t','u','r','e','C','o','m','p','o','n','e','n','t','s',0 };
5117 return msi_unimplemented_action_stub( package, "UnpublishFeatures", table );
5120 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5122 static const WCHAR table[] = { 'A','p','p','I','d',0 };
5123 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5126 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5128 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5129 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5132 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5134 static const WCHAR table[] = { 'M','I','M','E',0 };
5135 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5138 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5140 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5141 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5144 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5146 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5147 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5150 static const struct _actions StandardActions[] = {
5151 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5152 { szAppSearch, ACTION_AppSearch },
5153 { szBindImage, ACTION_BindImage },
5154 { szCCPSearch, ACTION_CCPSearch },
5155 { szCostFinalize, ACTION_CostFinalize },
5156 { szCostInitialize, ACTION_CostInitialize },
5157 { szCreateFolders, ACTION_CreateFolders },
5158 { szCreateShortcuts, ACTION_CreateShortcuts },
5159 { szDeleteServices, ACTION_DeleteServices },
5160 { szDisableRollback, NULL },
5161 { szDuplicateFiles, ACTION_DuplicateFiles },
5162 { szExecuteAction, ACTION_ExecuteAction },
5163 { szFileCost, ACTION_FileCost },
5164 { szFindRelatedProducts, ACTION_FindRelatedProducts },
5165 { szForceReboot, ACTION_ForceReboot },
5166 { szInstallAdminPackage, NULL },
5167 { szInstallExecute, ACTION_InstallExecute },
5168 { szInstallExecuteAgain, ACTION_InstallExecute },
5169 { szInstallFiles, ACTION_InstallFiles},
5170 { szInstallFinalize, ACTION_InstallFinalize },
5171 { szInstallInitialize, ACTION_InstallInitialize },
5172 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5173 { szInstallValidate, ACTION_InstallValidate },
5174 { szIsolateComponents, ACTION_IsolateComponents },
5175 { szLaunchConditions, ACTION_LaunchConditions },
5176 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5177 { szMoveFiles, ACTION_MoveFiles },
5178 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5179 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5180 { szInstallODBC, ACTION_InstallODBC },
5181 { szInstallServices, ACTION_InstallServices },
5182 { szPatchFiles, ACTION_PatchFiles },
5183 { szProcessComponents, ACTION_ProcessComponents },
5184 { szPublishComponents, ACTION_PublishComponents },
5185 { szPublishFeatures, ACTION_PublishFeatures },
5186 { szPublishProduct, ACTION_PublishProduct },
5187 { szRegisterClassInfo, ACTION_RegisterClassInfo },
5188 { szRegisterComPlus, ACTION_RegisterComPlus},
5189 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5190 { szRegisterFonts, ACTION_RegisterFonts },
5191 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5192 { szRegisterProduct, ACTION_RegisterProduct },
5193 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5194 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5195 { szRegisterUser, ACTION_RegisterUser },
5196 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5197 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5198 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5199 { szRemoveFiles, ACTION_RemoveFiles },
5200 { szRemoveFolders, ACTION_RemoveFolders },
5201 { szRemoveIniValues, ACTION_RemoveIniValues },
5202 { szRemoveODBC, ACTION_RemoveODBC },
5203 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5204 { szRemoveShortcuts, ACTION_RemoveShortcuts },
5205 { szResolveSource, ACTION_ResolveSource },
5206 { szRMCCPSearch, ACTION_RMCCPSearch },
5207 { szScheduleReboot, NULL },
5208 { szSelfRegModules, ACTION_SelfRegModules },
5209 { szSelfUnregModules, ACTION_SelfUnregModules },
5210 { szSetODBCFolders, NULL },
5211 { szStartServices, ACTION_StartServices },
5212 { szStopServices, ACTION_StopServices },
5213 { szUnpublishComponents, ACTION_UnpublishComponents },
5214 { szUnpublishFeatures, ACTION_UnpublishFeatures },
5215 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5216 { szUnregisterComPlus, ACTION_UnregisterComPlus },
5217 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5218 { szUnregisterFonts, ACTION_UnregisterFonts },
5219 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5220 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5221 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5222 { szValidateProductID, ACTION_ValidateProductID },
5223 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5224 { szWriteIniValues, ACTION_WriteIniValues },
5225 { szWriteRegistryValues, ACTION_WriteRegistryValues },
5226 { NULL, NULL },