msi: Read the disk prompt source list property from the user-unmanaged context.
[wine/wine-kai.git] / dlls / msi / action.c
blob54a9fbe1e2301cfee77ee2a36094e5195d371502
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 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
613 LPWSTR p, db;
614 LPWSTR source, check;
615 DWORD len;
617 static const WCHAR szOriginalDatabase[] =
618 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
620 db = msi_dup_property( package, szOriginalDatabase );
621 if (!db)
622 return ERROR_OUTOFMEMORY;
624 p = strrchrW( db, '\\' );
625 if (!p)
627 p = strrchrW( db, '/' );
628 if (!p)
630 msi_free(db);
631 return ERROR_SUCCESS;
635 len = p - db + 2;
636 source = msi_alloc( len * sizeof(WCHAR) );
637 lstrcpynW( source, db, len );
639 check = msi_dup_property( package, cszSourceDir );
640 if (!check || replace)
641 MSI_SetPropertyW( package, cszSourceDir, source );
643 msi_free( check );
645 check = msi_dup_property( package, cszSOURCEDIR );
646 if (!check || replace)
647 MSI_SetPropertyW( package, cszSOURCEDIR, source );
649 msi_free( check );
650 msi_free( source );
651 msi_free( db );
653 return ERROR_SUCCESS;
656 /****************************************************
657 * TOP level entry points
658 *****************************************************/
660 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
661 LPCWSTR szCommandLine )
663 UINT rc;
664 BOOL ui = FALSE, ui_exists;
665 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
666 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
667 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
669 MSI_SetPropertyW(package, szAction, szInstall);
671 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
673 package->script->InWhatSequence = SEQUENCE_INSTALL;
675 if (szPackagePath)
677 LPWSTR p, dir;
678 LPCWSTR file;
680 dir = strdupW(szPackagePath);
681 p = strrchrW(dir, '\\');
682 if (p)
684 *(++p) = 0;
685 file = szPackagePath + (p - dir);
687 else
689 msi_free(dir);
690 dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
691 GetCurrentDirectoryW(MAX_PATH, dir);
692 lstrcatW(dir, cszbs);
693 file = szPackagePath;
696 msi_free( package->PackagePath );
697 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
698 if (!package->PackagePath)
700 msi_free(dir);
701 return ERROR_OUTOFMEMORY;
704 lstrcpyW(package->PackagePath, dir);
705 lstrcatW(package->PackagePath, file);
706 msi_free(dir);
708 msi_set_sourcedir_props(package, FALSE);
711 msi_parse_command_line( package, szCommandLine );
713 msi_apply_transforms( package );
714 msi_apply_patches( package );
716 /* properties may have been added by a transform */
717 msi_clone_properties( package );
719 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
721 package->script->InWhatSequence |= SEQUENCE_UI;
722 rc = ACTION_ProcessUISequence(package);
723 ui = TRUE;
724 ui_exists = ui_sequence_exists(package);
725 if (rc == ERROR_SUCCESS || !ui_exists)
727 package->script->InWhatSequence |= SEQUENCE_EXEC;
728 rc = ACTION_ProcessExecSequence(package,ui_exists);
731 else
732 rc = ACTION_ProcessExecSequence(package,FALSE);
734 package->script->CurrentlyScripting= FALSE;
736 /* process the ending type action */
737 if (rc == ERROR_SUCCESS)
738 ACTION_PerformActionSequence(package,-1,ui);
739 else if (rc == ERROR_INSTALL_USEREXIT)
740 ACTION_PerformActionSequence(package,-2,ui);
741 else if (rc == ERROR_INSTALL_SUSPEND)
742 ACTION_PerformActionSequence(package,-4,ui);
743 else /* failed */
744 ACTION_PerformActionSequence(package,-3,ui);
746 /* finish up running custom actions */
747 ACTION_FinishCustomActions(package);
749 return rc;
752 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
754 UINT rc = ERROR_SUCCESS;
755 MSIRECORD * row = 0;
756 static const WCHAR ExecSeqQuery[] =
757 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
758 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
759 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
760 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
762 static const WCHAR UISeqQuery[] =
763 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
764 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
765 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
766 ' ', '=',' ','%','i',0};
768 if (UI)
769 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
770 else
771 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
773 if (row)
775 LPCWSTR action, cond;
777 TRACE("Running the actions\n");
779 /* check conditions */
780 cond = MSI_RecordGetString(row,2);
782 /* this is a hack to skip errors in the condition code */
783 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
784 goto end;
786 action = MSI_RecordGetString(row,1);
787 if (!action)
789 ERR("failed to fetch action\n");
790 rc = ERROR_FUNCTION_FAILED;
791 goto end;
794 if (UI)
795 rc = ACTION_PerformUIAction(package,action,-1);
796 else
797 rc = ACTION_PerformAction(package,action,-1,FALSE);
798 end:
799 msiobj_release(&row->hdr);
801 else
802 rc = ERROR_SUCCESS;
804 return rc;
807 typedef struct {
808 MSIPACKAGE* package;
809 BOOL UI;
810 } iterate_action_param;
812 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
814 iterate_action_param *iap= (iterate_action_param*)param;
815 UINT rc;
816 LPCWSTR cond, action;
818 action = MSI_RecordGetString(row,1);
819 if (!action)
821 ERR("Error is retrieving action name\n");
822 return ERROR_FUNCTION_FAILED;
825 /* check conditions */
826 cond = MSI_RecordGetString(row,2);
828 /* this is a hack to skip errors in the condition code */
829 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
831 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
832 return ERROR_SUCCESS;
835 if (iap->UI)
836 rc = ACTION_PerformUIAction(iap->package,action,-1);
837 else
838 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
840 msi_dialog_check_messages( NULL );
842 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
843 rc = iap->package->CurrentInstallState;
845 if (rc == ERROR_FUNCTION_NOT_CALLED)
846 rc = ERROR_SUCCESS;
848 if (rc != ERROR_SUCCESS)
849 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
851 return rc;
854 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
856 MSIQUERY * view;
857 UINT r;
858 static const WCHAR query[] =
859 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
860 '`','%','s','`',
861 ' ','W','H','E','R','E',' ',
862 '`','S','e','q','u','e','n','c','e','`',' ',
863 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
864 '`','S','e','q','u','e','n','c','e','`',0};
865 iterate_action_param iap;
868 * FIXME: probably should be checking UILevel in the
869 * ACTION_PerformUIAction/ACTION_PerformAction
870 * rather than saving the UI level here. Those
871 * two functions can be merged too.
873 iap.package = package;
874 iap.UI = TRUE;
876 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
878 r = MSI_OpenQuery( package->db, &view, query, szTable );
879 if (r == ERROR_SUCCESS)
881 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
882 msiobj_release(&view->hdr);
885 return r;
888 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
890 MSIQUERY * view;
891 UINT rc;
892 static const WCHAR ExecSeqQuery[] =
893 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
894 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
895 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
896 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
897 'O','R','D','E','R',' ', 'B','Y',' ',
898 '`','S','e','q','u','e','n','c','e','`',0 };
899 MSIRECORD * row = 0;
900 static const WCHAR IVQuery[] =
901 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
902 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
903 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
904 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
905 ' ','\'', 'I','n','s','t','a','l','l',
906 'V','a','l','i','d','a','t','e','\'', 0};
907 INT seq = 0;
908 iterate_action_param iap;
910 iap.package = package;
911 iap.UI = FALSE;
913 if (package->script->ExecuteSequenceRun)
915 TRACE("Execute Sequence already Run\n");
916 return ERROR_SUCCESS;
919 package->script->ExecuteSequenceRun = TRUE;
921 /* get the sequence number */
922 if (UIran)
924 row = MSI_QueryGetRecord(package->db, IVQuery);
925 if( !row )
926 return ERROR_FUNCTION_FAILED;
927 seq = MSI_RecordGetInteger(row,1);
928 msiobj_release(&row->hdr);
931 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
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 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
945 MSIQUERY * view;
946 UINT rc;
947 static const WCHAR ExecSeqQuery [] =
948 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
949 '`','I','n','s','t','a','l','l',
950 'U','I','S','e','q','u','e','n','c','e','`',
951 ' ','W','H','E','R','E',' ',
952 '`','S','e','q','u','e','n','c','e','`',' ',
953 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
954 '`','S','e','q','u','e','n','c','e','`',0};
955 iterate_action_param iap;
957 iap.package = package;
958 iap.UI = TRUE;
960 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
962 if (rc == ERROR_SUCCESS)
964 TRACE("Running the actions\n");
966 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
967 msiobj_release(&view->hdr);
970 return rc;
973 /********************************************************
974 * ACTION helper functions and functions that perform the actions
975 *******************************************************/
976 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
977 UINT* rc, BOOL force )
979 BOOL ret = FALSE;
980 BOOL run = force;
981 int i;
983 if (!run && !package->script->CurrentlyScripting)
984 run = TRUE;
986 if (!run)
988 if (strcmpW(action,szInstallFinalize) == 0 ||
989 strcmpW(action,szInstallExecute) == 0 ||
990 strcmpW(action,szInstallExecuteAgain) == 0)
991 run = TRUE;
994 i = 0;
995 while (StandardActions[i].action != NULL)
997 if (strcmpW(StandardActions[i].action, action)==0)
999 if (!run)
1001 ui_actioninfo(package, action, TRUE, 0);
1002 *rc = schedule_action(package,INSTALL_SCRIPT,action);
1003 ui_actioninfo(package, action, FALSE, *rc);
1005 else
1007 ui_actionstart(package, action);
1008 if (StandardActions[i].handler)
1010 *rc = StandardActions[i].handler(package);
1012 else
1014 FIXME("unhandled standard action %s\n",debugstr_w(action));
1015 *rc = ERROR_SUCCESS;
1018 ret = TRUE;
1019 break;
1021 i++;
1023 return ret;
1026 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
1027 UINT* rc, UINT script, BOOL force )
1029 BOOL ret=FALSE;
1030 UINT arc;
1032 arc = ACTION_CustomAction(package, action, script, force);
1034 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1036 *rc = arc;
1037 ret = TRUE;
1039 return ret;
1043 * A lot of actions are really important even if they don't do anything
1044 * explicit... Lots of properties are set at the beginning of the installation
1045 * CostFinalize does a bunch of work to translate the directories and such
1047 * But until I get write access to the database that is hard, so I am going to
1048 * hack it to see if I can get something to run.
1050 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1052 UINT rc = ERROR_SUCCESS;
1053 BOOL handled;
1055 TRACE("Performing action (%s)\n",debugstr_w(action));
1057 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1059 if (!handled)
1060 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1062 if (!handled)
1064 FIXME("unhandled msi action %s\n",debugstr_w(action));
1065 rc = ERROR_FUNCTION_NOT_CALLED;
1068 return rc;
1071 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1073 UINT rc = ERROR_SUCCESS;
1074 BOOL handled = FALSE;
1076 TRACE("Performing action (%s)\n",debugstr_w(action));
1078 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1080 if (!handled)
1081 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1083 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1084 handled = TRUE;
1086 if (!handled)
1088 FIXME("unhandled msi action %s\n",debugstr_w(action));
1089 rc = ERROR_FUNCTION_NOT_CALLED;
1092 return rc;
1097 * Actual Action Handlers
1100 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1102 MSIPACKAGE *package = (MSIPACKAGE*)param;
1103 LPCWSTR dir;
1104 LPWSTR full_path;
1105 MSIRECORD *uirow;
1106 MSIFOLDER *folder;
1108 dir = MSI_RecordGetString(row,1);
1109 if (!dir)
1111 ERR("Unable to get folder id\n");
1112 return ERROR_SUCCESS;
1115 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1116 if (!full_path)
1118 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1119 return ERROR_SUCCESS;
1122 TRACE("Folder is %s\n",debugstr_w(full_path));
1124 /* UI stuff */
1125 uirow = MSI_CreateRecord(1);
1126 MSI_RecordSetStringW(uirow,1,full_path);
1127 ui_actiondata(package,szCreateFolders,uirow);
1128 msiobj_release( &uirow->hdr );
1130 if (folder->State == 0)
1131 create_full_pathW(full_path);
1133 folder->State = 3;
1135 msi_free(full_path);
1136 return ERROR_SUCCESS;
1139 /* FIXME: probably should merge this with the above function */
1140 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1142 UINT rc = ERROR_SUCCESS;
1143 MSIFOLDER *folder;
1144 LPWSTR install_path;
1146 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1147 if (!install_path)
1148 return ERROR_FUNCTION_FAILED;
1150 /* create the path */
1151 if (folder->State == 0)
1153 create_full_pathW(install_path);
1154 folder->State = 2;
1156 msi_free(install_path);
1158 return rc;
1161 UINT msi_create_component_directories( MSIPACKAGE *package )
1163 MSICOMPONENT *comp;
1165 /* create all the folders required by the components are going to install */
1166 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1168 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1169 continue;
1170 msi_create_directory( package, comp->Directory );
1173 return ERROR_SUCCESS;
1177 * Also we cannot enable/disable components either, so for now I am just going
1178 * to do all the directories for all the components.
1180 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1182 static const WCHAR ExecSeqQuery[] =
1183 {'S','E','L','E','C','T',' ',
1184 '`','D','i','r','e','c','t','o','r','y','_','`',
1185 ' ','F','R','O','M',' ',
1186 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1187 UINT rc;
1188 MSIQUERY *view;
1190 /* create all the empty folders specified in the CreateFolder table */
1191 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1192 if (rc != ERROR_SUCCESS)
1193 return ERROR_SUCCESS;
1195 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1196 msiobj_release(&view->hdr);
1198 msi_create_component_directories( package );
1200 return rc;
1203 static UINT load_component( MSIRECORD *row, LPVOID param )
1205 MSIPACKAGE *package = param;
1206 MSICOMPONENT *comp;
1208 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1209 if (!comp)
1210 return ERROR_FUNCTION_FAILED;
1212 list_add_tail( &package->components, &comp->entry );
1214 /* fill in the data */
1215 comp->Component = msi_dup_record_field( row, 1 );
1217 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1219 comp->ComponentId = msi_dup_record_field( row, 2 );
1220 comp->Directory = msi_dup_record_field( row, 3 );
1221 comp->Attributes = MSI_RecordGetInteger(row,4);
1222 comp->Condition = msi_dup_record_field( row, 5 );
1223 comp->KeyPath = msi_dup_record_field( row, 6 );
1225 comp->Installed = INSTALLSTATE_UNKNOWN;
1226 msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1228 return ERROR_SUCCESS;
1231 static UINT load_all_components( MSIPACKAGE *package )
1233 static const WCHAR query[] = {
1234 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1235 '`','C','o','m','p','o','n','e','n','t','`',0 };
1236 MSIQUERY *view;
1237 UINT r;
1239 if (!list_empty(&package->components))
1240 return ERROR_SUCCESS;
1242 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1243 if (r != ERROR_SUCCESS)
1244 return r;
1246 r = MSI_IterateRecords(view, NULL, load_component, package);
1247 msiobj_release(&view->hdr);
1248 return r;
1251 typedef struct {
1252 MSIPACKAGE *package;
1253 MSIFEATURE *feature;
1254 } _ilfs;
1256 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1258 ComponentList *cl;
1260 cl = msi_alloc( sizeof (*cl) );
1261 if ( !cl )
1262 return ERROR_NOT_ENOUGH_MEMORY;
1263 cl->component = comp;
1264 list_add_tail( &feature->Components, &cl->entry );
1266 return ERROR_SUCCESS;
1269 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1271 FeatureList *fl;
1273 fl = msi_alloc( sizeof(*fl) );
1274 if ( !fl )
1275 return ERROR_NOT_ENOUGH_MEMORY;
1276 fl->feature = child;
1277 list_add_tail( &parent->Children, &fl->entry );
1279 return ERROR_SUCCESS;
1282 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1284 _ilfs* ilfs= (_ilfs*)param;
1285 LPCWSTR component;
1286 MSICOMPONENT *comp;
1288 component = MSI_RecordGetString(row,1);
1290 /* check to see if the component is already loaded */
1291 comp = get_loaded_component( ilfs->package, component );
1292 if (!comp)
1294 ERR("unknown component %s\n", debugstr_w(component));
1295 return ERROR_FUNCTION_FAILED;
1298 add_feature_component( ilfs->feature, comp );
1299 comp->Enabled = TRUE;
1301 return ERROR_SUCCESS;
1304 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1306 MSIFEATURE *feature;
1308 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1310 if ( !lstrcmpW( feature->Feature, name ) )
1311 return feature;
1314 return NULL;
1317 static UINT load_feature(MSIRECORD * row, LPVOID param)
1319 MSIPACKAGE* package = (MSIPACKAGE*)param;
1320 MSIFEATURE* feature;
1321 static const WCHAR Query1[] =
1322 {'S','E','L','E','C','T',' ',
1323 '`','C','o','m','p','o','n','e','n','t','_','`',
1324 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1325 'C','o','m','p','o','n','e','n','t','s','`',' ',
1326 'W','H','E','R','E',' ',
1327 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1328 MSIQUERY * view;
1329 UINT rc;
1330 _ilfs ilfs;
1332 /* fill in the data */
1334 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1335 if (!feature)
1336 return ERROR_NOT_ENOUGH_MEMORY;
1338 list_init( &feature->Children );
1339 list_init( &feature->Components );
1341 feature->Feature = msi_dup_record_field( row, 1 );
1343 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1345 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1346 feature->Title = msi_dup_record_field( row, 3 );
1347 feature->Description = msi_dup_record_field( row, 4 );
1349 if (!MSI_RecordIsNull(row,5))
1350 feature->Display = MSI_RecordGetInteger(row,5);
1352 feature->Level= MSI_RecordGetInteger(row,6);
1353 feature->Directory = msi_dup_record_field( row, 7 );
1354 feature->Attributes = MSI_RecordGetInteger(row,8);
1356 feature->Installed = INSTALLSTATE_UNKNOWN;
1357 msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1359 list_add_tail( &package->features, &feature->entry );
1361 /* load feature components */
1363 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1364 if (rc != ERROR_SUCCESS)
1365 return ERROR_SUCCESS;
1367 ilfs.package = package;
1368 ilfs.feature = feature;
1370 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1371 msiobj_release(&view->hdr);
1373 return ERROR_SUCCESS;
1376 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1378 MSIPACKAGE* package = (MSIPACKAGE*)param;
1379 MSIFEATURE *parent, *child;
1381 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1382 if (!child)
1383 return ERROR_FUNCTION_FAILED;
1385 if (!child->Feature_Parent)
1386 return ERROR_SUCCESS;
1388 parent = find_feature_by_name( package, child->Feature_Parent );
1389 if (!parent)
1390 return ERROR_FUNCTION_FAILED;
1392 add_feature_child( parent, child );
1393 return ERROR_SUCCESS;
1396 static UINT load_all_features( MSIPACKAGE *package )
1398 static const WCHAR query[] = {
1399 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1400 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1401 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1402 MSIQUERY *view;
1403 UINT r;
1405 if (!list_empty(&package->features))
1406 return ERROR_SUCCESS;
1408 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1409 if (r != ERROR_SUCCESS)
1410 return r;
1412 r = MSI_IterateRecords( view, NULL, load_feature, package );
1413 if (r != ERROR_SUCCESS)
1414 return r;
1416 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1417 msiobj_release( &view->hdr );
1419 return r;
1422 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1424 if (!p)
1425 return p;
1426 p = strchrW(p, ch);
1427 if (!p)
1428 return p;
1429 *p = 0;
1430 return p+1;
1433 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1435 static const WCHAR query[] = {
1436 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1437 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1438 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1439 MSIQUERY *view = NULL;
1440 MSIRECORD *row = NULL;
1441 UINT r;
1443 TRACE("%s\n", debugstr_w(file->File));
1445 r = MSI_OpenQuery(package->db, &view, query, file->File);
1446 if (r != ERROR_SUCCESS)
1447 goto done;
1449 r = MSI_ViewExecute(view, NULL);
1450 if (r != ERROR_SUCCESS)
1451 goto done;
1453 r = MSI_ViewFetch(view, &row);
1454 if (r != ERROR_SUCCESS)
1455 goto done;
1457 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1458 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1459 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1460 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1461 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1463 done:
1464 if (view) msiobj_release(&view->hdr);
1465 if (row) msiobj_release(&row->hdr);
1466 return r;
1469 static UINT load_file(MSIRECORD *row, LPVOID param)
1471 MSIPACKAGE* package = (MSIPACKAGE*)param;
1472 LPCWSTR component;
1473 MSIFILE *file;
1475 /* fill in the data */
1477 file = msi_alloc_zero( sizeof (MSIFILE) );
1478 if (!file)
1479 return ERROR_NOT_ENOUGH_MEMORY;
1481 file->File = msi_dup_record_field( row, 1 );
1483 component = MSI_RecordGetString( row, 2 );
1484 file->Component = get_loaded_component( package, component );
1486 if (!file->Component)
1487 ERR("Unfound Component %s\n",debugstr_w(component));
1489 file->FileName = msi_dup_record_field( row, 3 );
1490 reduce_to_longfilename( file->FileName );
1492 file->ShortName = msi_dup_record_field( row, 3 );
1493 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1495 file->FileSize = MSI_RecordGetInteger( row, 4 );
1496 file->Version = msi_dup_record_field( row, 5 );
1497 file->Language = msi_dup_record_field( row, 6 );
1498 file->Attributes = MSI_RecordGetInteger( row, 7 );
1499 file->Sequence = MSI_RecordGetInteger( row, 8 );
1501 file->state = msifs_invalid;
1503 /* if the compressed bits are not set in the file attributes,
1504 * then read the information from the package word count property
1506 if (file->Attributes & msidbFileAttributesCompressed)
1508 file->IsCompressed = TRUE;
1510 else if (file->Attributes & msidbFileAttributesNoncompressed)
1512 file->IsCompressed = FALSE;
1514 else
1516 file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1519 load_file_hash(package, file);
1521 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1523 list_add_tail( &package->files, &file->entry );
1525 return ERROR_SUCCESS;
1528 static UINT load_all_files(MSIPACKAGE *package)
1530 MSIQUERY * view;
1531 UINT rc;
1532 static const WCHAR Query[] =
1533 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1534 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1535 '`','S','e','q','u','e','n','c','e','`', 0};
1537 if (!list_empty(&package->files))
1538 return ERROR_SUCCESS;
1540 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1541 if (rc != ERROR_SUCCESS)
1542 return ERROR_SUCCESS;
1544 rc = MSI_IterateRecords(view, NULL, load_file, package);
1545 msiobj_release(&view->hdr);
1547 return ERROR_SUCCESS;
1550 static UINT load_folder( MSIRECORD *row, LPVOID param )
1552 MSIPACKAGE *package = param;
1553 static const WCHAR szDot[] = { '.',0 };
1554 static WCHAR szEmpty[] = { 0 };
1555 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1556 MSIFOLDER *folder;
1558 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1559 if (!folder)
1560 return ERROR_NOT_ENOUGH_MEMORY;
1562 folder->Directory = msi_dup_record_field( row, 1 );
1564 TRACE("%s\n", debugstr_w(folder->Directory));
1566 p = msi_dup_record_field(row, 3);
1568 /* split src and target dir */
1569 tgt_short = p;
1570 src_short = folder_split_path( p, ':' );
1572 /* split the long and short paths */
1573 tgt_long = folder_split_path( tgt_short, '|' );
1574 src_long = folder_split_path( src_short, '|' );
1576 /* check for no-op dirs */
1577 if (!lstrcmpW(szDot, tgt_short))
1578 tgt_short = szEmpty;
1579 if (!lstrcmpW(szDot, src_short))
1580 src_short = szEmpty;
1582 if (!tgt_long)
1583 tgt_long = tgt_short;
1585 if (!src_short) {
1586 src_short = tgt_short;
1587 src_long = tgt_long;
1590 if (!src_long)
1591 src_long = src_short;
1593 /* FIXME: use the target short path too */
1594 folder->TargetDefault = strdupW(tgt_long);
1595 folder->SourceShortPath = strdupW(src_short);
1596 folder->SourceLongPath = strdupW(src_long);
1597 msi_free(p);
1599 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1600 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1601 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1603 folder->Parent = msi_dup_record_field( row, 2 );
1605 folder->Property = msi_dup_property( package, folder->Directory );
1607 list_add_tail( &package->folders, &folder->entry );
1609 TRACE("returning %p\n", folder);
1611 return ERROR_SUCCESS;
1614 static UINT load_all_folders( MSIPACKAGE *package )
1616 static const WCHAR query[] = {
1617 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1618 '`','D','i','r','e','c','t','o','r','y','`',0 };
1619 MSIQUERY *view;
1620 UINT r;
1622 if (!list_empty(&package->folders))
1623 return ERROR_SUCCESS;
1625 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1626 if (r != ERROR_SUCCESS)
1627 return r;
1629 r = MSI_IterateRecords(view, NULL, load_folder, package);
1630 msiobj_release(&view->hdr);
1631 return r;
1635 * I am not doing any of the costing functionality yet.
1636 * Mostly looking at doing the Component and Feature loading
1638 * The native MSI does A LOT of modification to tables here. Mostly adding
1639 * a lot of temporary columns to the Feature and Component tables.
1641 * note: Native msi also tracks the short filename. But I am only going to
1642 * track the long ones. Also looking at this directory table
1643 * it appears that the directory table does not get the parents
1644 * resolved base on property only based on their entries in the
1645 * directory table.
1647 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1649 static const WCHAR szCosting[] =
1650 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1651 static const WCHAR szZero[] = { '0', 0 };
1653 MSI_SetPropertyW(package, szCosting, szZero);
1654 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1656 load_all_components( package );
1657 load_all_features( package );
1658 load_all_files( package );
1659 load_all_folders( package );
1661 return ERROR_SUCCESS;
1664 static UINT execute_script(MSIPACKAGE *package, UINT script )
1666 UINT i;
1667 UINT rc = ERROR_SUCCESS;
1669 TRACE("Executing Script %i\n",script);
1671 if (!package->script)
1673 ERR("no script!\n");
1674 return ERROR_FUNCTION_FAILED;
1677 for (i = 0; i < package->script->ActionCount[script]; i++)
1679 LPWSTR action;
1680 action = package->script->Actions[script][i];
1681 ui_actionstart(package, action);
1682 TRACE("Executing Action (%s)\n",debugstr_w(action));
1683 rc = ACTION_PerformAction(package, action, script, TRUE);
1684 if (rc != ERROR_SUCCESS)
1685 break;
1687 msi_free_action_script(package, script);
1688 return rc;
1691 static UINT ACTION_FileCost(MSIPACKAGE *package)
1693 return ERROR_SUCCESS;
1696 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1698 MSICOMPONENT *comp;
1700 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1702 INSTALLSTATE res;
1704 if (!comp->ComponentId)
1705 continue;
1707 res = MsiGetComponentPathW( package->ProductCode,
1708 comp->ComponentId, NULL, NULL);
1709 if (res < 0)
1710 res = INSTALLSTATE_ABSENT;
1711 comp->Installed = res;
1715 /* scan for and update current install states */
1716 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1718 MSICOMPONENT *comp;
1719 MSIFEATURE *feature;
1721 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1723 ComponentList *cl;
1724 INSTALLSTATE res = INSTALLSTATE_ABSENT;
1726 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1728 comp= cl->component;
1730 if (!comp->ComponentId)
1732 res = INSTALLSTATE_ABSENT;
1733 break;
1736 if (res == INSTALLSTATE_ABSENT)
1737 res = comp->Installed;
1738 else
1740 if (res == comp->Installed)
1741 continue;
1743 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1744 res != INSTALLSTATE_SOURCE)
1746 res = INSTALLSTATE_INCOMPLETE;
1750 feature->Installed = res;
1754 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
1755 INSTALLSTATE state)
1757 static const WCHAR all[]={'A','L','L',0};
1758 LPWSTR override;
1759 MSIFEATURE *feature;
1761 override = msi_dup_property( package, property );
1762 if (!override)
1763 return FALSE;
1765 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1767 if (strcmpiW(override,all)==0)
1768 msi_feature_set_state( feature, state );
1769 else
1771 LPWSTR ptr = override;
1772 LPWSTR ptr2 = strchrW(override,',');
1774 while (ptr)
1776 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1777 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1779 msi_feature_set_state( feature, state );
1780 break;
1782 if (ptr2)
1784 ptr=ptr2+1;
1785 ptr2 = strchrW(ptr,',');
1787 else
1788 break;
1792 msi_free(override);
1794 return TRUE;
1797 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1799 int install_level;
1800 static const WCHAR szlevel[] =
1801 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1802 static const WCHAR szAddLocal[] =
1803 {'A','D','D','L','O','C','A','L',0};
1804 static const WCHAR szAddSource[] =
1805 {'A','D','D','S','O','U','R','C','E',0};
1806 static const WCHAR szRemove[] =
1807 {'R','E','M','O','V','E',0};
1808 static const WCHAR szReinstall[] =
1809 {'R','E','I','N','S','T','A','L','L',0};
1810 BOOL override = FALSE;
1811 MSICOMPONENT* component;
1812 MSIFEATURE *feature;
1815 /* I do not know if this is where it should happen.. but */
1817 TRACE("Checking Install Level\n");
1819 install_level = msi_get_property_int( package, szlevel, 1 );
1821 /* ok here is the _real_ rub
1822 * all these activation/deactivation things happen in order and things
1823 * later on the list override things earlier on the list.
1824 * 1) INSTALLLEVEL processing
1825 * 2) ADDLOCAL
1826 * 3) REMOVE
1827 * 4) ADDSOURCE
1828 * 5) ADDDEFAULT
1829 * 6) REINSTALL
1830 * 7) COMPADDLOCAL
1831 * 8) COMPADDSOURCE
1832 * 9) FILEADDLOCAL
1833 * 10) FILEADDSOURCE
1834 * 11) FILEADDDEFAULT
1835 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1836 * ignored for all the features. seems strange, especially since it is not
1837 * documented anywhere, but it is how it works.
1839 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1840 * REMOVE are the big ones, since we don't handle administrative installs
1841 * yet anyway.
1843 override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1844 override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1845 override |= process_state_property(package,szAddSource,INSTALLSTATE_SOURCE);
1846 override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1848 if (!override)
1850 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1852 BOOL feature_state = ((feature->Level > 0) &&
1853 (feature->Level <= install_level));
1855 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1857 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1858 msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1859 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1860 msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1861 else
1862 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1866 /* disable child features of unselected parent features */
1867 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1869 FeatureList *fl;
1871 if (feature->Level > 0 && feature->Level <= install_level)
1872 continue;
1874 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1875 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1878 else
1880 /* set the Preselected Property */
1881 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1882 static const WCHAR szOne[] = { '1', 0 };
1884 MSI_SetPropertyW(package,szPreselected,szOne);
1888 * now we want to enable or disable components base on feature
1891 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1893 ComponentList *cl;
1895 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1896 debugstr_w(feature->Feature), feature->Installed, feature->Action);
1898 /* features with components that have compressed files are made local */
1899 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1901 if (cl->component->Enabled &&
1902 cl->component->ForceLocalState &&
1903 feature->Action == INSTALLSTATE_SOURCE)
1905 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1906 break;
1910 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1912 component = cl->component;
1914 if (!component->Enabled)
1915 continue;
1917 switch (feature->Action)
1919 case INSTALLSTATE_ABSENT:
1920 component->anyAbsent = 1;
1921 break;
1922 case INSTALLSTATE_ADVERTISED:
1923 component->hasAdvertiseFeature = 1;
1924 break;
1925 case INSTALLSTATE_SOURCE:
1926 component->hasSourceFeature = 1;
1927 break;
1928 case INSTALLSTATE_LOCAL:
1929 component->hasLocalFeature = 1;
1930 break;
1931 case INSTALLSTATE_DEFAULT:
1932 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1933 component->hasAdvertiseFeature = 1;
1934 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1935 component->hasSourceFeature = 1;
1936 else
1937 component->hasLocalFeature = 1;
1938 break;
1939 default:
1940 break;
1945 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1947 /* if the component isn't enabled, leave it alone */
1948 if (!component->Enabled)
1949 continue;
1951 /* check if it's local or source */
1952 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1953 (component->hasLocalFeature || component->hasSourceFeature))
1955 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1956 !component->ForceLocalState)
1957 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1958 else
1959 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1960 continue;
1963 /* if any feature is local, the component must be local too */
1964 if (component->hasLocalFeature)
1966 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1967 continue;
1970 if (component->hasSourceFeature)
1972 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1973 continue;
1976 if (component->hasAdvertiseFeature)
1978 msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1979 continue;
1982 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1983 if (component->anyAbsent)
1984 msi_component_set_state(component, INSTALLSTATE_ABSENT);
1987 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1989 if (component->Action == INSTALLSTATE_DEFAULT)
1991 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1992 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1995 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1996 debugstr_w(component->Component), component->Installed, component->Action);
2000 return ERROR_SUCCESS;
2003 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
2005 MSIPACKAGE *package = (MSIPACKAGE*)param;
2006 LPCWSTR name;
2007 LPWSTR path;
2008 MSIFOLDER *f;
2010 name = MSI_RecordGetString(row,1);
2012 f = get_loaded_folder(package, name);
2013 if (!f) return ERROR_SUCCESS;
2015 /* reset the ResolvedTarget */
2016 msi_free(f->ResolvedTarget);
2017 f->ResolvedTarget = NULL;
2019 /* This helper function now does ALL the work */
2020 TRACE("Dir %s ...\n",debugstr_w(name));
2021 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
2022 TRACE("resolves to %s\n",debugstr_w(path));
2023 msi_free(path);
2025 return ERROR_SUCCESS;
2028 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2030 MSIPACKAGE *package = (MSIPACKAGE*)param;
2031 LPCWSTR name;
2032 MSIFEATURE *feature;
2034 name = MSI_RecordGetString( row, 1 );
2036 feature = get_loaded_feature( package, name );
2037 if (!feature)
2038 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2039 else
2041 LPCWSTR Condition;
2042 Condition = MSI_RecordGetString(row,3);
2044 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2046 int level = MSI_RecordGetInteger(row,2);
2047 TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
2048 feature->Level = level;
2051 return ERROR_SUCCESS;
2054 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2056 static const WCHAR name_fmt[] =
2057 {'%','u','.','%','u','.','%','u','.','%','u',0};
2058 static WCHAR name[] = {'\\',0};
2059 VS_FIXEDFILEINFO *lpVer;
2060 WCHAR filever[0x100];
2061 LPVOID version;
2062 DWORD versize;
2063 DWORD handle;
2064 UINT sz;
2066 TRACE("%s\n", debugstr_w(filename));
2068 versize = GetFileVersionInfoSizeW( filename, &handle );
2069 if (!versize)
2070 return NULL;
2072 version = msi_alloc( versize );
2073 GetFileVersionInfoW( filename, 0, versize, version );
2075 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2077 msi_free( version );
2078 return NULL;
2081 sprintfW( filever, name_fmt,
2082 HIWORD(lpVer->dwFileVersionMS),
2083 LOWORD(lpVer->dwFileVersionMS),
2084 HIWORD(lpVer->dwFileVersionLS),
2085 LOWORD(lpVer->dwFileVersionLS));
2087 msi_free( version );
2089 return strdupW( filever );
2092 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2094 LPWSTR file_version;
2095 MSIFILE *file;
2097 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2099 MSICOMPONENT* comp = file->Component;
2100 LPWSTR p;
2102 if (!comp)
2103 continue;
2105 if (file->IsCompressed)
2106 comp->ForceLocalState = TRUE;
2108 /* calculate target */
2109 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2111 msi_free(file->TargetPath);
2113 TRACE("file %s is named %s\n",
2114 debugstr_w(file->File), debugstr_w(file->FileName));
2116 file->TargetPath = build_directory_name(2, p, file->FileName);
2118 msi_free(p);
2120 TRACE("file %s resolves to %s\n",
2121 debugstr_w(file->File), debugstr_w(file->TargetPath));
2123 /* don't check files of components that aren't installed */
2124 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2125 comp->Installed == INSTALLSTATE_ABSENT)
2127 file->state = msifs_missing; /* assume files are missing */
2128 continue;
2131 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2133 file->state = msifs_missing;
2134 comp->Cost += file->FileSize;
2135 comp->Installed = INSTALLSTATE_INCOMPLETE;
2136 continue;
2139 if (file->Version &&
2140 (file_version = msi_get_disk_file_version( file->TargetPath )))
2142 TRACE("new %s old %s\n", debugstr_w(file->Version),
2143 debugstr_w(file_version));
2144 /* FIXME: seems like a bad way to compare version numbers */
2145 if (lstrcmpiW(file_version, file->Version)<0)
2147 file->state = msifs_overwrite;
2148 comp->Cost += file->FileSize;
2149 comp->Installed = INSTALLSTATE_INCOMPLETE;
2151 else
2152 file->state = msifs_present;
2153 msi_free( file_version );
2155 else
2156 file->state = msifs_present;
2159 return ERROR_SUCCESS;
2163 * A lot is done in this function aside from just the costing.
2164 * The costing needs to be implemented at some point but for now I am going
2165 * to focus on the directory building
2168 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2170 static const WCHAR ExecSeqQuery[] =
2171 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2172 '`','D','i','r','e','c','t','o','r','y','`',0};
2173 static const WCHAR ConditionQuery[] =
2174 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2175 '`','C','o','n','d','i','t','i','o','n','`',0};
2176 static const WCHAR szCosting[] =
2177 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2178 static const WCHAR szlevel[] =
2179 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2180 static const WCHAR szOne[] = { '1', 0 };
2181 MSICOMPONENT *comp;
2182 UINT rc;
2183 MSIQUERY * view;
2184 LPWSTR level;
2186 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2187 return ERROR_SUCCESS;
2189 TRACE("Building Directory properties\n");
2191 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2192 if (rc == ERROR_SUCCESS)
2194 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2195 package);
2196 msiobj_release(&view->hdr);
2199 /* read components states from the registry */
2200 ACTION_GetComponentInstallStates(package);
2202 TRACE("File calculations\n");
2203 msi_check_file_install_states( package );
2205 TRACE("Evaluating Condition Table\n");
2207 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2208 if (rc == ERROR_SUCCESS)
2210 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2211 package);
2212 msiobj_release(&view->hdr);
2215 TRACE("Enabling or Disabling Components\n");
2216 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2218 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2220 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2221 comp->Enabled = FALSE;
2225 MSI_SetPropertyW(package,szCosting,szOne);
2226 /* set default run level if not set */
2227 level = msi_dup_property( package, szlevel );
2228 if (!level)
2229 MSI_SetPropertyW(package,szlevel, szOne);
2230 msi_free(level);
2232 ACTION_UpdateFeatureInstallStates(package);
2234 return MSI_SetFeatureStates(package);
2237 /* OK this value is "interpreted" and then formatted based on the
2238 first few characters */
2239 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2240 DWORD *size)
2242 LPSTR data = NULL;
2244 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2246 if (value[1]=='x')
2248 LPWSTR ptr;
2249 CHAR byte[5];
2250 LPWSTR deformated = NULL;
2251 int count;
2253 deformat_string(package, &value[2], &deformated);
2255 /* binary value type */
2256 ptr = deformated;
2257 *type = REG_BINARY;
2258 if (strlenW(ptr)%2)
2259 *size = (strlenW(ptr)/2)+1;
2260 else
2261 *size = strlenW(ptr)/2;
2263 data = msi_alloc(*size);
2265 byte[0] = '0';
2266 byte[1] = 'x';
2267 byte[4] = 0;
2268 count = 0;
2269 /* if uneven pad with a zero in front */
2270 if (strlenW(ptr)%2)
2272 byte[2]= '0';
2273 byte[3]= *ptr;
2274 ptr++;
2275 data[count] = (BYTE)strtol(byte,NULL,0);
2276 count ++;
2277 TRACE("Uneven byte count\n");
2279 while (*ptr)
2281 byte[2]= *ptr;
2282 ptr++;
2283 byte[3]= *ptr;
2284 ptr++;
2285 data[count] = (BYTE)strtol(byte,NULL,0);
2286 count ++;
2288 msi_free(deformated);
2290 TRACE("Data %i bytes(%i)\n",*size,count);
2292 else
2294 LPWSTR deformated;
2295 LPWSTR p;
2296 DWORD d = 0;
2297 deformat_string(package, &value[1], &deformated);
2299 *type=REG_DWORD;
2300 *size = sizeof(DWORD);
2301 data = msi_alloc(*size);
2302 p = deformated;
2303 if (*p == '-')
2304 p++;
2305 while (*p)
2307 if ( (*p < '0') || (*p > '9') )
2308 break;
2309 d *= 10;
2310 d += (*p - '0');
2311 p++;
2313 if (deformated[0] == '-')
2314 d = -d;
2315 *(LPDWORD)data = d;
2316 TRACE("DWORD %i\n",*(LPDWORD)data);
2318 msi_free(deformated);
2321 else
2323 static const WCHAR szMulti[] = {'[','~',']',0};
2324 LPCWSTR ptr;
2325 *type=REG_SZ;
2327 if (value[0]=='#')
2329 if (value[1]=='%')
2331 ptr = &value[2];
2332 *type=REG_EXPAND_SZ;
2334 else
2335 ptr = &value[1];
2337 else
2338 ptr=value;
2340 if (strstrW(value,szMulti))
2341 *type = REG_MULTI_SZ;
2343 /* remove initial delimiter */
2344 if (!strncmpW(value, szMulti, 3))
2345 ptr = value + 3;
2347 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2349 /* add double NULL terminator */
2350 if (*type == REG_MULTI_SZ)
2352 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2353 data = msi_realloc_zero(data, *size);
2356 return data;
2359 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2361 MSIPACKAGE *package = (MSIPACKAGE*)param;
2362 static const WCHAR szHCR[] =
2363 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2364 'R','O','O','T','\\',0};
2365 static const WCHAR szHCU[] =
2366 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2367 'U','S','E','R','\\',0};
2368 static const WCHAR szHLM[] =
2369 {'H','K','E','Y','_','L','O','C','A','L','_',
2370 'M','A','C','H','I','N','E','\\',0};
2371 static const WCHAR szHU[] =
2372 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2374 LPSTR value_data = NULL;
2375 HKEY root_key, hkey;
2376 DWORD type,size;
2377 LPWSTR deformated;
2378 LPCWSTR szRoot, component, name, key, value;
2379 MSICOMPONENT *comp;
2380 MSIRECORD * uirow;
2381 LPWSTR uikey;
2382 INT root;
2383 BOOL check_first = FALSE;
2384 UINT rc;
2386 ui_progress(package,2,0,0,0);
2388 value = NULL;
2389 key = NULL;
2390 uikey = NULL;
2391 name = NULL;
2393 component = MSI_RecordGetString(row, 6);
2394 comp = get_loaded_component(package,component);
2395 if (!comp)
2396 return ERROR_SUCCESS;
2398 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2400 TRACE("Skipping write due to disabled component %s\n",
2401 debugstr_w(component));
2403 comp->Action = comp->Installed;
2405 return ERROR_SUCCESS;
2408 comp->Action = INSTALLSTATE_LOCAL;
2410 name = MSI_RecordGetString(row, 4);
2411 if( MSI_RecordIsNull(row,5) && name )
2413 /* null values can have special meanings */
2414 if (name[0]=='-' && name[1] == 0)
2415 return ERROR_SUCCESS;
2416 else if ((name[0]=='+' && name[1] == 0) ||
2417 (name[0] == '*' && name[1] == 0))
2418 name = NULL;
2419 check_first = TRUE;
2422 root = MSI_RecordGetInteger(row,2);
2423 key = MSI_RecordGetString(row, 3);
2425 /* get the root key */
2426 switch (root)
2428 case -1:
2430 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2431 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2432 if (all_users && all_users[0] == '1')
2434 root_key = HKEY_LOCAL_MACHINE;
2435 szRoot = szHLM;
2437 else
2439 root_key = HKEY_CURRENT_USER;
2440 szRoot = szHCU;
2442 msi_free(all_users);
2444 break;
2445 case 0: root_key = HKEY_CLASSES_ROOT;
2446 szRoot = szHCR;
2447 break;
2448 case 1: root_key = HKEY_CURRENT_USER;
2449 szRoot = szHCU;
2450 break;
2451 case 2: root_key = HKEY_LOCAL_MACHINE;
2452 szRoot = szHLM;
2453 break;
2454 case 3: root_key = HKEY_USERS;
2455 szRoot = szHU;
2456 break;
2457 default:
2458 ERR("Unknown root %i\n",root);
2459 root_key=NULL;
2460 szRoot = NULL;
2461 break;
2463 if (!root_key)
2464 return ERROR_SUCCESS;
2466 deformat_string(package, key , &deformated);
2467 size = strlenW(deformated) + strlenW(szRoot) + 1;
2468 uikey = msi_alloc(size*sizeof(WCHAR));
2469 strcpyW(uikey,szRoot);
2470 strcatW(uikey,deformated);
2472 if (RegCreateKeyW( root_key, deformated, &hkey))
2474 ERR("Could not create key %s\n",debugstr_w(deformated));
2475 msi_free(deformated);
2476 msi_free(uikey);
2477 return ERROR_SUCCESS;
2479 msi_free(deformated);
2481 value = MSI_RecordGetString(row,5);
2482 if (value)
2483 value_data = parse_value(package, value, &type, &size);
2484 else
2486 static const WCHAR szEmpty[] = {0};
2487 value_data = (LPSTR)strdupW(szEmpty);
2488 size = 0;
2489 type = REG_SZ;
2492 deformat_string(package, name, &deformated);
2494 if (!check_first)
2496 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2497 debugstr_w(uikey));
2498 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2500 else
2502 DWORD sz = 0;
2503 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2504 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2506 TRACE("value %s of %s checked already exists\n",
2507 debugstr_w(deformated), debugstr_w(uikey));
2509 else
2511 TRACE("Checked and setting value %s of %s\n",
2512 debugstr_w(deformated), debugstr_w(uikey));
2513 if (deformated || size)
2514 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2517 RegCloseKey(hkey);
2519 uirow = MSI_CreateRecord(3);
2520 MSI_RecordSetStringW(uirow,2,deformated);
2521 MSI_RecordSetStringW(uirow,1,uikey);
2523 if (type == REG_SZ)
2524 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2525 else
2526 MSI_RecordSetStringW(uirow,3,value);
2528 ui_actiondata(package,szWriteRegistryValues,uirow);
2529 msiobj_release( &uirow->hdr );
2531 msi_free(value_data);
2532 msi_free(deformated);
2533 msi_free(uikey);
2535 return ERROR_SUCCESS;
2538 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2540 UINT rc;
2541 MSIQUERY * view;
2542 static const WCHAR ExecSeqQuery[] =
2543 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2544 '`','R','e','g','i','s','t','r','y','`',0 };
2546 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2547 if (rc != ERROR_SUCCESS)
2548 return ERROR_SUCCESS;
2550 /* increment progress bar each time action data is sent */
2551 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2553 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2555 msiobj_release(&view->hdr);
2556 return rc;
2559 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2561 package->script->CurrentlyScripting = TRUE;
2563 return ERROR_SUCCESS;
2567 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2569 MSICOMPONENT *comp;
2570 DWORD progress = 0;
2571 DWORD total = 0;
2572 static const WCHAR q1[]=
2573 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2574 '`','R','e','g','i','s','t','r','y','`',0};
2575 UINT rc;
2576 MSIQUERY * view;
2577 MSIFEATURE *feature;
2578 MSIFILE *file;
2580 TRACE("InstallValidate\n");
2582 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2583 if (rc == ERROR_SUCCESS)
2585 MSI_IterateRecords( view, &progress, NULL, package );
2586 msiobj_release( &view->hdr );
2587 total += progress * REG_PROGRESS_VALUE;
2590 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2591 total += COMPONENT_PROGRESS_VALUE;
2593 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2594 total += file->FileSize;
2596 ui_progress(package,0,total,0,0);
2598 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2600 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2601 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2602 feature->ActionRequest);
2605 return ERROR_SUCCESS;
2608 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2610 MSIPACKAGE* package = (MSIPACKAGE*)param;
2611 LPCWSTR cond = NULL;
2612 LPCWSTR message = NULL;
2613 UINT r;
2615 static const WCHAR title[]=
2616 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2618 cond = MSI_RecordGetString(row,1);
2620 r = MSI_EvaluateConditionW(package,cond);
2621 if (r == MSICONDITION_FALSE)
2623 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2625 LPWSTR deformated;
2626 message = MSI_RecordGetString(row,2);
2627 deformat_string(package,message,&deformated);
2628 MessageBoxW(NULL,deformated,title,MB_OK);
2629 msi_free(deformated);
2632 return ERROR_INSTALL_FAILURE;
2635 return ERROR_SUCCESS;
2638 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2640 UINT rc;
2641 MSIQUERY * view = NULL;
2642 static const WCHAR ExecSeqQuery[] =
2643 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2644 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2646 TRACE("Checking launch conditions\n");
2648 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2649 if (rc != ERROR_SUCCESS)
2650 return ERROR_SUCCESS;
2652 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2653 msiobj_release(&view->hdr);
2655 return rc;
2658 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2661 if (!cmp->KeyPath)
2662 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2664 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2666 MSIRECORD * row = 0;
2667 UINT root,len;
2668 LPWSTR deformated,buffer,deformated_name;
2669 LPCWSTR key,name;
2670 static const WCHAR ExecSeqQuery[] =
2671 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2672 '`','R','e','g','i','s','t','r','y','`',' ',
2673 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2674 ' ','=',' ' ,'\'','%','s','\'',0 };
2675 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2676 static const WCHAR fmt2[]=
2677 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2679 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2680 if (!row)
2681 return NULL;
2683 root = MSI_RecordGetInteger(row,2);
2684 key = MSI_RecordGetString(row, 3);
2685 name = MSI_RecordGetString(row, 4);
2686 deformat_string(package, key , &deformated);
2687 deformat_string(package, name, &deformated_name);
2689 len = strlenW(deformated) + 6;
2690 if (deformated_name)
2691 len+=strlenW(deformated_name);
2693 buffer = msi_alloc( len *sizeof(WCHAR));
2695 if (deformated_name)
2696 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2697 else
2698 sprintfW(buffer,fmt,root,deformated);
2700 msi_free(deformated);
2701 msi_free(deformated_name);
2702 msiobj_release(&row->hdr);
2704 return buffer;
2706 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2708 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2709 return NULL;
2711 else
2713 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2715 if (file)
2716 return strdupW( file->TargetPath );
2718 return NULL;
2721 static HKEY openSharedDLLsKey(void)
2723 HKEY hkey=0;
2724 static const WCHAR path[] =
2725 {'S','o','f','t','w','a','r','e','\\',
2726 'M','i','c','r','o','s','o','f','t','\\',
2727 'W','i','n','d','o','w','s','\\',
2728 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2729 'S','h','a','r','e','d','D','L','L','s',0};
2731 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2732 return hkey;
2735 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2737 HKEY hkey;
2738 DWORD count=0;
2739 DWORD type;
2740 DWORD sz = sizeof(count);
2741 DWORD rc;
2743 hkey = openSharedDLLsKey();
2744 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2745 if (rc != ERROR_SUCCESS)
2746 count = 0;
2747 RegCloseKey(hkey);
2748 return count;
2751 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2753 HKEY hkey;
2755 hkey = openSharedDLLsKey();
2756 if (count > 0)
2757 msi_reg_set_val_dword( hkey, path, count );
2758 else
2759 RegDeleteValueW(hkey,path);
2760 RegCloseKey(hkey);
2761 return count;
2765 * Return TRUE if the count should be written out and FALSE if not
2767 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2769 MSIFEATURE *feature;
2770 INT count = 0;
2771 BOOL write = FALSE;
2773 /* only refcount DLLs */
2774 if (comp->KeyPath == NULL ||
2775 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2776 comp->Attributes & msidbComponentAttributesODBCDataSource)
2777 write = FALSE;
2778 else
2780 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2781 write = (count > 0);
2783 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2784 write = TRUE;
2787 /* increment counts */
2788 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2790 ComponentList *cl;
2792 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2793 continue;
2795 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2797 if ( cl->component == comp )
2798 count++;
2802 /* decrement counts */
2803 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2805 ComponentList *cl;
2807 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2808 continue;
2810 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2812 if ( cl->component == comp )
2813 count--;
2817 /* ref count all the files in the component */
2818 if (write)
2820 MSIFILE *file;
2822 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2824 if (file->Component == comp)
2825 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2829 /* add a count for permenent */
2830 if (comp->Attributes & msidbComponentAttributesPermanent)
2831 count ++;
2833 comp->RefCount = count;
2835 if (write)
2836 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2840 * Ok further analysis makes me think that this work is
2841 * actually done in the PublishComponents and PublishFeatures
2842 * step, and not here. It appears like the keypath and all that is
2843 * resolved in this step, however actually written in the Publish steps.
2844 * But we will leave it here for now because it is unclear
2846 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2848 WCHAR squished_pc[GUID_SIZE];
2849 WCHAR squished_cc[GUID_SIZE];
2850 UINT rc;
2851 MSICOMPONENT *comp;
2852 HKEY hkey=0,hkey2=0;
2854 TRACE("\n");
2856 /* writes the Component and Features values to the registry */
2858 rc = MSIREG_OpenComponents(&hkey);
2859 if (rc != ERROR_SUCCESS)
2860 return rc;
2862 squash_guid(package->ProductCode,squished_pc);
2863 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2865 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2867 MSIRECORD * uirow;
2869 ui_progress(package,2,0,0,0);
2870 if (!comp->ComponentId)
2871 continue;
2873 squash_guid(comp->ComponentId,squished_cc);
2875 msi_free(comp->FullKeypath);
2876 comp->FullKeypath = resolve_keypath( package, comp );
2878 /* do the refcounting */
2879 ACTION_RefCountComponent( package, comp );
2881 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2882 debugstr_w(comp->Component),
2883 debugstr_w(squished_cc),
2884 debugstr_w(comp->FullKeypath),
2885 comp->RefCount);
2887 * Write the keypath out if the component is to be registered
2888 * and delete the key if the component is to be deregistered
2890 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2892 rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2893 if (rc != ERROR_SUCCESS)
2894 continue;
2896 if (!comp->FullKeypath)
2897 continue;
2899 msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2901 if (comp->Attributes & msidbComponentAttributesPermanent)
2903 static const WCHAR szPermKey[] =
2904 { '0','0','0','0','0','0','0','0','0','0','0','0',
2905 '0','0','0','0','0','0','0','0','0','0','0','0',
2906 '0','0','0','0','0','0','0','0',0 };
2908 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2911 RegCloseKey(hkey2);
2913 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey2, TRUE);
2914 if (rc != ERROR_SUCCESS)
2915 continue;
2917 msi_reg_set_val_str(hkey2, squished_pc, comp->FullKeypath);
2918 RegCloseKey(hkey2);
2920 else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2922 DWORD res;
2924 rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2925 if (rc != ERROR_SUCCESS)
2926 continue;
2928 RegDeleteValueW(hkey2,squished_pc);
2930 /* if the key is empty delete it */
2931 res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2932 RegCloseKey(hkey2);
2933 if (res == ERROR_NO_MORE_ITEMS)
2934 RegDeleteKeyW(hkey,squished_cc);
2936 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2939 /* UI stuff */
2940 uirow = MSI_CreateRecord(3);
2941 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2942 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2943 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2944 ui_actiondata(package,szProcessComponents,uirow);
2945 msiobj_release( &uirow->hdr );
2947 RegCloseKey(hkey);
2948 return rc;
2951 typedef struct {
2952 CLSID clsid;
2953 LPWSTR source;
2955 LPWSTR path;
2956 ITypeLib *ptLib;
2957 } typelib_struct;
2959 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2960 LPWSTR lpszName, LONG_PTR lParam)
2962 TLIBATTR *attr;
2963 typelib_struct *tl_struct = (typelib_struct*) lParam;
2964 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2965 int sz;
2966 HRESULT res;
2968 if (!IS_INTRESOURCE(lpszName))
2970 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2971 return TRUE;
2974 sz = strlenW(tl_struct->source)+4;
2975 sz *= sizeof(WCHAR);
2977 if ((INT_PTR)lpszName == 1)
2978 tl_struct->path = strdupW(tl_struct->source);
2979 else
2981 tl_struct->path = msi_alloc(sz);
2982 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2985 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2986 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2987 if (!SUCCEEDED(res))
2989 msi_free(tl_struct->path);
2990 tl_struct->path = NULL;
2992 return TRUE;
2995 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2996 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2998 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2999 return FALSE;
3002 msi_free(tl_struct->path);
3003 tl_struct->path = NULL;
3005 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3006 ITypeLib_Release(tl_struct->ptLib);
3008 return TRUE;
3011 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3013 MSIPACKAGE* package = (MSIPACKAGE*)param;
3014 LPCWSTR component;
3015 MSICOMPONENT *comp;
3016 MSIFILE *file;
3017 typelib_struct tl_struct;
3018 HMODULE module;
3019 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3021 component = MSI_RecordGetString(row,3);
3022 comp = get_loaded_component(package,component);
3023 if (!comp)
3024 return ERROR_SUCCESS;
3026 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3028 TRACE("Skipping typelib reg due to disabled component\n");
3030 comp->Action = comp->Installed;
3032 return ERROR_SUCCESS;
3035 comp->Action = INSTALLSTATE_LOCAL;
3037 file = get_loaded_file( package, comp->KeyPath );
3038 if (!file)
3039 return ERROR_SUCCESS;
3041 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3042 if (module)
3044 LPCWSTR guid;
3045 guid = MSI_RecordGetString(row,1);
3046 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3047 tl_struct.source = strdupW( file->TargetPath );
3048 tl_struct.path = NULL;
3050 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3051 (LONG_PTR)&tl_struct);
3053 if (tl_struct.path)
3055 LPWSTR help = NULL;
3056 LPCWSTR helpid;
3057 HRESULT res;
3059 helpid = MSI_RecordGetString(row,6);
3061 if (helpid)
3062 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3063 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3064 msi_free(help);
3066 if (!SUCCEEDED(res))
3067 ERR("Failed to register type library %s\n",
3068 debugstr_w(tl_struct.path));
3069 else
3071 ui_actiondata(package,szRegisterTypeLibraries,row);
3073 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3076 ITypeLib_Release(tl_struct.ptLib);
3077 msi_free(tl_struct.path);
3079 else
3080 ERR("Failed to load type library %s\n",
3081 debugstr_w(tl_struct.source));
3083 FreeLibrary(module);
3084 msi_free(tl_struct.source);
3086 else
3087 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
3089 return ERROR_SUCCESS;
3092 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3095 * OK this is a bit confusing.. I am given a _Component key and I believe
3096 * that the file that is being registered as a type library is the "key file
3097 * of that component" which I interpret to mean "The file in the KeyPath of
3098 * that component".
3100 UINT rc;
3101 MSIQUERY * view;
3102 static const WCHAR Query[] =
3103 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3104 '`','T','y','p','e','L','i','b','`',0};
3106 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3107 if (rc != ERROR_SUCCESS)
3108 return ERROR_SUCCESS;
3110 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3111 msiobj_release(&view->hdr);
3112 return rc;
3115 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3117 MSIPACKAGE *package = (MSIPACKAGE*)param;
3118 LPWSTR target_file, target_folder, filename;
3119 LPCWSTR buffer, extension;
3120 MSICOMPONENT *comp;
3121 static const WCHAR szlnk[]={'.','l','n','k',0};
3122 IShellLinkW *sl = NULL;
3123 IPersistFile *pf = NULL;
3124 HRESULT res;
3126 buffer = MSI_RecordGetString(row,4);
3127 comp = get_loaded_component(package,buffer);
3128 if (!comp)
3129 return ERROR_SUCCESS;
3131 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3133 TRACE("Skipping shortcut creation due to disabled component\n");
3135 comp->Action = comp->Installed;
3137 return ERROR_SUCCESS;
3140 comp->Action = INSTALLSTATE_LOCAL;
3142 ui_actiondata(package,szCreateShortcuts,row);
3144 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3145 &IID_IShellLinkW, (LPVOID *) &sl );
3147 if (FAILED( res ))
3149 ERR("CLSID_ShellLink not available\n");
3150 goto err;
3153 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3154 if (FAILED( res ))
3156 ERR("QueryInterface(IID_IPersistFile) failed\n");
3157 goto err;
3160 buffer = MSI_RecordGetString(row,2);
3161 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3163 /* may be needed because of a bug somehwere else */
3164 create_full_pathW(target_folder);
3166 filename = msi_dup_record_field( row, 3 );
3167 reduce_to_longfilename(filename);
3169 extension = strchrW(filename,'.');
3170 if (!extension || strcmpiW(extension,szlnk))
3172 int len = strlenW(filename);
3173 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3174 memcpy(filename + len, szlnk, sizeof(szlnk));
3176 target_file = build_directory_name(2, target_folder, filename);
3177 msi_free(target_folder);
3178 msi_free(filename);
3180 buffer = MSI_RecordGetString(row,5);
3181 if (strchrW(buffer,'['))
3183 LPWSTR deformated;
3184 deformat_string(package,buffer,&deformated);
3185 IShellLinkW_SetPath(sl,deformated);
3186 msi_free(deformated);
3188 else
3190 FIXME("poorly handled shortcut format, advertised shortcut\n");
3191 IShellLinkW_SetPath(sl,comp->FullKeypath);
3194 if (!MSI_RecordIsNull(row,6))
3196 LPWSTR deformated;
3197 buffer = MSI_RecordGetString(row,6);
3198 deformat_string(package,buffer,&deformated);
3199 IShellLinkW_SetArguments(sl,deformated);
3200 msi_free(deformated);
3203 if (!MSI_RecordIsNull(row,7))
3205 buffer = MSI_RecordGetString(row,7);
3206 IShellLinkW_SetDescription(sl,buffer);
3209 if (!MSI_RecordIsNull(row,8))
3210 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3212 if (!MSI_RecordIsNull(row,9))
3214 LPWSTR Path;
3215 INT index;
3217 buffer = MSI_RecordGetString(row,9);
3219 Path = build_icon_path(package,buffer);
3220 index = MSI_RecordGetInteger(row,10);
3222 /* no value means 0 */
3223 if (index == MSI_NULL_INTEGER)
3224 index = 0;
3226 IShellLinkW_SetIconLocation(sl,Path,index);
3227 msi_free(Path);
3230 if (!MSI_RecordIsNull(row,11))
3231 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3233 if (!MSI_RecordIsNull(row,12))
3235 LPWSTR Path;
3236 buffer = MSI_RecordGetString(row,12);
3237 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3238 if (Path)
3239 IShellLinkW_SetWorkingDirectory(sl,Path);
3240 msi_free(Path);
3243 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3244 IPersistFile_Save(pf,target_file,FALSE);
3246 msi_free(target_file);
3248 err:
3249 if (pf)
3250 IPersistFile_Release( pf );
3251 if (sl)
3252 IShellLinkW_Release( sl );
3254 return ERROR_SUCCESS;
3257 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3259 UINT rc;
3260 HRESULT res;
3261 MSIQUERY * view;
3262 static const WCHAR Query[] =
3263 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3264 '`','S','h','o','r','t','c','u','t','`',0};
3266 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3267 if (rc != ERROR_SUCCESS)
3268 return ERROR_SUCCESS;
3270 res = CoInitialize( NULL );
3271 if (FAILED (res))
3273 ERR("CoInitialize failed\n");
3274 return ERROR_FUNCTION_FAILED;
3277 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3278 msiobj_release(&view->hdr);
3280 CoUninitialize();
3282 return rc;
3285 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3287 MSIPACKAGE* package = (MSIPACKAGE*)param;
3288 HANDLE the_file;
3289 LPWSTR FilePath;
3290 LPCWSTR FileName;
3291 CHAR buffer[1024];
3292 DWORD sz;
3293 UINT rc;
3294 MSIRECORD *uirow;
3296 FileName = MSI_RecordGetString(row,1);
3297 if (!FileName)
3299 ERR("Unable to get FileName\n");
3300 return ERROR_SUCCESS;
3303 FilePath = build_icon_path(package,FileName);
3305 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3307 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3308 FILE_ATTRIBUTE_NORMAL, NULL);
3310 if (the_file == INVALID_HANDLE_VALUE)
3312 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3313 msi_free(FilePath);
3314 return ERROR_SUCCESS;
3319 DWORD write;
3320 sz = 1024;
3321 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3322 if (rc != ERROR_SUCCESS)
3324 ERR("Failed to get stream\n");
3325 CloseHandle(the_file);
3326 DeleteFileW(FilePath);
3327 break;
3329 WriteFile(the_file,buffer,sz,&write,NULL);
3330 } while (sz == 1024);
3332 msi_free(FilePath);
3334 CloseHandle(the_file);
3336 uirow = MSI_CreateRecord(1);
3337 MSI_RecordSetStringW(uirow,1,FileName);
3338 ui_actiondata(package,szPublishProduct,uirow);
3339 msiobj_release( &uirow->hdr );
3341 return ERROR_SUCCESS;
3344 static BOOL msi_check_publish(MSIPACKAGE *package)
3346 MSIFEATURE *feature;
3348 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3350 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3351 return TRUE;
3354 return FALSE;
3358 * 99% of the work done here is only done for
3359 * advertised installs. However this is where the
3360 * Icon table is processed and written out
3361 * so that is what I am going to do here.
3363 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3365 UINT rc;
3366 MSIQUERY * view;
3367 MSISOURCELISTINFO *info;
3368 MSIMEDIADISK *disk;
3369 static const WCHAR Query[]=
3370 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3371 '`','I','c','o','n','`',0};
3372 /* for registry stuff */
3373 HKEY hkey=0;
3374 HKEY hukey=0;
3375 HKEY hudkey=0, props=0;
3376 HKEY source;
3377 static const WCHAR szProductLanguage[] =
3378 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3379 static const WCHAR szARPProductIcon[] =
3380 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3381 static const WCHAR szProductVersion[] =
3382 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3383 static const WCHAR szSourceList[] =
3384 {'S','o','u','r','c','e','L','i','s','t',0};
3385 static const WCHAR szEmpty[] = {0};
3386 DWORD langid;
3387 LPWSTR buffer;
3388 DWORD size;
3389 MSIHANDLE hDb, hSumInfo;
3391 /* FIXME: also need to publish if the product is in advertise mode */
3392 if (!msi_check_publish(package))
3393 return ERROR_SUCCESS;
3395 /* write out icon files */
3397 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3398 if (rc == ERROR_SUCCESS)
3400 MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3401 msiobj_release(&view->hdr);
3404 /* ok there is a lot more done here but i need to figure out what */
3406 rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3407 if (rc != ERROR_SUCCESS)
3408 goto end;
3410 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3411 if (rc != ERROR_SUCCESS)
3412 goto end;
3414 rc = RegCreateKeyW(hukey, szSourceList, &source);
3415 if (rc != ERROR_SUCCESS)
3416 goto end;
3418 RegCloseKey(source);
3420 rc = MSIREG_OpenUserDataProductKey(package->ProductCode,&hudkey,TRUE);
3421 if (rc != ERROR_SUCCESS)
3422 goto end;
3424 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode,&props,TRUE);
3425 if (rc != ERROR_SUCCESS)
3426 goto end;
3428 buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3429 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3430 msi_free(buffer);
3432 langid = msi_get_property_int( package, szProductLanguage, 0 );
3433 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3435 buffer = msi_dup_property( package, szARPProductIcon );
3436 if (buffer)
3438 LPWSTR path = build_icon_path(package,buffer);
3439 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3440 msi_free( path );
3442 msi_free(buffer);
3444 buffer = msi_dup_property( package, szProductVersion );
3445 if (buffer)
3447 DWORD verdword = msi_version_str_to_dword(buffer);
3448 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3450 msi_free(buffer);
3452 buffer = strrchrW( package->PackagePath, '\\') + 1;
3453 rc = MsiSourceListSetInfoW( package->ProductCode, NULL,
3454 MSIINSTALLCONTEXT_USERUNMANAGED, MSICODE_PRODUCT,
3455 INSTALLPROPERTY_PACKAGENAMEW, buffer );
3456 if (rc != ERROR_SUCCESS)
3457 goto end;
3459 rc = MsiSourceListSetInfoW( package->ProductCode, NULL,
3460 MSIINSTALLCONTEXT_USERUNMANAGED, MSICODE_PRODUCT,
3461 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty );
3462 if (rc != ERROR_SUCCESS)
3463 goto end;
3465 rc = MsiSourceListSetInfoW( package->ProductCode, NULL,
3466 MSIINSTALLCONTEXT_USERUNMANAGED, MSICODE_PRODUCT,
3467 INSTALLPROPERTY_DISKPROMPTW, szEmpty );
3468 if (rc != ERROR_SUCCESS)
3469 goto end;
3471 buffer = msi_dup_property( package, cszSourceDir );
3473 rc = MsiSourceListSetInfoW( package->ProductCode, NULL,
3474 MSIINSTALLCONTEXT_USERUNMANAGED,
3475 MSICODE_PRODUCT | MSISOURCETYPE_NETWORK,
3476 INSTALLPROPERTY_LASTUSEDSOURCEW, buffer );
3477 msi_free(buffer);
3478 if (rc != ERROR_SUCCESS)
3479 goto end;
3481 /* FIXME: Need to write more keys to the user registry */
3483 hDb= alloc_msihandle( &package->db->hdr );
3484 if (!hDb) {
3485 rc = ERROR_NOT_ENOUGH_MEMORY;
3486 goto end;
3488 rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo);
3489 MsiCloseHandle(hDb);
3490 if (rc == ERROR_SUCCESS)
3492 WCHAR guidbuffer[0x200];
3493 size = 0x200;
3494 rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3495 guidbuffer, &size);
3496 if (rc == ERROR_SUCCESS)
3498 WCHAR squashed[GUID_SIZE];
3499 /* for now we only care about the first guid */
3500 LPWSTR ptr = strchrW(guidbuffer,';');
3501 if (ptr) *ptr = 0;
3502 squash_guid(guidbuffer,squashed);
3503 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3505 else
3507 ERR("Unable to query Revision_Number...\n");
3508 rc = ERROR_SUCCESS;
3510 MsiCloseHandle(hSumInfo);
3512 else
3514 ERR("Unable to open Summary Information\n");
3515 rc = ERROR_SUCCESS;
3518 /* publish the SourceList info */
3519 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3521 MsiSourceListSetInfoW(package->ProductCode, NULL,
3522 info->context, info->options,
3523 info->property, info->value);
3526 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3528 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3529 disk->context, disk->options,
3530 disk->disk_id, disk->volume_label, disk->disk_prompt);
3533 end:
3534 RegCloseKey(hkey);
3535 RegCloseKey(hukey);
3536 RegCloseKey(hudkey);
3537 RegCloseKey(props);
3539 return rc;
3542 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3544 MSIPACKAGE *package = (MSIPACKAGE*)param;
3545 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3546 LPWSTR deformated_section, deformated_key, deformated_value;
3547 LPWSTR folder, fullname = NULL;
3548 MSIRECORD * uirow;
3549 INT action;
3550 MSICOMPONENT *comp;
3551 static const WCHAR szWindowsFolder[] =
3552 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3554 component = MSI_RecordGetString(row, 8);
3555 comp = get_loaded_component(package,component);
3557 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3559 TRACE("Skipping ini file due to disabled component %s\n",
3560 debugstr_w(component));
3562 comp->Action = comp->Installed;
3564 return ERROR_SUCCESS;
3567 comp->Action = INSTALLSTATE_LOCAL;
3569 identifier = MSI_RecordGetString(row,1);
3570 filename = MSI_RecordGetString(row,2);
3571 dirproperty = MSI_RecordGetString(row,3);
3572 section = MSI_RecordGetString(row,4);
3573 key = MSI_RecordGetString(row,5);
3574 value = MSI_RecordGetString(row,6);
3575 action = MSI_RecordGetInteger(row,7);
3577 deformat_string(package,section,&deformated_section);
3578 deformat_string(package,key,&deformated_key);
3579 deformat_string(package,value,&deformated_value);
3581 if (dirproperty)
3583 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3584 if (!folder)
3585 folder = msi_dup_property( package, dirproperty );
3587 else
3588 folder = msi_dup_property( package, szWindowsFolder );
3590 if (!folder)
3592 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3593 goto cleanup;
3596 fullname = build_directory_name(2, folder, filename);
3598 if (action == 0)
3600 TRACE("Adding value %s to section %s in %s\n",
3601 debugstr_w(deformated_key), debugstr_w(deformated_section),
3602 debugstr_w(fullname));
3603 WritePrivateProfileStringW(deformated_section, deformated_key,
3604 deformated_value, fullname);
3606 else if (action == 1)
3608 WCHAR returned[10];
3609 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3610 returned, 10, fullname);
3611 if (returned[0] == 0)
3613 TRACE("Adding value %s to section %s in %s\n",
3614 debugstr_w(deformated_key), debugstr_w(deformated_section),
3615 debugstr_w(fullname));
3617 WritePrivateProfileStringW(deformated_section, deformated_key,
3618 deformated_value, fullname);
3621 else if (action == 3)
3622 FIXME("Append to existing section not yet implemented\n");
3624 uirow = MSI_CreateRecord(4);
3625 MSI_RecordSetStringW(uirow,1,identifier);
3626 MSI_RecordSetStringW(uirow,2,deformated_section);
3627 MSI_RecordSetStringW(uirow,3,deformated_key);
3628 MSI_RecordSetStringW(uirow,4,deformated_value);
3629 ui_actiondata(package,szWriteIniValues,uirow);
3630 msiobj_release( &uirow->hdr );
3631 cleanup:
3632 msi_free(fullname);
3633 msi_free(folder);
3634 msi_free(deformated_key);
3635 msi_free(deformated_value);
3636 msi_free(deformated_section);
3637 return ERROR_SUCCESS;
3640 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3642 UINT rc;
3643 MSIQUERY * view;
3644 static const WCHAR ExecSeqQuery[] =
3645 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3646 '`','I','n','i','F','i','l','e','`',0};
3648 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3649 if (rc != ERROR_SUCCESS)
3651 TRACE("no IniFile table\n");
3652 return ERROR_SUCCESS;
3655 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3656 msiobj_release(&view->hdr);
3657 return rc;
3660 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3662 MSIPACKAGE *package = (MSIPACKAGE*)param;
3663 LPCWSTR filename;
3664 LPWSTR FullName;
3665 MSIFILE *file;
3666 DWORD len;
3667 static const WCHAR ExeStr[] =
3668 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3669 static const WCHAR close[] = {'\"',0};
3670 STARTUPINFOW si;
3671 PROCESS_INFORMATION info;
3672 BOOL brc;
3673 MSIRECORD *uirow;
3674 LPWSTR uipath, p;
3676 memset(&si,0,sizeof(STARTUPINFOW));
3678 filename = MSI_RecordGetString(row,1);
3679 file = get_loaded_file( package, filename );
3681 if (!file)
3683 ERR("Unable to find file id %s\n",debugstr_w(filename));
3684 return ERROR_SUCCESS;
3687 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3689 FullName = msi_alloc(len*sizeof(WCHAR));
3690 strcpyW(FullName,ExeStr);
3691 strcatW( FullName, file->TargetPath );
3692 strcatW(FullName,close);
3694 TRACE("Registering %s\n",debugstr_w(FullName));
3695 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3696 &si, &info);
3698 if (brc)
3699 msi_dialog_check_messages(info.hProcess);
3701 msi_free(FullName);
3703 /* the UI chunk */
3704 uirow = MSI_CreateRecord( 2 );
3705 uipath = strdupW( file->TargetPath );
3706 p = strrchrW(uipath,'\\');
3707 if (p)
3708 p[0]=0;
3709 MSI_RecordSetStringW( uirow, 1, &p[1] );
3710 MSI_RecordSetStringW( uirow, 2, uipath);
3711 ui_actiondata( package, szSelfRegModules, uirow);
3712 msiobj_release( &uirow->hdr );
3713 msi_free( uipath );
3714 /* FIXME: call ui_progress? */
3716 return ERROR_SUCCESS;
3719 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3721 UINT rc;
3722 MSIQUERY * view;
3723 static const WCHAR ExecSeqQuery[] =
3724 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3725 '`','S','e','l','f','R','e','g','`',0};
3727 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3728 if (rc != ERROR_SUCCESS)
3730 TRACE("no SelfReg table\n");
3731 return ERROR_SUCCESS;
3734 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3735 msiobj_release(&view->hdr);
3737 return ERROR_SUCCESS;
3740 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3742 MSIFEATURE *feature;
3743 UINT rc;
3744 HKEY hkey=0;
3745 HKEY hukey=0;
3746 HKEY userdata=0;
3748 if (!msi_check_publish(package))
3749 return ERROR_SUCCESS;
3751 rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3752 if (rc != ERROR_SUCCESS)
3753 goto end;
3755 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3756 if (rc != ERROR_SUCCESS)
3757 goto end;
3759 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &userdata, TRUE);
3760 if (rc != ERROR_SUCCESS)
3761 goto end;
3763 /* here the guids are base 85 encoded */
3764 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3766 ComponentList *cl;
3767 LPWSTR data = NULL;
3768 GUID clsid;
3769 INT size;
3770 BOOL absent = FALSE;
3771 MSIRECORD *uirow;
3773 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3774 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3775 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3776 absent = TRUE;
3778 size = 1;
3779 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3781 size += 21;
3783 if (feature->Feature_Parent)
3784 size += strlenW( feature->Feature_Parent )+2;
3786 data = msi_alloc(size * sizeof(WCHAR));
3788 data[0] = 0;
3789 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3791 MSICOMPONENT* component = cl->component;
3792 WCHAR buf[21];
3794 buf[0] = 0;
3795 if (component->ComponentId)
3797 TRACE("From %s\n",debugstr_w(component->ComponentId));
3798 CLSIDFromString(component->ComponentId, &clsid);
3799 encode_base85_guid(&clsid,buf);
3800 TRACE("to %s\n",debugstr_w(buf));
3801 strcatW(data,buf);
3805 if (feature->Feature_Parent)
3807 static const WCHAR sep[] = {'\2',0};
3808 strcatW(data,sep);
3809 strcatW(data,feature->Feature_Parent);
3812 msi_reg_set_val_str( hkey, feature->Feature, data );
3813 msi_reg_set_val_str( userdata, feature->Feature, data );
3814 msi_free(data);
3816 size = 0;
3817 if (feature->Feature_Parent)
3818 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3819 if (!absent)
3821 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3822 (LPBYTE)feature->Feature_Parent,size);
3824 else
3826 size += 2*sizeof(WCHAR);
3827 data = msi_alloc(size);
3828 data[0] = 0x6;
3829 data[1] = 0;
3830 if (feature->Feature_Parent)
3831 strcpyW( &data[1], feature->Feature_Parent );
3832 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3833 (LPBYTE)data,size);
3834 msi_free(data);
3837 /* the UI chunk */
3838 uirow = MSI_CreateRecord( 1 );
3839 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3840 ui_actiondata( package, szPublishFeatures, uirow);
3841 msiobj_release( &uirow->hdr );
3842 /* FIXME: call ui_progress? */
3845 end:
3846 RegCloseKey(hkey);
3847 RegCloseKey(hukey);
3848 return rc;
3851 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3853 UINT r;
3854 HKEY hkey;
3856 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3858 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3859 if (r == ERROR_SUCCESS)
3861 RegDeleteValueW(hkey, feature->Feature);
3862 RegCloseKey(hkey);
3865 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3866 if (r == ERROR_SUCCESS)
3868 RegDeleteValueW(hkey, feature->Feature);
3869 RegCloseKey(hkey);
3872 return ERROR_SUCCESS;
3875 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3877 MSIFEATURE *feature;
3879 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3881 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3882 return FALSE;
3885 return TRUE;
3888 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3890 MSIFEATURE *feature;
3892 if (!msi_check_unpublish(package))
3893 return ERROR_SUCCESS;
3895 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3897 msi_unpublish_feature(package, feature);
3900 return ERROR_SUCCESS;
3903 static UINT msi_get_local_package_name( LPWSTR path )
3905 static const WCHAR szInstaller[] = {
3906 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3907 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3908 DWORD time, len, i;
3909 HANDLE handle;
3911 time = GetTickCount();
3912 GetWindowsDirectoryW( path, MAX_PATH );
3913 lstrcatW( path, szInstaller );
3914 CreateDirectoryW( path, NULL );
3916 len = lstrlenW(path);
3917 for (i=0; i<0x10000; i++)
3919 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3920 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3921 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3922 if (handle != INVALID_HANDLE_VALUE)
3924 CloseHandle(handle);
3925 break;
3927 if (GetLastError() != ERROR_FILE_EXISTS &&
3928 GetLastError() != ERROR_SHARING_VIOLATION)
3929 return ERROR_FUNCTION_FAILED;
3932 return ERROR_SUCCESS;
3935 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3937 WCHAR packagefile[MAX_PATH];
3938 HKEY props;
3939 UINT r;
3941 r = msi_get_local_package_name( packagefile );
3942 if (r != ERROR_SUCCESS)
3943 return r;
3945 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3947 r = CopyFileW( package->db->path, packagefile, FALSE);
3949 if (!r)
3951 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3952 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
3953 return ERROR_FUNCTION_FAILED;
3956 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3958 r = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
3959 if (r != ERROR_SUCCESS)
3960 return r;
3962 msi_reg_set_val_str(props, INSTALLPROPERTY_LOCALPACKAGEW, packagefile);
3963 RegCloseKey(props);
3964 return ERROR_SUCCESS;
3967 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3969 LPWSTR prop, val, key;
3970 static const LPCSTR propval[] = {
3971 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3972 "ARPCONTACT", "Contact",
3973 "ARPCOMMENTS", "Comments",
3974 "ProductName", "DisplayName",
3975 "ProductVersion", "DisplayVersion",
3976 "ARPHELPLINK", "HelpLink",
3977 "ARPHELPTELEPHONE", "HelpTelephone",
3978 "ARPINSTALLLOCATION", "InstallLocation",
3979 "SourceDir", "InstallSource",
3980 "Manufacturer", "Publisher",
3981 "ARPREADME", "Readme",
3982 "ARPSIZE", "Size",
3983 "ARPURLINFOABOUT", "URLInfoAbout",
3984 "ARPURLUPDATEINFO", "URLUpdateInfo",
3985 NULL,
3987 const LPCSTR *p = propval;
3989 while( *p )
3991 prop = strdupAtoW( *p++ );
3992 key = strdupAtoW( *p++ );
3993 val = msi_dup_property( package, prop );
3994 msi_reg_set_val_str( hkey, key, val );
3995 msi_free(val);
3996 msi_free(key);
3997 msi_free(prop);
3999 return ERROR_SUCCESS;
4002 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4004 HKEY hkey=0;
4005 HKEY hudkey=0, props=0;
4006 LPWSTR buffer = NULL;
4007 UINT rc;
4008 DWORD size, langid;
4009 static const WCHAR szWindowsInstaller[] =
4010 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4011 static const WCHAR szUpgradeCode[] =
4012 {'U','p','g','r','a','d','e','C','o','d','e',0};
4013 static const WCHAR modpath_fmt[] =
4014 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4015 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4016 static const WCHAR szModifyPath[] =
4017 {'M','o','d','i','f','y','P','a','t','h',0};
4018 static const WCHAR szUninstallString[] =
4019 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4020 static const WCHAR szEstimatedSize[] =
4021 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4022 static const WCHAR szProductLanguage[] =
4023 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4024 static const WCHAR szProductVersion[] =
4025 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4027 SYSTEMTIME systime;
4028 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4029 LPWSTR upgrade_code;
4030 WCHAR szDate[9];
4032 /* FIXME: also need to publish if the product is in advertise mode */
4033 if (!msi_check_publish(package))
4034 return ERROR_SUCCESS;
4036 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4037 if (rc != ERROR_SUCCESS)
4038 return rc;
4040 /* dump all the info i can grab */
4041 /* FIXME: Flesh out more information */
4043 msi_write_uninstall_property_vals( package, hkey );
4045 msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
4047 msi_make_package_local( package, hkey );
4049 /* do ModifyPath and UninstallString */
4050 size = deformat_string(package,modpath_fmt,&buffer);
4051 RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4052 RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4053 msi_free(buffer);
4055 /* FIXME: Write real Estimated Size when we have it */
4056 msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
4058 GetLocalTime(&systime);
4059 sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
4060 msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
4062 langid = msi_get_property_int( package, szProductLanguage, 0 );
4063 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
4065 buffer = msi_dup_property( package, szProductVersion );
4066 if (buffer)
4068 DWORD verdword = msi_version_str_to_dword(buffer);
4070 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
4071 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
4072 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
4074 msi_free(buffer);
4076 /* Handle Upgrade Codes */
4077 upgrade_code = msi_dup_property( package, szUpgradeCode );
4078 if (upgrade_code)
4080 HKEY hkey2;
4081 WCHAR squashed[33];
4082 MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4083 squash_guid(package->ProductCode,squashed);
4084 msi_reg_set_val_str( hkey2, squashed, NULL );
4085 RegCloseKey(hkey2);
4086 MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4087 squash_guid(package->ProductCode,squashed);
4088 msi_reg_set_val_str( hkey2, squashed, NULL );
4089 RegCloseKey(hkey2);
4091 msi_free(upgrade_code);
4094 RegCloseKey(hkey);
4096 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
4097 if (rc != ERROR_SUCCESS)
4098 return rc;
4100 RegCloseKey(hudkey);
4102 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
4103 if (rc != ERROR_SUCCESS)
4104 return rc;
4106 msi_reg_set_val_dword( props, szWindowsInstaller, 1 );
4107 RegCloseKey(props);
4109 return ERROR_SUCCESS;
4112 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4114 return execute_script(package,INSTALL_SCRIPT);
4117 static UINT msi_unpublish_product(MSIPACKAGE *package)
4119 LPWSTR remove = NULL;
4120 LPWSTR *features = NULL;
4121 BOOL full_uninstall = TRUE;
4122 MSIFEATURE *feature;
4124 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4125 static const WCHAR szAll[] = {'A','L','L',0};
4127 remove = msi_dup_property(package, szRemove);
4128 if (!remove)
4129 return ERROR_SUCCESS;
4131 features = msi_split_string(remove, ',');
4132 if (!features)
4134 msi_free(remove);
4135 ERR("REMOVE feature list is empty!\n");
4136 return ERROR_FUNCTION_FAILED;
4139 if (!lstrcmpW(features[0], szAll))
4140 full_uninstall = TRUE;
4141 else
4143 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4145 if (feature->Action != INSTALLSTATE_ABSENT)
4146 full_uninstall = FALSE;
4150 if (!full_uninstall)
4151 goto done;
4153 MSIREG_DeleteProductKey(package->ProductCode);
4154 MSIREG_DeleteUserProductKey(package->ProductCode);
4155 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4156 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4157 MSIREG_DeleteUninstallKey(package->ProductCode);
4159 done:
4160 msi_free(remove);
4161 msi_free(features);
4162 return ERROR_SUCCESS;
4165 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4167 UINT rc;
4169 rc = msi_unpublish_product(package);
4170 if (rc != ERROR_SUCCESS)
4171 return rc;
4173 /* turn off scheduling */
4174 package->script->CurrentlyScripting= FALSE;
4176 /* first do the same as an InstallExecute */
4177 rc = ACTION_InstallExecute(package);
4178 if (rc != ERROR_SUCCESS)
4179 return rc;
4181 /* then handle Commit Actions */
4182 rc = execute_script(package,COMMIT_SCRIPT);
4184 return rc;
4187 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4189 static const WCHAR RunOnce[] = {
4190 'S','o','f','t','w','a','r','e','\\',
4191 'M','i','c','r','o','s','o','f','t','\\',
4192 'W','i','n','d','o','w','s','\\',
4193 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4194 'R','u','n','O','n','c','e',0};
4195 static const WCHAR InstallRunOnce[] = {
4196 'S','o','f','t','w','a','r','e','\\',
4197 'M','i','c','r','o','s','o','f','t','\\',
4198 'W','i','n','d','o','w','s','\\',
4199 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4200 'I','n','s','t','a','l','l','e','r','\\',
4201 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4203 static const WCHAR msiexec_fmt[] = {
4204 '%','s',
4205 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4206 '\"','%','s','\"',0};
4207 static const WCHAR install_fmt[] = {
4208 '/','I',' ','\"','%','s','\"',' ',
4209 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4210 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4211 WCHAR buffer[256], sysdir[MAX_PATH];
4212 HKEY hkey;
4213 WCHAR squished_pc[100];
4215 squash_guid(package->ProductCode,squished_pc);
4217 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4218 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4219 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4220 squished_pc);
4222 msi_reg_set_val_str( hkey, squished_pc, buffer );
4223 RegCloseKey(hkey);
4225 TRACE("Reboot command %s\n",debugstr_w(buffer));
4227 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4228 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4230 msi_reg_set_val_str( hkey, squished_pc, buffer );
4231 RegCloseKey(hkey);
4233 return ERROR_INSTALL_SUSPEND;
4236 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4238 DWORD attrib;
4239 UINT rc;
4242 * We are currently doing what should be done here in the top level Install
4243 * however for Administrative and uninstalls this step will be needed
4245 if (!package->PackagePath)
4246 return ERROR_SUCCESS;
4248 msi_set_sourcedir_props(package, TRUE);
4250 attrib = GetFileAttributesW(package->db->path);
4251 if (attrib == INVALID_FILE_ATTRIBUTES)
4253 LPWSTR prompt;
4254 LPWSTR msg;
4255 DWORD size = 0;
4257 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4258 MSIINSTALLCONTEXT_USERUNMANAGED, MSICODE_PRODUCT,
4259 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4260 if (rc == ERROR_MORE_DATA)
4262 prompt = msi_alloc(size * sizeof(WCHAR));
4263 MsiSourceListGetInfoW(package->ProductCode, NULL,
4264 MSIINSTALLCONTEXT_USERUNMANAGED, MSICODE_PRODUCT,
4265 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4267 else
4268 prompt = strdupW(package->db->path);
4270 msg = generate_error_string(package,1302,1,prompt);
4271 while(attrib == INVALID_FILE_ATTRIBUTES)
4273 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4274 if (rc == IDCANCEL)
4276 rc = ERROR_INSTALL_USEREXIT;
4277 break;
4279 attrib = GetFileAttributesW(package->db->path);
4281 msi_free(prompt);
4282 rc = ERROR_SUCCESS;
4284 else
4285 return ERROR_SUCCESS;
4287 return rc;
4290 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4292 HKEY hkey=0;
4293 LPWSTR buffer;
4294 LPWSTR productid;
4295 UINT rc,i;
4297 static const WCHAR szPropKeys[][80] =
4299 {'P','r','o','d','u','c','t','I','D',0},
4300 {'U','S','E','R','N','A','M','E',0},
4301 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4302 {0},
4305 static const WCHAR szRegKeys[][80] =
4307 {'P','r','o','d','u','c','t','I','D',0},
4308 {'R','e','g','O','w','n','e','r',0},
4309 {'R','e','g','C','o','m','p','a','n','y',0},
4310 {0},
4313 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4314 if (!productid)
4315 return ERROR_SUCCESS;
4317 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4318 if (rc != ERROR_SUCCESS)
4319 goto end;
4321 for( i = 0; szPropKeys[i][0]; i++ )
4323 buffer = msi_dup_property( package, szPropKeys[i] );
4324 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4325 msi_free( buffer );
4328 end:
4329 msi_free(productid);
4330 RegCloseKey(hkey);
4332 /* FIXME: call ui_actiondata */
4334 return ERROR_SUCCESS;
4338 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4340 UINT rc;
4342 package->script->InWhatSequence |= SEQUENCE_EXEC;
4343 rc = ACTION_ProcessExecSequence(package,FALSE);
4344 return rc;
4348 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4350 MSIPACKAGE *package = (MSIPACKAGE*)param;
4351 LPCWSTR compgroupid=NULL;
4352 LPCWSTR feature=NULL;
4353 LPCWSTR text = NULL;
4354 LPCWSTR qualifier = NULL;
4355 LPCWSTR component = NULL;
4356 LPWSTR advertise = NULL;
4357 LPWSTR output = NULL;
4358 HKEY hkey;
4359 UINT rc = ERROR_SUCCESS;
4360 MSICOMPONENT *comp;
4361 DWORD sz = 0;
4362 MSIRECORD *uirow;
4364 component = MSI_RecordGetString(rec,3);
4365 comp = get_loaded_component(package,component);
4367 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4368 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4369 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4371 TRACE("Skipping: Component %s not scheduled for install\n",
4372 debugstr_w(component));
4374 return ERROR_SUCCESS;
4377 compgroupid = MSI_RecordGetString(rec,1);
4378 qualifier = MSI_RecordGetString(rec,2);
4380 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4381 if (rc != ERROR_SUCCESS)
4382 goto end;
4384 text = MSI_RecordGetString(rec,4);
4385 feature = MSI_RecordGetString(rec,5);
4387 advertise = create_component_advertise_string(package, comp, feature);
4389 sz = strlenW(advertise);
4391 if (text)
4392 sz += lstrlenW(text);
4394 sz+=3;
4395 sz *= sizeof(WCHAR);
4397 output = msi_alloc_zero(sz);
4398 strcpyW(output,advertise);
4399 msi_free(advertise);
4401 if (text)
4402 strcatW(output,text);
4404 msi_reg_set_val_multi_str( hkey, qualifier, output );
4406 end:
4407 RegCloseKey(hkey);
4408 msi_free(output);
4410 /* the UI chunk */
4411 uirow = MSI_CreateRecord( 2 );
4412 MSI_RecordSetStringW( uirow, 1, compgroupid );
4413 MSI_RecordSetStringW( uirow, 2, qualifier);
4414 ui_actiondata( package, szPublishComponents, uirow);
4415 msiobj_release( &uirow->hdr );
4416 /* FIXME: call ui_progress? */
4418 return rc;
4422 * At present I am ignorning the advertised components part of this and only
4423 * focusing on the qualified component sets
4425 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4427 UINT rc;
4428 MSIQUERY * view;
4429 static const WCHAR ExecSeqQuery[] =
4430 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4431 '`','P','u','b','l','i','s','h',
4432 'C','o','m','p','o','n','e','n','t','`',0};
4434 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4435 if (rc != ERROR_SUCCESS)
4436 return ERROR_SUCCESS;
4438 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4439 msiobj_release(&view->hdr);
4441 return rc;
4444 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4446 MSIPACKAGE *package = (MSIPACKAGE*)param;
4447 MSIRECORD *row;
4448 MSIFILE *file;
4449 SC_HANDLE hscm, service = NULL;
4450 LPCWSTR name, disp, comp, depends, pass;
4451 LPCWSTR load_order, serv_name, key;
4452 DWORD serv_type, start_type;
4453 DWORD err_control;
4455 static const WCHAR query[] =
4456 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4457 '`','C','o','m','p','o','n','e','n','t','`',' ',
4458 'W','H','E','R','E',' ',
4459 '`','C','o','m','p','o','n','e','n','t','`',' ',
4460 '=','\'','%','s','\'',0};
4462 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4463 if (!hscm)
4465 ERR("Failed to open the SC Manager!\n");
4466 goto done;
4469 start_type = MSI_RecordGetInteger(rec, 5);
4470 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4471 goto done;
4473 depends = MSI_RecordGetString(rec, 8);
4474 if (depends && *depends)
4475 FIXME("Dependency list unhandled!\n");
4477 name = MSI_RecordGetString(rec, 2);
4478 disp = MSI_RecordGetString(rec, 3);
4479 serv_type = MSI_RecordGetInteger(rec, 4);
4480 err_control = MSI_RecordGetInteger(rec, 6);
4481 load_order = MSI_RecordGetString(rec, 7);
4482 serv_name = MSI_RecordGetString(rec, 9);
4483 pass = MSI_RecordGetString(rec, 10);
4484 comp = MSI_RecordGetString(rec, 12);
4486 /* fetch the service path */
4487 row = MSI_QueryGetRecord(package->db, query, comp);
4488 if (!row)
4490 ERR("Control query failed!\n");
4491 goto done;
4494 key = MSI_RecordGetString(row, 6);
4496 file = get_loaded_file(package, key);
4497 msiobj_release(&row->hdr);
4498 if (!file)
4500 ERR("Failed to load the service file\n");
4501 goto done;
4504 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4505 start_type, err_control, file->TargetPath,
4506 load_order, NULL, NULL, serv_name, pass);
4507 if (!service)
4509 if (GetLastError() != ERROR_SERVICE_EXISTS)
4510 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4513 done:
4514 CloseServiceHandle(service);
4515 CloseServiceHandle(hscm);
4517 return ERROR_SUCCESS;
4520 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4522 UINT rc;
4523 MSIQUERY * view;
4524 static const WCHAR ExecSeqQuery[] =
4525 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4526 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4528 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4529 if (rc != ERROR_SUCCESS)
4530 return ERROR_SUCCESS;
4532 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4533 msiobj_release(&view->hdr);
4535 return rc;
4538 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4539 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4541 LPCWSTR *vector, *temp_vector;
4542 LPWSTR p, q;
4543 DWORD sep_len;
4545 static const WCHAR separator[] = {'[','~',']',0};
4547 *numargs = 0;
4548 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4550 if (!args)
4551 return NULL;
4553 vector = msi_alloc(sizeof(LPWSTR));
4554 if (!vector)
4555 return NULL;
4557 p = args;
4560 (*numargs)++;
4561 vector[*numargs - 1] = p;
4563 if ((q = strstrW(p, separator)))
4565 *q = '\0';
4567 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4568 if (!temp_vector)
4570 msi_free(vector);
4571 return NULL;
4573 vector = temp_vector;
4575 p = q + sep_len;
4577 } while (q);
4579 return vector;
4582 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4584 MSIPACKAGE *package = (MSIPACKAGE *)param;
4585 MSICOMPONENT *comp;
4586 SC_HANDLE scm, service = NULL;
4587 LPCWSTR name, *vector = NULL;
4588 LPWSTR args;
4589 DWORD event, numargs;
4590 UINT r = ERROR_FUNCTION_FAILED;
4592 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4593 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4594 return ERROR_SUCCESS;
4596 name = MSI_RecordGetString(rec, 2);
4597 event = MSI_RecordGetInteger(rec, 3);
4598 args = strdupW(MSI_RecordGetString(rec, 4));
4600 if (!(event & msidbServiceControlEventStart))
4601 return ERROR_SUCCESS;
4603 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4604 if (!scm)
4606 ERR("Failed to open the service control manager\n");
4607 goto done;
4610 service = OpenServiceW(scm, name, SERVICE_START);
4611 if (!service)
4613 ERR("Failed to open service %s\n", debugstr_w(name));
4614 goto done;
4617 vector = msi_service_args_to_vector(args, &numargs);
4619 if (!StartServiceW(service, numargs, vector))
4621 ERR("Failed to start service %s\n", debugstr_w(name));
4622 goto done;
4625 r = ERROR_SUCCESS;
4627 done:
4628 CloseServiceHandle(service);
4629 CloseServiceHandle(scm);
4631 msi_free(args);
4632 msi_free(vector);
4633 return r;
4636 static UINT ACTION_StartServices( MSIPACKAGE *package )
4638 UINT rc;
4639 MSIQUERY *view;
4641 static const WCHAR query[] = {
4642 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4643 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4645 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4646 if (rc != ERROR_SUCCESS)
4647 return ERROR_SUCCESS;
4649 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4650 msiobj_release(&view->hdr);
4652 return rc;
4655 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4657 MSIFILE *file;
4659 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4661 if (!lstrcmpW(file->File, filename))
4662 return file;
4665 return NULL;
4668 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4670 MSIPACKAGE *package = (MSIPACKAGE*)param;
4671 LPWSTR driver, driver_path, ptr;
4672 WCHAR outpath[MAX_PATH];
4673 MSIFILE *driver_file, *setup_file;
4674 LPCWSTR desc;
4675 DWORD len, usage;
4676 UINT r = ERROR_SUCCESS;
4678 static const WCHAR driver_fmt[] = {
4679 'D','r','i','v','e','r','=','%','s',0};
4680 static const WCHAR setup_fmt[] = {
4681 'S','e','t','u','p','=','%','s',0};
4682 static const WCHAR usage_fmt[] = {
4683 'F','i','l','e','U','s','a','g','e','=','1',0};
4685 desc = MSI_RecordGetString(rec, 3);
4687 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4688 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4690 if (!driver_file || !setup_file)
4692 ERR("ODBC Driver entry not found!\n");
4693 return ERROR_FUNCTION_FAILED;
4696 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4697 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4698 lstrlenW(usage_fmt) + 1;
4699 driver = msi_alloc(len * sizeof(WCHAR));
4700 if (!driver)
4701 return ERROR_OUTOFMEMORY;
4703 ptr = driver;
4704 lstrcpyW(ptr, desc);
4705 ptr += lstrlenW(ptr) + 1;
4707 sprintfW(ptr, driver_fmt, driver_file->FileName);
4708 ptr += lstrlenW(ptr) + 1;
4710 sprintfW(ptr, setup_fmt, setup_file->FileName);
4711 ptr += lstrlenW(ptr) + 1;
4713 lstrcpyW(ptr, usage_fmt);
4714 ptr += lstrlenW(ptr) + 1;
4715 *ptr = '\0';
4717 driver_path = strdupW(driver_file->TargetPath);
4718 ptr = strrchrW(driver_path, '\\');
4719 if (ptr) *ptr = '\0';
4721 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4722 NULL, ODBC_INSTALL_COMPLETE, &usage))
4724 ERR("Failed to install SQL driver!\n");
4725 r = ERROR_FUNCTION_FAILED;
4728 msi_free(driver);
4729 msi_free(driver_path);
4731 return r;
4734 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4736 MSIPACKAGE *package = (MSIPACKAGE*)param;
4737 LPWSTR translator, translator_path, ptr;
4738 WCHAR outpath[MAX_PATH];
4739 MSIFILE *translator_file, *setup_file;
4740 LPCWSTR desc;
4741 DWORD len, usage;
4742 UINT r = ERROR_SUCCESS;
4744 static const WCHAR translator_fmt[] = {
4745 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4746 static const WCHAR setup_fmt[] = {
4747 'S','e','t','u','p','=','%','s',0};
4749 desc = MSI_RecordGetString(rec, 3);
4751 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4752 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4754 if (!translator_file || !setup_file)
4756 ERR("ODBC Translator entry not found!\n");
4757 return ERROR_FUNCTION_FAILED;
4760 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4761 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4762 translator = msi_alloc(len * sizeof(WCHAR));
4763 if (!translator)
4764 return ERROR_OUTOFMEMORY;
4766 ptr = translator;
4767 lstrcpyW(ptr, desc);
4768 ptr += lstrlenW(ptr) + 1;
4770 sprintfW(ptr, translator_fmt, translator_file->FileName);
4771 ptr += lstrlenW(ptr) + 1;
4773 sprintfW(ptr, setup_fmt, setup_file->FileName);
4774 ptr += lstrlenW(ptr) + 1;
4775 *ptr = '\0';
4777 translator_path = strdupW(translator_file->TargetPath);
4778 ptr = strrchrW(translator_path, '\\');
4779 if (ptr) *ptr = '\0';
4781 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4782 NULL, ODBC_INSTALL_COMPLETE, &usage))
4784 ERR("Failed to install SQL translator!\n");
4785 r = ERROR_FUNCTION_FAILED;
4788 msi_free(translator);
4789 msi_free(translator_path);
4791 return r;
4794 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4796 LPWSTR attrs;
4797 LPCWSTR desc, driver;
4798 WORD request = ODBC_ADD_SYS_DSN;
4799 INT registration;
4800 DWORD len;
4801 UINT r = ERROR_SUCCESS;
4803 static const WCHAR attrs_fmt[] = {
4804 'D','S','N','=','%','s',0 };
4806 desc = MSI_RecordGetString(rec, 3);
4807 driver = MSI_RecordGetString(rec, 4);
4808 registration = MSI_RecordGetInteger(rec, 5);
4810 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4811 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4813 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4814 attrs = msi_alloc(len * sizeof(WCHAR));
4815 if (!attrs)
4816 return ERROR_OUTOFMEMORY;
4818 sprintfW(attrs, attrs_fmt, desc);
4819 attrs[len - 1] = '\0';
4821 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4823 ERR("Failed to install SQL data source!\n");
4824 r = ERROR_FUNCTION_FAILED;
4827 msi_free(attrs);
4829 return r;
4832 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4834 UINT rc;
4835 MSIQUERY *view;
4837 static const WCHAR driver_query[] = {
4838 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4839 'O','D','B','C','D','r','i','v','e','r',0 };
4841 static const WCHAR translator_query[] = {
4842 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4843 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4845 static const WCHAR source_query[] = {
4846 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4847 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4849 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4850 if (rc != ERROR_SUCCESS)
4851 return ERROR_SUCCESS;
4853 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4854 msiobj_release(&view->hdr);
4856 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4857 if (rc != ERROR_SUCCESS)
4858 return ERROR_SUCCESS;
4860 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4861 msiobj_release(&view->hdr);
4863 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4864 if (rc != ERROR_SUCCESS)
4865 return ERROR_SUCCESS;
4867 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4868 msiobj_release(&view->hdr);
4870 return rc;
4873 #define ENV_ACT_SETALWAYS 0x1
4874 #define ENV_ACT_SETABSENT 0x2
4875 #define ENV_ACT_REMOVE 0x4
4876 #define ENV_ACT_REMOVEMATCH 0x8
4878 #define ENV_MOD_MACHINE 0x20000000
4879 #define ENV_MOD_APPEND 0x40000000
4880 #define ENV_MOD_PREFIX 0x80000000
4881 #define ENV_MOD_MASK 0xC0000000
4883 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4885 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
4887 LPCWSTR cptr = *name;
4888 LPCWSTR ptr = *value;
4890 static const WCHAR prefix[] = {'[','~',']',0};
4891 static const int prefix_len = 3;
4893 *flags = 0;
4894 while (*cptr)
4896 if (*cptr == '=')
4897 *flags |= ENV_ACT_SETALWAYS;
4898 else if (*cptr == '+')
4899 *flags |= ENV_ACT_SETABSENT;
4900 else if (*cptr == '-')
4901 *flags |= ENV_ACT_REMOVE;
4902 else if (*cptr == '!')
4903 *flags |= ENV_ACT_REMOVEMATCH;
4904 else if (*cptr == '*')
4905 *flags |= ENV_MOD_MACHINE;
4906 else
4907 break;
4909 cptr++;
4910 (*name)++;
4913 if (!*cptr)
4915 ERR("Missing environment variable\n");
4916 return ERROR_FUNCTION_FAILED;
4919 if (!strncmpW(ptr, prefix, prefix_len))
4921 *flags |= ENV_MOD_APPEND;
4922 *value += lstrlenW(prefix);
4924 else if (lstrlenW(*value) >= prefix_len)
4926 ptr += lstrlenW(ptr) - prefix_len;
4927 if (!lstrcmpW(ptr, prefix))
4929 *flags |= ENV_MOD_PREFIX;
4930 /* the "[~]" will be removed by deformat_string */;
4934 if (!*flags ||
4935 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
4936 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
4937 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
4938 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
4940 ERR("Invalid flags: %08x\n", *flags);
4941 return ERROR_FUNCTION_FAILED;
4944 return ERROR_SUCCESS;
4947 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
4949 MSIPACKAGE *package = param;
4950 LPCWSTR name, value, comp;
4951 LPWSTR data = NULL, newval = NULL;
4952 LPWSTR deformatted = NULL, ptr;
4953 DWORD flags, type, size;
4954 LONG res;
4955 HKEY env = NULL, root;
4956 LPCWSTR environment;
4958 static const WCHAR user_env[] =
4959 {'E','n','v','i','r','o','n','m','e','n','t',0};
4960 static const WCHAR machine_env[] =
4961 {'S','y','s','t','e','m','\\',
4962 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4963 'C','o','n','t','r','o','l','\\',
4964 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4965 'E','n','v','i','r','o','n','m','e','n','t',0};
4966 static const WCHAR semicolon[] = {';',0};
4968 name = MSI_RecordGetString(rec, 2);
4969 value = MSI_RecordGetString(rec, 3);
4970 comp = MSI_RecordGetString(rec, 4);
4972 res = env_set_flags(&name, &value, &flags);
4973 if (res != ERROR_SUCCESS)
4974 goto done;
4976 deformat_string(package, value, &deformatted);
4977 if (!deformatted)
4979 res = ERROR_OUTOFMEMORY;
4980 goto done;
4983 value = deformatted;
4985 if (flags & ENV_MOD_MACHINE)
4987 environment = machine_env;
4988 root = HKEY_LOCAL_MACHINE;
4990 else
4992 environment = user_env;
4993 root = HKEY_CURRENT_USER;
4996 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
4997 KEY_ALL_ACCESS, NULL, &env, NULL);
4998 if (res != ERROR_SUCCESS)
4999 goto done;
5001 if (flags & ENV_ACT_REMOVE)
5002 FIXME("Not removing environment variable on uninstall!\n");
5004 size = 0;
5005 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5006 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5007 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5008 goto done;
5010 if (res != ERROR_FILE_NOT_FOUND)
5012 if (flags & ENV_ACT_SETABSENT)
5014 res = ERROR_SUCCESS;
5015 goto done;
5018 data = msi_alloc(size);
5019 if (!data)
5021 RegCloseKey(env);
5022 return ERROR_OUTOFMEMORY;
5025 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5026 if (res != ERROR_SUCCESS)
5027 goto done;
5029 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5031 res = RegDeleteKeyW(env, name);
5032 goto done;
5035 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5036 newval = msi_alloc(size);
5037 ptr = newval;
5038 if (!newval)
5040 res = ERROR_OUTOFMEMORY;
5041 goto done;
5044 if (!(flags & ENV_MOD_MASK))
5045 lstrcpyW(newval, value);
5046 else
5048 if (flags & ENV_MOD_PREFIX)
5050 lstrcpyW(newval, value);
5051 lstrcatW(newval, semicolon);
5052 ptr = newval + lstrlenW(value) + 1;
5055 lstrcpyW(ptr, data);
5057 if (flags & ENV_MOD_APPEND)
5059 lstrcatW(newval, semicolon);
5060 lstrcatW(newval, value);
5064 else
5066 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5067 newval = msi_alloc(size);
5068 if (!newval)
5070 res = ERROR_OUTOFMEMORY;
5071 goto done;
5074 lstrcpyW(newval, value);
5077 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5078 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5080 done:
5081 if (env) RegCloseKey(env);
5082 msi_free(deformatted);
5083 msi_free(data);
5084 msi_free(newval);
5085 return res;
5088 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5090 UINT rc;
5091 MSIQUERY * view;
5092 static const WCHAR ExecSeqQuery[] =
5093 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5094 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5095 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5096 if (rc != ERROR_SUCCESS)
5097 return ERROR_SUCCESS;
5099 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5100 msiobj_release(&view->hdr);
5102 return rc;
5105 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5107 typedef struct
5109 struct list entry;
5110 LPWSTR sourcename;
5111 LPWSTR destname;
5112 LPWSTR source;
5113 LPWSTR dest;
5114 } FILE_LIST;
5116 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5118 BOOL ret;
5120 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5121 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5123 WARN("Source or dest is directory, not moving\n");
5124 return FALSE;
5127 if (options == msidbMoveFileOptionsMove)
5129 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5130 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5131 if (!ret)
5133 WARN("MoveFile failed: %d\n", GetLastError());
5134 return FALSE;
5137 else
5139 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5140 ret = CopyFileW(source, dest, FALSE);
5141 if (!ret)
5143 WARN("CopyFile failed: %d\n", GetLastError());
5144 return FALSE;
5148 return TRUE;
5151 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5153 LPWSTR path, ptr;
5154 DWORD dirlen, pathlen;
5156 ptr = strrchrW(wildcard, '\\');
5157 dirlen = ptr - wildcard + 1;
5159 pathlen = dirlen + lstrlenW(filename) + 1;
5160 path = msi_alloc(pathlen * sizeof(WCHAR));
5162 lstrcpynW(path, wildcard, dirlen + 1);
5163 lstrcatW(path, filename);
5165 return path;
5168 static void free_file_entry(FILE_LIST *file)
5170 msi_free(file->source);
5171 msi_free(file->dest);
5172 msi_free(file);
5175 static void free_list(FILE_LIST *list)
5177 while (!list_empty(&list->entry))
5179 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5181 list_remove(&file->entry);
5182 free_file_entry(file);
5186 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5188 FILE_LIST *new, *file;
5189 LPWSTR ptr, filename;
5190 DWORD size;
5192 new = msi_alloc_zero(sizeof(FILE_LIST));
5193 if (!new)
5194 return FALSE;
5196 new->source = strdupW(source);
5197 ptr = strrchrW(dest, '\\') + 1;
5198 filename = strrchrW(new->source, '\\') + 1;
5200 new->sourcename = filename;
5202 if (*ptr)
5203 new->destname = ptr;
5204 else
5205 new->destname = new->sourcename;
5207 size = (ptr - dest) + lstrlenW(filename) + 1;
5208 new->dest = msi_alloc(size * sizeof(WCHAR));
5209 if (!new->dest)
5211 free_file_entry(new);
5212 return FALSE;
5215 lstrcpynW(new->dest, dest, ptr - dest + 1);
5216 lstrcatW(new->dest, filename);
5218 if (list_empty(&files->entry))
5220 list_add_head(&files->entry, &new->entry);
5221 return TRUE;
5224 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5226 if (lstrcmpW(source, file->source) < 0)
5228 list_add_before(&file->entry, &new->entry);
5229 return TRUE;
5233 list_add_after(&file->entry, &new->entry);
5234 return TRUE;
5237 BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5239 WIN32_FIND_DATAW wfd;
5240 HANDLE hfile;
5241 LPWSTR path;
5242 BOOL res;
5243 FILE_LIST files, *file;
5244 DWORD size;
5246 hfile = FindFirstFileW(source, &wfd);
5247 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5249 list_init(&files.entry);
5251 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5253 if (is_dot_dir(wfd.cFileName)) continue;
5255 path = wildcard_to_file(source, wfd.cFileName);
5256 if (!path)
5258 res = FALSE;
5259 goto done;
5262 add_wildcard(&files, path, dest);
5263 msi_free(path);
5266 /* only the first wildcard match gets renamed to dest */
5267 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5268 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5269 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5270 if (!file->dest)
5272 res = FALSE;
5273 goto done;
5276 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5278 while (!list_empty(&files.entry))
5280 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5282 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5284 list_remove(&file->entry);
5285 free_file_entry(file);
5288 res = TRUE;
5290 done:
5291 free_list(&files);
5292 FindClose(hfile);
5293 return res;
5296 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5298 MSIPACKAGE *package = param;
5299 MSICOMPONENT *comp;
5300 LPCWSTR sourcename, destname;
5301 LPWSTR sourcedir = NULL, destdir = NULL;
5302 LPWSTR source = NULL, dest = NULL;
5303 int options;
5304 DWORD size;
5305 BOOL ret, wildcards;
5307 static const WCHAR backslash[] = {'\\',0};
5309 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5310 if (!comp || !comp->Enabled ||
5311 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5313 TRACE("Component not set for install, not moving file\n");
5314 return ERROR_SUCCESS;
5317 sourcename = MSI_RecordGetString(rec, 3);
5318 destname = MSI_RecordGetString(rec, 4);
5319 options = MSI_RecordGetInteger(rec, 7);
5321 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5322 if (!sourcedir)
5323 goto done;
5325 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5326 if (!destdir)
5327 goto done;
5329 if (!sourcename)
5331 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5332 goto done;
5334 source = strdupW(sourcedir);
5335 if (!source)
5336 goto done;
5338 else
5340 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5341 source = msi_alloc(size * sizeof(WCHAR));
5342 if (!source)
5343 goto done;
5345 lstrcpyW(source, sourcedir);
5346 if (source[lstrlenW(source) - 1] != '\\')
5347 lstrcatW(source, backslash);
5348 lstrcatW(source, sourcename);
5351 wildcards = strchrW(source, '*') || strchrW(source, '?');
5353 if (!destname && !wildcards)
5355 destname = strdupW(sourcename);
5356 if (!destname)
5357 goto done;
5360 size = 0;
5361 if (destname)
5362 size = lstrlenW(destname);
5364 size += lstrlenW(destdir) + 2;
5365 dest = msi_alloc(size * sizeof(WCHAR));
5366 if (!dest)
5367 goto done;
5369 lstrcpyW(dest, destdir);
5370 if (dest[lstrlenW(dest) - 1] != '\\')
5371 lstrcatW(dest, backslash);
5373 if (destname)
5374 lstrcatW(dest, destname);
5376 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5378 ret = CreateDirectoryW(destdir, NULL);
5379 if (!ret)
5381 WARN("CreateDirectory failed: %d\n", GetLastError());
5382 return ERROR_SUCCESS;
5386 if (!wildcards)
5387 msi_move_file(source, dest, options);
5388 else
5389 move_files_wildcard(source, dest, options);
5391 done:
5392 msi_free(sourcedir);
5393 msi_free(destdir);
5394 msi_free(source);
5395 msi_free(dest);
5397 return ERROR_SUCCESS;
5400 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5402 UINT rc;
5403 MSIQUERY *view;
5405 static const WCHAR ExecSeqQuery[] =
5406 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5407 '`','M','o','v','e','F','i','l','e','`',0};
5409 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5410 if (rc != ERROR_SUCCESS)
5411 return ERROR_SUCCESS;
5413 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5414 msiobj_release(&view->hdr);
5416 return rc;
5419 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5420 LPCSTR action, LPCWSTR table )
5422 static const WCHAR query[] = {
5423 'S','E','L','E','C','T',' ','*',' ',
5424 'F','R','O','M',' ','`','%','s','`',0 };
5425 MSIQUERY *view = NULL;
5426 DWORD count = 0;
5427 UINT r;
5429 r = MSI_OpenQuery( package->db, &view, query, table );
5430 if (r == ERROR_SUCCESS)
5432 r = MSI_IterateRecords(view, &count, NULL, package);
5433 msiobj_release(&view->hdr);
5436 if (count)
5437 FIXME("%s -> %u ignored %s table values\n",
5438 action, count, debugstr_w(table));
5440 return ERROR_SUCCESS;
5443 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5445 TRACE("%p\n", package);
5446 return ERROR_SUCCESS;
5449 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5451 static const WCHAR table[] =
5452 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5453 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5456 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5458 static const WCHAR table[] = { 'P','a','t','c','h',0 };
5459 return msi_unimplemented_action_stub( package, "PatchFiles", table );
5462 static UINT ACTION_BindImage( MSIPACKAGE *package )
5464 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5465 return msi_unimplemented_action_stub( package, "BindImage", table );
5468 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5470 static const WCHAR table[] = {
5471 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5472 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5475 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5477 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5478 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5481 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5483 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5484 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5487 static UINT ACTION_StopServices( MSIPACKAGE *package )
5489 static const WCHAR table[] = {
5490 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5491 return msi_unimplemented_action_stub( package, "StopServices", table );
5494 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5496 static const WCHAR table[] = {
5497 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5498 return msi_unimplemented_action_stub( package, "DeleteServices", table );
5500 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5502 static const WCHAR table[] = {
5503 'P','r','o','d','u','c','t','I','D',0 };
5504 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5507 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5509 static const WCHAR table[] = {
5510 'E','n','v','i','r','o','n','m','e','n','t',0 };
5511 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5514 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5516 static const WCHAR table[] = {
5517 'M','s','i','A','s','s','e','m','b','l','y',0 };
5518 return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
5521 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5523 static const WCHAR table[] = {
5524 'M','s','i','A','s','s','e','m','b','l','y',0 };
5525 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5528 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5530 static const WCHAR table[] = { 'F','o','n','t',0 };
5531 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5534 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5536 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5537 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5540 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5542 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5543 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5546 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5548 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5549 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5552 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5554 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5555 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5558 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5560 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5561 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5564 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5566 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5567 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5570 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5572 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5573 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5576 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5578 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5579 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5582 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5584 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5585 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5588 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5590 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5591 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5594 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5596 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5597 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5600 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5602 static const WCHAR table[] = { 'A','p','p','I','d',0 };
5603 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5606 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5608 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5609 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5612 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5614 static const WCHAR table[] = { 'M','I','M','E',0 };
5615 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5618 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5620 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5621 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5624 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5626 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5627 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5630 static const struct _actions StandardActions[] = {
5631 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5632 { szAppSearch, ACTION_AppSearch },
5633 { szBindImage, ACTION_BindImage },
5634 { szCCPSearch, ACTION_CCPSearch },
5635 { szCostFinalize, ACTION_CostFinalize },
5636 { szCostInitialize, ACTION_CostInitialize },
5637 { szCreateFolders, ACTION_CreateFolders },
5638 { szCreateShortcuts, ACTION_CreateShortcuts },
5639 { szDeleteServices, ACTION_DeleteServices },
5640 { szDisableRollback, NULL },
5641 { szDuplicateFiles, ACTION_DuplicateFiles },
5642 { szExecuteAction, ACTION_ExecuteAction },
5643 { szFileCost, ACTION_FileCost },
5644 { szFindRelatedProducts, ACTION_FindRelatedProducts },
5645 { szForceReboot, ACTION_ForceReboot },
5646 { szInstallAdminPackage, NULL },
5647 { szInstallExecute, ACTION_InstallExecute },
5648 { szInstallExecuteAgain, ACTION_InstallExecute },
5649 { szInstallFiles, ACTION_InstallFiles},
5650 { szInstallFinalize, ACTION_InstallFinalize },
5651 { szInstallInitialize, ACTION_InstallInitialize },
5652 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5653 { szInstallValidate, ACTION_InstallValidate },
5654 { szIsolateComponents, ACTION_IsolateComponents },
5655 { szLaunchConditions, ACTION_LaunchConditions },
5656 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5657 { szMoveFiles, ACTION_MoveFiles },
5658 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5659 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5660 { szInstallODBC, ACTION_InstallODBC },
5661 { szInstallServices, ACTION_InstallServices },
5662 { szPatchFiles, ACTION_PatchFiles },
5663 { szProcessComponents, ACTION_ProcessComponents },
5664 { szPublishComponents, ACTION_PublishComponents },
5665 { szPublishFeatures, ACTION_PublishFeatures },
5666 { szPublishProduct, ACTION_PublishProduct },
5667 { szRegisterClassInfo, ACTION_RegisterClassInfo },
5668 { szRegisterComPlus, ACTION_RegisterComPlus},
5669 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5670 { szRegisterFonts, ACTION_RegisterFonts },
5671 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5672 { szRegisterProduct, ACTION_RegisterProduct },
5673 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5674 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5675 { szRegisterUser, ACTION_RegisterUser },
5676 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5677 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5678 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5679 { szRemoveFiles, ACTION_RemoveFiles },
5680 { szRemoveFolders, ACTION_RemoveFolders },
5681 { szRemoveIniValues, ACTION_RemoveIniValues },
5682 { szRemoveODBC, ACTION_RemoveODBC },
5683 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5684 { szRemoveShortcuts, ACTION_RemoveShortcuts },
5685 { szResolveSource, ACTION_ResolveSource },
5686 { szRMCCPSearch, ACTION_RMCCPSearch },
5687 { szScheduleReboot, NULL },
5688 { szSelfRegModules, ACTION_SelfRegModules },
5689 { szSelfUnregModules, ACTION_SelfUnregModules },
5690 { szSetODBCFolders, NULL },
5691 { szStartServices, ACTION_StartServices },
5692 { szStopServices, ACTION_StopServices },
5693 { szUnpublishComponents, ACTION_UnpublishComponents },
5694 { szUnpublishFeatures, ACTION_UnpublishFeatures },
5695 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5696 { szUnregisterComPlus, ACTION_UnregisterComPlus },
5697 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5698 { szUnregisterFonts, ACTION_UnregisterFonts },
5699 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5700 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5701 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5702 { szValidateProductID, ACTION_ValidateProductID },
5703 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5704 { szWriteIniValues, ACTION_WriteIniValues },
5705 { szWriteRegistryValues, ACTION_WriteRegistryValues },
5706 { NULL, NULL },