msi: Fix handling of published installer properties.
[wine.git] / dlls / msi / action.c
blob04ccef57720fb8e3da93a2e53205989b82956bbc
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
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "wine/unicode.h"
37 #include "winver.h"
39 #define REG_PROGRESS_VALUE 13200
40 #define COMPONENT_PROGRESS_VALUE 24000
42 WINE_DEFAULT_DEBUG_CHANNEL(msi);
45 * Prototypes
47 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
48 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
49 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
52 * consts and values used
54 static const WCHAR c_colon[] = {'C',':','\\',0};
56 static const WCHAR szCreateFolders[] =
57 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
58 static const WCHAR szCostFinalize[] =
59 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
60 const WCHAR szInstallFiles[] =
61 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
62 const WCHAR szDuplicateFiles[] =
63 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
64 static const WCHAR szWriteRegistryValues[] =
65 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
66 'V','a','l','u','e','s',0};
67 static const WCHAR szCostInitialize[] =
68 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
69 static const WCHAR szFileCost[] =
70 {'F','i','l','e','C','o','s','t',0};
71 static const WCHAR szInstallInitialize[] =
72 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
73 static const WCHAR szInstallValidate[] =
74 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
75 static const WCHAR szLaunchConditions[] =
76 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
77 static const WCHAR szProcessComponents[] =
78 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
79 static const WCHAR szRegisterTypeLibraries[] =
80 {'R','e','g','i','s','t','e','r','T','y','p','e',
81 'L','i','b','r','a','r','i','e','s',0};
82 const WCHAR szRegisterClassInfo[] =
83 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
84 const WCHAR szRegisterProgIdInfo[] =
85 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
86 static const WCHAR szCreateShortcuts[] =
87 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
88 static const WCHAR szPublishProduct[] =
89 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
90 static const WCHAR szWriteIniValues[] =
91 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
92 static const WCHAR szSelfRegModules[] =
93 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
94 static const WCHAR szPublishFeatures[] =
95 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
96 static const WCHAR szRegisterProduct[] =
97 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
98 static const WCHAR szInstallExecute[] =
99 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
100 static const WCHAR szInstallExecuteAgain[] =
101 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
102 'A','g','a','i','n',0};
103 static const WCHAR szInstallFinalize[] =
104 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
105 static const WCHAR szForceReboot[] =
106 {'F','o','r','c','e','R','e','b','o','o','t',0};
107 static const WCHAR szResolveSource[] =
108 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
109 static const WCHAR szAppSearch[] =
110 {'A','p','p','S','e','a','r','c','h',0};
111 static const WCHAR szAllocateRegistrySpace[] =
112 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
113 'S','p','a','c','e',0};
114 static const WCHAR szBindImage[] =
115 {'B','i','n','d','I','m','a','g','e',0};
116 static const WCHAR szCCPSearch[] =
117 {'C','C','P','S','e','a','r','c','h',0};
118 static const WCHAR szDeleteServices[] =
119 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
120 static const WCHAR szDisableRollback[] =
121 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
122 static const WCHAR szExecuteAction[] =
123 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
124 const WCHAR szFindRelatedProducts[] =
125 {'F','i','n','d','R','e','l','a','t','e','d',
126 'P','r','o','d','u','c','t','s',0};
127 static const WCHAR szInstallAdminPackage[] =
128 {'I','n','s','t','a','l','l','A','d','m','i','n',
129 'P','a','c','k','a','g','e',0};
130 static const WCHAR szInstallSFPCatalogFile[] =
131 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
132 'F','i','l','e',0};
133 static const WCHAR szIsolateComponents[] =
134 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
135 const WCHAR szMigrateFeatureStates[] =
136 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
137 'S','t','a','t','e','s',0};
138 const WCHAR szMoveFiles[] =
139 {'M','o','v','e','F','i','l','e','s',0};
140 static const WCHAR szMsiPublishAssemblies[] =
141 {'M','s','i','P','u','b','l','i','s','h',
142 'A','s','s','e','m','b','l','i','e','s',0};
143 static const WCHAR szMsiUnpublishAssemblies[] =
144 {'M','s','i','U','n','p','u','b','l','i','s','h',
145 'A','s','s','e','m','b','l','i','e','s',0};
146 static const WCHAR szInstallODBC[] =
147 {'I','n','s','t','a','l','l','O','D','B','C',0};
148 static const WCHAR szInstallServices[] =
149 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
150 const WCHAR szPatchFiles[] =
151 {'P','a','t','c','h','F','i','l','e','s',0};
152 static const WCHAR szPublishComponents[] =
153 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
154 static const WCHAR szRegisterComPlus[] =
155 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
156 const WCHAR szRegisterExtensionInfo[] =
157 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
158 'I','n','f','o',0};
159 static const WCHAR szRegisterFonts[] =
160 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
161 const WCHAR szRegisterMIMEInfo[] =
162 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
163 static const WCHAR szRegisterUser[] =
164 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
165 const WCHAR szRemoveDuplicateFiles[] =
166 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
167 'F','i','l','e','s',0};
168 static const WCHAR szRemoveEnvironmentStrings[] =
169 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
170 'S','t','r','i','n','g','s',0};
171 const WCHAR szRemoveExistingProducts[] =
172 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
173 'P','r','o','d','u','c','t','s',0};
174 const WCHAR szRemoveFiles[] =
175 {'R','e','m','o','v','e','F','i','l','e','s',0};
176 static const WCHAR szRemoveFolders[] =
177 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
178 static const WCHAR szRemoveIniValues[] =
179 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
180 static const WCHAR szRemoveODBC[] =
181 {'R','e','m','o','v','e','O','D','B','C',0};
182 static const WCHAR szRemoveRegistryValues[] =
183 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
184 'V','a','l','u','e','s',0};
185 static const WCHAR szRemoveShortcuts[] =
186 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
187 static const WCHAR szRMCCPSearch[] =
188 {'R','M','C','C','P','S','e','a','r','c','h',0};
189 static const WCHAR szScheduleReboot[] =
190 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
191 static const WCHAR szSelfUnregModules[] =
192 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
193 static const WCHAR szSetODBCFolders[] =
194 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
195 static const WCHAR szStartServices[] =
196 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
197 static const WCHAR szStopServices[] =
198 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
199 static const WCHAR szUnpublishComponents[] =
200 {'U','n','p','u','b','l','i','s','h',
201 'C','o','m','p','o','n','e','n','t','s',0};
202 static const WCHAR szUnpublishFeatures[] =
203 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
204 const WCHAR szUnregisterClassInfo[] =
205 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
206 'I','n','f','o',0};
207 static const WCHAR szUnregisterComPlus[] =
208 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
209 const WCHAR szUnregisterExtensionInfo[] =
210 {'U','n','r','e','g','i','s','t','e','r',
211 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
212 static const WCHAR szUnregisterFonts[] =
213 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
214 const WCHAR szUnregisterMIMEInfo[] =
215 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
216 const WCHAR szUnregisterProgIdInfo[] =
217 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
218 'I','n','f','o',0};
219 static const WCHAR szUnregisterTypeLibraries[] =
220 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
221 'L','i','b','r','a','r','i','e','s',0};
222 static const WCHAR szValidateProductID[] =
223 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
224 static const WCHAR szWriteEnvironmentStrings[] =
225 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
226 'S','t','r','i','n','g','s',0};
228 /* action handlers */
229 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
231 struct _actions {
232 LPCWSTR action;
233 STANDARDACTIONHANDLER handler;
236 static const struct _actions StandardActions[];
239 /********************************************************
240 * helper functions
241 ********************************************************/
243 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
245 static const WCHAR Query_t[] =
246 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
247 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
248 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
249 ' ','\'','%','s','\'',0};
250 MSIRECORD * row;
252 row = MSI_QueryGetRecord( package->db, Query_t, action );
253 if (!row)
254 return;
255 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
256 msiobj_release(&row->hdr);
259 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
260 UINT rc)
262 MSIRECORD * row;
263 static const WCHAR template_s[]=
264 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
265 '%','s', '.',0};
266 static const WCHAR template_e[]=
267 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
268 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
269 '%','i','.',0};
270 static const WCHAR format[] =
271 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
272 WCHAR message[1024];
273 WCHAR timet[0x100];
275 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
276 if (start)
277 sprintfW(message,template_s,timet,action);
278 else
279 sprintfW(message,template_e,timet,action,rc);
281 row = MSI_CreateRecord(1);
282 MSI_RecordSetStringW(row,1,message);
284 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
285 msiobj_release(&row->hdr);
288 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine )
290 LPCWSTR ptr,ptr2;
291 BOOL quote;
292 DWORD len;
293 LPWSTR prop = NULL, val = NULL;
295 if (!szCommandLine)
296 return ERROR_SUCCESS;
298 ptr = szCommandLine;
300 while (*ptr)
302 if (*ptr==' ')
304 ptr++;
305 continue;
308 TRACE("Looking at %s\n",debugstr_w(ptr));
310 ptr2 = strchrW(ptr,'=');
311 if (!ptr2)
313 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
314 break;
317 quote = FALSE;
319 len = ptr2-ptr;
320 prop = msi_alloc((len+1)*sizeof(WCHAR));
321 memcpy(prop,ptr,len*sizeof(WCHAR));
322 prop[len]=0;
323 ptr2++;
325 len = 0;
326 ptr = ptr2;
327 while (*ptr && (quote || (!quote && *ptr!=' ')))
329 if (*ptr == '"')
330 quote = !quote;
331 ptr++;
332 len++;
335 if (*ptr2=='"')
337 ptr2++;
338 len -= 2;
340 val = msi_alloc((len+1)*sizeof(WCHAR));
341 memcpy(val,ptr2,len*sizeof(WCHAR));
342 val[len] = 0;
344 if (lstrlenW(prop) > 0)
346 TRACE("Found commandline property (%s) = (%s)\n",
347 debugstr_w(prop), debugstr_w(val));
348 MSI_SetPropertyW(package,prop,val);
350 msi_free(val);
351 msi_free(prop);
354 return ERROR_SUCCESS;
358 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
360 LPCWSTR pc;
361 LPWSTR p, *ret = NULL;
362 UINT count = 0;
364 if (!str)
365 return ret;
367 /* count the number of substrings */
368 for ( pc = str, count = 0; pc; count++ )
370 pc = strchrW( pc, sep );
371 if (pc)
372 pc++;
375 /* allocate space for an array of substring pointers and the substrings */
376 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
377 (lstrlenW(str)+1) * sizeof(WCHAR) );
378 if (!ret)
379 return ret;
381 /* copy the string and set the pointers */
382 p = (LPWSTR) &ret[count+1];
383 lstrcpyW( p, str );
384 for( count = 0; (ret[count] = p); count++ )
386 p = strchrW( p, sep );
387 if (p)
388 *p++ = 0;
391 return ret;
394 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
396 WCHAR szProductCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
397 LPWSTR prod_code, patch_product;
398 UINT ret;
400 prod_code = msi_dup_property( package, szProductCode );
401 patch_product = msi_get_suminfo_product( patch );
403 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
405 if ( strstrW( patch_product, prod_code ) )
406 ret = ERROR_SUCCESS;
407 else
408 ret = ERROR_FUNCTION_FAILED;
410 msi_free( patch_product );
411 msi_free( prod_code );
413 return ret;
416 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
417 MSIDATABASE *patch_db, LPCWSTR name )
419 UINT ret = ERROR_FUNCTION_FAILED;
420 IStorage *stg = NULL;
421 HRESULT r;
423 TRACE("%p %s\n", package, debugstr_w(name) );
425 if (*name++ != ':')
427 ERR("expected a colon in %s\n", debugstr_w(name));
428 return ERROR_FUNCTION_FAILED;
431 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
432 if (SUCCEEDED(r))
434 ret = msi_check_transform_applicable( package, stg );
435 if (ret == ERROR_SUCCESS)
436 msi_table_apply_transform( package->db, stg );
437 else
438 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
439 IStorage_Release( stg );
441 else
442 ERR("failed to open substorage %s\n", debugstr_w(name));
444 return ERROR_SUCCESS;
447 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
449 static const WCHAR szProdCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
450 LPWSTR guid_list, *guids, product_code;
451 UINT i, ret = ERROR_FUNCTION_FAILED;
453 product_code = msi_dup_property( package, szProdCode );
454 if (!product_code)
456 /* FIXME: the property ProductCode should be written into the DB somewhere */
457 ERR("no product code to check\n");
458 return ERROR_SUCCESS;
461 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
462 guids = msi_split_string( guid_list, ';' );
463 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
465 if (!lstrcmpW( guids[i], product_code ))
466 ret = ERROR_SUCCESS;
468 msi_free( guids );
469 msi_free( guid_list );
470 msi_free( product_code );
472 return ret;
475 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
477 MSISUMMARYINFO *si;
478 LPWSTR str, *substorage;
479 UINT i, r = ERROR_SUCCESS;
481 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
482 if (!si)
483 return ERROR_FUNCTION_FAILED;
485 msi_check_patch_applicable( package, si );
487 /* enumerate the substorage */
488 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
489 substorage = msi_split_string( str, ';' );
490 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
491 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
492 msi_free( substorage );
493 msi_free( str );
495 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
497 msiobj_release( &si->hdr );
499 return r;
502 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
504 MSIDATABASE *patch_db = NULL;
505 UINT r;
507 TRACE("%p %s\n", package, debugstr_w( file ) );
509 /* FIXME:
510 * We probably want to make sure we only open a patch collection here.
511 * Patch collections (.msp) and databases (.msi) have different GUIDs
512 * but currently MSI_OpenDatabaseW will accept both.
514 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
515 if ( r != ERROR_SUCCESS )
517 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
518 return r;
521 msi_parse_patch_summary( package, patch_db );
524 * There might be a CAB file in the patch package,
525 * so append it to the list of storage to search for streams.
527 append_storage_to_db( package->db, patch_db->storage );
529 msiobj_release( &patch_db->hdr );
531 return ERROR_SUCCESS;
534 /* get the PATCH property, and apply all the patches it specifies */
535 static UINT msi_apply_patches( MSIPACKAGE *package )
537 static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
538 LPWSTR patch_list, *patches;
539 UINT i, r = ERROR_SUCCESS;
541 patch_list = msi_dup_property( package, szPatch );
543 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
545 patches = msi_split_string( patch_list, ';' );
546 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
547 r = msi_apply_patch_package( package, patches[i] );
549 msi_free( patches );
550 msi_free( patch_list );
552 return r;
555 static UINT msi_apply_transforms( MSIPACKAGE *package )
557 static const WCHAR szTransforms[] = {
558 'T','R','A','N','S','F','O','R','M','S',0 };
559 LPWSTR xform_list, *xforms;
560 UINT i, r = ERROR_SUCCESS;
562 xform_list = msi_dup_property( package, szTransforms );
563 xforms = msi_split_string( xform_list, ';' );
565 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
567 if (xforms[i][0] == ':')
568 r = msi_apply_substorage_transform( package, package->db, &xforms[i][1] );
569 else
570 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
573 msi_free( xforms );
574 msi_free( xform_list );
576 return r;
579 static BOOL ui_sequence_exists( MSIPACKAGE *package )
581 MSIQUERY *view;
582 UINT rc;
584 static const WCHAR ExecSeqQuery [] =
585 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
586 '`','I','n','s','t','a','l','l',
587 'U','I','S','e','q','u','e','n','c','e','`',
588 ' ','W','H','E','R','E',' ',
589 '`','S','e','q','u','e','n','c','e','`',' ',
590 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
591 '`','S','e','q','u','e','n','c','e','`',0};
593 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
594 if (rc == ERROR_SUCCESS)
596 msiobj_release(&view->hdr);
597 return TRUE;
600 return FALSE;
603 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
605 LPWSTR p, db;
606 LPWSTR source, check;
607 DWORD len;
609 static const WCHAR szOriginalDatabase[] =
610 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
612 db = msi_dup_property( package, szOriginalDatabase );
613 if (!db)
614 return ERROR_OUTOFMEMORY;
616 p = strrchrW( db, '\\' );
617 if (!p)
619 p = strrchrW( db, '/' );
620 if (!p)
622 msi_free(db);
623 return ERROR_SUCCESS;
627 len = p - db + 2;
628 source = msi_alloc( len * sizeof(WCHAR) );
629 lstrcpynW( source, db, len );
631 check = msi_dup_property( package, cszSourceDir );
632 if (!check || replace)
633 MSI_SetPropertyW( package, cszSourceDir, source );
635 msi_free( check );
637 check = msi_dup_property( package, cszSOURCEDIR );
638 if (!check || replace)
639 MSI_SetPropertyW( package, cszSOURCEDIR, source );
641 msi_free( check );
642 msi_free( source );
643 msi_free( db );
645 return ERROR_SUCCESS;
648 /****************************************************
649 * TOP level entry points
650 *****************************************************/
652 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
653 LPCWSTR szCommandLine )
655 UINT rc;
656 BOOL ui = FALSE, ui_exists;
657 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
658 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
659 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
661 MSI_SetPropertyW(package, szAction, szInstall);
663 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
665 package->script->InWhatSequence = SEQUENCE_INSTALL;
667 if (szPackagePath)
669 LPWSTR p, dir;
670 LPCWSTR file;
672 dir = strdupW(szPackagePath);
673 p = strrchrW(dir, '\\');
674 if (p)
676 *(++p) = 0;
677 file = szPackagePath + (p - dir);
679 else
681 msi_free(dir);
682 dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
683 GetCurrentDirectoryW(MAX_PATH, dir);
684 lstrcatW(dir, cszbs);
685 file = szPackagePath;
688 msi_free( package->PackagePath );
689 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
690 if (!package->PackagePath)
692 msi_free(dir);
693 return ERROR_OUTOFMEMORY;
696 lstrcpyW(package->PackagePath, dir);
697 lstrcatW(package->PackagePath, file);
698 msi_free(dir);
700 msi_set_sourcedir_props(package, FALSE);
703 msi_parse_command_line( package, szCommandLine );
705 msi_apply_transforms( package );
706 msi_apply_patches( package );
708 /* properties may have been added by a transform */
709 msi_clone_properties( package );
711 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
713 package->script->InWhatSequence |= SEQUENCE_UI;
714 rc = ACTION_ProcessUISequence(package);
715 ui = TRUE;
716 ui_exists = ui_sequence_exists(package);
717 if (rc == ERROR_SUCCESS || !ui_exists)
719 package->script->InWhatSequence |= SEQUENCE_EXEC;
720 rc = ACTION_ProcessExecSequence(package,ui_exists);
723 else
724 rc = ACTION_ProcessExecSequence(package,FALSE);
726 package->script->CurrentlyScripting= FALSE;
728 /* process the ending type action */
729 if (rc == ERROR_SUCCESS)
730 ACTION_PerformActionSequence(package,-1,ui);
731 else if (rc == ERROR_INSTALL_USEREXIT)
732 ACTION_PerformActionSequence(package,-2,ui);
733 else if (rc == ERROR_INSTALL_SUSPEND)
734 ACTION_PerformActionSequence(package,-4,ui);
735 else /* failed */
736 ACTION_PerformActionSequence(package,-3,ui);
738 /* finish up running custom actions */
739 ACTION_FinishCustomActions(package);
741 return rc;
744 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
746 UINT rc = ERROR_SUCCESS;
747 MSIRECORD * row = 0;
748 static const WCHAR ExecSeqQuery[] =
749 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
750 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
751 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
752 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
754 static const WCHAR UISeqQuery[] =
755 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
756 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
757 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
758 ' ', '=',' ','%','i',0};
760 if (UI)
761 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
762 else
763 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
765 if (row)
767 LPCWSTR action, cond;
769 TRACE("Running the actions\n");
771 /* check conditions */
772 cond = MSI_RecordGetString(row,2);
774 /* this is a hack to skip errors in the condition code */
775 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
776 goto end;
778 action = MSI_RecordGetString(row,1);
779 if (!action)
781 ERR("failed to fetch action\n");
782 rc = ERROR_FUNCTION_FAILED;
783 goto end;
786 if (UI)
787 rc = ACTION_PerformUIAction(package,action,-1);
788 else
789 rc = ACTION_PerformAction(package,action,-1,FALSE);
790 end:
791 msiobj_release(&row->hdr);
793 else
794 rc = ERROR_SUCCESS;
796 return rc;
799 typedef struct {
800 MSIPACKAGE* package;
801 BOOL UI;
802 } iterate_action_param;
804 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
806 iterate_action_param *iap= (iterate_action_param*)param;
807 UINT rc;
808 LPCWSTR cond, action;
810 action = MSI_RecordGetString(row,1);
811 if (!action)
813 ERR("Error is retrieving action name\n");
814 return ERROR_FUNCTION_FAILED;
817 /* check conditions */
818 cond = MSI_RecordGetString(row,2);
820 /* this is a hack to skip errors in the condition code */
821 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
823 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
824 return ERROR_SUCCESS;
827 if (iap->UI)
828 rc = ACTION_PerformUIAction(iap->package,action,-1);
829 else
830 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
832 msi_dialog_check_messages( NULL );
834 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
835 rc = iap->package->CurrentInstallState;
837 if (rc == ERROR_FUNCTION_NOT_CALLED)
838 rc = ERROR_SUCCESS;
840 if (rc != ERROR_SUCCESS)
841 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
843 return rc;
846 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
848 MSIQUERY * view;
849 UINT r;
850 static const WCHAR query[] =
851 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
852 '`','%','s','`',
853 ' ','W','H','E','R','E',' ',
854 '`','S','e','q','u','e','n','c','e','`',' ',
855 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
856 '`','S','e','q','u','e','n','c','e','`',0};
857 iterate_action_param iap;
860 * FIXME: probably should be checking UILevel in the
861 * ACTION_PerformUIAction/ACTION_PerformAction
862 * rather than saving the UI level here. Those
863 * two functions can be merged too.
865 iap.package = package;
866 iap.UI = TRUE;
868 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
870 r = MSI_OpenQuery( package->db, &view, query, szTable );
871 if (r == ERROR_SUCCESS)
873 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
874 msiobj_release(&view->hdr);
877 return r;
880 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
882 MSIQUERY * view;
883 UINT rc;
884 static const WCHAR ExecSeqQuery[] =
885 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
886 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
887 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
888 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
889 'O','R','D','E','R',' ', 'B','Y',' ',
890 '`','S','e','q','u','e','n','c','e','`',0 };
891 MSIRECORD * row = 0;
892 static const WCHAR IVQuery[] =
893 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
894 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
895 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
896 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
897 ' ','\'', 'I','n','s','t','a','l','l',
898 'V','a','l','i','d','a','t','e','\'', 0};
899 INT seq = 0;
900 iterate_action_param iap;
902 iap.package = package;
903 iap.UI = FALSE;
905 if (package->script->ExecuteSequenceRun)
907 TRACE("Execute Sequence already Run\n");
908 return ERROR_SUCCESS;
911 package->script->ExecuteSequenceRun = TRUE;
913 /* get the sequence number */
914 if (UIran)
916 row = MSI_QueryGetRecord(package->db, IVQuery);
917 if( !row )
918 return ERROR_FUNCTION_FAILED;
919 seq = MSI_RecordGetInteger(row,1);
920 msiobj_release(&row->hdr);
923 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
924 if (rc == ERROR_SUCCESS)
926 TRACE("Running the actions\n");
928 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
929 msiobj_release(&view->hdr);
932 return rc;
935 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
937 MSIQUERY * view;
938 UINT rc;
939 static const WCHAR ExecSeqQuery [] =
940 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
941 '`','I','n','s','t','a','l','l',
942 'U','I','S','e','q','u','e','n','c','e','`',
943 ' ','W','H','E','R','E',' ',
944 '`','S','e','q','u','e','n','c','e','`',' ',
945 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
946 '`','S','e','q','u','e','n','c','e','`',0};
947 iterate_action_param iap;
949 iap.package = package;
950 iap.UI = TRUE;
952 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
954 if (rc == ERROR_SUCCESS)
956 TRACE("Running the actions\n");
958 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
959 msiobj_release(&view->hdr);
962 return rc;
965 /********************************************************
966 * ACTION helper functions and functions that perform the actions
967 *******************************************************/
968 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
969 UINT* rc, BOOL force )
971 BOOL ret = FALSE;
972 BOOL run = force;
973 int i;
975 if (!run && !package->script->CurrentlyScripting)
976 run = TRUE;
978 if (!run)
980 if (strcmpW(action,szInstallFinalize) == 0 ||
981 strcmpW(action,szInstallExecute) == 0 ||
982 strcmpW(action,szInstallExecuteAgain) == 0)
983 run = TRUE;
986 i = 0;
987 while (StandardActions[i].action != NULL)
989 if (strcmpW(StandardActions[i].action, action)==0)
991 if (!run)
993 ui_actioninfo(package, action, TRUE, 0);
994 *rc = schedule_action(package,INSTALL_SCRIPT,action);
995 ui_actioninfo(package, action, FALSE, *rc);
997 else
999 ui_actionstart(package, action);
1000 if (StandardActions[i].handler)
1002 *rc = StandardActions[i].handler(package);
1004 else
1006 FIXME("unhandled standard action %s\n",debugstr_w(action));
1007 *rc = ERROR_SUCCESS;
1010 ret = TRUE;
1011 break;
1013 i++;
1015 return ret;
1018 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
1019 UINT* rc, UINT script, BOOL force )
1021 BOOL ret=FALSE;
1022 UINT arc;
1024 arc = ACTION_CustomAction(package, action, script, force);
1026 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1028 *rc = arc;
1029 ret = TRUE;
1031 return ret;
1035 * A lot of actions are really important even if they don't do anything
1036 * explicit... Lots of properties are set at the beginning of the installation
1037 * CostFinalize does a bunch of work to translate the directories and such
1039 * But until I get write access to the database that is hard, so I am going to
1040 * hack it to see if I can get something to run.
1042 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1044 UINT rc = ERROR_SUCCESS;
1045 BOOL handled;
1047 TRACE("Performing action (%s)\n",debugstr_w(action));
1049 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1051 if (!handled)
1052 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1054 if (!handled)
1056 FIXME("unhandled msi action %s\n",debugstr_w(action));
1057 rc = ERROR_FUNCTION_NOT_CALLED;
1060 return rc;
1063 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1065 UINT rc = ERROR_SUCCESS;
1066 BOOL handled = FALSE;
1068 TRACE("Performing action (%s)\n",debugstr_w(action));
1070 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1072 if (!handled)
1073 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1075 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1076 handled = TRUE;
1078 if (!handled)
1080 FIXME("unhandled msi action %s\n",debugstr_w(action));
1081 rc = ERROR_FUNCTION_NOT_CALLED;
1084 return rc;
1089 * Actual Action Handlers
1092 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1094 MSIPACKAGE *package = (MSIPACKAGE*)param;
1095 LPCWSTR dir;
1096 LPWSTR full_path;
1097 MSIRECORD *uirow;
1098 MSIFOLDER *folder;
1100 dir = MSI_RecordGetString(row,1);
1101 if (!dir)
1103 ERR("Unable to get folder id\n");
1104 return ERROR_SUCCESS;
1107 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1108 if (!full_path)
1110 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1111 return ERROR_SUCCESS;
1114 TRACE("Folder is %s\n",debugstr_w(full_path));
1116 /* UI stuff */
1117 uirow = MSI_CreateRecord(1);
1118 MSI_RecordSetStringW(uirow,1,full_path);
1119 ui_actiondata(package,szCreateFolders,uirow);
1120 msiobj_release( &uirow->hdr );
1122 if (folder->State == 0)
1123 create_full_pathW(full_path);
1125 folder->State = 3;
1127 msi_free(full_path);
1128 return ERROR_SUCCESS;
1131 /* FIXME: probably should merge this with the above function */
1132 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1134 UINT rc = ERROR_SUCCESS;
1135 MSIFOLDER *folder;
1136 LPWSTR install_path;
1138 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1139 if (!install_path)
1140 return ERROR_FUNCTION_FAILED;
1142 /* create the path */
1143 if (folder->State == 0)
1145 create_full_pathW(install_path);
1146 folder->State = 2;
1148 msi_free(install_path);
1150 return rc;
1153 UINT msi_create_component_directories( MSIPACKAGE *package )
1155 MSICOMPONENT *comp;
1157 /* create all the folders required by the components are going to install */
1158 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1160 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1161 continue;
1162 msi_create_directory( package, comp->Directory );
1165 return ERROR_SUCCESS;
1169 * Also we cannot enable/disable components either, so for now I am just going
1170 * to do all the directories for all the components.
1172 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1174 static const WCHAR ExecSeqQuery[] =
1175 {'S','E','L','E','C','T',' ',
1176 '`','D','i','r','e','c','t','o','r','y','_','`',
1177 ' ','F','R','O','M',' ',
1178 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1179 UINT rc;
1180 MSIQUERY *view;
1182 /* create all the empty folders specified in the CreateFolder table */
1183 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1184 if (rc != ERROR_SUCCESS)
1185 return ERROR_SUCCESS;
1187 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1188 msiobj_release(&view->hdr);
1190 msi_create_component_directories( package );
1192 return rc;
1195 static UINT load_component( MSIRECORD *row, LPVOID param )
1197 MSIPACKAGE *package = param;
1198 MSICOMPONENT *comp;
1200 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1201 if (!comp)
1202 return ERROR_FUNCTION_FAILED;
1204 list_add_tail( &package->components, &comp->entry );
1206 /* fill in the data */
1207 comp->Component = msi_dup_record_field( row, 1 );
1209 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1211 comp->ComponentId = msi_dup_record_field( row, 2 );
1212 comp->Directory = msi_dup_record_field( row, 3 );
1213 comp->Attributes = MSI_RecordGetInteger(row,4);
1214 comp->Condition = msi_dup_record_field( row, 5 );
1215 comp->KeyPath = msi_dup_record_field( row, 6 );
1217 comp->Installed = INSTALLSTATE_UNKNOWN;
1218 msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1220 return ERROR_SUCCESS;
1223 static UINT load_all_components( MSIPACKAGE *package )
1225 static const WCHAR query[] = {
1226 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1227 '`','C','o','m','p','o','n','e','n','t','`',0 };
1228 MSIQUERY *view;
1229 UINT r;
1231 if (!list_empty(&package->components))
1232 return ERROR_SUCCESS;
1234 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1235 if (r != ERROR_SUCCESS)
1236 return r;
1238 r = MSI_IterateRecords(view, NULL, load_component, package);
1239 msiobj_release(&view->hdr);
1240 return r;
1243 typedef struct {
1244 MSIPACKAGE *package;
1245 MSIFEATURE *feature;
1246 } _ilfs;
1248 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1250 ComponentList *cl;
1252 cl = msi_alloc( sizeof (*cl) );
1253 if ( !cl )
1254 return ERROR_NOT_ENOUGH_MEMORY;
1255 cl->component = comp;
1256 list_add_tail( &feature->Components, &cl->entry );
1258 return ERROR_SUCCESS;
1261 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1263 FeatureList *fl;
1265 fl = msi_alloc( sizeof(*fl) );
1266 if ( !fl )
1267 return ERROR_NOT_ENOUGH_MEMORY;
1268 fl->feature = child;
1269 list_add_tail( &parent->Children, &fl->entry );
1271 return ERROR_SUCCESS;
1274 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1276 _ilfs* ilfs= (_ilfs*)param;
1277 LPCWSTR component;
1278 MSICOMPONENT *comp;
1280 component = MSI_RecordGetString(row,1);
1282 /* check to see if the component is already loaded */
1283 comp = get_loaded_component( ilfs->package, component );
1284 if (!comp)
1286 ERR("unknown component %s\n", debugstr_w(component));
1287 return ERROR_FUNCTION_FAILED;
1290 add_feature_component( ilfs->feature, comp );
1291 comp->Enabled = TRUE;
1293 return ERROR_SUCCESS;
1296 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1298 MSIFEATURE *feature;
1300 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1302 if ( !lstrcmpW( feature->Feature, name ) )
1303 return feature;
1306 return NULL;
1309 static UINT load_feature(MSIRECORD * row, LPVOID param)
1311 MSIPACKAGE* package = (MSIPACKAGE*)param;
1312 MSIFEATURE* feature;
1313 static const WCHAR Query1[] =
1314 {'S','E','L','E','C','T',' ',
1315 '`','C','o','m','p','o','n','e','n','t','_','`',
1316 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1317 'C','o','m','p','o','n','e','n','t','s','`',' ',
1318 'W','H','E','R','E',' ',
1319 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1320 MSIQUERY * view;
1321 UINT rc;
1322 _ilfs ilfs;
1324 /* fill in the data */
1326 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1327 if (!feature)
1328 return ERROR_NOT_ENOUGH_MEMORY;
1330 list_init( &feature->Children );
1331 list_init( &feature->Components );
1333 feature->Feature = msi_dup_record_field( row, 1 );
1335 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1337 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1338 feature->Title = msi_dup_record_field( row, 3 );
1339 feature->Description = msi_dup_record_field( row, 4 );
1341 if (!MSI_RecordIsNull(row,5))
1342 feature->Display = MSI_RecordGetInteger(row,5);
1344 feature->Level= MSI_RecordGetInteger(row,6);
1345 feature->Directory = msi_dup_record_field( row, 7 );
1346 feature->Attributes = MSI_RecordGetInteger(row,8);
1348 feature->Installed = INSTALLSTATE_UNKNOWN;
1349 msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1351 list_add_tail( &package->features, &feature->entry );
1353 /* load feature components */
1355 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1356 if (rc != ERROR_SUCCESS)
1357 return ERROR_SUCCESS;
1359 ilfs.package = package;
1360 ilfs.feature = feature;
1362 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1363 msiobj_release(&view->hdr);
1365 return ERROR_SUCCESS;
1368 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1370 MSIPACKAGE* package = (MSIPACKAGE*)param;
1371 MSIFEATURE *parent, *child;
1373 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1374 if (!child)
1375 return ERROR_FUNCTION_FAILED;
1377 if (!child->Feature_Parent)
1378 return ERROR_SUCCESS;
1380 parent = find_feature_by_name( package, child->Feature_Parent );
1381 if (!parent)
1382 return ERROR_FUNCTION_FAILED;
1384 add_feature_child( parent, child );
1385 return ERROR_SUCCESS;
1388 static UINT load_all_features( MSIPACKAGE *package )
1390 static const WCHAR query[] = {
1391 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1392 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1393 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1394 MSIQUERY *view;
1395 UINT r;
1397 if (!list_empty(&package->features))
1398 return ERROR_SUCCESS;
1400 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1401 if (r != ERROR_SUCCESS)
1402 return r;
1404 r = MSI_IterateRecords( view, NULL, load_feature, package );
1405 if (r != ERROR_SUCCESS)
1406 return r;
1408 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1409 msiobj_release( &view->hdr );
1411 return r;
1414 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1416 if (!p)
1417 return p;
1418 p = strchrW(p, ch);
1419 if (!p)
1420 return p;
1421 *p = 0;
1422 return p+1;
1425 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1427 static const WCHAR query[] = {
1428 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1429 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1430 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1431 MSIQUERY *view = NULL;
1432 MSIRECORD *row = NULL;
1433 UINT r;
1435 TRACE("%s\n", debugstr_w(file->File));
1437 r = MSI_OpenQuery(package->db, &view, query, file->File);
1438 if (r != ERROR_SUCCESS)
1439 goto done;
1441 r = MSI_ViewExecute(view, NULL);
1442 if (r != ERROR_SUCCESS)
1443 goto done;
1445 r = MSI_ViewFetch(view, &row);
1446 if (r != ERROR_SUCCESS)
1447 goto done;
1449 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1450 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1451 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1452 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1453 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1455 done:
1456 if (view) msiobj_release(&view->hdr);
1457 if (row) msiobj_release(&row->hdr);
1458 return r;
1461 static UINT load_file(MSIRECORD *row, LPVOID param)
1463 MSIPACKAGE* package = (MSIPACKAGE*)param;
1464 LPCWSTR component;
1465 MSIFILE *file;
1467 /* fill in the data */
1469 file = msi_alloc_zero( sizeof (MSIFILE) );
1470 if (!file)
1471 return ERROR_NOT_ENOUGH_MEMORY;
1473 file->File = msi_dup_record_field( row, 1 );
1475 component = MSI_RecordGetString( row, 2 );
1476 file->Component = get_loaded_component( package, component );
1478 if (!file->Component)
1479 ERR("Unfound Component %s\n",debugstr_w(component));
1481 file->FileName = msi_dup_record_field( row, 3 );
1482 reduce_to_longfilename( file->FileName );
1484 file->ShortName = msi_dup_record_field( row, 3 );
1485 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1487 file->FileSize = MSI_RecordGetInteger( row, 4 );
1488 file->Version = msi_dup_record_field( row, 5 );
1489 file->Language = msi_dup_record_field( row, 6 );
1490 file->Attributes = MSI_RecordGetInteger( row, 7 );
1491 file->Sequence = MSI_RecordGetInteger( row, 8 );
1493 file->state = msifs_invalid;
1495 /* if the compressed bits are not set in the file attributes,
1496 * then read the information from the package word count property
1498 if (file->Attributes & msidbFileAttributesCompressed)
1500 file->IsCompressed = TRUE;
1502 else if (file->Attributes & msidbFileAttributesNoncompressed)
1504 file->IsCompressed = FALSE;
1506 else
1508 file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1511 load_file_hash(package, file);
1513 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1515 list_add_tail( &package->files, &file->entry );
1517 return ERROR_SUCCESS;
1520 static UINT load_all_files(MSIPACKAGE *package)
1522 MSIQUERY * view;
1523 UINT rc;
1524 static const WCHAR Query[] =
1525 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1526 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1527 '`','S','e','q','u','e','n','c','e','`', 0};
1529 if (!list_empty(&package->files))
1530 return ERROR_SUCCESS;
1532 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1533 if (rc != ERROR_SUCCESS)
1534 return ERROR_SUCCESS;
1536 rc = MSI_IterateRecords(view, NULL, load_file, package);
1537 msiobj_release(&view->hdr);
1539 return ERROR_SUCCESS;
1542 static UINT load_folder( MSIRECORD *row, LPVOID param )
1544 MSIPACKAGE *package = param;
1545 static const WCHAR szDot[] = { '.',0 };
1546 static WCHAR szEmpty[] = { 0 };
1547 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1548 MSIFOLDER *folder;
1550 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1551 if (!folder)
1552 return ERROR_NOT_ENOUGH_MEMORY;
1554 folder->Directory = msi_dup_record_field( row, 1 );
1556 TRACE("%s\n", debugstr_w(folder->Directory));
1558 p = msi_dup_record_field(row, 3);
1560 /* split src and target dir */
1561 tgt_short = p;
1562 src_short = folder_split_path( p, ':' );
1564 /* split the long and short paths */
1565 tgt_long = folder_split_path( tgt_short, '|' );
1566 src_long = folder_split_path( src_short, '|' );
1568 /* check for no-op dirs */
1569 if (!lstrcmpW(szDot, tgt_short))
1570 tgt_short = szEmpty;
1571 if (!lstrcmpW(szDot, src_short))
1572 src_short = szEmpty;
1574 if (!tgt_long)
1575 tgt_long = tgt_short;
1577 if (!src_short) {
1578 src_short = tgt_short;
1579 src_long = tgt_long;
1582 if (!src_long)
1583 src_long = src_short;
1585 /* FIXME: use the target short path too */
1586 folder->TargetDefault = strdupW(tgt_long);
1587 folder->SourceShortPath = strdupW(src_short);
1588 folder->SourceLongPath = strdupW(src_long);
1589 msi_free(p);
1591 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1592 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1593 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1595 folder->Parent = msi_dup_record_field( row, 2 );
1597 folder->Property = msi_dup_property( package, folder->Directory );
1599 list_add_tail( &package->folders, &folder->entry );
1601 TRACE("returning %p\n", folder);
1603 return ERROR_SUCCESS;
1606 static UINT load_all_folders( MSIPACKAGE *package )
1608 static const WCHAR query[] = {
1609 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1610 '`','D','i','r','e','c','t','o','r','y','`',0 };
1611 MSIQUERY *view;
1612 UINT r;
1614 if (!list_empty(&package->folders))
1615 return ERROR_SUCCESS;
1617 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1618 if (r != ERROR_SUCCESS)
1619 return r;
1621 r = MSI_IterateRecords(view, NULL, load_folder, package);
1622 msiobj_release(&view->hdr);
1623 return r;
1627 * I am not doing any of the costing functionality yet.
1628 * Mostly looking at doing the Component and Feature loading
1630 * The native MSI does A LOT of modification to tables here. Mostly adding
1631 * a lot of temporary columns to the Feature and Component tables.
1633 * note: Native msi also tracks the short filename. But I am only going to
1634 * track the long ones. Also looking at this directory table
1635 * it appears that the directory table does not get the parents
1636 * resolved base on property only based on their entries in the
1637 * directory table.
1639 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1641 static const WCHAR szCosting[] =
1642 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1643 static const WCHAR szZero[] = { '0', 0 };
1645 MSI_SetPropertyW(package, szCosting, szZero);
1646 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1648 load_all_components( package );
1649 load_all_features( package );
1650 load_all_files( package );
1651 load_all_folders( package );
1653 return ERROR_SUCCESS;
1656 static UINT execute_script(MSIPACKAGE *package, UINT script )
1658 UINT i;
1659 UINT rc = ERROR_SUCCESS;
1661 TRACE("Executing Script %i\n",script);
1663 if (!package->script)
1665 ERR("no script!\n");
1666 return ERROR_FUNCTION_FAILED;
1669 for (i = 0; i < package->script->ActionCount[script]; i++)
1671 LPWSTR action;
1672 action = package->script->Actions[script][i];
1673 ui_actionstart(package, action);
1674 TRACE("Executing Action (%s)\n",debugstr_w(action));
1675 rc = ACTION_PerformAction(package, action, script, TRUE);
1676 if (rc != ERROR_SUCCESS)
1677 break;
1679 msi_free_action_script(package, script);
1680 return rc;
1683 static UINT ACTION_FileCost(MSIPACKAGE *package)
1685 return ERROR_SUCCESS;
1688 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1690 MSICOMPONENT *comp;
1692 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1694 INSTALLSTATE res;
1696 if (!comp->ComponentId)
1697 continue;
1699 res = MsiGetComponentPathW( package->ProductCode,
1700 comp->ComponentId, NULL, NULL);
1701 if (res < 0)
1702 res = INSTALLSTATE_ABSENT;
1703 comp->Installed = res;
1707 /* scan for and update current install states */
1708 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1710 MSICOMPONENT *comp;
1711 MSIFEATURE *feature;
1713 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1715 ComponentList *cl;
1716 INSTALLSTATE res = INSTALLSTATE_ABSENT;
1718 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1720 comp= cl->component;
1722 if (!comp->ComponentId)
1724 res = INSTALLSTATE_ABSENT;
1725 break;
1728 if (res == INSTALLSTATE_ABSENT)
1729 res = comp->Installed;
1730 else
1732 if (res == comp->Installed)
1733 continue;
1735 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1736 res != INSTALLSTATE_SOURCE)
1738 res = INSTALLSTATE_INCOMPLETE;
1742 feature->Installed = res;
1746 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
1747 INSTALLSTATE state)
1749 static const WCHAR all[]={'A','L','L',0};
1750 LPWSTR override;
1751 MSIFEATURE *feature;
1753 override = msi_dup_property( package, property );
1754 if (!override)
1755 return FALSE;
1757 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1759 if (strcmpiW(override,all)==0)
1760 msi_feature_set_state( feature, state );
1761 else
1763 LPWSTR ptr = override;
1764 LPWSTR ptr2 = strchrW(override,',');
1766 while (ptr)
1768 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1769 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1771 msi_feature_set_state( feature, state );
1772 break;
1774 if (ptr2)
1776 ptr=ptr2+1;
1777 ptr2 = strchrW(ptr,',');
1779 else
1780 break;
1784 msi_free(override);
1786 return TRUE;
1789 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1791 int install_level;
1792 static const WCHAR szlevel[] =
1793 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1794 static const WCHAR szAddLocal[] =
1795 {'A','D','D','L','O','C','A','L',0};
1796 static const WCHAR szAddSource[] =
1797 {'A','D','D','S','O','U','R','C','E',0};
1798 static const WCHAR szRemove[] =
1799 {'R','E','M','O','V','E',0};
1800 static const WCHAR szReinstall[] =
1801 {'R','E','I','N','S','T','A','L','L',0};
1802 BOOL override = FALSE;
1803 MSICOMPONENT* component;
1804 MSIFEATURE *feature;
1807 /* I do not know if this is where it should happen.. but */
1809 TRACE("Checking Install Level\n");
1811 install_level = msi_get_property_int( package, szlevel, 1 );
1813 /* ok here is the _real_ rub
1814 * all these activation/deactivation things happen in order and things
1815 * later on the list override things earlier on the list.
1816 * 1) INSTALLLEVEL processing
1817 * 2) ADDLOCAL
1818 * 3) REMOVE
1819 * 4) ADDSOURCE
1820 * 5) ADDDEFAULT
1821 * 6) REINSTALL
1822 * 7) COMPADDLOCAL
1823 * 8) COMPADDSOURCE
1824 * 9) FILEADDLOCAL
1825 * 10) FILEADDSOURCE
1826 * 11) FILEADDDEFAULT
1827 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1828 * ignored for all the features. seems strange, especially since it is not
1829 * documented anywhere, but it is how it works.
1831 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1832 * REMOVE are the big ones, since we don't handle administrative installs
1833 * yet anyway.
1835 override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1836 override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1837 override |= process_state_property(package,szAddSource,INSTALLSTATE_SOURCE);
1838 override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1840 if (!override)
1842 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1844 BOOL feature_state = ((feature->Level > 0) &&
1845 (feature->Level <= install_level));
1847 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1849 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1850 msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1851 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1852 msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1853 else
1854 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1858 /* disable child features of unselected parent features */
1859 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1861 FeatureList *fl;
1863 if (feature->Level > 0 && feature->Level <= install_level)
1864 continue;
1866 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1867 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1870 else
1872 /* set the Preselected Property */
1873 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1874 static const WCHAR szOne[] = { '1', 0 };
1876 MSI_SetPropertyW(package,szPreselected,szOne);
1880 * now we want to enable or disable components base on feature
1883 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1885 ComponentList *cl;
1887 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1888 debugstr_w(feature->Feature), feature->Installed, feature->Action);
1890 /* features with components that have compressed files are made local */
1891 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1893 if (cl->component->Enabled &&
1894 cl->component->ForceLocalState &&
1895 feature->Action == INSTALLSTATE_SOURCE)
1897 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1898 break;
1902 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1904 component = cl->component;
1906 if (!component->Enabled)
1907 continue;
1909 switch (feature->Action)
1911 case INSTALLSTATE_ABSENT:
1912 component->anyAbsent = 1;
1913 break;
1914 case INSTALLSTATE_ADVERTISED:
1915 component->hasAdvertiseFeature = 1;
1916 break;
1917 case INSTALLSTATE_SOURCE:
1918 component->hasSourceFeature = 1;
1919 break;
1920 case INSTALLSTATE_LOCAL:
1921 component->hasLocalFeature = 1;
1922 break;
1923 case INSTALLSTATE_DEFAULT:
1924 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1925 component->hasAdvertiseFeature = 1;
1926 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1927 component->hasSourceFeature = 1;
1928 else
1929 component->hasLocalFeature = 1;
1930 break;
1931 default:
1932 break;
1937 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1939 /* if the component isn't enabled, leave it alone */
1940 if (!component->Enabled)
1941 continue;
1943 /* check if it's local or source */
1944 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1945 (component->hasLocalFeature || component->hasSourceFeature))
1947 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1948 !component->ForceLocalState)
1949 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1950 else
1951 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1952 continue;
1955 /* if any feature is local, the component must be local too */
1956 if (component->hasLocalFeature)
1958 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1959 continue;
1962 if (component->hasSourceFeature)
1964 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1965 continue;
1968 if (component->hasAdvertiseFeature)
1970 msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1971 continue;
1974 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1975 if (component->anyAbsent)
1976 msi_component_set_state(component, INSTALLSTATE_ABSENT);
1979 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1981 if (component->Action == INSTALLSTATE_DEFAULT)
1983 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1984 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1987 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1988 debugstr_w(component->Component), component->Installed, component->Action);
1992 return ERROR_SUCCESS;
1995 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1997 MSIPACKAGE *package = (MSIPACKAGE*)param;
1998 LPCWSTR name;
1999 LPWSTR path;
2000 MSIFOLDER *f;
2002 name = MSI_RecordGetString(row,1);
2004 f = get_loaded_folder(package, name);
2005 if (!f) return ERROR_SUCCESS;
2007 /* reset the ResolvedTarget */
2008 msi_free(f->ResolvedTarget);
2009 f->ResolvedTarget = NULL;
2011 /* This helper function now does ALL the work */
2012 TRACE("Dir %s ...\n",debugstr_w(name));
2013 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
2014 TRACE("resolves to %s\n",debugstr_w(path));
2015 msi_free(path);
2017 return ERROR_SUCCESS;
2020 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2022 MSIPACKAGE *package = (MSIPACKAGE*)param;
2023 LPCWSTR name;
2024 MSIFEATURE *feature;
2026 name = MSI_RecordGetString( row, 1 );
2028 feature = get_loaded_feature( package, name );
2029 if (!feature)
2030 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2031 else
2033 LPCWSTR Condition;
2034 Condition = MSI_RecordGetString(row,3);
2036 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2038 int level = MSI_RecordGetInteger(row,2);
2039 TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
2040 feature->Level = level;
2043 return ERROR_SUCCESS;
2046 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2048 static const WCHAR name_fmt[] =
2049 {'%','u','.','%','u','.','%','u','.','%','u',0};
2050 static WCHAR name[] = {'\\',0};
2051 VS_FIXEDFILEINFO *lpVer;
2052 WCHAR filever[0x100];
2053 LPVOID version;
2054 DWORD versize;
2055 DWORD handle;
2056 UINT sz;
2058 TRACE("%s\n", debugstr_w(filename));
2060 versize = GetFileVersionInfoSizeW( filename, &handle );
2061 if (!versize)
2062 return NULL;
2064 version = msi_alloc( versize );
2065 GetFileVersionInfoW( filename, 0, versize, version );
2067 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2069 msi_free( version );
2070 return NULL;
2073 sprintfW( filever, name_fmt,
2074 HIWORD(lpVer->dwFileVersionMS),
2075 LOWORD(lpVer->dwFileVersionMS),
2076 HIWORD(lpVer->dwFileVersionLS),
2077 LOWORD(lpVer->dwFileVersionLS));
2079 msi_free( version );
2081 return strdupW( filever );
2084 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2086 LPWSTR file_version;
2087 MSIFILE *file;
2089 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2091 MSICOMPONENT* comp = file->Component;
2092 LPWSTR p;
2094 if (!comp)
2095 continue;
2097 if (file->IsCompressed)
2098 comp->ForceLocalState = TRUE;
2100 /* calculate target */
2101 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2103 msi_free(file->TargetPath);
2105 TRACE("file %s is named %s\n",
2106 debugstr_w(file->File), debugstr_w(file->FileName));
2108 file->TargetPath = build_directory_name(2, p, file->FileName);
2110 msi_free(p);
2112 TRACE("file %s resolves to %s\n",
2113 debugstr_w(file->File), debugstr_w(file->TargetPath));
2115 /* don't check files of components that aren't installed */
2116 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2117 comp->Installed == INSTALLSTATE_ABSENT)
2119 file->state = msifs_missing; /* assume files are missing */
2120 continue;
2123 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2125 file->state = msifs_missing;
2126 comp->Cost += file->FileSize;
2127 comp->Installed = INSTALLSTATE_INCOMPLETE;
2128 continue;
2131 if (file->Version &&
2132 (file_version = msi_get_disk_file_version( file->TargetPath )))
2134 TRACE("new %s old %s\n", debugstr_w(file->Version),
2135 debugstr_w(file_version));
2136 /* FIXME: seems like a bad way to compare version numbers */
2137 if (lstrcmpiW(file_version, file->Version)<0)
2139 file->state = msifs_overwrite;
2140 comp->Cost += file->FileSize;
2141 comp->Installed = INSTALLSTATE_INCOMPLETE;
2143 else
2144 file->state = msifs_present;
2145 msi_free( file_version );
2147 else
2148 file->state = msifs_present;
2151 return ERROR_SUCCESS;
2155 * A lot is done in this function aside from just the costing.
2156 * The costing needs to be implemented at some point but for now I am going
2157 * to focus on the directory building
2160 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2162 static const WCHAR ExecSeqQuery[] =
2163 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2164 '`','D','i','r','e','c','t','o','r','y','`',0};
2165 static const WCHAR ConditionQuery[] =
2166 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2167 '`','C','o','n','d','i','t','i','o','n','`',0};
2168 static const WCHAR szCosting[] =
2169 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2170 static const WCHAR szlevel[] =
2171 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2172 static const WCHAR szOne[] = { '1', 0 };
2173 MSICOMPONENT *comp;
2174 UINT rc;
2175 MSIQUERY * view;
2176 LPWSTR level;
2178 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2179 return ERROR_SUCCESS;
2181 TRACE("Building Directory properties\n");
2183 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2184 if (rc == ERROR_SUCCESS)
2186 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2187 package);
2188 msiobj_release(&view->hdr);
2191 /* read components states from the registry */
2192 ACTION_GetComponentInstallStates(package);
2194 TRACE("File calculations\n");
2195 msi_check_file_install_states( package );
2197 TRACE("Evaluating Condition Table\n");
2199 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2200 if (rc == ERROR_SUCCESS)
2202 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2203 package);
2204 msiobj_release(&view->hdr);
2207 TRACE("Enabling or Disabling Components\n");
2208 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2210 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2212 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2213 comp->Enabled = FALSE;
2217 MSI_SetPropertyW(package,szCosting,szOne);
2218 /* set default run level if not set */
2219 level = msi_dup_property( package, szlevel );
2220 if (!level)
2221 MSI_SetPropertyW(package,szlevel, szOne);
2222 msi_free(level);
2224 ACTION_UpdateFeatureInstallStates(package);
2226 return MSI_SetFeatureStates(package);
2229 /* OK this value is "interpreted" and then formatted based on the
2230 first few characters */
2231 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2232 DWORD *size)
2234 LPSTR data = NULL;
2236 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2238 if (value[1]=='x')
2240 LPWSTR ptr;
2241 CHAR byte[5];
2242 LPWSTR deformated = NULL;
2243 int count;
2245 deformat_string(package, &value[2], &deformated);
2247 /* binary value type */
2248 ptr = deformated;
2249 *type = REG_BINARY;
2250 if (strlenW(ptr)%2)
2251 *size = (strlenW(ptr)/2)+1;
2252 else
2253 *size = strlenW(ptr)/2;
2255 data = msi_alloc(*size);
2257 byte[0] = '0';
2258 byte[1] = 'x';
2259 byte[4] = 0;
2260 count = 0;
2261 /* if uneven pad with a zero in front */
2262 if (strlenW(ptr)%2)
2264 byte[2]= '0';
2265 byte[3]= *ptr;
2266 ptr++;
2267 data[count] = (BYTE)strtol(byte,NULL,0);
2268 count ++;
2269 TRACE("Uneven byte count\n");
2271 while (*ptr)
2273 byte[2]= *ptr;
2274 ptr++;
2275 byte[3]= *ptr;
2276 ptr++;
2277 data[count] = (BYTE)strtol(byte,NULL,0);
2278 count ++;
2280 msi_free(deformated);
2282 TRACE("Data %i bytes(%i)\n",*size,count);
2284 else
2286 LPWSTR deformated;
2287 LPWSTR p;
2288 DWORD d = 0;
2289 deformat_string(package, &value[1], &deformated);
2291 *type=REG_DWORD;
2292 *size = sizeof(DWORD);
2293 data = msi_alloc(*size);
2294 p = deformated;
2295 if (*p == '-')
2296 p++;
2297 while (*p)
2299 if ( (*p < '0') || (*p > '9') )
2300 break;
2301 d *= 10;
2302 d += (*p - '0');
2303 p++;
2305 if (deformated[0] == '-')
2306 d = -d;
2307 *(LPDWORD)data = d;
2308 TRACE("DWORD %i\n",*(LPDWORD)data);
2310 msi_free(deformated);
2313 else
2315 static const WCHAR szMulti[] = {'[','~',']',0};
2316 LPCWSTR ptr;
2317 *type=REG_SZ;
2319 if (value[0]=='#')
2321 if (value[1]=='%')
2323 ptr = &value[2];
2324 *type=REG_EXPAND_SZ;
2326 else
2327 ptr = &value[1];
2329 else
2330 ptr=value;
2332 if (strstrW(value,szMulti))
2333 *type = REG_MULTI_SZ;
2335 /* remove initial delimiter */
2336 if (!strncmpW(value, szMulti, 3))
2337 ptr = value + 3;
2339 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2341 /* add double NULL terminator */
2342 if (*type == REG_MULTI_SZ)
2344 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2345 data = msi_realloc_zero(data, *size);
2348 return data;
2351 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2353 MSIPACKAGE *package = (MSIPACKAGE*)param;
2354 static const WCHAR szHCR[] =
2355 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2356 'R','O','O','T','\\',0};
2357 static const WCHAR szHCU[] =
2358 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2359 'U','S','E','R','\\',0};
2360 static const WCHAR szHLM[] =
2361 {'H','K','E','Y','_','L','O','C','A','L','_',
2362 'M','A','C','H','I','N','E','\\',0};
2363 static const WCHAR szHU[] =
2364 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2366 LPSTR value_data = NULL;
2367 HKEY root_key, hkey;
2368 DWORD type,size;
2369 LPWSTR deformated;
2370 LPCWSTR szRoot, component, name, key, value;
2371 MSICOMPONENT *comp;
2372 MSIRECORD * uirow;
2373 LPWSTR uikey;
2374 INT root;
2375 BOOL check_first = FALSE;
2376 UINT rc;
2378 ui_progress(package,2,0,0,0);
2380 value = NULL;
2381 key = NULL;
2382 uikey = NULL;
2383 name = NULL;
2385 component = MSI_RecordGetString(row, 6);
2386 comp = get_loaded_component(package,component);
2387 if (!comp)
2388 return ERROR_SUCCESS;
2390 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2392 TRACE("Skipping write due to disabled component %s\n",
2393 debugstr_w(component));
2395 comp->Action = comp->Installed;
2397 return ERROR_SUCCESS;
2400 comp->Action = INSTALLSTATE_LOCAL;
2402 name = MSI_RecordGetString(row, 4);
2403 if( MSI_RecordIsNull(row,5) && name )
2405 /* null values can have special meanings */
2406 if (name[0]=='-' && name[1] == 0)
2407 return ERROR_SUCCESS;
2408 else if ((name[0]=='+' && name[1] == 0) ||
2409 (name[0] == '*' && name[1] == 0))
2410 name = NULL;
2411 check_first = TRUE;
2414 root = MSI_RecordGetInteger(row,2);
2415 key = MSI_RecordGetString(row, 3);
2417 /* get the root key */
2418 switch (root)
2420 case -1:
2422 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2423 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2424 if (all_users && all_users[0] == '1')
2426 root_key = HKEY_LOCAL_MACHINE;
2427 szRoot = szHLM;
2429 else
2431 root_key = HKEY_CURRENT_USER;
2432 szRoot = szHCU;
2434 msi_free(all_users);
2436 break;
2437 case 0: root_key = HKEY_CLASSES_ROOT;
2438 szRoot = szHCR;
2439 break;
2440 case 1: root_key = HKEY_CURRENT_USER;
2441 szRoot = szHCU;
2442 break;
2443 case 2: root_key = HKEY_LOCAL_MACHINE;
2444 szRoot = szHLM;
2445 break;
2446 case 3: root_key = HKEY_USERS;
2447 szRoot = szHU;
2448 break;
2449 default:
2450 ERR("Unknown root %i\n",root);
2451 root_key=NULL;
2452 szRoot = NULL;
2453 break;
2455 if (!root_key)
2456 return ERROR_SUCCESS;
2458 deformat_string(package, key , &deformated);
2459 size = strlenW(deformated) + strlenW(szRoot) + 1;
2460 uikey = msi_alloc(size*sizeof(WCHAR));
2461 strcpyW(uikey,szRoot);
2462 strcatW(uikey,deformated);
2464 if (RegCreateKeyW( root_key, deformated, &hkey))
2466 ERR("Could not create key %s\n",debugstr_w(deformated));
2467 msi_free(deformated);
2468 msi_free(uikey);
2469 return ERROR_SUCCESS;
2471 msi_free(deformated);
2473 value = MSI_RecordGetString(row,5);
2474 if (value)
2475 value_data = parse_value(package, value, &type, &size);
2476 else
2478 static const WCHAR szEmpty[] = {0};
2479 value_data = (LPSTR)strdupW(szEmpty);
2480 size = 0;
2481 type = REG_SZ;
2484 deformat_string(package, name, &deformated);
2486 if (!check_first)
2488 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2489 debugstr_w(uikey));
2490 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2492 else
2494 DWORD sz = 0;
2495 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2496 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2498 TRACE("value %s of %s checked already exists\n",
2499 debugstr_w(deformated), debugstr_w(uikey));
2501 else
2503 TRACE("Checked and setting value %s of %s\n",
2504 debugstr_w(deformated), debugstr_w(uikey));
2505 if (deformated || size)
2506 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2509 RegCloseKey(hkey);
2511 uirow = MSI_CreateRecord(3);
2512 MSI_RecordSetStringW(uirow,2,deformated);
2513 MSI_RecordSetStringW(uirow,1,uikey);
2515 if (type == REG_SZ)
2516 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2517 else
2518 MSI_RecordSetStringW(uirow,3,value);
2520 ui_actiondata(package,szWriteRegistryValues,uirow);
2521 msiobj_release( &uirow->hdr );
2523 msi_free(value_data);
2524 msi_free(deformated);
2525 msi_free(uikey);
2527 return ERROR_SUCCESS;
2530 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2532 UINT rc;
2533 MSIQUERY * view;
2534 static const WCHAR ExecSeqQuery[] =
2535 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2536 '`','R','e','g','i','s','t','r','y','`',0 };
2538 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2539 if (rc != ERROR_SUCCESS)
2540 return ERROR_SUCCESS;
2542 /* increment progress bar each time action data is sent */
2543 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2545 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2547 msiobj_release(&view->hdr);
2548 return rc;
2551 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2553 package->script->CurrentlyScripting = TRUE;
2555 return ERROR_SUCCESS;
2559 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2561 MSICOMPONENT *comp;
2562 DWORD progress = 0;
2563 DWORD total = 0;
2564 static const WCHAR q1[]=
2565 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2566 '`','R','e','g','i','s','t','r','y','`',0};
2567 UINT rc;
2568 MSIQUERY * view;
2569 MSIFEATURE *feature;
2570 MSIFILE *file;
2572 TRACE("InstallValidate\n");
2574 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2575 if (rc == ERROR_SUCCESS)
2577 MSI_IterateRecords( view, &progress, NULL, package );
2578 msiobj_release( &view->hdr );
2579 total += progress * REG_PROGRESS_VALUE;
2582 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2583 total += COMPONENT_PROGRESS_VALUE;
2585 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2586 total += file->FileSize;
2588 ui_progress(package,0,total,0,0);
2590 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2592 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2593 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2594 feature->ActionRequest);
2597 return ERROR_SUCCESS;
2600 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2602 MSIPACKAGE* package = (MSIPACKAGE*)param;
2603 LPCWSTR cond = NULL;
2604 LPCWSTR message = NULL;
2605 UINT r;
2607 static const WCHAR title[]=
2608 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2610 cond = MSI_RecordGetString(row,1);
2612 r = MSI_EvaluateConditionW(package,cond);
2613 if (r == MSICONDITION_FALSE)
2615 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2617 LPWSTR deformated;
2618 message = MSI_RecordGetString(row,2);
2619 deformat_string(package,message,&deformated);
2620 MessageBoxW(NULL,deformated,title,MB_OK);
2621 msi_free(deformated);
2624 return ERROR_INSTALL_FAILURE;
2627 return ERROR_SUCCESS;
2630 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2632 UINT rc;
2633 MSIQUERY * view = NULL;
2634 static const WCHAR ExecSeqQuery[] =
2635 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2636 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2638 TRACE("Checking launch conditions\n");
2640 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2641 if (rc != ERROR_SUCCESS)
2642 return ERROR_SUCCESS;
2644 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2645 msiobj_release(&view->hdr);
2647 return rc;
2650 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2653 if (!cmp->KeyPath)
2654 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2656 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2658 MSIRECORD * row = 0;
2659 UINT root,len;
2660 LPWSTR deformated,buffer,deformated_name;
2661 LPCWSTR key,name;
2662 static const WCHAR ExecSeqQuery[] =
2663 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2664 '`','R','e','g','i','s','t','r','y','`',' ',
2665 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2666 ' ','=',' ' ,'\'','%','s','\'',0 };
2667 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2668 static const WCHAR fmt2[]=
2669 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2671 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2672 if (!row)
2673 return NULL;
2675 root = MSI_RecordGetInteger(row,2);
2676 key = MSI_RecordGetString(row, 3);
2677 name = MSI_RecordGetString(row, 4);
2678 deformat_string(package, key , &deformated);
2679 deformat_string(package, name, &deformated_name);
2681 len = strlenW(deformated) + 6;
2682 if (deformated_name)
2683 len+=strlenW(deformated_name);
2685 buffer = msi_alloc( len *sizeof(WCHAR));
2687 if (deformated_name)
2688 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2689 else
2690 sprintfW(buffer,fmt,root,deformated);
2692 msi_free(deformated);
2693 msi_free(deformated_name);
2694 msiobj_release(&row->hdr);
2696 return buffer;
2698 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2700 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2701 return NULL;
2703 else
2705 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2707 if (file)
2708 return strdupW( file->TargetPath );
2710 return NULL;
2713 static HKEY openSharedDLLsKey(void)
2715 HKEY hkey=0;
2716 static const WCHAR path[] =
2717 {'S','o','f','t','w','a','r','e','\\',
2718 'M','i','c','r','o','s','o','f','t','\\',
2719 'W','i','n','d','o','w','s','\\',
2720 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2721 'S','h','a','r','e','d','D','L','L','s',0};
2723 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2724 return hkey;
2727 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2729 HKEY hkey;
2730 DWORD count=0;
2731 DWORD type;
2732 DWORD sz = sizeof(count);
2733 DWORD rc;
2735 hkey = openSharedDLLsKey();
2736 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2737 if (rc != ERROR_SUCCESS)
2738 count = 0;
2739 RegCloseKey(hkey);
2740 return count;
2743 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2745 HKEY hkey;
2747 hkey = openSharedDLLsKey();
2748 if (count > 0)
2749 msi_reg_set_val_dword( hkey, path, count );
2750 else
2751 RegDeleteValueW(hkey,path);
2752 RegCloseKey(hkey);
2753 return count;
2757 * Return TRUE if the count should be written out and FALSE if not
2759 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2761 MSIFEATURE *feature;
2762 INT count = 0;
2763 BOOL write = FALSE;
2765 /* only refcount DLLs */
2766 if (comp->KeyPath == NULL ||
2767 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2768 comp->Attributes & msidbComponentAttributesODBCDataSource)
2769 write = FALSE;
2770 else
2772 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2773 write = (count > 0);
2775 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2776 write = TRUE;
2779 /* increment counts */
2780 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2782 ComponentList *cl;
2784 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2785 continue;
2787 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2789 if ( cl->component == comp )
2790 count++;
2794 /* decrement counts */
2795 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2797 ComponentList *cl;
2799 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2800 continue;
2802 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2804 if ( cl->component == comp )
2805 count--;
2809 /* ref count all the files in the component */
2810 if (write)
2812 MSIFILE *file;
2814 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2816 if (file->Component == comp)
2817 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2821 /* add a count for permenent */
2822 if (comp->Attributes & msidbComponentAttributesPermanent)
2823 count ++;
2825 comp->RefCount = count;
2827 if (write)
2828 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2832 * Ok further analysis makes me think that this work is
2833 * actually done in the PublishComponents and PublishFeatures
2834 * step, and not here. It appears like the keypath and all that is
2835 * resolved in this step, however actually written in the Publish steps.
2836 * But we will leave it here for now because it is unclear
2838 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2840 WCHAR squished_pc[GUID_SIZE];
2841 WCHAR squished_cc[GUID_SIZE];
2842 UINT rc;
2843 MSICOMPONENT *comp;
2844 HKEY hkey=0,hkey2=0;
2846 TRACE("\n");
2848 /* writes the Component and Features values to the registry */
2850 rc = MSIREG_OpenComponents(&hkey);
2851 if (rc != ERROR_SUCCESS)
2852 return rc;
2854 squash_guid(package->ProductCode,squished_pc);
2855 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2857 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2859 MSIRECORD * uirow;
2861 ui_progress(package,2,0,0,0);
2862 if (!comp->ComponentId)
2863 continue;
2865 squash_guid(comp->ComponentId,squished_cc);
2867 msi_free(comp->FullKeypath);
2868 comp->FullKeypath = resolve_keypath( package, comp );
2870 /* do the refcounting */
2871 ACTION_RefCountComponent( package, comp );
2873 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2874 debugstr_w(comp->Component),
2875 debugstr_w(squished_cc),
2876 debugstr_w(comp->FullKeypath),
2877 comp->RefCount);
2879 * Write the keypath out if the component is to be registered
2880 * and delete the key if the component is to be deregistered
2882 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2884 rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2885 if (rc != ERROR_SUCCESS)
2886 continue;
2888 if (!comp->FullKeypath)
2889 continue;
2891 msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2893 if (comp->Attributes & msidbComponentAttributesPermanent)
2895 static const WCHAR szPermKey[] =
2896 { '0','0','0','0','0','0','0','0','0','0','0','0',
2897 '0','0','0','0','0','0','0','0','0','0','0','0',
2898 '0','0','0','0','0','0','0','0',0 };
2900 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2903 RegCloseKey(hkey2);
2905 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey2, TRUE);
2906 if (rc != ERROR_SUCCESS)
2907 continue;
2909 msi_reg_set_val_str(hkey2, squished_pc, comp->FullKeypath);
2910 RegCloseKey(hkey2);
2912 else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2914 DWORD res;
2916 rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2917 if (rc != ERROR_SUCCESS)
2918 continue;
2920 RegDeleteValueW(hkey2,squished_pc);
2922 /* if the key is empty delete it */
2923 res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2924 RegCloseKey(hkey2);
2925 if (res == ERROR_NO_MORE_ITEMS)
2926 RegDeleteKeyW(hkey,squished_cc);
2928 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2931 /* UI stuff */
2932 uirow = MSI_CreateRecord(3);
2933 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2934 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2935 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2936 ui_actiondata(package,szProcessComponents,uirow);
2937 msiobj_release( &uirow->hdr );
2939 RegCloseKey(hkey);
2940 return rc;
2943 typedef struct {
2944 CLSID clsid;
2945 LPWSTR source;
2947 LPWSTR path;
2948 ITypeLib *ptLib;
2949 } typelib_struct;
2951 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2952 LPWSTR lpszName, LONG_PTR lParam)
2954 TLIBATTR *attr;
2955 typelib_struct *tl_struct = (typelib_struct*) lParam;
2956 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2957 int sz;
2958 HRESULT res;
2960 if (!IS_INTRESOURCE(lpszName))
2962 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2963 return TRUE;
2966 sz = strlenW(tl_struct->source)+4;
2967 sz *= sizeof(WCHAR);
2969 if ((INT_PTR)lpszName == 1)
2970 tl_struct->path = strdupW(tl_struct->source);
2971 else
2973 tl_struct->path = msi_alloc(sz);
2974 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2977 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2978 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2979 if (!SUCCEEDED(res))
2981 msi_free(tl_struct->path);
2982 tl_struct->path = NULL;
2984 return TRUE;
2987 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2988 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2990 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2991 return FALSE;
2994 msi_free(tl_struct->path);
2995 tl_struct->path = NULL;
2997 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2998 ITypeLib_Release(tl_struct->ptLib);
3000 return TRUE;
3003 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3005 MSIPACKAGE* package = (MSIPACKAGE*)param;
3006 LPCWSTR component;
3007 MSICOMPONENT *comp;
3008 MSIFILE *file;
3009 typelib_struct tl_struct;
3010 HMODULE module;
3011 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3013 component = MSI_RecordGetString(row,3);
3014 comp = get_loaded_component(package,component);
3015 if (!comp)
3016 return ERROR_SUCCESS;
3018 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3020 TRACE("Skipping typelib reg due to disabled component\n");
3022 comp->Action = comp->Installed;
3024 return ERROR_SUCCESS;
3027 comp->Action = INSTALLSTATE_LOCAL;
3029 file = get_loaded_file( package, comp->KeyPath );
3030 if (!file)
3031 return ERROR_SUCCESS;
3033 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3034 if (module)
3036 LPCWSTR guid;
3037 guid = MSI_RecordGetString(row,1);
3038 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3039 tl_struct.source = strdupW( file->TargetPath );
3040 tl_struct.path = NULL;
3042 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3043 (LONG_PTR)&tl_struct);
3045 if (tl_struct.path)
3047 LPWSTR help = NULL;
3048 LPCWSTR helpid;
3049 HRESULT res;
3051 helpid = MSI_RecordGetString(row,6);
3053 if (helpid)
3054 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3055 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3056 msi_free(help);
3058 if (!SUCCEEDED(res))
3059 ERR("Failed to register type library %s\n",
3060 debugstr_w(tl_struct.path));
3061 else
3063 ui_actiondata(package,szRegisterTypeLibraries,row);
3065 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3068 ITypeLib_Release(tl_struct.ptLib);
3069 msi_free(tl_struct.path);
3071 else
3072 ERR("Failed to load type library %s\n",
3073 debugstr_w(tl_struct.source));
3075 FreeLibrary(module);
3076 msi_free(tl_struct.source);
3078 else
3079 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
3081 return ERROR_SUCCESS;
3084 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3087 * OK this is a bit confusing.. I am given a _Component key and I believe
3088 * that the file that is being registered as a type library is the "key file
3089 * of that component" which I interpret to mean "The file in the KeyPath of
3090 * that component".
3092 UINT rc;
3093 MSIQUERY * view;
3094 static const WCHAR Query[] =
3095 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3096 '`','T','y','p','e','L','i','b','`',0};
3098 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3099 if (rc != ERROR_SUCCESS)
3100 return ERROR_SUCCESS;
3102 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3103 msiobj_release(&view->hdr);
3104 return rc;
3107 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3109 MSIPACKAGE *package = (MSIPACKAGE*)param;
3110 LPWSTR target_file, target_folder, filename;
3111 LPCWSTR buffer, extension;
3112 MSICOMPONENT *comp;
3113 static const WCHAR szlnk[]={'.','l','n','k',0};
3114 IShellLinkW *sl = NULL;
3115 IPersistFile *pf = NULL;
3116 HRESULT res;
3118 buffer = MSI_RecordGetString(row,4);
3119 comp = get_loaded_component(package,buffer);
3120 if (!comp)
3121 return ERROR_SUCCESS;
3123 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3125 TRACE("Skipping shortcut creation due to disabled component\n");
3127 comp->Action = comp->Installed;
3129 return ERROR_SUCCESS;
3132 comp->Action = INSTALLSTATE_LOCAL;
3134 ui_actiondata(package,szCreateShortcuts,row);
3136 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3137 &IID_IShellLinkW, (LPVOID *) &sl );
3139 if (FAILED( res ))
3141 ERR("CLSID_ShellLink not available\n");
3142 goto err;
3145 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3146 if (FAILED( res ))
3148 ERR("QueryInterface(IID_IPersistFile) failed\n");
3149 goto err;
3152 buffer = MSI_RecordGetString(row,2);
3153 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3155 /* may be needed because of a bug somehwere else */
3156 create_full_pathW(target_folder);
3158 filename = msi_dup_record_field( row, 3 );
3159 reduce_to_longfilename(filename);
3161 extension = strchrW(filename,'.');
3162 if (!extension || strcmpiW(extension,szlnk))
3164 int len = strlenW(filename);
3165 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3166 memcpy(filename + len, szlnk, sizeof(szlnk));
3168 target_file = build_directory_name(2, target_folder, filename);
3169 msi_free(target_folder);
3170 msi_free(filename);
3172 buffer = MSI_RecordGetString(row,5);
3173 if (strchrW(buffer,'['))
3175 LPWSTR deformated;
3176 deformat_string(package,buffer,&deformated);
3177 IShellLinkW_SetPath(sl,deformated);
3178 msi_free(deformated);
3180 else
3182 FIXME("poorly handled shortcut format, advertised shortcut\n");
3183 IShellLinkW_SetPath(sl,comp->FullKeypath);
3186 if (!MSI_RecordIsNull(row,6))
3188 LPWSTR deformated;
3189 buffer = MSI_RecordGetString(row,6);
3190 deformat_string(package,buffer,&deformated);
3191 IShellLinkW_SetArguments(sl,deformated);
3192 msi_free(deformated);
3195 if (!MSI_RecordIsNull(row,7))
3197 buffer = MSI_RecordGetString(row,7);
3198 IShellLinkW_SetDescription(sl,buffer);
3201 if (!MSI_RecordIsNull(row,8))
3202 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3204 if (!MSI_RecordIsNull(row,9))
3206 LPWSTR Path;
3207 INT index;
3209 buffer = MSI_RecordGetString(row,9);
3211 Path = build_icon_path(package,buffer);
3212 index = MSI_RecordGetInteger(row,10);
3214 /* no value means 0 */
3215 if (index == MSI_NULL_INTEGER)
3216 index = 0;
3218 IShellLinkW_SetIconLocation(sl,Path,index);
3219 msi_free(Path);
3222 if (!MSI_RecordIsNull(row,11))
3223 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3225 if (!MSI_RecordIsNull(row,12))
3227 LPWSTR Path;
3228 buffer = MSI_RecordGetString(row,12);
3229 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3230 if (Path)
3231 IShellLinkW_SetWorkingDirectory(sl,Path);
3232 msi_free(Path);
3235 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3236 IPersistFile_Save(pf,target_file,FALSE);
3238 msi_free(target_file);
3240 err:
3241 if (pf)
3242 IPersistFile_Release( pf );
3243 if (sl)
3244 IShellLinkW_Release( sl );
3246 return ERROR_SUCCESS;
3249 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3251 UINT rc;
3252 HRESULT res;
3253 MSIQUERY * view;
3254 static const WCHAR Query[] =
3255 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3256 '`','S','h','o','r','t','c','u','t','`',0};
3258 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3259 if (rc != ERROR_SUCCESS)
3260 return ERROR_SUCCESS;
3262 res = CoInitialize( NULL );
3263 if (FAILED (res))
3265 ERR("CoInitialize failed\n");
3266 return ERROR_FUNCTION_FAILED;
3269 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3270 msiobj_release(&view->hdr);
3272 CoUninitialize();
3274 return rc;
3277 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3279 MSIPACKAGE* package = (MSIPACKAGE*)param;
3280 HANDLE the_file;
3281 LPWSTR FilePath;
3282 LPCWSTR FileName;
3283 CHAR buffer[1024];
3284 DWORD sz;
3285 UINT rc;
3286 MSIRECORD *uirow;
3288 FileName = MSI_RecordGetString(row,1);
3289 if (!FileName)
3291 ERR("Unable to get FileName\n");
3292 return ERROR_SUCCESS;
3295 FilePath = build_icon_path(package,FileName);
3297 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3299 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3300 FILE_ATTRIBUTE_NORMAL, NULL);
3302 if (the_file == INVALID_HANDLE_VALUE)
3304 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3305 msi_free(FilePath);
3306 return ERROR_SUCCESS;
3311 DWORD write;
3312 sz = 1024;
3313 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3314 if (rc != ERROR_SUCCESS)
3316 ERR("Failed to get stream\n");
3317 CloseHandle(the_file);
3318 DeleteFileW(FilePath);
3319 break;
3321 WriteFile(the_file,buffer,sz,&write,NULL);
3322 } while (sz == 1024);
3324 msi_free(FilePath);
3326 CloseHandle(the_file);
3328 uirow = MSI_CreateRecord(1);
3329 MSI_RecordSetStringW(uirow,1,FileName);
3330 ui_actiondata(package,szPublishProduct,uirow);
3331 msiobj_release( &uirow->hdr );
3333 return ERROR_SUCCESS;
3336 static BOOL msi_check_publish(MSIPACKAGE *package)
3338 MSIFEATURE *feature;
3340 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3342 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3343 return TRUE;
3346 return FALSE;
3350 * 99% of the work done here is only done for
3351 * advertised installs. However this is where the
3352 * Icon table is processed and written out
3353 * so that is what I am going to do here.
3355 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3357 UINT rc;
3358 LPWSTR packname;
3359 MSIQUERY * view;
3360 MSISOURCELISTINFO *info;
3361 MSIMEDIADISK *disk;
3362 static const WCHAR Query[]=
3363 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3364 '`','I','c','o','n','`',0};
3365 /* for registry stuff */
3366 HKEY hkey=0;
3367 HKEY hukey=0;
3368 HKEY hudkey=0, props=0;
3369 HKEY source;
3370 static const WCHAR szProductLanguage[] =
3371 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3372 static const WCHAR szARPProductIcon[] =
3373 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3374 static const WCHAR szProductVersion[] =
3375 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3376 static const WCHAR szSourceList[] =
3377 {'S','o','u','r','c','e','L','i','s','t',0};
3378 static const WCHAR szEmpty[] = {0};
3379 DWORD langid;
3380 LPWSTR buffer;
3381 DWORD size;
3382 MSIHANDLE hDb, hSumInfo;
3384 /* FIXME: also need to publish if the product is in advertise mode */
3385 if (!msi_check_publish(package))
3386 return ERROR_SUCCESS;
3388 /* write out icon files */
3390 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3391 if (rc == ERROR_SUCCESS)
3393 MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3394 msiobj_release(&view->hdr);
3397 /* ok there is a lot more done here but i need to figure out what */
3399 rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3400 if (rc != ERROR_SUCCESS)
3401 goto end;
3403 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3404 if (rc != ERROR_SUCCESS)
3405 goto end;
3407 rc = RegCreateKeyW(hukey, szSourceList, &source);
3408 if (rc != ERROR_SUCCESS)
3409 goto end;
3411 RegCloseKey(source);
3413 rc = MSIREG_OpenUserDataProductKey(package->ProductCode,&hudkey,TRUE);
3414 if (rc != ERROR_SUCCESS)
3415 goto end;
3417 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode,&props,TRUE);
3418 if (rc != ERROR_SUCCESS)
3419 goto end;
3421 buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3422 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3423 msi_free(buffer);
3425 langid = msi_get_property_int( package, szProductLanguage, 0 );
3426 msi_reg_set_val_dword( hukey, INSTALLPROPERTY_LANGUAGEW, langid );
3428 packname = strrchrW( package->PackagePath, '\\' ) + 1;
3429 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGENAMEW, packname );
3431 /* FIXME */
3432 msi_reg_set_val_dword( hukey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0 );
3433 msi_reg_set_val_dword( props, INSTALLPROPERTY_INSTANCETYPEW, 0 );
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( hukey, 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 /* FIXME: Need to write more keys to the user registry */
3473 hDb= alloc_msihandle( &package->db->hdr );
3474 if (!hDb) {
3475 rc = ERROR_NOT_ENOUGH_MEMORY;
3476 goto end;
3478 rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo);
3479 MsiCloseHandle(hDb);
3480 if (rc == ERROR_SUCCESS)
3482 WCHAR guidbuffer[0x200];
3483 size = 0x200;
3484 rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3485 guidbuffer, &size);
3486 if (rc == ERROR_SUCCESS)
3488 /* for now we only care about the first guid */
3489 LPWSTR ptr = strchrW(guidbuffer,';');
3490 if (ptr) *ptr = 0;
3491 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, guidbuffer );
3493 else
3495 ERR("Unable to query Revision_Number...\n");
3496 rc = ERROR_SUCCESS;
3498 MsiCloseHandle(hSumInfo);
3500 else
3502 ERR("Unable to open Summary Information\n");
3503 rc = ERROR_SUCCESS;
3506 /* publish the SourceList info */
3507 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3509 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3510 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3511 info->options, info->value);
3512 else
3513 MsiSourceListSetInfoW(package->ProductCode, NULL,
3514 info->context, info->options,
3515 info->property, info->value);
3518 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3520 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3521 disk->context, disk->options,
3522 disk->disk_id, disk->volume_label, disk->disk_prompt);
3525 end:
3526 RegCloseKey(hkey);
3527 RegCloseKey(hukey);
3528 RegCloseKey(hudkey);
3529 RegCloseKey(props);
3531 return rc;
3534 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3536 MSIPACKAGE *package = (MSIPACKAGE*)param;
3537 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3538 LPWSTR deformated_section, deformated_key, deformated_value;
3539 LPWSTR folder, fullname = NULL;
3540 MSIRECORD * uirow;
3541 INT action;
3542 MSICOMPONENT *comp;
3543 static const WCHAR szWindowsFolder[] =
3544 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3546 component = MSI_RecordGetString(row, 8);
3547 comp = get_loaded_component(package,component);
3549 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3551 TRACE("Skipping ini file due to disabled component %s\n",
3552 debugstr_w(component));
3554 comp->Action = comp->Installed;
3556 return ERROR_SUCCESS;
3559 comp->Action = INSTALLSTATE_LOCAL;
3561 identifier = MSI_RecordGetString(row,1);
3562 filename = MSI_RecordGetString(row,2);
3563 dirproperty = MSI_RecordGetString(row,3);
3564 section = MSI_RecordGetString(row,4);
3565 key = MSI_RecordGetString(row,5);
3566 value = MSI_RecordGetString(row,6);
3567 action = MSI_RecordGetInteger(row,7);
3569 deformat_string(package,section,&deformated_section);
3570 deformat_string(package,key,&deformated_key);
3571 deformat_string(package,value,&deformated_value);
3573 if (dirproperty)
3575 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3576 if (!folder)
3577 folder = msi_dup_property( package, dirproperty );
3579 else
3580 folder = msi_dup_property( package, szWindowsFolder );
3582 if (!folder)
3584 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3585 goto cleanup;
3588 fullname = build_directory_name(2, folder, filename);
3590 if (action == 0)
3592 TRACE("Adding value %s to section %s in %s\n",
3593 debugstr_w(deformated_key), debugstr_w(deformated_section),
3594 debugstr_w(fullname));
3595 WritePrivateProfileStringW(deformated_section, deformated_key,
3596 deformated_value, fullname);
3598 else if (action == 1)
3600 WCHAR returned[10];
3601 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3602 returned, 10, fullname);
3603 if (returned[0] == 0)
3605 TRACE("Adding value %s to section %s in %s\n",
3606 debugstr_w(deformated_key), debugstr_w(deformated_section),
3607 debugstr_w(fullname));
3609 WritePrivateProfileStringW(deformated_section, deformated_key,
3610 deformated_value, fullname);
3613 else if (action == 3)
3614 FIXME("Append to existing section not yet implemented\n");
3616 uirow = MSI_CreateRecord(4);
3617 MSI_RecordSetStringW(uirow,1,identifier);
3618 MSI_RecordSetStringW(uirow,2,deformated_section);
3619 MSI_RecordSetStringW(uirow,3,deformated_key);
3620 MSI_RecordSetStringW(uirow,4,deformated_value);
3621 ui_actiondata(package,szWriteIniValues,uirow);
3622 msiobj_release( &uirow->hdr );
3623 cleanup:
3624 msi_free(fullname);
3625 msi_free(folder);
3626 msi_free(deformated_key);
3627 msi_free(deformated_value);
3628 msi_free(deformated_section);
3629 return ERROR_SUCCESS;
3632 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3634 UINT rc;
3635 MSIQUERY * view;
3636 static const WCHAR ExecSeqQuery[] =
3637 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3638 '`','I','n','i','F','i','l','e','`',0};
3640 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3641 if (rc != ERROR_SUCCESS)
3643 TRACE("no IniFile table\n");
3644 return ERROR_SUCCESS;
3647 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3648 msiobj_release(&view->hdr);
3649 return rc;
3652 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3654 MSIPACKAGE *package = (MSIPACKAGE*)param;
3655 LPCWSTR filename;
3656 LPWSTR FullName;
3657 MSIFILE *file;
3658 DWORD len;
3659 static const WCHAR ExeStr[] =
3660 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3661 static const WCHAR close[] = {'\"',0};
3662 STARTUPINFOW si;
3663 PROCESS_INFORMATION info;
3664 BOOL brc;
3665 MSIRECORD *uirow;
3666 LPWSTR uipath, p;
3668 memset(&si,0,sizeof(STARTUPINFOW));
3670 filename = MSI_RecordGetString(row,1);
3671 file = get_loaded_file( package, filename );
3673 if (!file)
3675 ERR("Unable to find file id %s\n",debugstr_w(filename));
3676 return ERROR_SUCCESS;
3679 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3681 FullName = msi_alloc(len*sizeof(WCHAR));
3682 strcpyW(FullName,ExeStr);
3683 strcatW( FullName, file->TargetPath );
3684 strcatW(FullName,close);
3686 TRACE("Registering %s\n",debugstr_w(FullName));
3687 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3688 &si, &info);
3690 if (brc)
3691 msi_dialog_check_messages(info.hProcess);
3693 msi_free(FullName);
3695 /* the UI chunk */
3696 uirow = MSI_CreateRecord( 2 );
3697 uipath = strdupW( file->TargetPath );
3698 p = strrchrW(uipath,'\\');
3699 if (p)
3700 p[0]=0;
3701 MSI_RecordSetStringW( uirow, 1, &p[1] );
3702 MSI_RecordSetStringW( uirow, 2, uipath);
3703 ui_actiondata( package, szSelfRegModules, uirow);
3704 msiobj_release( &uirow->hdr );
3705 msi_free( uipath );
3706 /* FIXME: call ui_progress? */
3708 return ERROR_SUCCESS;
3711 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3713 UINT rc;
3714 MSIQUERY * view;
3715 static const WCHAR ExecSeqQuery[] =
3716 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3717 '`','S','e','l','f','R','e','g','`',0};
3719 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3720 if (rc != ERROR_SUCCESS)
3722 TRACE("no SelfReg table\n");
3723 return ERROR_SUCCESS;
3726 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3727 msiobj_release(&view->hdr);
3729 return ERROR_SUCCESS;
3732 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3734 MSIFEATURE *feature;
3735 UINT rc;
3736 HKEY hkey=0;
3737 HKEY hukey=0;
3738 HKEY userdata=0;
3740 if (!msi_check_publish(package))
3741 return ERROR_SUCCESS;
3743 rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3744 if (rc != ERROR_SUCCESS)
3745 goto end;
3747 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3748 if (rc != ERROR_SUCCESS)
3749 goto end;
3751 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &userdata, TRUE);
3752 if (rc != ERROR_SUCCESS)
3753 goto end;
3755 /* here the guids are base 85 encoded */
3756 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3758 ComponentList *cl;
3759 LPWSTR data = NULL;
3760 GUID clsid;
3761 INT size;
3762 BOOL absent = FALSE;
3763 MSIRECORD *uirow;
3765 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3766 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3767 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3768 absent = TRUE;
3770 size = 1;
3771 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3773 size += 21;
3775 if (feature->Feature_Parent)
3776 size += strlenW( feature->Feature_Parent )+2;
3778 data = msi_alloc(size * sizeof(WCHAR));
3780 data[0] = 0;
3781 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3783 MSICOMPONENT* component = cl->component;
3784 WCHAR buf[21];
3786 buf[0] = 0;
3787 if (component->ComponentId)
3789 TRACE("From %s\n",debugstr_w(component->ComponentId));
3790 CLSIDFromString(component->ComponentId, &clsid);
3791 encode_base85_guid(&clsid,buf);
3792 TRACE("to %s\n",debugstr_w(buf));
3793 strcatW(data,buf);
3797 if (feature->Feature_Parent)
3799 static const WCHAR sep[] = {'\2',0};
3800 strcatW(data,sep);
3801 strcatW(data,feature->Feature_Parent);
3804 msi_reg_set_val_str( hkey, feature->Feature, data );
3805 msi_reg_set_val_str( userdata, feature->Feature, data );
3806 msi_free(data);
3808 size = 0;
3809 if (feature->Feature_Parent)
3810 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3811 if (!absent)
3813 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3814 (LPBYTE)feature->Feature_Parent,size);
3816 else
3818 size += 2*sizeof(WCHAR);
3819 data = msi_alloc(size);
3820 data[0] = 0x6;
3821 data[1] = 0;
3822 if (feature->Feature_Parent)
3823 strcpyW( &data[1], feature->Feature_Parent );
3824 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3825 (LPBYTE)data,size);
3826 msi_free(data);
3829 /* the UI chunk */
3830 uirow = MSI_CreateRecord( 1 );
3831 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3832 ui_actiondata( package, szPublishFeatures, uirow);
3833 msiobj_release( &uirow->hdr );
3834 /* FIXME: call ui_progress? */
3837 end:
3838 RegCloseKey(hkey);
3839 RegCloseKey(hukey);
3840 return rc;
3843 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3845 UINT r;
3846 HKEY hkey;
3848 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3850 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3851 if (r == ERROR_SUCCESS)
3853 RegDeleteValueW(hkey, feature->Feature);
3854 RegCloseKey(hkey);
3857 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3858 if (r == ERROR_SUCCESS)
3860 RegDeleteValueW(hkey, feature->Feature);
3861 RegCloseKey(hkey);
3864 return ERROR_SUCCESS;
3867 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3869 MSIFEATURE *feature;
3871 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3873 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3874 return FALSE;
3877 return TRUE;
3880 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3882 MSIFEATURE *feature;
3884 if (!msi_check_unpublish(package))
3885 return ERROR_SUCCESS;
3887 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3889 msi_unpublish_feature(package, feature);
3892 return ERROR_SUCCESS;
3895 static UINT msi_get_local_package_name( LPWSTR path )
3897 static const WCHAR szInstaller[] = {
3898 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3899 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3900 DWORD time, len, i;
3901 HANDLE handle;
3903 time = GetTickCount();
3904 GetWindowsDirectoryW( path, MAX_PATH );
3905 lstrcatW( path, szInstaller );
3906 CreateDirectoryW( path, NULL );
3908 len = lstrlenW(path);
3909 for (i=0; i<0x10000; i++)
3911 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3912 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3913 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3914 if (handle != INVALID_HANDLE_VALUE)
3916 CloseHandle(handle);
3917 break;
3919 if (GetLastError() != ERROR_FILE_EXISTS &&
3920 GetLastError() != ERROR_SHARING_VIOLATION)
3921 return ERROR_FUNCTION_FAILED;
3924 return ERROR_SUCCESS;
3927 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3929 WCHAR packagefile[MAX_PATH];
3930 HKEY props;
3931 UINT r;
3933 r = msi_get_local_package_name( packagefile );
3934 if (r != ERROR_SUCCESS)
3935 return r;
3937 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3939 r = CopyFileW( package->db->path, packagefile, FALSE);
3941 if (!r)
3943 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3944 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
3945 return ERROR_FUNCTION_FAILED;
3948 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3950 r = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
3951 if (r != ERROR_SUCCESS)
3952 return r;
3954 msi_reg_set_val_str(props, INSTALLPROPERTY_LOCALPACKAGEW, packagefile);
3955 RegCloseKey(props);
3956 return ERROR_SUCCESS;
3959 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3961 LPWSTR prop, val, key;
3962 static const LPCSTR propval[] = {
3963 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3964 "ARPCONTACT", "Contact",
3965 "ARPCOMMENTS", "Comments",
3966 "ProductName", "DisplayName",
3967 "ProductVersion", "DisplayVersion",
3968 "ARPHELPLINK", "HelpLink",
3969 "ARPHELPTELEPHONE", "HelpTelephone",
3970 "ARPINSTALLLOCATION", "InstallLocation",
3971 "SourceDir", "InstallSource",
3972 "Manufacturer", "Publisher",
3973 "ARPREADME", "Readme",
3974 "ARPSIZE", "Size",
3975 "ARPURLINFOABOUT", "URLInfoAbout",
3976 "ARPURLUPDATEINFO", "URLUpdateInfo",
3977 NULL,
3979 const LPCSTR *p = propval;
3981 while( *p )
3983 prop = strdupAtoW( *p++ );
3984 key = strdupAtoW( *p++ );
3985 val = msi_dup_property( package, prop );
3986 msi_reg_set_val_str( hkey, key, val );
3987 msi_free(val);
3988 msi_free(key);
3989 msi_free(prop);
3991 return ERROR_SUCCESS;
3994 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3996 HKEY hkey=0;
3997 HKEY hudkey=0, props=0;
3998 LPWSTR buffer = NULL;
3999 UINT rc;
4000 DWORD size, langid;
4001 static const WCHAR szWindowsInstaller[] =
4002 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4003 static const WCHAR szUpgradeCode[] =
4004 {'U','p','g','r','a','d','e','C','o','d','e',0};
4005 static const WCHAR modpath_fmt[] =
4006 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4007 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4008 static const WCHAR szModifyPath[] =
4009 {'M','o','d','i','f','y','P','a','t','h',0};
4010 static const WCHAR szUninstallString[] =
4011 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4012 static const WCHAR szEstimatedSize[] =
4013 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4014 static const WCHAR szProductLanguage[] =
4015 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4016 static const WCHAR szProductVersion[] =
4017 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4018 static const WCHAR szProductName[] =
4019 {'P','r','o','d','u','c','t','N','a','m','e',0};
4020 static const WCHAR szDisplayName[] =
4021 {'D','i','s','p','l','a','y','N','a','m','e',0};
4022 static const WCHAR szDisplayVersion[] =
4023 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4024 static const WCHAR szManufacturer[] =
4025 {'M','a','n','u','f','a','c','t','u','r','e','r',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 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
4041 if (rc != ERROR_SUCCESS)
4042 return rc;
4044 /* dump all the info i can grab */
4045 /* FIXME: Flesh out more information */
4047 msi_write_uninstall_property_vals( package, hkey );
4049 msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
4051 msi_make_package_local( package, hkey );
4053 /* do ModifyPath and UninstallString */
4054 size = deformat_string(package,modpath_fmt,&buffer);
4055 RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4056 RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4057 msi_free(buffer);
4059 /* FIXME: Write real Estimated Size when we have it */
4060 msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
4062 buffer = msi_dup_property( package, szProductName );
4063 msi_reg_set_val_str( props, szDisplayName, buffer );
4064 msi_free(buffer);
4066 buffer = msi_dup_property( package, cszSourceDir );
4067 msi_reg_set_val_str( props, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4068 msi_free(buffer);
4070 buffer = msi_dup_property( package, szManufacturer );
4071 msi_reg_set_val_str( props, INSTALLPROPERTY_PUBLISHERW, buffer);
4072 msi_free(buffer);
4074 GetLocalTime(&systime);
4075 sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
4076 msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
4077 msi_reg_set_val_str( props, INSTALLPROPERTY_INSTALLDATEW, szDate );
4079 langid = msi_get_property_int( package, szProductLanguage, 0 );
4080 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
4082 buffer = msi_dup_property( package, szProductVersion );
4083 msi_reg_set_val_str( props, szDisplayVersion, buffer );
4084 if (buffer)
4086 DWORD verdword = msi_version_str_to_dword(buffer);
4088 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
4089 msi_reg_set_val_dword( props, INSTALLPROPERTY_VERSIONW, verdword );
4090 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
4091 msi_reg_set_val_dword( props, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
4092 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
4093 msi_reg_set_val_dword( props, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
4095 msi_free(buffer);
4097 /* Handle Upgrade Codes */
4098 upgrade_code = msi_dup_property( package, szUpgradeCode );
4099 if (upgrade_code)
4101 HKEY hkey2;
4102 WCHAR squashed[33];
4103 MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4104 squash_guid(package->ProductCode,squashed);
4105 msi_reg_set_val_str( hkey2, squashed, NULL );
4106 RegCloseKey(hkey2);
4107 MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4108 squash_guid(package->ProductCode,squashed);
4109 msi_reg_set_val_str( hkey2, squashed, NULL );
4110 RegCloseKey(hkey2);
4112 msi_free(upgrade_code);
4115 RegCloseKey(hkey);
4117 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
4118 if (rc != ERROR_SUCCESS)
4119 return rc;
4121 RegCloseKey(hudkey);
4123 msi_reg_set_val_dword( props, szWindowsInstaller, 1 );
4124 RegCloseKey(props);
4126 return ERROR_SUCCESS;
4129 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4131 return execute_script(package,INSTALL_SCRIPT);
4134 static UINT msi_unpublish_product(MSIPACKAGE *package)
4136 LPWSTR remove = NULL;
4137 LPWSTR *features = NULL;
4138 BOOL full_uninstall = TRUE;
4139 MSIFEATURE *feature;
4141 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4142 static const WCHAR szAll[] = {'A','L','L',0};
4144 remove = msi_dup_property(package, szRemove);
4145 if (!remove)
4146 return ERROR_SUCCESS;
4148 features = msi_split_string(remove, ',');
4149 if (!features)
4151 msi_free(remove);
4152 ERR("REMOVE feature list is empty!\n");
4153 return ERROR_FUNCTION_FAILED;
4156 if (!lstrcmpW(features[0], szAll))
4157 full_uninstall = TRUE;
4158 else
4160 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4162 if (feature->Action != INSTALLSTATE_ABSENT)
4163 full_uninstall = FALSE;
4167 if (!full_uninstall)
4168 goto done;
4170 MSIREG_DeleteProductKey(package->ProductCode);
4171 MSIREG_DeleteUserProductKey(package->ProductCode);
4172 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4173 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4174 MSIREG_DeleteUninstallKey(package->ProductCode);
4176 done:
4177 msi_free(remove);
4178 msi_free(features);
4179 return ERROR_SUCCESS;
4182 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4184 UINT rc;
4186 rc = msi_unpublish_product(package);
4187 if (rc != ERROR_SUCCESS)
4188 return rc;
4190 /* turn off scheduling */
4191 package->script->CurrentlyScripting= FALSE;
4193 /* first do the same as an InstallExecute */
4194 rc = ACTION_InstallExecute(package);
4195 if (rc != ERROR_SUCCESS)
4196 return rc;
4198 /* then handle Commit Actions */
4199 rc = execute_script(package,COMMIT_SCRIPT);
4201 return rc;
4204 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4206 static const WCHAR RunOnce[] = {
4207 'S','o','f','t','w','a','r','e','\\',
4208 'M','i','c','r','o','s','o','f','t','\\',
4209 'W','i','n','d','o','w','s','\\',
4210 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4211 'R','u','n','O','n','c','e',0};
4212 static const WCHAR InstallRunOnce[] = {
4213 'S','o','f','t','w','a','r','e','\\',
4214 'M','i','c','r','o','s','o','f','t','\\',
4215 'W','i','n','d','o','w','s','\\',
4216 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4217 'I','n','s','t','a','l','l','e','r','\\',
4218 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4220 static const WCHAR msiexec_fmt[] = {
4221 '%','s',
4222 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4223 '\"','%','s','\"',0};
4224 static const WCHAR install_fmt[] = {
4225 '/','I',' ','\"','%','s','\"',' ',
4226 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4227 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4228 WCHAR buffer[256], sysdir[MAX_PATH];
4229 HKEY hkey;
4230 WCHAR squished_pc[100];
4232 squash_guid(package->ProductCode,squished_pc);
4234 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4235 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4236 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4237 squished_pc);
4239 msi_reg_set_val_str( hkey, squished_pc, buffer );
4240 RegCloseKey(hkey);
4242 TRACE("Reboot command %s\n",debugstr_w(buffer));
4244 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4245 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4247 msi_reg_set_val_str( hkey, squished_pc, buffer );
4248 RegCloseKey(hkey);
4250 return ERROR_INSTALL_SUSPEND;
4253 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4255 DWORD attrib;
4256 UINT rc;
4259 * We are currently doing what should be done here in the top level Install
4260 * however for Administrative and uninstalls this step will be needed
4262 if (!package->PackagePath)
4263 return ERROR_SUCCESS;
4265 msi_set_sourcedir_props(package, TRUE);
4267 attrib = GetFileAttributesW(package->db->path);
4268 if (attrib == INVALID_FILE_ATTRIBUTES)
4270 LPWSTR prompt;
4271 LPWSTR msg;
4272 DWORD size = 0;
4274 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4275 MSIINSTALLCONTEXT_USERUNMANAGED, MSICODE_PRODUCT,
4276 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4277 if (rc == ERROR_MORE_DATA)
4279 prompt = msi_alloc(size * sizeof(WCHAR));
4280 MsiSourceListGetInfoW(package->ProductCode, NULL,
4281 MSIINSTALLCONTEXT_USERUNMANAGED, MSICODE_PRODUCT,
4282 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4284 else
4285 prompt = strdupW(package->db->path);
4287 msg = generate_error_string(package,1302,1,prompt);
4288 while(attrib == INVALID_FILE_ATTRIBUTES)
4290 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4291 if (rc == IDCANCEL)
4293 rc = ERROR_INSTALL_USEREXIT;
4294 break;
4296 attrib = GetFileAttributesW(package->db->path);
4298 msi_free(prompt);
4299 rc = ERROR_SUCCESS;
4301 else
4302 return ERROR_SUCCESS;
4304 return rc;
4307 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4309 HKEY hkey=0;
4310 LPWSTR buffer;
4311 LPWSTR productid;
4312 UINT rc,i;
4314 static const WCHAR szPropKeys[][80] =
4316 {'P','r','o','d','u','c','t','I','D',0},
4317 {'U','S','E','R','N','A','M','E',0},
4318 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4319 {0},
4322 static const WCHAR szRegKeys[][80] =
4324 {'P','r','o','d','u','c','t','I','D',0},
4325 {'R','e','g','O','w','n','e','r',0},
4326 {'R','e','g','C','o','m','p','a','n','y',0},
4327 {0},
4330 if (msi_check_unpublish(package))
4332 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4333 return ERROR_SUCCESS;
4336 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4337 if (!productid)
4338 return ERROR_SUCCESS;
4340 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &hkey, TRUE);
4341 if (rc != ERROR_SUCCESS)
4342 goto end;
4344 for( i = 0; szPropKeys[i][0]; i++ )
4346 buffer = msi_dup_property( package, szPropKeys[i] );
4347 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4348 msi_free( buffer );
4351 end:
4352 msi_free(productid);
4353 RegCloseKey(hkey);
4355 /* FIXME: call ui_actiondata */
4357 return rc;
4361 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4363 UINT rc;
4365 package->script->InWhatSequence |= SEQUENCE_EXEC;
4366 rc = ACTION_ProcessExecSequence(package,FALSE);
4367 return rc;
4371 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4373 MSIPACKAGE *package = (MSIPACKAGE*)param;
4374 LPCWSTR compgroupid=NULL;
4375 LPCWSTR feature=NULL;
4376 LPCWSTR text = NULL;
4377 LPCWSTR qualifier = NULL;
4378 LPCWSTR component = NULL;
4379 LPWSTR advertise = NULL;
4380 LPWSTR output = NULL;
4381 HKEY hkey;
4382 UINT rc = ERROR_SUCCESS;
4383 MSICOMPONENT *comp;
4384 DWORD sz = 0;
4385 MSIRECORD *uirow;
4387 component = MSI_RecordGetString(rec,3);
4388 comp = get_loaded_component(package,component);
4390 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4391 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4392 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4394 TRACE("Skipping: Component %s not scheduled for install\n",
4395 debugstr_w(component));
4397 return ERROR_SUCCESS;
4400 compgroupid = MSI_RecordGetString(rec,1);
4401 qualifier = MSI_RecordGetString(rec,2);
4403 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4404 if (rc != ERROR_SUCCESS)
4405 goto end;
4407 text = MSI_RecordGetString(rec,4);
4408 feature = MSI_RecordGetString(rec,5);
4410 advertise = create_component_advertise_string(package, comp, feature);
4412 sz = strlenW(advertise);
4414 if (text)
4415 sz += lstrlenW(text);
4417 sz+=3;
4418 sz *= sizeof(WCHAR);
4420 output = msi_alloc_zero(sz);
4421 strcpyW(output,advertise);
4422 msi_free(advertise);
4424 if (text)
4425 strcatW(output,text);
4427 msi_reg_set_val_multi_str( hkey, qualifier, output );
4429 end:
4430 RegCloseKey(hkey);
4431 msi_free(output);
4433 /* the UI chunk */
4434 uirow = MSI_CreateRecord( 2 );
4435 MSI_RecordSetStringW( uirow, 1, compgroupid );
4436 MSI_RecordSetStringW( uirow, 2, qualifier);
4437 ui_actiondata( package, szPublishComponents, uirow);
4438 msiobj_release( &uirow->hdr );
4439 /* FIXME: call ui_progress? */
4441 return rc;
4445 * At present I am ignorning the advertised components part of this and only
4446 * focusing on the qualified component sets
4448 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4450 UINT rc;
4451 MSIQUERY * view;
4452 static const WCHAR ExecSeqQuery[] =
4453 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4454 '`','P','u','b','l','i','s','h',
4455 'C','o','m','p','o','n','e','n','t','`',0};
4457 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4458 if (rc != ERROR_SUCCESS)
4459 return ERROR_SUCCESS;
4461 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4462 msiobj_release(&view->hdr);
4464 return rc;
4467 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4469 MSIPACKAGE *package = (MSIPACKAGE*)param;
4470 MSIRECORD *row;
4471 MSIFILE *file;
4472 SC_HANDLE hscm, service = NULL;
4473 LPCWSTR name, disp, comp, depends, pass;
4474 LPCWSTR load_order, serv_name, key;
4475 DWORD serv_type, start_type;
4476 DWORD err_control;
4478 static const WCHAR query[] =
4479 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4480 '`','C','o','m','p','o','n','e','n','t','`',' ',
4481 'W','H','E','R','E',' ',
4482 '`','C','o','m','p','o','n','e','n','t','`',' ',
4483 '=','\'','%','s','\'',0};
4485 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4486 if (!hscm)
4488 ERR("Failed to open the SC Manager!\n");
4489 goto done;
4492 start_type = MSI_RecordGetInteger(rec, 5);
4493 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4494 goto done;
4496 depends = MSI_RecordGetString(rec, 8);
4497 if (depends && *depends)
4498 FIXME("Dependency list unhandled!\n");
4500 name = MSI_RecordGetString(rec, 2);
4501 disp = MSI_RecordGetString(rec, 3);
4502 serv_type = MSI_RecordGetInteger(rec, 4);
4503 err_control = MSI_RecordGetInteger(rec, 6);
4504 load_order = MSI_RecordGetString(rec, 7);
4505 serv_name = MSI_RecordGetString(rec, 9);
4506 pass = MSI_RecordGetString(rec, 10);
4507 comp = MSI_RecordGetString(rec, 12);
4509 /* fetch the service path */
4510 row = MSI_QueryGetRecord(package->db, query, comp);
4511 if (!row)
4513 ERR("Control query failed!\n");
4514 goto done;
4517 key = MSI_RecordGetString(row, 6);
4519 file = get_loaded_file(package, key);
4520 msiobj_release(&row->hdr);
4521 if (!file)
4523 ERR("Failed to load the service file\n");
4524 goto done;
4527 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4528 start_type, err_control, file->TargetPath,
4529 load_order, NULL, NULL, serv_name, pass);
4530 if (!service)
4532 if (GetLastError() != ERROR_SERVICE_EXISTS)
4533 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4536 done:
4537 CloseServiceHandle(service);
4538 CloseServiceHandle(hscm);
4540 return ERROR_SUCCESS;
4543 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4545 UINT rc;
4546 MSIQUERY * view;
4547 static const WCHAR ExecSeqQuery[] =
4548 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4549 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4551 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4552 if (rc != ERROR_SUCCESS)
4553 return ERROR_SUCCESS;
4555 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4556 msiobj_release(&view->hdr);
4558 return rc;
4561 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4562 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4564 LPCWSTR *vector, *temp_vector;
4565 LPWSTR p, q;
4566 DWORD sep_len;
4568 static const WCHAR separator[] = {'[','~',']',0};
4570 *numargs = 0;
4571 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4573 if (!args)
4574 return NULL;
4576 vector = msi_alloc(sizeof(LPWSTR));
4577 if (!vector)
4578 return NULL;
4580 p = args;
4583 (*numargs)++;
4584 vector[*numargs - 1] = p;
4586 if ((q = strstrW(p, separator)))
4588 *q = '\0';
4590 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4591 if (!temp_vector)
4593 msi_free(vector);
4594 return NULL;
4596 vector = temp_vector;
4598 p = q + sep_len;
4600 } while (q);
4602 return vector;
4605 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4607 MSIPACKAGE *package = (MSIPACKAGE *)param;
4608 MSICOMPONENT *comp;
4609 SC_HANDLE scm, service = NULL;
4610 LPCWSTR name, *vector = NULL;
4611 LPWSTR args;
4612 DWORD event, numargs;
4613 UINT r = ERROR_FUNCTION_FAILED;
4615 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4616 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4617 return ERROR_SUCCESS;
4619 name = MSI_RecordGetString(rec, 2);
4620 event = MSI_RecordGetInteger(rec, 3);
4621 args = strdupW(MSI_RecordGetString(rec, 4));
4623 if (!(event & msidbServiceControlEventStart))
4624 return ERROR_SUCCESS;
4626 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4627 if (!scm)
4629 ERR("Failed to open the service control manager\n");
4630 goto done;
4633 service = OpenServiceW(scm, name, SERVICE_START);
4634 if (!service)
4636 ERR("Failed to open service %s\n", debugstr_w(name));
4637 goto done;
4640 vector = msi_service_args_to_vector(args, &numargs);
4642 if (!StartServiceW(service, numargs, vector))
4644 ERR("Failed to start service %s\n", debugstr_w(name));
4645 goto done;
4648 r = ERROR_SUCCESS;
4650 done:
4651 CloseServiceHandle(service);
4652 CloseServiceHandle(scm);
4654 msi_free(args);
4655 msi_free(vector);
4656 return r;
4659 static UINT ACTION_StartServices( MSIPACKAGE *package )
4661 UINT rc;
4662 MSIQUERY *view;
4664 static const WCHAR query[] = {
4665 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4666 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4668 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4669 if (rc != ERROR_SUCCESS)
4670 return ERROR_SUCCESS;
4672 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4673 msiobj_release(&view->hdr);
4675 return rc;
4678 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4680 MSIFILE *file;
4682 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4684 if (!lstrcmpW(file->File, filename))
4685 return file;
4688 return NULL;
4691 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4693 MSIPACKAGE *package = (MSIPACKAGE*)param;
4694 LPWSTR driver, driver_path, ptr;
4695 WCHAR outpath[MAX_PATH];
4696 MSIFILE *driver_file, *setup_file;
4697 LPCWSTR desc;
4698 DWORD len, usage;
4699 UINT r = ERROR_SUCCESS;
4701 static const WCHAR driver_fmt[] = {
4702 'D','r','i','v','e','r','=','%','s',0};
4703 static const WCHAR setup_fmt[] = {
4704 'S','e','t','u','p','=','%','s',0};
4705 static const WCHAR usage_fmt[] = {
4706 'F','i','l','e','U','s','a','g','e','=','1',0};
4708 desc = MSI_RecordGetString(rec, 3);
4710 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4711 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4713 if (!driver_file || !setup_file)
4715 ERR("ODBC Driver entry not found!\n");
4716 return ERROR_FUNCTION_FAILED;
4719 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4720 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4721 lstrlenW(usage_fmt) + 1;
4722 driver = msi_alloc(len * sizeof(WCHAR));
4723 if (!driver)
4724 return ERROR_OUTOFMEMORY;
4726 ptr = driver;
4727 lstrcpyW(ptr, desc);
4728 ptr += lstrlenW(ptr) + 1;
4730 sprintfW(ptr, driver_fmt, driver_file->FileName);
4731 ptr += lstrlenW(ptr) + 1;
4733 sprintfW(ptr, setup_fmt, setup_file->FileName);
4734 ptr += lstrlenW(ptr) + 1;
4736 lstrcpyW(ptr, usage_fmt);
4737 ptr += lstrlenW(ptr) + 1;
4738 *ptr = '\0';
4740 driver_path = strdupW(driver_file->TargetPath);
4741 ptr = strrchrW(driver_path, '\\');
4742 if (ptr) *ptr = '\0';
4744 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4745 NULL, ODBC_INSTALL_COMPLETE, &usage))
4747 ERR("Failed to install SQL driver!\n");
4748 r = ERROR_FUNCTION_FAILED;
4751 msi_free(driver);
4752 msi_free(driver_path);
4754 return r;
4757 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4759 MSIPACKAGE *package = (MSIPACKAGE*)param;
4760 LPWSTR translator, translator_path, ptr;
4761 WCHAR outpath[MAX_PATH];
4762 MSIFILE *translator_file, *setup_file;
4763 LPCWSTR desc;
4764 DWORD len, usage;
4765 UINT r = ERROR_SUCCESS;
4767 static const WCHAR translator_fmt[] = {
4768 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4769 static const WCHAR setup_fmt[] = {
4770 'S','e','t','u','p','=','%','s',0};
4772 desc = MSI_RecordGetString(rec, 3);
4774 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4775 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4777 if (!translator_file || !setup_file)
4779 ERR("ODBC Translator entry not found!\n");
4780 return ERROR_FUNCTION_FAILED;
4783 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4784 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4785 translator = msi_alloc(len * sizeof(WCHAR));
4786 if (!translator)
4787 return ERROR_OUTOFMEMORY;
4789 ptr = translator;
4790 lstrcpyW(ptr, desc);
4791 ptr += lstrlenW(ptr) + 1;
4793 sprintfW(ptr, translator_fmt, translator_file->FileName);
4794 ptr += lstrlenW(ptr) + 1;
4796 sprintfW(ptr, setup_fmt, setup_file->FileName);
4797 ptr += lstrlenW(ptr) + 1;
4798 *ptr = '\0';
4800 translator_path = strdupW(translator_file->TargetPath);
4801 ptr = strrchrW(translator_path, '\\');
4802 if (ptr) *ptr = '\0';
4804 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4805 NULL, ODBC_INSTALL_COMPLETE, &usage))
4807 ERR("Failed to install SQL translator!\n");
4808 r = ERROR_FUNCTION_FAILED;
4811 msi_free(translator);
4812 msi_free(translator_path);
4814 return r;
4817 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4819 LPWSTR attrs;
4820 LPCWSTR desc, driver;
4821 WORD request = ODBC_ADD_SYS_DSN;
4822 INT registration;
4823 DWORD len;
4824 UINT r = ERROR_SUCCESS;
4826 static const WCHAR attrs_fmt[] = {
4827 'D','S','N','=','%','s',0 };
4829 desc = MSI_RecordGetString(rec, 3);
4830 driver = MSI_RecordGetString(rec, 4);
4831 registration = MSI_RecordGetInteger(rec, 5);
4833 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4834 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4836 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4837 attrs = msi_alloc(len * sizeof(WCHAR));
4838 if (!attrs)
4839 return ERROR_OUTOFMEMORY;
4841 sprintfW(attrs, attrs_fmt, desc);
4842 attrs[len - 1] = '\0';
4844 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4846 ERR("Failed to install SQL data source!\n");
4847 r = ERROR_FUNCTION_FAILED;
4850 msi_free(attrs);
4852 return r;
4855 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4857 UINT rc;
4858 MSIQUERY *view;
4860 static const WCHAR driver_query[] = {
4861 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4862 'O','D','B','C','D','r','i','v','e','r',0 };
4864 static const WCHAR translator_query[] = {
4865 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4866 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4868 static const WCHAR source_query[] = {
4869 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4870 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4872 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4873 if (rc != ERROR_SUCCESS)
4874 return ERROR_SUCCESS;
4876 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4877 msiobj_release(&view->hdr);
4879 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4880 if (rc != ERROR_SUCCESS)
4881 return ERROR_SUCCESS;
4883 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4884 msiobj_release(&view->hdr);
4886 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4887 if (rc != ERROR_SUCCESS)
4888 return ERROR_SUCCESS;
4890 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4891 msiobj_release(&view->hdr);
4893 return rc;
4896 #define ENV_ACT_SETALWAYS 0x1
4897 #define ENV_ACT_SETABSENT 0x2
4898 #define ENV_ACT_REMOVE 0x4
4899 #define ENV_ACT_REMOVEMATCH 0x8
4901 #define ENV_MOD_MACHINE 0x20000000
4902 #define ENV_MOD_APPEND 0x40000000
4903 #define ENV_MOD_PREFIX 0x80000000
4904 #define ENV_MOD_MASK 0xC0000000
4906 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4908 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
4910 LPCWSTR cptr = *name;
4911 LPCWSTR ptr = *value;
4913 static const WCHAR prefix[] = {'[','~',']',0};
4914 static const int prefix_len = 3;
4916 *flags = 0;
4917 while (*cptr)
4919 if (*cptr == '=')
4920 *flags |= ENV_ACT_SETALWAYS;
4921 else if (*cptr == '+')
4922 *flags |= ENV_ACT_SETABSENT;
4923 else if (*cptr == '-')
4924 *flags |= ENV_ACT_REMOVE;
4925 else if (*cptr == '!')
4926 *flags |= ENV_ACT_REMOVEMATCH;
4927 else if (*cptr == '*')
4928 *flags |= ENV_MOD_MACHINE;
4929 else
4930 break;
4932 cptr++;
4933 (*name)++;
4936 if (!*cptr)
4938 ERR("Missing environment variable\n");
4939 return ERROR_FUNCTION_FAILED;
4942 if (!strncmpW(ptr, prefix, prefix_len))
4944 *flags |= ENV_MOD_APPEND;
4945 *value += lstrlenW(prefix);
4947 else if (lstrlenW(*value) >= prefix_len)
4949 ptr += lstrlenW(ptr) - prefix_len;
4950 if (!lstrcmpW(ptr, prefix))
4952 *flags |= ENV_MOD_PREFIX;
4953 /* the "[~]" will be removed by deformat_string */;
4957 if (!*flags ||
4958 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
4959 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
4960 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
4961 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
4963 ERR("Invalid flags: %08x\n", *flags);
4964 return ERROR_FUNCTION_FAILED;
4967 return ERROR_SUCCESS;
4970 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
4972 MSIPACKAGE *package = param;
4973 LPCWSTR name, value, comp;
4974 LPWSTR data = NULL, newval = NULL;
4975 LPWSTR deformatted = NULL, ptr;
4976 DWORD flags, type, size;
4977 LONG res;
4978 HKEY env = NULL, root;
4979 LPCWSTR environment;
4981 static const WCHAR user_env[] =
4982 {'E','n','v','i','r','o','n','m','e','n','t',0};
4983 static const WCHAR machine_env[] =
4984 {'S','y','s','t','e','m','\\',
4985 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4986 'C','o','n','t','r','o','l','\\',
4987 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4988 'E','n','v','i','r','o','n','m','e','n','t',0};
4989 static const WCHAR semicolon[] = {';',0};
4991 name = MSI_RecordGetString(rec, 2);
4992 value = MSI_RecordGetString(rec, 3);
4993 comp = MSI_RecordGetString(rec, 4);
4995 res = env_set_flags(&name, &value, &flags);
4996 if (res != ERROR_SUCCESS)
4997 goto done;
4999 deformat_string(package, value, &deformatted);
5000 if (!deformatted)
5002 res = ERROR_OUTOFMEMORY;
5003 goto done;
5006 value = deformatted;
5008 if (flags & ENV_MOD_MACHINE)
5010 environment = machine_env;
5011 root = HKEY_LOCAL_MACHINE;
5013 else
5015 environment = user_env;
5016 root = HKEY_CURRENT_USER;
5019 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5020 KEY_ALL_ACCESS, NULL, &env, NULL);
5021 if (res != ERROR_SUCCESS)
5022 goto done;
5024 if (flags & ENV_ACT_REMOVE)
5025 FIXME("Not removing environment variable on uninstall!\n");
5027 size = 0;
5028 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5029 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5030 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5031 goto done;
5033 if (res != ERROR_FILE_NOT_FOUND)
5035 if (flags & ENV_ACT_SETABSENT)
5037 res = ERROR_SUCCESS;
5038 goto done;
5041 data = msi_alloc(size);
5042 if (!data)
5044 RegCloseKey(env);
5045 return ERROR_OUTOFMEMORY;
5048 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5049 if (res != ERROR_SUCCESS)
5050 goto done;
5052 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5054 res = RegDeleteKeyW(env, name);
5055 goto done;
5058 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5059 newval = msi_alloc(size);
5060 ptr = newval;
5061 if (!newval)
5063 res = ERROR_OUTOFMEMORY;
5064 goto done;
5067 if (!(flags & ENV_MOD_MASK))
5068 lstrcpyW(newval, value);
5069 else
5071 if (flags & ENV_MOD_PREFIX)
5073 lstrcpyW(newval, value);
5074 lstrcatW(newval, semicolon);
5075 ptr = newval + lstrlenW(value) + 1;
5078 lstrcpyW(ptr, data);
5080 if (flags & ENV_MOD_APPEND)
5082 lstrcatW(newval, semicolon);
5083 lstrcatW(newval, value);
5087 else
5089 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5090 newval = msi_alloc(size);
5091 if (!newval)
5093 res = ERROR_OUTOFMEMORY;
5094 goto done;
5097 lstrcpyW(newval, value);
5100 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5101 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5103 done:
5104 if (env) RegCloseKey(env);
5105 msi_free(deformatted);
5106 msi_free(data);
5107 msi_free(newval);
5108 return res;
5111 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5113 UINT rc;
5114 MSIQUERY * view;
5115 static const WCHAR ExecSeqQuery[] =
5116 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5117 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5118 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5119 if (rc != ERROR_SUCCESS)
5120 return ERROR_SUCCESS;
5122 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5123 msiobj_release(&view->hdr);
5125 return rc;
5128 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5130 typedef struct
5132 struct list entry;
5133 LPWSTR sourcename;
5134 LPWSTR destname;
5135 LPWSTR source;
5136 LPWSTR dest;
5137 } FILE_LIST;
5139 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5141 BOOL ret;
5143 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5144 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5146 WARN("Source or dest is directory, not moving\n");
5147 return FALSE;
5150 if (options == msidbMoveFileOptionsMove)
5152 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5153 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5154 if (!ret)
5156 WARN("MoveFile failed: %d\n", GetLastError());
5157 return FALSE;
5160 else
5162 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5163 ret = CopyFileW(source, dest, FALSE);
5164 if (!ret)
5166 WARN("CopyFile failed: %d\n", GetLastError());
5167 return FALSE;
5171 return TRUE;
5174 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5176 LPWSTR path, ptr;
5177 DWORD dirlen, pathlen;
5179 ptr = strrchrW(wildcard, '\\');
5180 dirlen = ptr - wildcard + 1;
5182 pathlen = dirlen + lstrlenW(filename) + 1;
5183 path = msi_alloc(pathlen * sizeof(WCHAR));
5185 lstrcpynW(path, wildcard, dirlen + 1);
5186 lstrcatW(path, filename);
5188 return path;
5191 static void free_file_entry(FILE_LIST *file)
5193 msi_free(file->source);
5194 msi_free(file->dest);
5195 msi_free(file);
5198 static void free_list(FILE_LIST *list)
5200 while (!list_empty(&list->entry))
5202 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5204 list_remove(&file->entry);
5205 free_file_entry(file);
5209 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5211 FILE_LIST *new, *file;
5212 LPWSTR ptr, filename;
5213 DWORD size;
5215 new = msi_alloc_zero(sizeof(FILE_LIST));
5216 if (!new)
5217 return FALSE;
5219 new->source = strdupW(source);
5220 ptr = strrchrW(dest, '\\') + 1;
5221 filename = strrchrW(new->source, '\\') + 1;
5223 new->sourcename = filename;
5225 if (*ptr)
5226 new->destname = ptr;
5227 else
5228 new->destname = new->sourcename;
5230 size = (ptr - dest) + lstrlenW(filename) + 1;
5231 new->dest = msi_alloc(size * sizeof(WCHAR));
5232 if (!new->dest)
5234 free_file_entry(new);
5235 return FALSE;
5238 lstrcpynW(new->dest, dest, ptr - dest + 1);
5239 lstrcatW(new->dest, filename);
5241 if (list_empty(&files->entry))
5243 list_add_head(&files->entry, &new->entry);
5244 return TRUE;
5247 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5249 if (lstrcmpW(source, file->source) < 0)
5251 list_add_before(&file->entry, &new->entry);
5252 return TRUE;
5256 list_add_after(&file->entry, &new->entry);
5257 return TRUE;
5260 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5262 WIN32_FIND_DATAW wfd;
5263 HANDLE hfile;
5264 LPWSTR path;
5265 BOOL res;
5266 FILE_LIST files, *file;
5267 DWORD size;
5269 hfile = FindFirstFileW(source, &wfd);
5270 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5272 list_init(&files.entry);
5274 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5276 if (is_dot_dir(wfd.cFileName)) continue;
5278 path = wildcard_to_file(source, wfd.cFileName);
5279 if (!path)
5281 res = FALSE;
5282 goto done;
5285 add_wildcard(&files, path, dest);
5286 msi_free(path);
5289 /* only the first wildcard match gets renamed to dest */
5290 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5291 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5292 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5293 if (!file->dest)
5295 res = FALSE;
5296 goto done;
5299 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5301 while (!list_empty(&files.entry))
5303 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5305 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5307 list_remove(&file->entry);
5308 free_file_entry(file);
5311 res = TRUE;
5313 done:
5314 free_list(&files);
5315 FindClose(hfile);
5316 return res;
5319 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5321 MSIPACKAGE *package = param;
5322 MSICOMPONENT *comp;
5323 LPCWSTR sourcename, destname;
5324 LPWSTR sourcedir = NULL, destdir = NULL;
5325 LPWSTR source = NULL, dest = NULL;
5326 int options;
5327 DWORD size;
5328 BOOL ret, wildcards;
5330 static const WCHAR backslash[] = {'\\',0};
5332 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5333 if (!comp || !comp->Enabled ||
5334 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5336 TRACE("Component not set for install, not moving file\n");
5337 return ERROR_SUCCESS;
5340 sourcename = MSI_RecordGetString(rec, 3);
5341 destname = MSI_RecordGetString(rec, 4);
5342 options = MSI_RecordGetInteger(rec, 7);
5344 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5345 if (!sourcedir)
5346 goto done;
5348 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5349 if (!destdir)
5350 goto done;
5352 if (!sourcename)
5354 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5355 goto done;
5357 source = strdupW(sourcedir);
5358 if (!source)
5359 goto done;
5361 else
5363 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5364 source = msi_alloc(size * sizeof(WCHAR));
5365 if (!source)
5366 goto done;
5368 lstrcpyW(source, sourcedir);
5369 if (source[lstrlenW(source) - 1] != '\\')
5370 lstrcatW(source, backslash);
5371 lstrcatW(source, sourcename);
5374 wildcards = strchrW(source, '*') || strchrW(source, '?');
5376 if (!destname && !wildcards)
5378 destname = strdupW(sourcename);
5379 if (!destname)
5380 goto done;
5383 size = 0;
5384 if (destname)
5385 size = lstrlenW(destname);
5387 size += lstrlenW(destdir) + 2;
5388 dest = msi_alloc(size * sizeof(WCHAR));
5389 if (!dest)
5390 goto done;
5392 lstrcpyW(dest, destdir);
5393 if (dest[lstrlenW(dest) - 1] != '\\')
5394 lstrcatW(dest, backslash);
5396 if (destname)
5397 lstrcatW(dest, destname);
5399 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5401 ret = CreateDirectoryW(destdir, NULL);
5402 if (!ret)
5404 WARN("CreateDirectory failed: %d\n", GetLastError());
5405 return ERROR_SUCCESS;
5409 if (!wildcards)
5410 msi_move_file(source, dest, options);
5411 else
5412 move_files_wildcard(source, dest, options);
5414 done:
5415 msi_free(sourcedir);
5416 msi_free(destdir);
5417 msi_free(source);
5418 msi_free(dest);
5420 return ERROR_SUCCESS;
5423 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5425 UINT rc;
5426 MSIQUERY *view;
5428 static const WCHAR ExecSeqQuery[] =
5429 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5430 '`','M','o','v','e','F','i','l','e','`',0};
5432 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5433 if (rc != ERROR_SUCCESS)
5434 return ERROR_SUCCESS;
5436 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5437 msiobj_release(&view->hdr);
5439 return rc;
5442 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5443 LPCSTR action, LPCWSTR table )
5445 static const WCHAR query[] = {
5446 'S','E','L','E','C','T',' ','*',' ',
5447 'F','R','O','M',' ','`','%','s','`',0 };
5448 MSIQUERY *view = NULL;
5449 DWORD count = 0;
5450 UINT r;
5452 r = MSI_OpenQuery( package->db, &view, query, table );
5453 if (r == ERROR_SUCCESS)
5455 r = MSI_IterateRecords(view, &count, NULL, package);
5456 msiobj_release(&view->hdr);
5459 if (count)
5460 FIXME("%s -> %u ignored %s table values\n",
5461 action, count, debugstr_w(table));
5463 return ERROR_SUCCESS;
5466 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5468 TRACE("%p\n", package);
5469 return ERROR_SUCCESS;
5472 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5474 static const WCHAR table[] =
5475 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5476 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5479 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5481 static const WCHAR table[] = { 'P','a','t','c','h',0 };
5482 return msi_unimplemented_action_stub( package, "PatchFiles", table );
5485 static UINT ACTION_BindImage( MSIPACKAGE *package )
5487 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5488 return msi_unimplemented_action_stub( package, "BindImage", table );
5491 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5493 static const WCHAR table[] = {
5494 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5495 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5498 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5500 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5501 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5504 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5506 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5507 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5510 static UINT ACTION_StopServices( MSIPACKAGE *package )
5512 static const WCHAR table[] = {
5513 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5514 return msi_unimplemented_action_stub( package, "StopServices", table );
5517 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5519 static const WCHAR table[] = {
5520 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5521 return msi_unimplemented_action_stub( package, "DeleteServices", table );
5523 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5525 static const WCHAR table[] = {
5526 'P','r','o','d','u','c','t','I','D',0 };
5527 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5530 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5532 static const WCHAR table[] = {
5533 'E','n','v','i','r','o','n','m','e','n','t',0 };
5534 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5537 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5539 static const WCHAR table[] = {
5540 'M','s','i','A','s','s','e','m','b','l','y',0 };
5541 return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
5544 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5546 static const WCHAR table[] = {
5547 'M','s','i','A','s','s','e','m','b','l','y',0 };
5548 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5551 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5553 static const WCHAR table[] = { 'F','o','n','t',0 };
5554 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5557 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5559 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5560 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5563 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5565 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5566 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5569 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5571 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5572 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5575 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5577 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5578 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5581 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5583 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5584 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5587 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5589 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5590 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5593 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5595 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5596 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5599 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5601 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5602 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5605 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5607 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5608 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5611 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5613 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5614 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5617 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5619 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5620 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5623 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5625 static const WCHAR table[] = { 'A','p','p','I','d',0 };
5626 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5629 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5631 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5632 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5635 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5637 static const WCHAR table[] = { 'M','I','M','E',0 };
5638 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5641 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5643 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5644 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5647 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5649 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5650 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5653 static const struct _actions StandardActions[] = {
5654 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5655 { szAppSearch, ACTION_AppSearch },
5656 { szBindImage, ACTION_BindImage },
5657 { szCCPSearch, ACTION_CCPSearch },
5658 { szCostFinalize, ACTION_CostFinalize },
5659 { szCostInitialize, ACTION_CostInitialize },
5660 { szCreateFolders, ACTION_CreateFolders },
5661 { szCreateShortcuts, ACTION_CreateShortcuts },
5662 { szDeleteServices, ACTION_DeleteServices },
5663 { szDisableRollback, NULL },
5664 { szDuplicateFiles, ACTION_DuplicateFiles },
5665 { szExecuteAction, ACTION_ExecuteAction },
5666 { szFileCost, ACTION_FileCost },
5667 { szFindRelatedProducts, ACTION_FindRelatedProducts },
5668 { szForceReboot, ACTION_ForceReboot },
5669 { szInstallAdminPackage, NULL },
5670 { szInstallExecute, ACTION_InstallExecute },
5671 { szInstallExecuteAgain, ACTION_InstallExecute },
5672 { szInstallFiles, ACTION_InstallFiles},
5673 { szInstallFinalize, ACTION_InstallFinalize },
5674 { szInstallInitialize, ACTION_InstallInitialize },
5675 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5676 { szInstallValidate, ACTION_InstallValidate },
5677 { szIsolateComponents, ACTION_IsolateComponents },
5678 { szLaunchConditions, ACTION_LaunchConditions },
5679 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5680 { szMoveFiles, ACTION_MoveFiles },
5681 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5682 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5683 { szInstallODBC, ACTION_InstallODBC },
5684 { szInstallServices, ACTION_InstallServices },
5685 { szPatchFiles, ACTION_PatchFiles },
5686 { szProcessComponents, ACTION_ProcessComponents },
5687 { szPublishComponents, ACTION_PublishComponents },
5688 { szPublishFeatures, ACTION_PublishFeatures },
5689 { szPublishProduct, ACTION_PublishProduct },
5690 { szRegisterClassInfo, ACTION_RegisterClassInfo },
5691 { szRegisterComPlus, ACTION_RegisterComPlus},
5692 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5693 { szRegisterFonts, ACTION_RegisterFonts },
5694 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5695 { szRegisterProduct, ACTION_RegisterProduct },
5696 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5697 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5698 { szRegisterUser, ACTION_RegisterUser },
5699 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5700 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5701 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5702 { szRemoveFiles, ACTION_RemoveFiles },
5703 { szRemoveFolders, ACTION_RemoveFolders },
5704 { szRemoveIniValues, ACTION_RemoveIniValues },
5705 { szRemoveODBC, ACTION_RemoveODBC },
5706 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5707 { szRemoveShortcuts, ACTION_RemoveShortcuts },
5708 { szResolveSource, ACTION_ResolveSource },
5709 { szRMCCPSearch, ACTION_RMCCPSearch },
5710 { szScheduleReboot, NULL },
5711 { szSelfRegModules, ACTION_SelfRegModules },
5712 { szSelfUnregModules, ACTION_SelfUnregModules },
5713 { szSetODBCFolders, NULL },
5714 { szStartServices, ACTION_StartServices },
5715 { szStopServices, ACTION_StopServices },
5716 { szUnpublishComponents, ACTION_UnpublishComponents },
5717 { szUnpublishFeatures, ACTION_UnpublishFeatures },
5718 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5719 { szUnregisterComPlus, ACTION_UnregisterComPlus },
5720 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5721 { szUnregisterFonts, ACTION_UnregisterFonts },
5722 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5723 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5724 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5725 { szValidateProductID, ACTION_ValidateProductID },
5726 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5727 { szWriteIniValues, ACTION_WriteIniValues },
5728 { szWriteRegistryValues, ACTION_WriteRegistryValues },
5729 { NULL, NULL },