msi: Fixed the WriteEnvironmentStrings action.
[wine/dibdrv.git] / dlls / msi / action.c
blob00ed5721b3cd6fbc61fa32a955a17d65a7b8c4ec
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 * Pages I need
24 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
26 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
29 #include <stdarg.h>
31 #define COBJMACROS
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winerror.h"
36 #include "winreg.h"
37 #include "winsvc.h"
38 #include "odbcinst.h"
39 #include "wine/debug.h"
40 #include "msidefs.h"
41 #include "msipriv.h"
42 #include "winuser.h"
43 #include "shlobj.h"
44 #include "wine/unicode.h"
45 #include "winver.h"
47 #define REG_PROGRESS_VALUE 13200
48 #define COMPONENT_PROGRESS_VALUE 24000
50 WINE_DEFAULT_DEBUG_CHANNEL(msi);
53 * Prototypes
55 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
56 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
57 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
60 * consts and values used
62 static const WCHAR c_colon[] = {'C',':','\\',0};
64 static const WCHAR szCreateFolders[] =
65 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
66 static const WCHAR szCostFinalize[] =
67 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
68 const WCHAR szInstallFiles[] =
69 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
70 const WCHAR szDuplicateFiles[] =
71 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
72 static const WCHAR szWriteRegistryValues[] =
73 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
74 'V','a','l','u','e','s',0};
75 static const WCHAR szCostInitialize[] =
76 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
77 static const WCHAR szFileCost[] =
78 {'F','i','l','e','C','o','s','t',0};
79 static const WCHAR szInstallInitialize[] =
80 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
81 static const WCHAR szInstallValidate[] =
82 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
83 static const WCHAR szLaunchConditions[] =
84 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
85 static const WCHAR szProcessComponents[] =
86 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
87 static const WCHAR szRegisterTypeLibraries[] =
88 {'R','e','g','i','s','t','e','r','T','y','p','e',
89 'L','i','b','r','a','r','i','e','s',0};
90 const WCHAR szRegisterClassInfo[] =
91 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
92 const WCHAR szRegisterProgIdInfo[] =
93 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
94 static const WCHAR szCreateShortcuts[] =
95 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
96 static const WCHAR szPublishProduct[] =
97 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
98 static const WCHAR szWriteIniValues[] =
99 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
100 static const WCHAR szSelfRegModules[] =
101 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
102 static const WCHAR szPublishFeatures[] =
103 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
104 static const WCHAR szRegisterProduct[] =
105 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
106 static const WCHAR szInstallExecute[] =
107 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
108 static const WCHAR szInstallExecuteAgain[] =
109 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
110 'A','g','a','i','n',0};
111 static const WCHAR szInstallFinalize[] =
112 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
113 static const WCHAR szForceReboot[] =
114 {'F','o','r','c','e','R','e','b','o','o','t',0};
115 static const WCHAR szResolveSource[] =
116 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
117 static const WCHAR szAppSearch[] =
118 {'A','p','p','S','e','a','r','c','h',0};
119 static const WCHAR szAllocateRegistrySpace[] =
120 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
121 'S','p','a','c','e',0};
122 static const WCHAR szBindImage[] =
123 {'B','i','n','d','I','m','a','g','e',0};
124 static const WCHAR szCCPSearch[] =
125 {'C','C','P','S','e','a','r','c','h',0};
126 static const WCHAR szDeleteServices[] =
127 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
128 static const WCHAR szDisableRollback[] =
129 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
130 static const WCHAR szExecuteAction[] =
131 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
132 const WCHAR szFindRelatedProducts[] =
133 {'F','i','n','d','R','e','l','a','t','e','d',
134 'P','r','o','d','u','c','t','s',0};
135 static const WCHAR szInstallAdminPackage[] =
136 {'I','n','s','t','a','l','l','A','d','m','i','n',
137 'P','a','c','k','a','g','e',0};
138 static const WCHAR szInstallSFPCatalogFile[] =
139 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
140 'F','i','l','e',0};
141 static const WCHAR szIsolateComponents[] =
142 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
143 const WCHAR szMigrateFeatureStates[] =
144 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
145 'S','t','a','t','e','s',0};
146 const WCHAR szMoveFiles[] =
147 {'M','o','v','e','F','i','l','e','s',0};
148 static const WCHAR szMsiPublishAssemblies[] =
149 {'M','s','i','P','u','b','l','i','s','h',
150 'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szMsiUnpublishAssemblies[] =
152 {'M','s','i','U','n','p','u','b','l','i','s','h',
153 'A','s','s','e','m','b','l','i','e','s',0};
154 static const WCHAR szInstallODBC[] =
155 {'I','n','s','t','a','l','l','O','D','B','C',0};
156 static const WCHAR szInstallServices[] =
157 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
158 const WCHAR szPatchFiles[] =
159 {'P','a','t','c','h','F','i','l','e','s',0};
160 static const WCHAR szPublishComponents[] =
161 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
162 static const WCHAR szRegisterComPlus[] =
163 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
164 const WCHAR szRegisterExtensionInfo[] =
165 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
166 'I','n','f','o',0};
167 static const WCHAR szRegisterFonts[] =
168 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
169 const WCHAR szRegisterMIMEInfo[] =
170 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
171 static const WCHAR szRegisterUser[] =
172 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
173 const WCHAR szRemoveDuplicateFiles[] =
174 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
175 'F','i','l','e','s',0};
176 static const WCHAR szRemoveEnvironmentStrings[] =
177 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
178 'S','t','r','i','n','g','s',0};
179 const WCHAR szRemoveExistingProducts[] =
180 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
181 'P','r','o','d','u','c','t','s',0};
182 const WCHAR szRemoveFiles[] =
183 {'R','e','m','o','v','e','F','i','l','e','s',0};
184 static const WCHAR szRemoveFolders[] =
185 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
186 static const WCHAR szRemoveIniValues[] =
187 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
188 static const WCHAR szRemoveODBC[] =
189 {'R','e','m','o','v','e','O','D','B','C',0};
190 static const WCHAR szRemoveRegistryValues[] =
191 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
192 'V','a','l','u','e','s',0};
193 static const WCHAR szRemoveShortcuts[] =
194 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
195 static const WCHAR szRMCCPSearch[] =
196 {'R','M','C','C','P','S','e','a','r','c','h',0};
197 static const WCHAR szScheduleReboot[] =
198 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
199 static const WCHAR szSelfUnregModules[] =
200 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
201 static const WCHAR szSetODBCFolders[] =
202 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
203 static const WCHAR szStartServices[] =
204 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
205 static const WCHAR szStopServices[] =
206 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
207 static const WCHAR szUnpublishComponents[] =
208 {'U','n','p','u','b','l','i','s','h',
209 'C','o','m','p','o','n','e','n','t','s',0};
210 static const WCHAR szUnpublishFeatures[] =
211 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
212 const WCHAR szUnregisterClassInfo[] =
213 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
214 'I','n','f','o',0};
215 static const WCHAR szUnregisterComPlus[] =
216 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
217 const WCHAR szUnregisterExtensionInfo[] =
218 {'U','n','r','e','g','i','s','t','e','r',
219 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
220 static const WCHAR szUnregisterFonts[] =
221 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
222 const WCHAR szUnregisterMIMEInfo[] =
223 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
224 const WCHAR szUnregisterProgIdInfo[] =
225 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
226 'I','n','f','o',0};
227 static const WCHAR szUnregisterTypeLibraries[] =
228 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
229 'L','i','b','r','a','r','i','e','s',0};
230 static const WCHAR szValidateProductID[] =
231 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
232 static const WCHAR szWriteEnvironmentStrings[] =
233 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
234 'S','t','r','i','n','g','s',0};
236 /* action handlers */
237 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
239 struct _actions {
240 LPCWSTR action;
241 STANDARDACTIONHANDLER handler;
244 static const struct _actions StandardActions[];
247 /********************************************************
248 * helper functions
249 ********************************************************/
251 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
253 static const WCHAR Query_t[] =
254 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
255 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
256 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
257 ' ','\'','%','s','\'',0};
258 MSIRECORD * row;
260 row = MSI_QueryGetRecord( package->db, Query_t, action );
261 if (!row)
262 return;
263 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
264 msiobj_release(&row->hdr);
267 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
268 UINT rc)
270 MSIRECORD * row;
271 static const WCHAR template_s[]=
272 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
273 '%','s', '.',0};
274 static const WCHAR template_e[]=
275 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
276 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
277 '%','i','.',0};
278 static const WCHAR format[] =
279 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
280 WCHAR message[1024];
281 WCHAR timet[0x100];
283 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
284 if (start)
285 sprintfW(message,template_s,timet,action);
286 else
287 sprintfW(message,template_e,timet,action,rc);
289 row = MSI_CreateRecord(1);
290 MSI_RecordSetStringW(row,1,message);
292 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
293 msiobj_release(&row->hdr);
296 static UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine )
298 LPCWSTR ptr,ptr2;
299 BOOL quote;
300 DWORD len;
301 LPWSTR prop = NULL, val = NULL;
303 if (!szCommandLine)
304 return ERROR_SUCCESS;
306 ptr = szCommandLine;
308 while (*ptr)
310 if (*ptr==' ')
312 ptr++;
313 continue;
316 TRACE("Looking at %s\n",debugstr_w(ptr));
318 ptr2 = strchrW(ptr,'=');
319 if (!ptr2)
321 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
322 break;
325 quote = FALSE;
327 len = ptr2-ptr;
328 prop = msi_alloc((len+1)*sizeof(WCHAR));
329 memcpy(prop,ptr,len*sizeof(WCHAR));
330 prop[len]=0;
331 ptr2++;
333 len = 0;
334 ptr = ptr2;
335 while (*ptr && (quote || (!quote && *ptr!=' ')))
337 if (*ptr == '"')
338 quote = !quote;
339 ptr++;
340 len++;
343 if (*ptr2=='"')
345 ptr2++;
346 len -= 2;
348 val = msi_alloc((len+1)*sizeof(WCHAR));
349 memcpy(val,ptr2,len*sizeof(WCHAR));
350 val[len] = 0;
352 if (lstrlenW(prop) > 0)
354 TRACE("Found commandline property (%s) = (%s)\n",
355 debugstr_w(prop), debugstr_w(val));
356 MSI_SetPropertyW(package,prop,val);
358 msi_free(val);
359 msi_free(prop);
362 return ERROR_SUCCESS;
366 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
368 LPCWSTR pc;
369 LPWSTR p, *ret = NULL;
370 UINT count = 0;
372 if (!str)
373 return ret;
375 /* count the number of substrings */
376 for ( pc = str, count = 0; pc; count++ )
378 pc = strchrW( pc, sep );
379 if (pc)
380 pc++;
383 /* allocate space for an array of substring pointers and the substrings */
384 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
385 (lstrlenW(str)+1) * sizeof(WCHAR) );
386 if (!ret)
387 return ret;
389 /* copy the string and set the pointers */
390 p = (LPWSTR) &ret[count+1];
391 lstrcpyW( p, str );
392 for( count = 0; (ret[count] = p); count++ )
394 p = strchrW( p, sep );
395 if (p)
396 *p++ = 0;
399 return ret;
402 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
404 WCHAR szProductCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
405 LPWSTR prod_code, patch_product;
406 UINT ret;
408 prod_code = msi_dup_property( package, szProductCode );
409 patch_product = msi_get_suminfo_product( patch );
411 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
413 if ( strstrW( patch_product, prod_code ) )
414 ret = ERROR_SUCCESS;
415 else
416 ret = ERROR_FUNCTION_FAILED;
418 msi_free( patch_product );
419 msi_free( prod_code );
421 return ret;
424 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
425 MSIDATABASE *patch_db, LPCWSTR name )
427 UINT ret = ERROR_FUNCTION_FAILED;
428 IStorage *stg = NULL;
429 HRESULT r;
431 TRACE("%p %s\n", package, debugstr_w(name) );
433 if (*name++ != ':')
435 ERR("expected a colon in %s\n", debugstr_w(name));
436 return ERROR_FUNCTION_FAILED;
439 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
440 if (SUCCEEDED(r))
442 ret = msi_check_transform_applicable( package, stg );
443 if (ret == ERROR_SUCCESS)
444 msi_table_apply_transform( package->db, stg );
445 else
446 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
447 IStorage_Release( stg );
449 else
450 ERR("failed to open substorage %s\n", debugstr_w(name));
452 return ERROR_SUCCESS;
455 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
457 static const WCHAR szProdID[] = { 'P','r','o','d','u','c','t','I','D',0 };
458 LPWSTR guid_list, *guids, product_id;
459 UINT i, ret = ERROR_FUNCTION_FAILED;
461 product_id = msi_dup_property( package, szProdID );
462 if (!product_id)
464 /* FIXME: the property ProductID should be written into the DB somewhere */
465 ERR("no product ID to check\n");
466 return ERROR_SUCCESS;
469 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
470 guids = msi_split_string( guid_list, ';' );
471 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
473 if (!lstrcmpW( guids[i], product_id ))
474 ret = ERROR_SUCCESS;
476 msi_free( guids );
477 msi_free( guid_list );
478 msi_free( product_id );
480 return ret;
483 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
485 MSISUMMARYINFO *si;
486 LPWSTR str, *substorage;
487 UINT i, r = ERROR_SUCCESS;
489 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
490 if (!si)
491 return ERROR_FUNCTION_FAILED;
493 msi_check_patch_applicable( package, si );
495 /* enumerate the substorage */
496 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
497 substorage = msi_split_string( str, ';' );
498 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
499 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
500 msi_free( substorage );
501 msi_free( str );
503 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
505 msiobj_release( &si->hdr );
507 return r;
510 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
512 MSIDATABASE *patch_db = NULL;
513 UINT r;
515 TRACE("%p %s\n", package, debugstr_w( file ) );
517 /* FIXME:
518 * We probably want to make sure we only open a patch collection here.
519 * Patch collections (.msp) and databases (.msi) have different GUIDs
520 * but currently MSI_OpenDatabaseW will accept both.
522 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
523 if ( r != ERROR_SUCCESS )
525 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
526 return r;
529 msi_parse_patch_summary( package, patch_db );
532 * There might be a CAB file in the patch package,
533 * so append it to the list of storage to search for streams.
535 append_storage_to_db( package->db, patch_db->storage );
537 msiobj_release( &patch_db->hdr );
539 return ERROR_SUCCESS;
542 /* get the PATCH property, and apply all the patches it specifies */
543 static UINT msi_apply_patches( MSIPACKAGE *package )
545 static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
546 LPWSTR patch_list, *patches;
547 UINT i, r = ERROR_SUCCESS;
549 patch_list = msi_dup_property( package, szPatch );
551 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
553 patches = msi_split_string( patch_list, ';' );
554 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
555 r = msi_apply_patch_package( package, patches[i] );
557 msi_free( patches );
558 msi_free( patch_list );
560 return r;
563 static UINT msi_apply_transforms( MSIPACKAGE *package )
565 static const WCHAR szTransforms[] = {
566 'T','R','A','N','S','F','O','R','M','S',0 };
567 LPWSTR xform_list, *xforms;
568 UINT i, r = ERROR_SUCCESS;
570 xform_list = msi_dup_property( package, szTransforms );
571 xforms = msi_split_string( xform_list, ';' );
573 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
575 if (xforms[i][0] == ':')
576 r = msi_apply_substorage_transform( package, package->db, &xforms[i][1] );
577 else
578 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
581 msi_free( xforms );
582 msi_free( xform_list );
584 return r;
587 BOOL ui_sequence_exists( MSIPACKAGE *package )
589 MSIQUERY *view;
590 UINT rc;
592 static const WCHAR ExecSeqQuery [] =
593 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
594 '`','I','n','s','t','a','l','l',
595 'U','I','S','e','q','u','e','n','c','e','`',
596 ' ','W','H','E','R','E',' ',
597 '`','S','e','q','u','e','n','c','e','`',' ',
598 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
599 '`','S','e','q','u','e','n','c','e','`',0};
601 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
602 if (rc == ERROR_SUCCESS)
604 msiobj_release(&view->hdr);
605 return TRUE;
608 return FALSE;
611 /****************************************************
612 * TOP level entry points
613 *****************************************************/
615 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
616 LPCWSTR szCommandLine )
618 UINT rc;
619 BOOL ui = FALSE, ui_exists;
620 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
621 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
622 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
624 MSI_SetPropertyW(package, szAction, szInstall);
626 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
628 package->script->InWhatSequence = SEQUENCE_INSTALL;
630 if (szPackagePath)
632 LPWSTR p, check, path;
634 path = strdupW(szPackagePath);
635 p = strrchrW(path,'\\');
636 if (p)
638 p++;
639 *p=0;
641 else
643 msi_free(path);
644 path = msi_alloc(MAX_PATH*sizeof(WCHAR));
645 GetCurrentDirectoryW(MAX_PATH,path);
646 strcatW(path,cszbs);
649 check = msi_dup_property( package, cszSourceDir );
650 if (!check)
651 MSI_SetPropertyW(package, cszSourceDir, path);
652 msi_free(check);
654 check = msi_dup_property( package, cszSOURCEDIR );
655 if (!check)
656 MSI_SetPropertyW(package, cszSOURCEDIR, path);
658 msi_free( package->PackagePath );
659 package->PackagePath = path;
661 msi_free(check);
664 msi_parse_command_line( package, szCommandLine );
666 msi_apply_transforms( package );
667 msi_apply_patches( package );
669 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
671 package->script->InWhatSequence |= SEQUENCE_UI;
672 rc = ACTION_ProcessUISequence(package);
673 ui = TRUE;
674 ui_exists = ui_sequence_exists(package);
675 if (rc == ERROR_SUCCESS || !ui_exists)
677 package->script->InWhatSequence |= SEQUENCE_EXEC;
678 rc = ACTION_ProcessExecSequence(package,ui_exists);
681 else
682 rc = ACTION_ProcessExecSequence(package,FALSE);
684 if (rc == -1)
686 /* install was halted but should be considered a success */
687 rc = ERROR_SUCCESS;
690 package->script->CurrentlyScripting= FALSE;
692 /* process the ending type action */
693 if (rc == ERROR_SUCCESS)
694 ACTION_PerformActionSequence(package,-1,ui);
695 else if (rc == ERROR_INSTALL_USEREXIT)
696 ACTION_PerformActionSequence(package,-2,ui);
697 else if (rc == ERROR_INSTALL_SUSPEND)
698 ACTION_PerformActionSequence(package,-4,ui);
699 else /* failed */
700 ACTION_PerformActionSequence(package,-3,ui);
702 /* finish up running custom actions */
703 ACTION_FinishCustomActions(package);
705 return rc;
708 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
710 UINT rc = ERROR_SUCCESS;
711 MSIRECORD * row = 0;
712 static const WCHAR ExecSeqQuery[] =
713 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
714 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
715 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
716 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
718 static const WCHAR UISeqQuery[] =
719 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
720 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
721 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
722 ' ', '=',' ','%','i',0};
724 if (UI)
725 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
726 else
727 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
729 if (row)
731 LPCWSTR action, cond;
733 TRACE("Running the actions\n");
735 /* check conditions */
736 cond = MSI_RecordGetString(row,2);
738 /* this is a hack to skip errors in the condition code */
739 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
740 goto end;
742 action = MSI_RecordGetString(row,1);
743 if (!action)
745 ERR("failed to fetch action\n");
746 rc = ERROR_FUNCTION_FAILED;
747 goto end;
750 if (UI)
751 rc = ACTION_PerformUIAction(package,action);
752 else
753 rc = ACTION_PerformAction(package,action,FALSE);
754 end:
755 msiobj_release(&row->hdr);
757 else
758 rc = ERROR_SUCCESS;
760 return rc;
763 typedef struct {
764 MSIPACKAGE* package;
765 BOOL UI;
766 } iterate_action_param;
768 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
770 iterate_action_param *iap= (iterate_action_param*)param;
771 UINT rc;
772 LPCWSTR cond, action;
774 action = MSI_RecordGetString(row,1);
775 if (!action)
777 ERR("Error is retrieving action name\n");
778 return ERROR_FUNCTION_FAILED;
781 /* check conditions */
782 cond = MSI_RecordGetString(row,2);
784 /* this is a hack to skip errors in the condition code */
785 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
787 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
788 return ERROR_SUCCESS;
791 if (iap->UI)
792 rc = ACTION_PerformUIAction(iap->package,action);
793 else
794 rc = ACTION_PerformAction(iap->package,action,FALSE);
796 msi_dialog_check_messages( NULL );
798 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
799 rc = iap->package->CurrentInstallState;
801 if (rc == ERROR_FUNCTION_NOT_CALLED)
802 rc = ERROR_SUCCESS;
804 if (rc != ERROR_SUCCESS)
805 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
807 return rc;
810 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
812 MSIQUERY * view;
813 UINT r;
814 static const WCHAR query[] =
815 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
816 '`','%','s','`',
817 ' ','W','H','E','R','E',' ',
818 '`','S','e','q','u','e','n','c','e','`',' ',
819 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
820 '`','S','e','q','u','e','n','c','e','`',0};
821 iterate_action_param iap;
824 * FIXME: probably should be checking UILevel in the
825 * ACTION_PerformUIAction/ACTION_PerformAction
826 * rather than saving the UI level here. Those
827 * two functions can be merged too.
829 iap.package = package;
830 iap.UI = TRUE;
832 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
834 r = MSI_OpenQuery( package->db, &view, query, szTable );
835 if (r == ERROR_SUCCESS)
837 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
838 msiobj_release(&view->hdr);
841 return r;
844 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
846 MSIQUERY * view;
847 UINT rc;
848 static const WCHAR ExecSeqQuery[] =
849 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
850 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
851 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
852 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
853 'O','R','D','E','R',' ', 'B','Y',' ',
854 '`','S','e','q','u','e','n','c','e','`',0 };
855 MSIRECORD * row = 0;
856 static const WCHAR IVQuery[] =
857 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
858 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
859 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
860 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
861 ' ','\'', 'I','n','s','t','a','l','l',
862 'V','a','l','i','d','a','t','e','\'', 0};
863 INT seq = 0;
864 iterate_action_param iap;
866 iap.package = package;
867 iap.UI = FALSE;
869 if (package->script->ExecuteSequenceRun)
871 TRACE("Execute Sequence already Run\n");
872 return ERROR_SUCCESS;
875 package->script->ExecuteSequenceRun = TRUE;
877 /* get the sequence number */
878 if (UIran)
880 row = MSI_QueryGetRecord(package->db, IVQuery);
881 if( !row )
882 return ERROR_FUNCTION_FAILED;
883 seq = MSI_RecordGetInteger(row,1);
884 msiobj_release(&row->hdr);
887 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
888 if (rc == ERROR_SUCCESS)
890 TRACE("Running the actions\n");
892 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
893 msiobj_release(&view->hdr);
896 return rc;
899 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
901 MSIQUERY * view;
902 UINT rc;
903 static const WCHAR ExecSeqQuery [] =
904 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
905 '`','I','n','s','t','a','l','l',
906 'U','I','S','e','q','u','e','n','c','e','`',
907 ' ','W','H','E','R','E',' ',
908 '`','S','e','q','u','e','n','c','e','`',' ',
909 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
910 '`','S','e','q','u','e','n','c','e','`',0};
911 iterate_action_param iap;
913 iap.package = package;
914 iap.UI = TRUE;
916 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
918 if (rc == ERROR_SUCCESS)
920 TRACE("Running the actions\n");
922 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
923 msiobj_release(&view->hdr);
926 return rc;
929 /********************************************************
930 * ACTION helper functions and functions that perform the actions
931 *******************************************************/
932 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
933 UINT* rc, BOOL force )
935 BOOL ret = FALSE;
936 BOOL run = force;
937 int i;
939 if (!run && !package->script->CurrentlyScripting)
940 run = TRUE;
942 if (!run)
944 if (strcmpW(action,szInstallFinalize) == 0 ||
945 strcmpW(action,szInstallExecute) == 0 ||
946 strcmpW(action,szInstallExecuteAgain) == 0)
947 run = TRUE;
950 i = 0;
951 while (StandardActions[i].action != NULL)
953 if (strcmpW(StandardActions[i].action, action)==0)
955 if (!run)
957 ui_actioninfo(package, action, TRUE, 0);
958 *rc = schedule_action(package,INSTALL_SCRIPT,action);
959 ui_actioninfo(package, action, FALSE, *rc);
961 else
963 ui_actionstart(package, action);
964 if (StandardActions[i].handler)
966 *rc = StandardActions[i].handler(package);
968 else
970 FIXME("unhandled standard action %s\n",debugstr_w(action));
971 *rc = ERROR_SUCCESS;
974 ret = TRUE;
975 break;
977 i++;
979 return ret;
982 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
983 UINT* rc, BOOL force )
985 BOOL ret=FALSE;
986 UINT arc;
988 arc = ACTION_CustomAction(package,action, force);
990 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
992 *rc = arc;
993 ret = TRUE;
995 return ret;
999 * A lot of actions are really important even if they don't do anything
1000 * explicit... Lots of properties are set at the beginning of the installation
1001 * CostFinalize does a bunch of work to translate the directories and such
1003 * But until I get write access to the database that is hard, so I am going to
1004 * hack it to see if I can get something to run.
1006 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, BOOL force)
1008 UINT rc = ERROR_SUCCESS;
1009 BOOL handled;
1011 TRACE("Performing action (%s)\n",debugstr_w(action));
1013 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1015 if (!handled)
1016 handled = ACTION_HandleCustomAction(package, action, &rc, force);
1018 if (!handled)
1020 FIXME("unhandled msi action %s\n",debugstr_w(action));
1021 rc = ERROR_FUNCTION_NOT_CALLED;
1024 return rc;
1027 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action)
1029 UINT rc = ERROR_SUCCESS;
1030 BOOL handled = FALSE;
1032 TRACE("Performing action (%s)\n",debugstr_w(action));
1034 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1036 if (!handled)
1037 handled = ACTION_HandleCustomAction(package, action, &rc, FALSE);
1039 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1040 handled = TRUE;
1042 if (!handled)
1044 FIXME("unhandled msi action %s\n",debugstr_w(action));
1045 rc = ERROR_FUNCTION_NOT_CALLED;
1048 return rc;
1053 * Actual Action Handlers
1056 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1058 MSIPACKAGE *package = (MSIPACKAGE*)param;
1059 LPCWSTR dir;
1060 LPWSTR full_path;
1061 MSIRECORD *uirow;
1062 MSIFOLDER *folder;
1064 dir = MSI_RecordGetString(row,1);
1065 if (!dir)
1067 ERR("Unable to get folder id\n");
1068 return ERROR_SUCCESS;
1071 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1072 if (!full_path)
1074 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1075 return ERROR_SUCCESS;
1078 TRACE("Folder is %s\n",debugstr_w(full_path));
1080 /* UI stuff */
1081 uirow = MSI_CreateRecord(1);
1082 MSI_RecordSetStringW(uirow,1,full_path);
1083 ui_actiondata(package,szCreateFolders,uirow);
1084 msiobj_release( &uirow->hdr );
1086 if (folder->State == 0)
1087 create_full_pathW(full_path);
1089 folder->State = 3;
1091 msi_free(full_path);
1092 return ERROR_SUCCESS;
1095 /* FIXME: probably should merge this with the above function */
1096 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1098 UINT rc = ERROR_SUCCESS;
1099 MSIFOLDER *folder;
1100 LPWSTR install_path;
1102 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1103 if (!install_path)
1104 return ERROR_FUNCTION_FAILED;
1106 /* create the path */
1107 if (folder->State == 0)
1109 create_full_pathW(install_path);
1110 folder->State = 2;
1112 msi_free(install_path);
1114 return rc;
1117 UINT msi_create_component_directories( MSIPACKAGE *package )
1119 MSICOMPONENT *comp;
1121 /* create all the folders required by the components are going to install */
1122 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1124 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1125 continue;
1126 msi_create_directory( package, comp->Directory );
1129 return ERROR_SUCCESS;
1133 * Also we cannot enable/disable components either, so for now I am just going
1134 * to do all the directories for all the components.
1136 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1138 static const WCHAR ExecSeqQuery[] =
1139 {'S','E','L','E','C','T',' ',
1140 '`','D','i','r','e','c','t','o','r','y','_','`',
1141 ' ','F','R','O','M',' ',
1142 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1143 UINT rc;
1144 MSIQUERY *view;
1146 /* create all the empty folders specified in the CreateFolder table */
1147 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1148 if (rc != ERROR_SUCCESS)
1149 return ERROR_SUCCESS;
1151 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1152 msiobj_release(&view->hdr);
1154 msi_create_component_directories( package );
1156 return rc;
1159 static UINT load_component( MSIRECORD *row, LPVOID param )
1161 MSIPACKAGE *package = param;
1162 MSICOMPONENT *comp;
1164 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1165 if (!comp)
1166 return ERROR_FUNCTION_FAILED;
1168 list_add_tail( &package->components, &comp->entry );
1170 /* fill in the data */
1171 comp->Component = msi_dup_record_field( row, 1 );
1173 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1175 comp->ComponentId = msi_dup_record_field( row, 2 );
1176 comp->Directory = msi_dup_record_field( row, 3 );
1177 comp->Attributes = MSI_RecordGetInteger(row,4);
1178 comp->Condition = msi_dup_record_field( row, 5 );
1179 comp->KeyPath = msi_dup_record_field( row, 6 );
1181 comp->Installed = INSTALLSTATE_UNKNOWN;
1182 msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1184 return ERROR_SUCCESS;
1187 static UINT load_all_components( MSIPACKAGE *package )
1189 static const WCHAR query[] = {
1190 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1191 '`','C','o','m','p','o','n','e','n','t','`',0 };
1192 MSIQUERY *view;
1193 UINT r;
1195 if (!list_empty(&package->components))
1196 return ERROR_SUCCESS;
1198 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1199 if (r != ERROR_SUCCESS)
1200 return r;
1202 r = MSI_IterateRecords(view, NULL, load_component, package);
1203 msiobj_release(&view->hdr);
1204 return r;
1207 typedef struct {
1208 MSIPACKAGE *package;
1209 MSIFEATURE *feature;
1210 } _ilfs;
1212 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1214 ComponentList *cl;
1216 cl = msi_alloc( sizeof (*cl) );
1217 if ( !cl )
1218 return ERROR_NOT_ENOUGH_MEMORY;
1219 cl->component = comp;
1220 list_add_tail( &feature->Components, &cl->entry );
1222 return ERROR_SUCCESS;
1225 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1227 FeatureList *fl;
1229 fl = msi_alloc( sizeof(*fl) );
1230 if ( !fl )
1231 return ERROR_NOT_ENOUGH_MEMORY;
1232 fl->feature = child;
1233 list_add_tail( &parent->Children, &fl->entry );
1235 return ERROR_SUCCESS;
1238 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1240 _ilfs* ilfs= (_ilfs*)param;
1241 LPCWSTR component;
1242 MSICOMPONENT *comp;
1244 component = MSI_RecordGetString(row,1);
1246 /* check to see if the component is already loaded */
1247 comp = get_loaded_component( ilfs->package, component );
1248 if (!comp)
1250 ERR("unknown component %s\n", debugstr_w(component));
1251 return ERROR_FUNCTION_FAILED;
1254 add_feature_component( ilfs->feature, comp );
1255 comp->Enabled = TRUE;
1257 return ERROR_SUCCESS;
1260 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1262 MSIFEATURE *feature;
1264 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1266 if ( !lstrcmpW( feature->Feature, name ) )
1267 return feature;
1270 return NULL;
1273 static UINT load_feature(MSIRECORD * row, LPVOID param)
1275 MSIPACKAGE* package = (MSIPACKAGE*)param;
1276 MSIFEATURE* feature;
1277 static const WCHAR Query1[] =
1278 {'S','E','L','E','C','T',' ',
1279 '`','C','o','m','p','o','n','e','n','t','_','`',
1280 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1281 'C','o','m','p','o','n','e','n','t','s','`',' ',
1282 'W','H','E','R','E',' ',
1283 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1284 MSIQUERY * view;
1285 UINT rc;
1286 _ilfs ilfs;
1288 /* fill in the data */
1290 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1291 if (!feature)
1292 return ERROR_NOT_ENOUGH_MEMORY;
1294 list_init( &feature->Children );
1295 list_init( &feature->Components );
1297 feature->Feature = msi_dup_record_field( row, 1 );
1299 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1301 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1302 feature->Title = msi_dup_record_field( row, 3 );
1303 feature->Description = msi_dup_record_field( row, 4 );
1305 if (!MSI_RecordIsNull(row,5))
1306 feature->Display = MSI_RecordGetInteger(row,5);
1308 feature->Level= MSI_RecordGetInteger(row,6);
1309 feature->Directory = msi_dup_record_field( row, 7 );
1310 feature->Attributes = MSI_RecordGetInteger(row,8);
1312 feature->Installed = INSTALLSTATE_UNKNOWN;
1313 msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1315 list_add_tail( &package->features, &feature->entry );
1317 /* load feature components */
1319 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1320 if (rc != ERROR_SUCCESS)
1321 return ERROR_SUCCESS;
1323 ilfs.package = package;
1324 ilfs.feature = feature;
1326 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1327 msiobj_release(&view->hdr);
1329 return ERROR_SUCCESS;
1332 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1334 MSIPACKAGE* package = (MSIPACKAGE*)param;
1335 MSIFEATURE *parent, *child;
1337 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1338 if (!child)
1339 return ERROR_FUNCTION_FAILED;
1341 if (!child->Feature_Parent)
1342 return ERROR_SUCCESS;
1344 parent = find_feature_by_name( package, child->Feature_Parent );
1345 if (!parent)
1346 return ERROR_FUNCTION_FAILED;
1348 add_feature_child( parent, child );
1349 return ERROR_SUCCESS;
1352 static UINT load_all_features( MSIPACKAGE *package )
1354 static const WCHAR query[] = {
1355 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1356 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1357 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1358 MSIQUERY *view;
1359 UINT r;
1361 if (!list_empty(&package->features))
1362 return ERROR_SUCCESS;
1364 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1365 if (r != ERROR_SUCCESS)
1366 return r;
1368 r = MSI_IterateRecords( view, NULL, load_feature, package );
1369 if (r != ERROR_SUCCESS)
1370 return r;
1372 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1373 msiobj_release( &view->hdr );
1375 return r;
1378 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1380 if (!p)
1381 return p;
1382 p = strchrW(p, ch);
1383 if (!p)
1384 return p;
1385 *p = 0;
1386 return p+1;
1389 static UINT load_file(MSIRECORD *row, LPVOID param)
1391 MSIPACKAGE* package = (MSIPACKAGE*)param;
1392 LPCWSTR component;
1393 MSIFILE *file;
1395 /* fill in the data */
1397 file = msi_alloc_zero( sizeof (MSIFILE) );
1398 if (!file)
1399 return ERROR_NOT_ENOUGH_MEMORY;
1401 file->File = msi_dup_record_field( row, 1 );
1403 component = MSI_RecordGetString( row, 2 );
1404 file->Component = get_loaded_component( package, component );
1406 if (!file->Component)
1407 ERR("Unfound Component %s\n",debugstr_w(component));
1409 file->FileName = msi_dup_record_field( row, 3 );
1410 reduce_to_longfilename( file->FileName );
1412 file->ShortName = msi_dup_record_field( row, 3 );
1413 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1415 file->FileSize = MSI_RecordGetInteger( row, 4 );
1416 file->Version = msi_dup_record_field( row, 5 );
1417 file->Language = msi_dup_record_field( row, 6 );
1418 file->Attributes = MSI_RecordGetInteger( row, 7 );
1419 file->Sequence = MSI_RecordGetInteger( row, 8 );
1421 file->state = msifs_invalid;
1423 /* if the compressed bits are not set in the file attributes,
1424 * then read the information from the package word count property
1426 if (file->Attributes & msidbFileAttributesCompressed)
1428 file->IsCompressed = TRUE;
1430 else if (file->Attributes & msidbFileAttributesNoncompressed)
1432 file->IsCompressed = FALSE;
1434 else
1436 file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1439 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1441 list_add_tail( &package->files, &file->entry );
1443 return ERROR_SUCCESS;
1446 static UINT load_all_files(MSIPACKAGE *package)
1448 MSIQUERY * view;
1449 UINT rc;
1450 static const WCHAR Query[] =
1451 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1452 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1453 '`','S','e','q','u','e','n','c','e','`', 0};
1455 if (!list_empty(&package->files))
1456 return ERROR_SUCCESS;
1458 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1459 if (rc != ERROR_SUCCESS)
1460 return ERROR_SUCCESS;
1462 rc = MSI_IterateRecords(view, NULL, load_file, package);
1463 msiobj_release(&view->hdr);
1465 return ERROR_SUCCESS;
1468 static UINT load_folder( MSIRECORD *row, LPVOID param )
1470 MSIPACKAGE *package = param;
1471 static const WCHAR szDot[] = { '.',0 };
1472 static WCHAR szEmpty[] = { 0 };
1473 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1474 MSIFOLDER *folder;
1476 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1477 if (!folder)
1478 return ERROR_NOT_ENOUGH_MEMORY;
1480 folder->Directory = msi_dup_record_field( row, 1 );
1482 TRACE("%s\n", debugstr_w(folder->Directory));
1484 p = msi_dup_record_field(row, 3);
1486 /* split src and target dir */
1487 tgt_short = p;
1488 src_short = folder_split_path( p, ':' );
1490 /* split the long and short paths */
1491 tgt_long = folder_split_path( tgt_short, '|' );
1492 src_long = folder_split_path( src_short, '|' );
1494 /* check for no-op dirs */
1495 if (!lstrcmpW(szDot, tgt_short))
1496 tgt_short = szEmpty;
1497 if (!lstrcmpW(szDot, src_short))
1498 src_short = szEmpty;
1500 if (!tgt_long)
1501 tgt_long = tgt_short;
1503 if (!src_short) {
1504 src_short = tgt_short;
1505 src_long = tgt_long;
1508 if (!src_long)
1509 src_long = src_short;
1511 /* FIXME: use the target short path too */
1512 folder->TargetDefault = strdupW(tgt_long);
1513 folder->SourceShortPath = strdupW(src_short);
1514 folder->SourceLongPath = strdupW(src_long);
1515 msi_free(p);
1517 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1518 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1519 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1521 folder->Parent = msi_dup_record_field( row, 2 );
1523 folder->Property = msi_dup_property( package, folder->Directory );
1525 list_add_tail( &package->folders, &folder->entry );
1527 TRACE("returning %p\n", folder);
1529 return ERROR_SUCCESS;
1532 static UINT load_all_folders( MSIPACKAGE *package )
1534 static const WCHAR query[] = {
1535 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1536 '`','D','i','r','e','c','t','o','r','y','`',0 };
1537 MSIQUERY *view;
1538 UINT r;
1540 if (!list_empty(&package->folders))
1541 return ERROR_SUCCESS;
1543 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1544 if (r != ERROR_SUCCESS)
1545 return r;
1547 r = MSI_IterateRecords(view, NULL, load_folder, package);
1548 msiobj_release(&view->hdr);
1549 return r;
1553 * I am not doing any of the costing functionality yet.
1554 * Mostly looking at doing the Component and Feature loading
1556 * The native MSI does A LOT of modification to tables here. Mostly adding
1557 * a lot of temporary columns to the Feature and Component tables.
1559 * note: Native msi also tracks the short filename. But I am only going to
1560 * track the long ones. Also looking at this directory table
1561 * it appears that the directory table does not get the parents
1562 * resolved base on property only based on their entries in the
1563 * directory table.
1565 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1567 static const WCHAR szCosting[] =
1568 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1569 static const WCHAR szZero[] = { '0', 0 };
1571 MSI_SetPropertyW(package, szCosting, szZero);
1572 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1574 load_all_components( package );
1575 load_all_features( package );
1576 load_all_files( package );
1577 load_all_folders( package );
1579 return ERROR_SUCCESS;
1582 static UINT execute_script(MSIPACKAGE *package, UINT script )
1584 int i;
1585 UINT rc = ERROR_SUCCESS;
1587 TRACE("Executing Script %i\n",script);
1589 if (!package->script)
1591 ERR("no script!\n");
1592 return ERROR_FUNCTION_FAILED;
1595 for (i = 0; i < package->script->ActionCount[script]; i++)
1597 LPWSTR action;
1598 action = package->script->Actions[script][i];
1599 ui_actionstart(package, action);
1600 TRACE("Executing Action (%s)\n",debugstr_w(action));
1601 rc = ACTION_PerformAction(package, action, TRUE);
1602 if (rc != ERROR_SUCCESS)
1603 break;
1605 msi_free_action_script(package, script);
1606 return rc;
1609 static UINT ACTION_FileCost(MSIPACKAGE *package)
1611 return ERROR_SUCCESS;
1614 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1616 MSICOMPONENT *comp;
1618 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1620 INSTALLSTATE res;
1622 if (!comp->ComponentId)
1623 continue;
1625 res = MsiGetComponentPathW( package->ProductCode,
1626 comp->ComponentId, NULL, NULL);
1627 if (res < 0)
1628 res = INSTALLSTATE_ABSENT;
1629 comp->Installed = res;
1633 /* scan for and update current install states */
1634 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1636 MSICOMPONENT *comp;
1637 MSIFEATURE *feature;
1639 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1641 ComponentList *cl;
1642 INSTALLSTATE res = INSTALLSTATE_ABSENT;
1644 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1646 comp= cl->component;
1648 if (!comp->ComponentId)
1650 res = INSTALLSTATE_ABSENT;
1651 break;
1654 if (res == INSTALLSTATE_ABSENT)
1655 res = comp->Installed;
1656 else
1658 if (res == comp->Installed)
1659 continue;
1661 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1662 res != INSTALLSTATE_SOURCE)
1664 res = INSTALLSTATE_INCOMPLETE;
1668 feature->Installed = res;
1672 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
1673 INSTALLSTATE state)
1675 static const WCHAR all[]={'A','L','L',0};
1676 LPWSTR override;
1677 MSIFEATURE *feature;
1679 override = msi_dup_property( package, property );
1680 if (!override)
1681 return FALSE;
1683 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1685 if (strcmpiW(override,all)==0)
1686 msi_feature_set_state( feature, state );
1687 else
1689 LPWSTR ptr = override;
1690 LPWSTR ptr2 = strchrW(override,',');
1692 while (ptr)
1694 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1695 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1697 msi_feature_set_state( feature, state );
1698 break;
1700 if (ptr2)
1702 ptr=ptr2+1;
1703 ptr2 = strchrW(ptr,',');
1705 else
1706 break;
1710 msi_free(override);
1712 return TRUE;
1715 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1717 int install_level;
1718 static const WCHAR szlevel[] =
1719 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1720 static const WCHAR szAddLocal[] =
1721 {'A','D','D','L','O','C','A','L',0};
1722 static const WCHAR szRemove[] =
1723 {'R','E','M','O','V','E',0};
1724 static const WCHAR szReinstall[] =
1725 {'R','E','I','N','S','T','A','L','L',0};
1726 BOOL override = FALSE;
1727 MSICOMPONENT* component;
1728 MSIFEATURE *feature;
1731 /* I do not know if this is where it should happen.. but */
1733 TRACE("Checking Install Level\n");
1735 install_level = msi_get_property_int( package, szlevel, 1 );
1737 /* ok here is the _real_ rub
1738 * all these activation/deactivation things happen in order and things
1739 * later on the list override things earlier on the list.
1740 * 1) INSTALLLEVEL processing
1741 * 2) ADDLOCAL
1742 * 3) REMOVE
1743 * 4) ADDSOURCE
1744 * 5) ADDDEFAULT
1745 * 6) REINSTALL
1746 * 7) COMPADDLOCAL
1747 * 8) COMPADDSOURCE
1748 * 9) FILEADDLOCAL
1749 * 10) FILEADDSOURCE
1750 * 11) FILEADDDEFAULT
1751 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1752 * ignored for all the features. seems strange, especially since it is not
1753 * documented anywhere, but it is how it works.
1755 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1756 * REMOVE are the big ones, since we don't handle administrative installs
1757 * yet anyway.
1759 override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1760 override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1761 override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1763 if (!override)
1765 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1767 BOOL feature_state = ((feature->Level > 0) &&
1768 (feature->Level <= install_level));
1770 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1772 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1773 msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1774 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1775 msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1776 else
1777 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1781 /* disable child features of unselected parent features */
1782 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1784 FeatureList *fl;
1786 if (feature->Level > 0 && feature->Level <= install_level)
1787 continue;
1789 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1790 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1793 else
1795 /* set the Preselected Property */
1796 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1797 static const WCHAR szOne[] = { '1', 0 };
1799 MSI_SetPropertyW(package,szPreselected,szOne);
1803 * now we want to enable or disable components base on feature
1806 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1808 ComponentList *cl;
1810 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1811 debugstr_w(feature->Feature), feature->Installed, feature->Action);
1813 /* features with components that have compressed files are made local */
1814 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1816 if (cl->component->Enabled &&
1817 cl->component->ForceLocalState &&
1818 feature->Action == INSTALLSTATE_SOURCE)
1820 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1821 break;
1825 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1827 component = cl->component;
1829 if (!component->Enabled)
1830 continue;
1832 switch (feature->Action)
1834 case INSTALLSTATE_ADVERTISED:
1835 component->hasAdvertiseFeature = 1;
1836 break;
1837 case INSTALLSTATE_SOURCE:
1838 component->hasSourceFeature = 1;
1839 break;
1840 case INSTALLSTATE_LOCAL:
1841 component->hasLocalFeature = 1;
1842 break;
1843 case INSTALLSTATE_DEFAULT:
1844 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1845 component->hasAdvertiseFeature = 1;
1846 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1847 component->hasSourceFeature = 1;
1848 else
1849 component->hasLocalFeature = 1;
1850 break;
1851 default:
1852 break;
1857 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1859 /* if the component isn't enabled, leave it alone */
1860 if (!component->Enabled)
1861 continue;
1863 /* check if it's local or source */
1864 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1865 (component->hasLocalFeature || component->hasSourceFeature))
1867 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1868 !component->ForceLocalState)
1869 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1870 else
1871 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1872 continue;
1875 /* if any feature is local, the component must be local too */
1876 if (component->hasLocalFeature)
1878 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1879 continue;
1882 if (component->hasSourceFeature)
1884 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1885 continue;
1888 if (component->hasAdvertiseFeature)
1890 msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1891 continue;
1894 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1897 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1899 if (component->Action == INSTALLSTATE_DEFAULT)
1901 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1902 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1905 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1906 debugstr_w(component->Component), component->Installed, component->Action);
1910 return ERROR_SUCCESS;
1913 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1915 MSIPACKAGE *package = (MSIPACKAGE*)param;
1916 LPCWSTR name;
1917 LPWSTR path;
1918 MSIFOLDER *f;
1920 name = MSI_RecordGetString(row,1);
1922 f = get_loaded_folder(package, name);
1923 if (!f) return ERROR_SUCCESS;
1925 /* reset the ResolvedTarget */
1926 msi_free(f->ResolvedTarget);
1927 f->ResolvedTarget = NULL;
1929 /* This helper function now does ALL the work */
1930 TRACE("Dir %s ...\n",debugstr_w(name));
1931 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1932 TRACE("resolves to %s\n",debugstr_w(path));
1933 msi_free(path);
1935 return ERROR_SUCCESS;
1938 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1940 MSIPACKAGE *package = (MSIPACKAGE*)param;
1941 LPCWSTR name;
1942 MSIFEATURE *feature;
1944 name = MSI_RecordGetString( row, 1 );
1946 feature = get_loaded_feature( package, name );
1947 if (!feature)
1948 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1949 else
1951 LPCWSTR Condition;
1952 Condition = MSI_RecordGetString(row,3);
1954 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1956 int level = MSI_RecordGetInteger(row,2);
1957 TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
1958 feature->Level = level;
1961 return ERROR_SUCCESS;
1964 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1966 static const WCHAR name_fmt[] =
1967 {'%','u','.','%','u','.','%','u','.','%','u',0};
1968 static WCHAR name[] = {'\\',0};
1969 VS_FIXEDFILEINFO *lpVer;
1970 WCHAR filever[0x100];
1971 LPVOID version;
1972 DWORD versize;
1973 DWORD handle;
1974 UINT sz;
1976 TRACE("%s\n", debugstr_w(filename));
1978 versize = GetFileVersionInfoSizeW( filename, &handle );
1979 if (!versize)
1980 return NULL;
1982 version = msi_alloc( versize );
1983 GetFileVersionInfoW( filename, 0, versize, version );
1985 VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz );
1986 msi_free( version );
1988 sprintfW( filever, name_fmt,
1989 HIWORD(lpVer->dwFileVersionMS),
1990 LOWORD(lpVer->dwFileVersionMS),
1991 HIWORD(lpVer->dwFileVersionLS),
1992 LOWORD(lpVer->dwFileVersionLS));
1994 return strdupW( filever );
1997 static UINT msi_check_file_install_states( MSIPACKAGE *package )
1999 LPWSTR file_version;
2000 MSIFILE *file;
2002 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2004 MSICOMPONENT* comp = file->Component;
2005 LPWSTR p;
2007 if (!comp)
2008 continue;
2010 if (file->IsCompressed)
2011 comp->ForceLocalState = TRUE;
2013 /* calculate target */
2014 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2016 msi_free(file->TargetPath);
2018 TRACE("file %s is named %s\n",
2019 debugstr_w(file->File), debugstr_w(file->FileName));
2021 file->TargetPath = build_directory_name(2, p, file->FileName);
2023 msi_free(p);
2025 TRACE("file %s resolves to %s\n",
2026 debugstr_w(file->File), debugstr_w(file->TargetPath));
2028 /* don't check files of components that aren't installed */
2029 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2030 comp->Installed == INSTALLSTATE_ABSENT)
2032 file->state = msifs_missing; /* assume files are missing */
2033 continue;
2036 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2038 file->state = msifs_missing;
2039 comp->Cost += file->FileSize;
2040 comp->Installed = INSTALLSTATE_INCOMPLETE;
2041 continue;
2044 if (file->Version &&
2045 (file_version = msi_get_disk_file_version( file->TargetPath )))
2047 TRACE("new %s old %s\n", debugstr_w(file->Version),
2048 debugstr_w(file_version));
2049 /* FIXME: seems like a bad way to compare version numbers */
2050 if (lstrcmpiW(file_version, file->Version)<0)
2052 file->state = msifs_overwrite;
2053 comp->Cost += file->FileSize;
2054 comp->Installed = INSTALLSTATE_INCOMPLETE;
2056 else
2057 file->state = msifs_present;
2058 msi_free( file_version );
2060 else
2061 file->state = msifs_present;
2064 return ERROR_SUCCESS;
2068 * A lot is done in this function aside from just the costing.
2069 * The costing needs to be implemented at some point but for now I am going
2070 * to focus on the directory building
2073 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2075 static const WCHAR ExecSeqQuery[] =
2076 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2077 '`','D','i','r','e','c','t','o','r','y','`',0};
2078 static const WCHAR ConditionQuery[] =
2079 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2080 '`','C','o','n','d','i','t','i','o','n','`',0};
2081 static const WCHAR szCosting[] =
2082 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2083 static const WCHAR szlevel[] =
2084 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2085 static const WCHAR szOne[] = { '1', 0 };
2086 MSICOMPONENT *comp;
2087 UINT rc;
2088 MSIQUERY * view;
2089 LPWSTR level;
2091 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2092 return ERROR_SUCCESS;
2094 TRACE("Building Directory properties\n");
2096 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2097 if (rc == ERROR_SUCCESS)
2099 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2100 package);
2101 msiobj_release(&view->hdr);
2104 /* read components states from the registry */
2105 ACTION_GetComponentInstallStates(package);
2107 TRACE("File calculations\n");
2108 msi_check_file_install_states( package );
2110 TRACE("Evaluating Condition Table\n");
2112 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2113 if (rc == ERROR_SUCCESS)
2115 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2116 package);
2117 msiobj_release(&view->hdr);
2120 TRACE("Enabling or Disabling Components\n");
2121 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2123 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2125 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2126 comp->Enabled = FALSE;
2130 MSI_SetPropertyW(package,szCosting,szOne);
2131 /* set default run level if not set */
2132 level = msi_dup_property( package, szlevel );
2133 if (!level)
2134 MSI_SetPropertyW(package,szlevel, szOne);
2135 msi_free(level);
2137 ACTION_UpdateFeatureInstallStates(package);
2139 return MSI_SetFeatureStates(package);
2142 /* OK this value is "interpreted" and then formatted based on the
2143 first few characters */
2144 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2145 DWORD *size)
2147 LPSTR data = NULL;
2148 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2150 if (value[1]=='x')
2152 LPWSTR ptr;
2153 CHAR byte[5];
2154 LPWSTR deformated = NULL;
2155 int count;
2157 deformat_string(package, &value[2], &deformated);
2159 /* binary value type */
2160 ptr = deformated;
2161 *type = REG_BINARY;
2162 if (strlenW(ptr)%2)
2163 *size = (strlenW(ptr)/2)+1;
2164 else
2165 *size = strlenW(ptr)/2;
2167 data = msi_alloc(*size);
2169 byte[0] = '0';
2170 byte[1] = 'x';
2171 byte[4] = 0;
2172 count = 0;
2173 /* if uneven pad with a zero in front */
2174 if (strlenW(ptr)%2)
2176 byte[2]= '0';
2177 byte[3]= *ptr;
2178 ptr++;
2179 data[count] = (BYTE)strtol(byte,NULL,0);
2180 count ++;
2181 TRACE("Uneven byte count\n");
2183 while (*ptr)
2185 byte[2]= *ptr;
2186 ptr++;
2187 byte[3]= *ptr;
2188 ptr++;
2189 data[count] = (BYTE)strtol(byte,NULL,0);
2190 count ++;
2192 msi_free(deformated);
2194 TRACE("Data %i bytes(%i)\n",*size,count);
2196 else
2198 LPWSTR deformated;
2199 LPWSTR p;
2200 DWORD d = 0;
2201 deformat_string(package, &value[1], &deformated);
2203 *type=REG_DWORD;
2204 *size = sizeof(DWORD);
2205 data = msi_alloc(*size);
2206 p = deformated;
2207 if (*p == '-')
2208 p++;
2209 while (*p)
2211 if ( (*p < '0') || (*p > '9') )
2212 break;
2213 d *= 10;
2214 d += (*p - '0');
2215 p++;
2217 if (deformated[0] == '-')
2218 d = -d;
2219 *(LPDWORD)data = d;
2220 TRACE("DWORD %i\n",*(LPDWORD)data);
2222 msi_free(deformated);
2225 else
2227 static const WCHAR szMulti[] = {'[','~',']',0};
2228 LPCWSTR ptr;
2229 *type=REG_SZ;
2231 if (value[0]=='#')
2233 if (value[1]=='%')
2235 ptr = &value[2];
2236 *type=REG_EXPAND_SZ;
2238 else
2239 ptr = &value[1];
2241 else
2242 ptr=value;
2244 if (strstrW(value,szMulti))
2245 *type = REG_MULTI_SZ;
2247 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2249 return data;
2252 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2254 MSIPACKAGE *package = (MSIPACKAGE*)param;
2255 static const WCHAR szHCR[] =
2256 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2257 'R','O','O','T','\\',0};
2258 static const WCHAR szHCU[] =
2259 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2260 'U','S','E','R','\\',0};
2261 static const WCHAR szHLM[] =
2262 {'H','K','E','Y','_','L','O','C','A','L','_',
2263 'M','A','C','H','I','N','E','\\',0};
2264 static const WCHAR szHU[] =
2265 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2267 LPSTR value_data = NULL;
2268 HKEY root_key, hkey;
2269 DWORD type,size;
2270 LPWSTR deformated;
2271 LPCWSTR szRoot, component, name, key, value;
2272 MSICOMPONENT *comp;
2273 MSIRECORD * uirow;
2274 LPWSTR uikey;
2275 INT root;
2276 BOOL check_first = FALSE;
2277 UINT rc;
2279 ui_progress(package,2,0,0,0);
2281 value = NULL;
2282 key = NULL;
2283 uikey = NULL;
2284 name = NULL;
2286 component = MSI_RecordGetString(row, 6);
2287 comp = get_loaded_component(package,component);
2288 if (!comp)
2289 return ERROR_SUCCESS;
2291 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2293 TRACE("Skipping write due to disabled component %s\n",
2294 debugstr_w(component));
2296 comp->Action = comp->Installed;
2298 return ERROR_SUCCESS;
2301 comp->Action = INSTALLSTATE_LOCAL;
2303 name = MSI_RecordGetString(row, 4);
2304 if( MSI_RecordIsNull(row,5) && name )
2306 /* null values can have special meanings */
2307 if (name[0]=='-' && name[1] == 0)
2308 return ERROR_SUCCESS;
2309 else if ((name[0]=='+' && name[1] == 0) ||
2310 (name[0] == '*' && name[1] == 0))
2311 name = NULL;
2312 check_first = TRUE;
2315 root = MSI_RecordGetInteger(row,2);
2316 key = MSI_RecordGetString(row, 3);
2318 /* get the root key */
2319 switch (root)
2321 case -1:
2323 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2324 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2325 if (all_users && all_users[0] == '1')
2327 root_key = HKEY_LOCAL_MACHINE;
2328 szRoot = szHLM;
2330 else
2332 root_key = HKEY_CURRENT_USER;
2333 szRoot = szHCU;
2335 msi_free(all_users);
2337 break;
2338 case 0: root_key = HKEY_CLASSES_ROOT;
2339 szRoot = szHCR;
2340 break;
2341 case 1: root_key = HKEY_CURRENT_USER;
2342 szRoot = szHCU;
2343 break;
2344 case 2: root_key = HKEY_LOCAL_MACHINE;
2345 szRoot = szHLM;
2346 break;
2347 case 3: root_key = HKEY_USERS;
2348 szRoot = szHU;
2349 break;
2350 default:
2351 ERR("Unknown root %i\n",root);
2352 root_key=NULL;
2353 szRoot = NULL;
2354 break;
2356 if (!root_key)
2357 return ERROR_SUCCESS;
2359 deformat_string(package, key , &deformated);
2360 size = strlenW(deformated) + strlenW(szRoot) + 1;
2361 uikey = msi_alloc(size*sizeof(WCHAR));
2362 strcpyW(uikey,szRoot);
2363 strcatW(uikey,deformated);
2365 if (RegCreateKeyW( root_key, deformated, &hkey))
2367 ERR("Could not create key %s\n",debugstr_w(deformated));
2368 msi_free(deformated);
2369 msi_free(uikey);
2370 return ERROR_SUCCESS;
2372 msi_free(deformated);
2374 value = MSI_RecordGetString(row,5);
2375 if (value)
2376 value_data = parse_value(package, value, &type, &size);
2377 else
2379 static const WCHAR szEmpty[] = {0};
2380 value_data = (LPSTR)strdupW(szEmpty);
2381 size = 0;
2382 type = REG_SZ;
2385 deformat_string(package, name, &deformated);
2387 /* get the double nulls to terminate SZ_MULTI */
2388 if (type == REG_MULTI_SZ)
2389 size +=sizeof(WCHAR);
2391 if (!check_first)
2393 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2394 debugstr_w(uikey));
2395 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2397 else
2399 DWORD sz = 0;
2400 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2401 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2403 TRACE("value %s of %s checked already exists\n",
2404 debugstr_w(deformated), debugstr_w(uikey));
2406 else
2408 TRACE("Checked and setting value %s of %s\n",
2409 debugstr_w(deformated), debugstr_w(uikey));
2410 if (deformated || size)
2411 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2414 RegCloseKey(hkey);
2416 uirow = MSI_CreateRecord(3);
2417 MSI_RecordSetStringW(uirow,2,deformated);
2418 MSI_RecordSetStringW(uirow,1,uikey);
2420 if (type == REG_SZ)
2421 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2422 else
2423 MSI_RecordSetStringW(uirow,3,value);
2425 ui_actiondata(package,szWriteRegistryValues,uirow);
2426 msiobj_release( &uirow->hdr );
2428 msi_free(value_data);
2429 msi_free(deformated);
2430 msi_free(uikey);
2432 return ERROR_SUCCESS;
2435 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2437 UINT rc;
2438 MSIQUERY * view;
2439 static const WCHAR ExecSeqQuery[] =
2440 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2441 '`','R','e','g','i','s','t','r','y','`',0 };
2443 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2444 if (rc != ERROR_SUCCESS)
2445 return ERROR_SUCCESS;
2447 /* increment progress bar each time action data is sent */
2448 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2450 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2452 msiobj_release(&view->hdr);
2453 return rc;
2456 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2458 package->script->CurrentlyScripting = TRUE;
2460 return ERROR_SUCCESS;
2464 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2466 MSICOMPONENT *comp;
2467 DWORD progress = 0;
2468 DWORD total = 0;
2469 static const WCHAR q1[]=
2470 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2471 '`','R','e','g','i','s','t','r','y','`',0};
2472 UINT rc;
2473 MSIQUERY * view;
2474 MSIFEATURE *feature;
2475 MSIFILE *file;
2477 TRACE("InstallValidate\n");
2479 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2480 if (rc == ERROR_SUCCESS)
2482 MSI_IterateRecords( view, &progress, NULL, package );
2483 msiobj_release( &view->hdr );
2484 total += progress * REG_PROGRESS_VALUE;
2487 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2488 total += COMPONENT_PROGRESS_VALUE;
2490 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2491 total += file->FileSize;
2493 ui_progress(package,0,total,0,0);
2495 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2497 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2498 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2499 feature->ActionRequest);
2502 return ERROR_SUCCESS;
2505 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2507 MSIPACKAGE* package = (MSIPACKAGE*)param;
2508 LPCWSTR cond = NULL;
2509 LPCWSTR message = NULL;
2510 static const WCHAR title[]=
2511 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2513 cond = MSI_RecordGetString(row,1);
2515 if (MSI_EvaluateConditionW(package,cond) != MSICONDITION_TRUE)
2517 LPWSTR deformated;
2518 message = MSI_RecordGetString(row,2);
2519 deformat_string(package,message,&deformated);
2520 MessageBoxW(NULL,deformated,title,MB_OK);
2521 msi_free(deformated);
2522 return ERROR_FUNCTION_FAILED;
2525 return ERROR_SUCCESS;
2528 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2530 UINT rc;
2531 MSIQUERY * view = NULL;
2532 static const WCHAR ExecSeqQuery[] =
2533 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2534 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2536 TRACE("Checking launch conditions\n");
2538 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2539 if (rc != ERROR_SUCCESS)
2540 return ERROR_SUCCESS;
2542 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2543 msiobj_release(&view->hdr);
2545 return rc;
2548 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2551 if (!cmp->KeyPath)
2552 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2554 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2556 MSIRECORD * row = 0;
2557 UINT root,len;
2558 LPWSTR deformated,buffer,deformated_name;
2559 LPCWSTR key,name;
2560 static const WCHAR ExecSeqQuery[] =
2561 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2562 '`','R','e','g','i','s','t','r','y','`',' ',
2563 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2564 ' ','=',' ' ,'\'','%','s','\'',0 };
2565 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2566 static const WCHAR fmt2[]=
2567 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2569 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2570 if (!row)
2571 return NULL;
2573 root = MSI_RecordGetInteger(row,2);
2574 key = MSI_RecordGetString(row, 3);
2575 name = MSI_RecordGetString(row, 4);
2576 deformat_string(package, key , &deformated);
2577 deformat_string(package, name, &deformated_name);
2579 len = strlenW(deformated) + 6;
2580 if (deformated_name)
2581 len+=strlenW(deformated_name);
2583 buffer = msi_alloc( len *sizeof(WCHAR));
2585 if (deformated_name)
2586 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2587 else
2588 sprintfW(buffer,fmt,root,deformated);
2590 msi_free(deformated);
2591 msi_free(deformated_name);
2592 msiobj_release(&row->hdr);
2594 return buffer;
2596 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2598 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2599 return NULL;
2601 else
2603 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2605 if (file)
2606 return strdupW( file->TargetPath );
2608 return NULL;
2611 static HKEY openSharedDLLsKey(void)
2613 HKEY hkey=0;
2614 static const WCHAR path[] =
2615 {'S','o','f','t','w','a','r','e','\\',
2616 'M','i','c','r','o','s','o','f','t','\\',
2617 'W','i','n','d','o','w','s','\\',
2618 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2619 'S','h','a','r','e','d','D','L','L','s',0};
2621 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2622 return hkey;
2625 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2627 HKEY hkey;
2628 DWORD count=0;
2629 DWORD type;
2630 DWORD sz = sizeof(count);
2631 DWORD rc;
2633 hkey = openSharedDLLsKey();
2634 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2635 if (rc != ERROR_SUCCESS)
2636 count = 0;
2637 RegCloseKey(hkey);
2638 return count;
2641 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2643 HKEY hkey;
2645 hkey = openSharedDLLsKey();
2646 if (count > 0)
2647 msi_reg_set_val_dword( hkey, path, count );
2648 else
2649 RegDeleteValueW(hkey,path);
2650 RegCloseKey(hkey);
2651 return count;
2655 * Return TRUE if the count should be written out and FALSE if not
2657 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2659 MSIFEATURE *feature;
2660 INT count = 0;
2661 BOOL write = FALSE;
2663 /* only refcount DLLs */
2664 if (comp->KeyPath == NULL ||
2665 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2666 comp->Attributes & msidbComponentAttributesODBCDataSource)
2667 write = FALSE;
2668 else
2670 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2671 write = (count > 0);
2673 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2674 write = TRUE;
2677 /* increment counts */
2678 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2680 ComponentList *cl;
2682 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2683 continue;
2685 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2687 if ( cl->component == comp )
2688 count++;
2692 /* decrement counts */
2693 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2695 ComponentList *cl;
2697 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2698 continue;
2700 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2702 if ( cl->component == comp )
2703 count--;
2707 /* ref count all the files in the component */
2708 if (write)
2710 MSIFILE *file;
2712 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2714 if (file->Component == comp)
2715 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2719 /* add a count for permenent */
2720 if (comp->Attributes & msidbComponentAttributesPermanent)
2721 count ++;
2723 comp->RefCount = count;
2725 if (write)
2726 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2730 * Ok further analysis makes me think that this work is
2731 * actually done in the PublishComponents and PublishFeatures
2732 * step, and not here. It appears like the keypath and all that is
2733 * resolved in this step, however actually written in the Publish steps.
2734 * But we will leave it here for now because it is unclear
2736 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2738 WCHAR squished_pc[GUID_SIZE];
2739 WCHAR squished_cc[GUID_SIZE];
2740 UINT rc;
2741 MSICOMPONENT *comp;
2742 HKEY hkey=0,hkey2=0;
2744 /* writes the Component and Features values to the registry */
2746 rc = MSIREG_OpenComponents(&hkey);
2747 if (rc != ERROR_SUCCESS)
2748 return rc;
2750 squash_guid(package->ProductCode,squished_pc);
2751 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2753 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2755 MSIRECORD * uirow;
2757 ui_progress(package,2,0,0,0);
2758 if (!comp->ComponentId)
2759 continue;
2761 squash_guid(comp->ComponentId,squished_cc);
2763 msi_free(comp->FullKeypath);
2764 comp->FullKeypath = resolve_keypath( package, comp );
2766 /* do the refcounting */
2767 ACTION_RefCountComponent( package, comp );
2769 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2770 debugstr_w(comp->Component),
2771 debugstr_w(squished_cc),
2772 debugstr_w(comp->FullKeypath),
2773 comp->RefCount);
2775 * Write the keypath out if the component is to be registered
2776 * and delete the key if the component is to be deregistered
2778 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2780 rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2781 if (rc != ERROR_SUCCESS)
2782 continue;
2784 if (!comp->FullKeypath)
2785 continue;
2787 msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2789 if (comp->Attributes & msidbComponentAttributesPermanent)
2791 static const WCHAR szPermKey[] =
2792 { '0','0','0','0','0','0','0','0','0','0','0','0',
2793 '0','0','0','0','0','0','0','0','0','0','0','0',
2794 '0','0','0','0','0','0','0','0',0 };
2796 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2799 RegCloseKey(hkey2);
2801 else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2803 DWORD res;
2805 rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2806 if (rc != ERROR_SUCCESS)
2807 continue;
2809 RegDeleteValueW(hkey2,squished_pc);
2811 /* if the key is empty delete it */
2812 res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2813 RegCloseKey(hkey2);
2814 if (res == ERROR_NO_MORE_ITEMS)
2815 RegDeleteKeyW(hkey,squished_cc);
2819 /* UI stuff */
2820 uirow = MSI_CreateRecord(3);
2821 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2822 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2823 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2824 ui_actiondata(package,szProcessComponents,uirow);
2825 msiobj_release( &uirow->hdr );
2827 RegCloseKey(hkey);
2828 return rc;
2831 typedef struct {
2832 CLSID clsid;
2833 LPWSTR source;
2835 LPWSTR path;
2836 ITypeLib *ptLib;
2837 } typelib_struct;
2839 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2840 LPWSTR lpszName, LONG_PTR lParam)
2842 TLIBATTR *attr;
2843 typelib_struct *tl_struct = (typelib_struct*) lParam;
2844 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2845 int sz;
2846 HRESULT res;
2848 if (!IS_INTRESOURCE(lpszName))
2850 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2851 return TRUE;
2854 sz = strlenW(tl_struct->source)+4;
2855 sz *= sizeof(WCHAR);
2857 if ((INT_PTR)lpszName == 1)
2858 tl_struct->path = strdupW(tl_struct->source);
2859 else
2861 tl_struct->path = msi_alloc(sz);
2862 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2865 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2866 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2867 if (!SUCCEEDED(res))
2869 msi_free(tl_struct->path);
2870 tl_struct->path = NULL;
2872 return TRUE;
2875 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2876 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2878 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2879 return FALSE;
2882 msi_free(tl_struct->path);
2883 tl_struct->path = NULL;
2885 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2886 ITypeLib_Release(tl_struct->ptLib);
2888 return TRUE;
2891 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2893 MSIPACKAGE* package = (MSIPACKAGE*)param;
2894 LPCWSTR component;
2895 MSICOMPONENT *comp;
2896 MSIFILE *file;
2897 typelib_struct tl_struct;
2898 HMODULE module;
2899 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2901 component = MSI_RecordGetString(row,3);
2902 comp = get_loaded_component(package,component);
2903 if (!comp)
2904 return ERROR_SUCCESS;
2906 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2908 TRACE("Skipping typelib reg due to disabled component\n");
2910 comp->Action = comp->Installed;
2912 return ERROR_SUCCESS;
2915 comp->Action = INSTALLSTATE_LOCAL;
2917 file = get_loaded_file( package, comp->KeyPath );
2918 if (!file)
2919 return ERROR_SUCCESS;
2921 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2922 if (module)
2924 LPCWSTR guid;
2925 guid = MSI_RecordGetString(row,1);
2926 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2927 tl_struct.source = strdupW( file->TargetPath );
2928 tl_struct.path = NULL;
2930 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2931 (LONG_PTR)&tl_struct);
2933 if (tl_struct.path)
2935 LPWSTR help = NULL;
2936 LPCWSTR helpid;
2937 HRESULT res;
2939 helpid = MSI_RecordGetString(row,6);
2941 if (helpid)
2942 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
2943 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2944 msi_free(help);
2946 if (!SUCCEEDED(res))
2947 ERR("Failed to register type library %s\n",
2948 debugstr_w(tl_struct.path));
2949 else
2951 ui_actiondata(package,szRegisterTypeLibraries,row);
2953 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2956 ITypeLib_Release(tl_struct.ptLib);
2957 msi_free(tl_struct.path);
2959 else
2960 ERR("Failed to load type library %s\n",
2961 debugstr_w(tl_struct.source));
2963 FreeLibrary(module);
2964 msi_free(tl_struct.source);
2966 else
2967 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
2969 return ERROR_SUCCESS;
2972 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
2975 * OK this is a bit confusing.. I am given a _Component key and I believe
2976 * that the file that is being registered as a type library is the "key file
2977 * of that component" which I interpret to mean "The file in the KeyPath of
2978 * that component".
2980 UINT rc;
2981 MSIQUERY * view;
2982 static const WCHAR Query[] =
2983 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2984 '`','T','y','p','e','L','i','b','`',0};
2986 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2987 if (rc != ERROR_SUCCESS)
2988 return ERROR_SUCCESS;
2990 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
2991 msiobj_release(&view->hdr);
2992 return rc;
2995 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
2997 MSIPACKAGE *package = (MSIPACKAGE*)param;
2998 LPWSTR target_file, target_folder, filename;
2999 LPCWSTR buffer, extension;
3000 MSICOMPONENT *comp;
3001 static const WCHAR szlnk[]={'.','l','n','k',0};
3002 IShellLinkW *sl = NULL;
3003 IPersistFile *pf = NULL;
3004 HRESULT res;
3006 buffer = MSI_RecordGetString(row,4);
3007 comp = get_loaded_component(package,buffer);
3008 if (!comp)
3009 return ERROR_SUCCESS;
3011 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3013 TRACE("Skipping shortcut creation due to disabled component\n");
3015 comp->Action = comp->Installed;
3017 return ERROR_SUCCESS;
3020 comp->Action = INSTALLSTATE_LOCAL;
3022 ui_actiondata(package,szCreateShortcuts,row);
3024 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3025 &IID_IShellLinkW, (LPVOID *) &sl );
3027 if (FAILED( res ))
3029 ERR("CLSID_ShellLink not available\n");
3030 goto err;
3033 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3034 if (FAILED( res ))
3036 ERR("QueryInterface(IID_IPersistFile) failed\n");
3037 goto err;
3040 buffer = MSI_RecordGetString(row,2);
3041 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3043 /* may be needed because of a bug somehwere else */
3044 create_full_pathW(target_folder);
3046 filename = msi_dup_record_field( row, 3 );
3047 reduce_to_longfilename(filename);
3049 extension = strchrW(filename,'.');
3050 if (!extension || strcmpiW(extension,szlnk))
3052 int len = strlenW(filename);
3053 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3054 memcpy(filename + len, szlnk, sizeof(szlnk));
3056 target_file = build_directory_name(2, target_folder, filename);
3057 msi_free(target_folder);
3058 msi_free(filename);
3060 buffer = MSI_RecordGetString(row,5);
3061 if (strchrW(buffer,'['))
3063 LPWSTR deformated;
3064 deformat_string(package,buffer,&deformated);
3065 IShellLinkW_SetPath(sl,deformated);
3066 msi_free(deformated);
3068 else
3070 FIXME("poorly handled shortcut format, advertised shortcut\n");
3071 IShellLinkW_SetPath(sl,comp->FullKeypath);
3074 if (!MSI_RecordIsNull(row,6))
3076 LPWSTR deformated;
3077 buffer = MSI_RecordGetString(row,6);
3078 deformat_string(package,buffer,&deformated);
3079 IShellLinkW_SetArguments(sl,deformated);
3080 msi_free(deformated);
3083 if (!MSI_RecordIsNull(row,7))
3085 buffer = MSI_RecordGetString(row,7);
3086 IShellLinkW_SetDescription(sl,buffer);
3089 if (!MSI_RecordIsNull(row,8))
3090 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3092 if (!MSI_RecordIsNull(row,9))
3094 LPWSTR Path;
3095 INT index;
3097 buffer = MSI_RecordGetString(row,9);
3099 Path = build_icon_path(package,buffer);
3100 index = MSI_RecordGetInteger(row,10);
3102 /* no value means 0 */
3103 if (index == MSI_NULL_INTEGER)
3104 index = 0;
3106 IShellLinkW_SetIconLocation(sl,Path,index);
3107 msi_free(Path);
3110 if (!MSI_RecordIsNull(row,11))
3111 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3113 if (!MSI_RecordIsNull(row,12))
3115 LPWSTR Path;
3116 buffer = MSI_RecordGetString(row,12);
3117 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3118 if (Path)
3119 IShellLinkW_SetWorkingDirectory(sl,Path);
3120 msi_free(Path);
3123 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3124 IPersistFile_Save(pf,target_file,FALSE);
3126 msi_free(target_file);
3128 err:
3129 if (pf)
3130 IPersistFile_Release( pf );
3131 if (sl)
3132 IShellLinkW_Release( sl );
3134 return ERROR_SUCCESS;
3137 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3139 UINT rc;
3140 HRESULT res;
3141 MSIQUERY * view;
3142 static const WCHAR Query[] =
3143 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3144 '`','S','h','o','r','t','c','u','t','`',0};
3146 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3147 if (rc != ERROR_SUCCESS)
3148 return ERROR_SUCCESS;
3150 res = CoInitialize( NULL );
3151 if (FAILED (res))
3153 ERR("CoInitialize failed\n");
3154 return ERROR_FUNCTION_FAILED;
3157 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3158 msiobj_release(&view->hdr);
3160 CoUninitialize();
3162 return rc;
3165 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3167 MSIPACKAGE* package = (MSIPACKAGE*)param;
3168 HANDLE the_file;
3169 LPWSTR FilePath;
3170 LPCWSTR FileName;
3171 CHAR buffer[1024];
3172 DWORD sz;
3173 UINT rc;
3174 MSIRECORD *uirow;
3176 FileName = MSI_RecordGetString(row,1);
3177 if (!FileName)
3179 ERR("Unable to get FileName\n");
3180 return ERROR_SUCCESS;
3183 FilePath = build_icon_path(package,FileName);
3185 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3187 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3188 FILE_ATTRIBUTE_NORMAL, NULL);
3190 if (the_file == INVALID_HANDLE_VALUE)
3192 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3193 msi_free(FilePath);
3194 return ERROR_SUCCESS;
3199 DWORD write;
3200 sz = 1024;
3201 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3202 if (rc != ERROR_SUCCESS)
3204 ERR("Failed to get stream\n");
3205 CloseHandle(the_file);
3206 DeleteFileW(FilePath);
3207 break;
3209 WriteFile(the_file,buffer,sz,&write,NULL);
3210 } while (sz == 1024);
3212 msi_free(FilePath);
3214 CloseHandle(the_file);
3216 uirow = MSI_CreateRecord(1);
3217 MSI_RecordSetStringW(uirow,1,FileName);
3218 ui_actiondata(package,szPublishProduct,uirow);
3219 msiobj_release( &uirow->hdr );
3221 return ERROR_SUCCESS;
3225 * 99% of the work done here is only done for
3226 * advertised installs. However this is where the
3227 * Icon table is processed and written out
3228 * so that is what I am going to do here.
3230 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3232 UINT rc;
3233 MSIQUERY * view;
3234 static const WCHAR Query[]=
3235 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3236 '`','I','c','o','n','`',0};
3237 /* for registry stuff */
3238 HKEY hkey=0;
3239 HKEY hukey=0;
3240 static const WCHAR szProductLanguage[] =
3241 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3242 static const WCHAR szARPProductIcon[] =
3243 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3244 static const WCHAR szProductVersion[] =
3245 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3246 DWORD langid;
3247 LPWSTR buffer;
3248 DWORD size;
3249 MSIHANDLE hDb, hSumInfo;
3251 /* write out icon files */
3253 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3254 if (rc == ERROR_SUCCESS)
3256 MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3257 msiobj_release(&view->hdr);
3260 /* ok there is a lot more done here but i need to figure out what */
3262 rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3263 if (rc != ERROR_SUCCESS)
3264 goto end;
3266 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3267 if (rc != ERROR_SUCCESS)
3268 goto end;
3271 buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3272 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3273 msi_free(buffer);
3275 langid = msi_get_property_int( package, szProductLanguage, 0 );
3276 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3278 buffer = msi_dup_property( package, szARPProductIcon );
3279 if (buffer)
3281 LPWSTR path = build_icon_path(package,buffer);
3282 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3283 msi_free( path );
3285 msi_free(buffer);
3287 buffer = msi_dup_property( package, szProductVersion );
3288 if (buffer)
3290 DWORD verdword = msi_version_str_to_dword(buffer);
3291 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3293 msi_free(buffer);
3295 /* FIXME: Need to write more keys to the user registry */
3297 hDb= alloc_msihandle( &package->db->hdr );
3298 if (!hDb) {
3299 rc = ERROR_NOT_ENOUGH_MEMORY;
3300 goto end;
3302 rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo);
3303 MsiCloseHandle(hDb);
3304 if (rc == ERROR_SUCCESS)
3306 WCHAR guidbuffer[0x200];
3307 size = 0x200;
3308 rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3309 guidbuffer, &size);
3310 if (rc == ERROR_SUCCESS)
3312 WCHAR squashed[GUID_SIZE];
3313 /* for now we only care about the first guid */
3314 LPWSTR ptr = strchrW(guidbuffer,';');
3315 if (ptr) *ptr = 0;
3316 squash_guid(guidbuffer,squashed);
3317 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3319 else
3321 ERR("Unable to query Revision_Number...\n");
3322 rc = ERROR_SUCCESS;
3324 MsiCloseHandle(hSumInfo);
3326 else
3328 ERR("Unable to open Summary Information\n");
3329 rc = ERROR_SUCCESS;
3332 end:
3334 RegCloseKey(hkey);
3335 RegCloseKey(hukey);
3337 return rc;
3340 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3342 MSIPACKAGE *package = (MSIPACKAGE*)param;
3343 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3344 LPWSTR deformated_section, deformated_key, deformated_value;
3345 LPWSTR folder, fullname = NULL;
3346 MSIRECORD * uirow;
3347 INT action;
3348 MSICOMPONENT *comp;
3349 static const WCHAR szWindowsFolder[] =
3350 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3352 component = MSI_RecordGetString(row, 8);
3353 comp = get_loaded_component(package,component);
3355 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3357 TRACE("Skipping ini file due to disabled component %s\n",
3358 debugstr_w(component));
3360 comp->Action = comp->Installed;
3362 return ERROR_SUCCESS;
3365 comp->Action = INSTALLSTATE_LOCAL;
3367 identifier = MSI_RecordGetString(row,1);
3368 filename = MSI_RecordGetString(row,2);
3369 dirproperty = MSI_RecordGetString(row,3);
3370 section = MSI_RecordGetString(row,4);
3371 key = MSI_RecordGetString(row,5);
3372 value = MSI_RecordGetString(row,6);
3373 action = MSI_RecordGetInteger(row,7);
3375 deformat_string(package,section,&deformated_section);
3376 deformat_string(package,key,&deformated_key);
3377 deformat_string(package,value,&deformated_value);
3379 if (dirproperty)
3381 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3382 if (!folder)
3383 folder = msi_dup_property( package, dirproperty );
3385 else
3386 folder = msi_dup_property( package, szWindowsFolder );
3388 if (!folder)
3390 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3391 goto cleanup;
3394 fullname = build_directory_name(2, folder, filename);
3396 if (action == 0)
3398 TRACE("Adding value %s to section %s in %s\n",
3399 debugstr_w(deformated_key), debugstr_w(deformated_section),
3400 debugstr_w(fullname));
3401 WritePrivateProfileStringW(deformated_section, deformated_key,
3402 deformated_value, fullname);
3404 else if (action == 1)
3406 WCHAR returned[10];
3407 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3408 returned, 10, fullname);
3409 if (returned[0] == 0)
3411 TRACE("Adding value %s to section %s in %s\n",
3412 debugstr_w(deformated_key), debugstr_w(deformated_section),
3413 debugstr_w(fullname));
3415 WritePrivateProfileStringW(deformated_section, deformated_key,
3416 deformated_value, fullname);
3419 else if (action == 3)
3420 FIXME("Append to existing section not yet implemented\n");
3422 uirow = MSI_CreateRecord(4);
3423 MSI_RecordSetStringW(uirow,1,identifier);
3424 MSI_RecordSetStringW(uirow,2,deformated_section);
3425 MSI_RecordSetStringW(uirow,3,deformated_key);
3426 MSI_RecordSetStringW(uirow,4,deformated_value);
3427 ui_actiondata(package,szWriteIniValues,uirow);
3428 msiobj_release( &uirow->hdr );
3429 cleanup:
3430 msi_free(fullname);
3431 msi_free(folder);
3432 msi_free(deformated_key);
3433 msi_free(deformated_value);
3434 msi_free(deformated_section);
3435 return ERROR_SUCCESS;
3438 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3440 UINT rc;
3441 MSIQUERY * view;
3442 static const WCHAR ExecSeqQuery[] =
3443 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3444 '`','I','n','i','F','i','l','e','`',0};
3446 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3447 if (rc != ERROR_SUCCESS)
3449 TRACE("no IniFile table\n");
3450 return ERROR_SUCCESS;
3453 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3454 msiobj_release(&view->hdr);
3455 return rc;
3458 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3460 MSIPACKAGE *package = (MSIPACKAGE*)param;
3461 LPCWSTR filename;
3462 LPWSTR FullName;
3463 MSIFILE *file;
3464 DWORD len;
3465 static const WCHAR ExeStr[] =
3466 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3467 static const WCHAR close[] = {'\"',0};
3468 STARTUPINFOW si;
3469 PROCESS_INFORMATION info;
3470 BOOL brc;
3471 MSIRECORD *uirow;
3472 LPWSTR uipath, p;
3474 memset(&si,0,sizeof(STARTUPINFOW));
3476 filename = MSI_RecordGetString(row,1);
3477 file = get_loaded_file( package, filename );
3479 if (!file)
3481 ERR("Unable to find file id %s\n",debugstr_w(filename));
3482 return ERROR_SUCCESS;
3485 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3487 FullName = msi_alloc(len*sizeof(WCHAR));
3488 strcpyW(FullName,ExeStr);
3489 strcatW( FullName, file->TargetPath );
3490 strcatW(FullName,close);
3492 TRACE("Registering %s\n",debugstr_w(FullName));
3493 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3494 &si, &info);
3496 if (brc)
3497 msi_dialog_check_messages(info.hProcess);
3499 msi_free(FullName);
3501 /* the UI chunk */
3502 uirow = MSI_CreateRecord( 2 );
3503 uipath = strdupW( file->TargetPath );
3504 p = strrchrW(uipath,'\\');
3505 if (p)
3506 p[0]=0;
3507 MSI_RecordSetStringW( uirow, 1, &p[1] );
3508 MSI_RecordSetStringW( uirow, 2, uipath);
3509 ui_actiondata( package, szSelfRegModules, uirow);
3510 msiobj_release( &uirow->hdr );
3511 msi_free( uipath );
3512 /* FIXME: call ui_progress? */
3514 return ERROR_SUCCESS;
3517 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3519 UINT rc;
3520 MSIQUERY * view;
3521 static const WCHAR ExecSeqQuery[] =
3522 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3523 '`','S','e','l','f','R','e','g','`',0};
3525 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3526 if (rc != ERROR_SUCCESS)
3528 TRACE("no SelfReg table\n");
3529 return ERROR_SUCCESS;
3532 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3533 msiobj_release(&view->hdr);
3535 return ERROR_SUCCESS;
3538 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3540 MSIFEATURE *feature;
3541 UINT rc;
3542 HKEY hkey=0;
3543 HKEY hukey=0;
3545 rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3546 if (rc != ERROR_SUCCESS)
3547 goto end;
3549 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3550 if (rc != ERROR_SUCCESS)
3551 goto end;
3553 /* here the guids are base 85 encoded */
3554 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3556 ComponentList *cl;
3557 LPWSTR data = NULL;
3558 GUID clsid;
3559 INT size;
3560 BOOL absent = FALSE;
3561 MSIRECORD *uirow;
3563 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3564 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3565 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3566 absent = TRUE;
3568 size = 1;
3569 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3571 size += 21;
3573 if (feature->Feature_Parent)
3574 size += strlenW( feature->Feature_Parent )+2;
3576 data = msi_alloc(size * sizeof(WCHAR));
3578 data[0] = 0;
3579 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3581 MSICOMPONENT* component = cl->component;
3582 WCHAR buf[21];
3584 buf[0] = 0;
3585 if (component->ComponentId)
3587 TRACE("From %s\n",debugstr_w(component->ComponentId));
3588 CLSIDFromString(component->ComponentId, &clsid);
3589 encode_base85_guid(&clsid,buf);
3590 TRACE("to %s\n",debugstr_w(buf));
3591 strcatW(data,buf);
3594 if (feature->Feature_Parent)
3596 static const WCHAR sep[] = {'\2',0};
3597 strcatW(data,sep);
3598 strcatW(data,feature->Feature_Parent);
3601 msi_reg_set_val_str( hkey, feature->Feature, data );
3602 msi_free(data);
3604 size = 0;
3605 if (feature->Feature_Parent)
3606 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3607 if (!absent)
3609 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3610 (LPBYTE)feature->Feature_Parent,size);
3612 else
3614 size += 2*sizeof(WCHAR);
3615 data = msi_alloc(size);
3616 data[0] = 0x6;
3617 data[1] = 0;
3618 if (feature->Feature_Parent)
3619 strcpyW( &data[1], feature->Feature_Parent );
3620 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3621 (LPBYTE)data,size);
3622 msi_free(data);
3625 /* the UI chunk */
3626 uirow = MSI_CreateRecord( 1 );
3627 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3628 ui_actiondata( package, szPublishFeatures, uirow);
3629 msiobj_release( &uirow->hdr );
3630 /* FIXME: call ui_progress? */
3633 end:
3634 RegCloseKey(hkey);
3635 RegCloseKey(hukey);
3636 return rc;
3639 static UINT msi_get_local_package_name( LPWSTR path )
3641 static const WCHAR szInstaller[] = {
3642 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3643 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3644 DWORD time, len, i;
3645 HANDLE handle;
3647 time = GetTickCount();
3648 GetWindowsDirectoryW( path, MAX_PATH );
3649 lstrcatW( path, szInstaller );
3650 CreateDirectoryW( path, NULL );
3652 len = lstrlenW(path);
3653 for (i=0; i<0x10000; i++)
3655 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3656 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3657 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3658 if (handle != INVALID_HANDLE_VALUE)
3660 CloseHandle(handle);
3661 break;
3663 if (GetLastError() != ERROR_FILE_EXISTS &&
3664 GetLastError() != ERROR_SHARING_VIOLATION)
3665 return ERROR_FUNCTION_FAILED;
3668 return ERROR_SUCCESS;
3671 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3673 static const WCHAR szOriginalDatabase[] =
3674 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3675 WCHAR packagefile[MAX_PATH];
3676 LPWSTR msiFilePath;
3677 UINT r;
3679 r = msi_get_local_package_name( packagefile );
3680 if (r != ERROR_SUCCESS)
3681 return r;
3683 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3685 msiFilePath = msi_dup_property( package, szOriginalDatabase );
3686 r = CopyFileW( msiFilePath, packagefile, FALSE);
3688 if (!r)
3690 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3691 debugstr_w(msiFilePath), debugstr_w(packagefile), GetLastError());
3692 msi_free( msiFilePath );
3693 return ERROR_FUNCTION_FAILED;
3695 msi_free( msiFilePath );
3697 /* FIXME: maybe set this key in ACTION_RegisterProduct instead */
3698 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3699 return ERROR_SUCCESS;
3702 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3704 LPWSTR prop, val, key;
3705 static const LPCSTR propval[] = {
3706 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3707 "ARPCONTACT", "Contact",
3708 "ARPCOMMENTS", "Comments",
3709 "ProductName", "DisplayName",
3710 "ProductVersion", "DisplayVersion",
3711 "ARPHELPLINK", "HelpLink",
3712 "ARPHELPTELEPHONE", "HelpTelephone",
3713 "ARPINSTALLLOCATION", "InstallLocation",
3714 "SourceDir", "InstallSource",
3715 "Manufacturer", "Publisher",
3716 "ARPREADME", "Readme",
3717 "ARPSIZE", "Size",
3718 "ARPURLINFOABOUT", "URLInfoAbout",
3719 "ARPURLUPDATEINFO", "URLUpdateInfo",
3720 NULL,
3722 const LPCSTR *p = propval;
3724 while( *p )
3726 prop = strdupAtoW( *p++ );
3727 key = strdupAtoW( *p++ );
3728 val = msi_dup_property( package, prop );
3729 msi_reg_set_val_str( hkey, key, val );
3730 msi_free(val);
3731 msi_free(key);
3732 msi_free(prop);
3734 return ERROR_SUCCESS;
3737 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3739 HKEY hkey=0;
3740 LPWSTR buffer = NULL;
3741 UINT rc;
3742 DWORD size, langid;
3743 static const WCHAR szWindowsInstaller[] =
3744 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3745 static const WCHAR szUpgradeCode[] =
3746 {'U','p','g','r','a','d','e','C','o','d','e',0};
3747 static const WCHAR modpath_fmt[] =
3748 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3749 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3750 static const WCHAR szModifyPath[] =
3751 {'M','o','d','i','f','y','P','a','t','h',0};
3752 static const WCHAR szUninstallString[] =
3753 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3754 static const WCHAR szEstimatedSize[] =
3755 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3756 static const WCHAR szProductLanguage[] =
3757 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3758 static const WCHAR szProductVersion[] =
3759 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3761 SYSTEMTIME systime;
3762 static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0};
3763 LPWSTR upgrade_code;
3764 WCHAR szDate[9];
3766 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3767 if (rc != ERROR_SUCCESS)
3768 return rc;
3770 /* dump all the info i can grab */
3771 /* FIXME: Flesh out more information */
3773 msi_write_uninstall_property_vals( package, hkey );
3775 msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
3777 msi_make_package_local( package, hkey );
3779 /* do ModifyPath and UninstallString */
3780 size = deformat_string(package,modpath_fmt,&buffer);
3781 RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3782 RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3783 msi_free(buffer);
3785 /* FIXME: Write real Estimated Size when we have it */
3786 msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
3788 GetLocalTime(&systime);
3789 sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
3790 msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
3792 langid = msi_get_property_int( package, szProductLanguage, 0 );
3793 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3795 buffer = msi_dup_property( package, szProductVersion );
3796 if (buffer)
3798 DWORD verdword = msi_version_str_to_dword(buffer);
3800 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3801 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
3802 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
3804 msi_free(buffer);
3806 /* Handle Upgrade Codes */
3807 upgrade_code = msi_dup_property( package, szUpgradeCode );
3808 if (upgrade_code)
3810 HKEY hkey2;
3811 WCHAR squashed[33];
3812 MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3813 squash_guid(package->ProductCode,squashed);
3814 msi_reg_set_val_str( hkey2, squashed, NULL );
3815 RegCloseKey(hkey2);
3816 MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3817 squash_guid(package->ProductCode,squashed);
3818 msi_reg_set_val_str( hkey2, squashed, NULL );
3819 RegCloseKey(hkey2);
3821 msi_free(upgrade_code);
3824 RegCloseKey(hkey);
3826 /* FIXME: call ui_actiondata */
3828 return ERROR_SUCCESS;
3831 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
3833 return execute_script(package,INSTALL_SCRIPT);
3836 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
3838 UINT rc;
3840 /* turn off scheduling */
3841 package->script->CurrentlyScripting= FALSE;
3843 /* first do the same as an InstallExecute */
3844 rc = ACTION_InstallExecute(package);
3845 if (rc != ERROR_SUCCESS)
3846 return rc;
3848 /* then handle Commit Actions */
3849 rc = execute_script(package,COMMIT_SCRIPT);
3851 return rc;
3854 UINT ACTION_ForceReboot(MSIPACKAGE *package)
3856 static const WCHAR RunOnce[] = {
3857 'S','o','f','t','w','a','r','e','\\',
3858 'M','i','c','r','o','s','o','f','t','\\',
3859 'W','i','n','d','o','w','s','\\',
3860 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3861 'R','u','n','O','n','c','e',0};
3862 static const WCHAR InstallRunOnce[] = {
3863 'S','o','f','t','w','a','r','e','\\',
3864 'M','i','c','r','o','s','o','f','t','\\',
3865 'W','i','n','d','o','w','s','\\',
3866 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3867 'I','n','s','t','a','l','l','e','r','\\',
3868 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
3870 static const WCHAR msiexec_fmt[] = {
3871 '%','s',
3872 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
3873 '\"','%','s','\"',0};
3874 static const WCHAR install_fmt[] = {
3875 '/','I',' ','\"','%','s','\"',' ',
3876 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
3877 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
3878 WCHAR buffer[256], sysdir[MAX_PATH];
3879 HKEY hkey;
3880 WCHAR squished_pc[100];
3882 squash_guid(package->ProductCode,squished_pc);
3884 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
3885 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
3886 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
3887 squished_pc);
3889 msi_reg_set_val_str( hkey, squished_pc, buffer );
3890 RegCloseKey(hkey);
3892 TRACE("Reboot command %s\n",debugstr_w(buffer));
3894 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
3895 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
3897 msi_reg_set_val_str( hkey, squished_pc, buffer );
3898 RegCloseKey(hkey);
3900 return ERROR_INSTALL_SUSPEND;
3903 static UINT msi_set_sourcedir_props(MSIPACKAGE *package)
3905 LPWSTR p, source;
3906 DWORD len;
3908 p = strrchrW( package->PackagePath, '\\' );
3909 if (!p)
3910 return ERROR_SUCCESS;
3912 len = p - package->PackagePath + 2;
3913 source = msi_alloc( len * sizeof(WCHAR) );
3914 lstrcpynW( source, package->PackagePath, len );
3916 MSI_SetPropertyW( package, cszSourceDir, source );
3917 MSI_SetPropertyW( package, cszSOURCEDIR, source );
3919 msi_free( source );
3921 return ERROR_SUCCESS;
3924 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
3926 DWORD attrib;
3927 UINT rc;
3930 * We are currently doing what should be done here in the top level Install
3931 * however for Administrative and uninstalls this step will be needed
3933 if (!package->PackagePath)
3934 return ERROR_SUCCESS;
3936 msi_set_sourcedir_props(package);
3938 attrib = GetFileAttributesW(package->PackagePath);
3939 if (attrib == INVALID_FILE_ATTRIBUTES)
3941 LPWSTR prompt;
3942 LPWSTR msg;
3943 DWORD size = 0;
3945 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
3946 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
3947 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
3948 if (rc == ERROR_MORE_DATA)
3950 prompt = msi_alloc(size * sizeof(WCHAR));
3951 MsiSourceListGetInfoW(package->ProductCode, NULL,
3952 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
3953 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
3955 else
3956 prompt = strdupW(package->PackagePath);
3958 msg = generate_error_string(package,1302,1,prompt);
3959 while(attrib == INVALID_FILE_ATTRIBUTES)
3961 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
3962 if (rc == IDCANCEL)
3964 rc = ERROR_INSTALL_USEREXIT;
3965 break;
3967 attrib = GetFileAttributesW(package->PackagePath);
3969 msi_free(prompt);
3970 rc = ERROR_SUCCESS;
3972 else
3973 return ERROR_SUCCESS;
3975 return rc;
3978 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
3980 HKEY hkey=0;
3981 LPWSTR buffer;
3982 LPWSTR productid;
3983 UINT rc,i;
3985 static const WCHAR szPropKeys[][80] =
3987 {'P','r','o','d','u','c','t','I','D',0},
3988 {'U','S','E','R','N','A','M','E',0},
3989 {'C','O','M','P','A','N','Y','N','A','M','E',0},
3990 {0},
3993 static const WCHAR szRegKeys[][80] =
3995 {'P','r','o','d','u','c','t','I','D',0},
3996 {'R','e','g','O','w','n','e','r',0},
3997 {'R','e','g','C','o','m','p','a','n','y',0},
3998 {0},
4001 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4002 if (!productid)
4003 return ERROR_SUCCESS;
4005 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4006 if (rc != ERROR_SUCCESS)
4007 goto end;
4009 for( i = 0; szPropKeys[i][0]; i++ )
4011 buffer = msi_dup_property( package, szPropKeys[i] );
4012 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4013 msi_free( buffer );
4016 end:
4017 msi_free(productid);
4018 RegCloseKey(hkey);
4020 /* FIXME: call ui_actiondata */
4022 return ERROR_SUCCESS;
4026 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4028 UINT rc;
4030 package->script->InWhatSequence |= SEQUENCE_EXEC;
4031 rc = ACTION_ProcessExecSequence(package,FALSE);
4032 return rc;
4036 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4038 MSIPACKAGE *package = (MSIPACKAGE*)param;
4039 LPCWSTR compgroupid=NULL;
4040 LPCWSTR feature=NULL;
4041 LPCWSTR text = NULL;
4042 LPCWSTR qualifier = NULL;
4043 LPCWSTR component = NULL;
4044 LPWSTR advertise = NULL;
4045 LPWSTR output = NULL;
4046 HKEY hkey;
4047 UINT rc = ERROR_SUCCESS;
4048 MSICOMPONENT *comp;
4049 DWORD sz = 0;
4050 MSIRECORD *uirow;
4052 component = MSI_RecordGetString(rec,3);
4053 comp = get_loaded_component(package,component);
4055 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4056 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4057 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4059 TRACE("Skipping: Component %s not scheduled for install\n",
4060 debugstr_w(component));
4062 return ERROR_SUCCESS;
4065 compgroupid = MSI_RecordGetString(rec,1);
4066 qualifier = MSI_RecordGetString(rec,2);
4068 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4069 if (rc != ERROR_SUCCESS)
4070 goto end;
4072 text = MSI_RecordGetString(rec,4);
4073 feature = MSI_RecordGetString(rec,5);
4075 advertise = create_component_advertise_string(package, comp, feature);
4077 sz = strlenW(advertise);
4079 if (text)
4080 sz += lstrlenW(text);
4082 sz+=3;
4083 sz *= sizeof(WCHAR);
4085 output = msi_alloc_zero(sz);
4086 strcpyW(output,advertise);
4087 msi_free(advertise);
4089 if (text)
4090 strcatW(output,text);
4092 msi_reg_set_val_multi_str( hkey, qualifier, output );
4094 end:
4095 RegCloseKey(hkey);
4096 msi_free(output);
4098 /* the UI chunk */
4099 uirow = MSI_CreateRecord( 2 );
4100 MSI_RecordSetStringW( uirow, 1, compgroupid );
4101 MSI_RecordSetStringW( uirow, 2, qualifier);
4102 ui_actiondata( package, szPublishComponents, uirow);
4103 msiobj_release( &uirow->hdr );
4104 /* FIXME: call ui_progress? */
4106 return rc;
4110 * At present I am ignorning the advertised components part of this and only
4111 * focusing on the qualified component sets
4113 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4115 UINT rc;
4116 MSIQUERY * view;
4117 static const WCHAR ExecSeqQuery[] =
4118 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4119 '`','P','u','b','l','i','s','h',
4120 'C','o','m','p','o','n','e','n','t','`',0};
4122 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4123 if (rc != ERROR_SUCCESS)
4124 return ERROR_SUCCESS;
4126 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4127 msiobj_release(&view->hdr);
4129 return rc;
4132 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4134 MSIPACKAGE *package = (MSIPACKAGE*)param;
4135 MSIRECORD *row;
4136 MSIFILE *file;
4137 SC_HANDLE hscm, service = NULL;
4138 LPCWSTR name, disp, comp, depends, pass;
4139 LPCWSTR load_order, serv_name, key;
4140 DWORD serv_type, start_type;
4141 DWORD err_control;
4143 static const WCHAR query[] =
4144 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4145 '`','C','o','m','p','o','n','e','n','t','`',' ',
4146 'W','H','E','R','E',' ',
4147 '`','C','o','m','p','o','n','e','n','t','`',' ',
4148 '=','\'','%','s','\'',0};
4150 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4151 if (!hscm)
4153 ERR("Failed to open the SC Manager!\n");
4154 goto done;
4157 start_type = MSI_RecordGetInteger(rec, 5);
4158 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4159 goto done;
4161 depends = MSI_RecordGetString(rec, 8);
4162 if (depends && *depends)
4163 FIXME("Dependency list unhandled!\n");
4165 name = MSI_RecordGetString(rec, 2);
4166 disp = MSI_RecordGetString(rec, 3);
4167 serv_type = MSI_RecordGetInteger(rec, 4);
4168 err_control = MSI_RecordGetInteger(rec, 6);
4169 load_order = MSI_RecordGetString(rec, 7);
4170 serv_name = MSI_RecordGetString(rec, 9);
4171 pass = MSI_RecordGetString(rec, 10);
4172 comp = MSI_RecordGetString(rec, 12);
4174 /* fetch the service path */
4175 row = MSI_QueryGetRecord(package->db, query, comp);
4176 if (!row)
4178 ERR("Control query failed!\n");
4179 goto done;
4182 key = MSI_RecordGetString(row, 6);
4183 msiobj_release(&row->hdr);
4185 file = get_loaded_file(package, key);
4186 if (!file)
4188 ERR("Failed to load the service file\n");
4189 goto done;
4192 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4193 start_type, err_control, file->TargetPath,
4194 load_order, NULL, NULL, serv_name, pass);
4195 if (!service)
4197 if (GetLastError() != ERROR_SERVICE_EXISTS)
4198 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4201 done:
4202 CloseServiceHandle(service);
4203 CloseServiceHandle(hscm);
4205 return ERROR_SUCCESS;
4208 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4210 UINT rc;
4211 MSIQUERY * view;
4212 static const WCHAR ExecSeqQuery[] =
4213 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4214 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4216 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4217 if (rc != ERROR_SUCCESS)
4218 return ERROR_SUCCESS;
4220 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4221 msiobj_release(&view->hdr);
4223 return rc;
4226 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4227 static LPCWSTR *msi_service_args_to_vector(LPCWSTR name, LPWSTR args, DWORD *numargs)
4229 LPCWSTR *vector;
4230 LPWSTR p, q;
4231 DWORD sep_len;
4233 static const WCHAR separator[] = {'[','~',']',0};
4235 *numargs = 0;
4236 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4238 if (!args)
4239 return NULL;
4241 vector = msi_alloc(sizeof(LPWSTR));
4242 if (!vector)
4243 return NULL;
4245 p = args;
4248 (*numargs)++;
4249 vector[*numargs - 1] = p;
4251 if ((q = strstrW(p, separator)))
4253 *q = '\0';
4255 vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4256 if (!vector)
4257 return NULL;
4259 p = q + sep_len;
4261 } while (q);
4263 return vector;
4266 static MSICOMPONENT *msi_find_component( MSIPACKAGE *package, LPCWSTR component )
4268 MSICOMPONENT *comp;
4270 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
4272 if (!lstrcmpW(comp->Component, component))
4273 return comp;
4276 return NULL;
4279 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4281 MSIPACKAGE *package = (MSIPACKAGE *)param;
4282 MSICOMPONENT *comp;
4283 SC_HANDLE scm, service = NULL;
4284 LPCWSTR name, *vector = NULL;
4285 LPWSTR args;
4286 DWORD event, numargs;
4287 UINT r = ERROR_FUNCTION_FAILED;
4289 comp = msi_find_component(package, MSI_RecordGetString(rec, 6));
4290 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4291 return ERROR_SUCCESS;
4293 name = MSI_RecordGetString(rec, 2);
4294 event = MSI_RecordGetInteger(rec, 3);
4295 args = strdupW(MSI_RecordGetString(rec, 4));
4297 if (!(event & msidbServiceControlEventStart))
4298 return ERROR_SUCCESS;
4300 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4301 if (!scm)
4303 ERR("Failed to open the service control manager\n");
4304 goto done;
4307 service = OpenServiceW(scm, name, SERVICE_START);
4308 if (!service)
4310 ERR("Failed to open service %s\n", debugstr_w(name));
4311 goto done;
4314 vector = msi_service_args_to_vector(name, args, &numargs);
4316 if (!StartServiceW(service, numargs, vector))
4318 ERR("Failed to start service %s\n", debugstr_w(name));
4319 goto done;
4322 r = ERROR_SUCCESS;
4324 done:
4325 CloseServiceHandle(service);
4326 CloseServiceHandle(scm);
4328 msi_free(args);
4329 msi_free(vector);
4330 return r;
4333 static UINT ACTION_StartServices( MSIPACKAGE *package )
4335 UINT rc;
4336 MSIQUERY *view;
4338 static const WCHAR query[] = {
4339 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4340 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4342 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4343 if (rc != ERROR_SUCCESS)
4344 return ERROR_SUCCESS;
4346 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4347 msiobj_release(&view->hdr);
4349 return rc;
4352 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4354 MSIFILE *file;
4356 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4358 if (!lstrcmpW(file->File, filename))
4359 return file;
4362 return NULL;
4365 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4367 MSIPACKAGE *package = (MSIPACKAGE*)param;
4368 LPWSTR driver, driver_path, ptr;
4369 WCHAR outpath[MAX_PATH];
4370 MSIFILE *driver_file, *setup_file;
4371 LPCWSTR desc;
4372 DWORD len, usage;
4373 UINT r = ERROR_SUCCESS;
4375 static const WCHAR driver_fmt[] = {
4376 'D','r','i','v','e','r','=','%','s',0};
4377 static const WCHAR setup_fmt[] = {
4378 'S','e','t','u','p','=','%','s',0};
4379 static const WCHAR usage_fmt[] = {
4380 'F','i','l','e','U','s','a','g','e','=','1',0};
4382 desc = MSI_RecordGetString(rec, 3);
4384 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4385 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4387 if (!driver_file || !setup_file)
4389 ERR("ODBC Driver entry not found!\n");
4390 return ERROR_FUNCTION_FAILED;
4393 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4394 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4395 lstrlenW(usage_fmt) + 1;
4396 driver = msi_alloc(len * sizeof(WCHAR));
4397 if (!driver)
4398 return ERROR_OUTOFMEMORY;
4400 ptr = driver;
4401 lstrcpyW(ptr, desc);
4402 ptr += lstrlenW(ptr) + 1;
4404 sprintfW(ptr, driver_fmt, driver_file->FileName);
4405 ptr += lstrlenW(ptr) + 1;
4407 sprintfW(ptr, setup_fmt, setup_file->FileName);
4408 ptr += lstrlenW(ptr) + 1;
4410 lstrcpyW(ptr, usage_fmt);
4411 ptr += lstrlenW(ptr) + 1;
4412 *ptr = '\0';
4414 driver_path = strdupW(driver_file->TargetPath);
4415 ptr = strrchrW(driver_path, '\\');
4416 if (ptr) *ptr = '\0';
4418 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4419 NULL, ODBC_INSTALL_COMPLETE, &usage))
4421 ERR("Failed to install SQL driver!\n");
4422 r = ERROR_FUNCTION_FAILED;
4425 msi_free(driver);
4426 msi_free(driver_path);
4428 return r;
4431 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4433 MSIPACKAGE *package = (MSIPACKAGE*)param;
4434 LPWSTR translator, translator_path, ptr;
4435 WCHAR outpath[MAX_PATH];
4436 MSIFILE *translator_file, *setup_file;
4437 LPCWSTR desc;
4438 DWORD len, usage;
4439 UINT r = ERROR_SUCCESS;
4441 static const WCHAR translator_fmt[] = {
4442 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4443 static const WCHAR setup_fmt[] = {
4444 'S','e','t','u','p','=','%','s',0};
4446 desc = MSI_RecordGetString(rec, 3);
4448 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4449 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4451 if (!translator_file || !setup_file)
4453 ERR("ODBC Translator entry not found!\n");
4454 return ERROR_FUNCTION_FAILED;
4457 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4458 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4459 translator = msi_alloc(len * sizeof(WCHAR));
4460 if (!translator)
4461 return ERROR_OUTOFMEMORY;
4463 ptr = translator;
4464 lstrcpyW(ptr, desc);
4465 ptr += lstrlenW(ptr) + 1;
4467 sprintfW(ptr, translator_fmt, translator_file->FileName);
4468 ptr += lstrlenW(ptr) + 1;
4470 sprintfW(ptr, setup_fmt, setup_file->FileName);
4471 ptr += lstrlenW(ptr) + 1;
4472 *ptr = '\0';
4474 translator_path = strdupW(translator_file->TargetPath);
4475 ptr = strrchrW(translator_path, '\\');
4476 if (ptr) *ptr = '\0';
4478 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4479 NULL, ODBC_INSTALL_COMPLETE, &usage))
4481 ERR("Failed to install SQL translator!\n");
4482 r = ERROR_FUNCTION_FAILED;
4485 msi_free(translator);
4486 msi_free(translator_path);
4488 return r;
4491 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4493 LPWSTR attrs;
4494 LPCWSTR desc, driver;
4495 WORD request = ODBC_ADD_SYS_DSN;
4496 INT registration;
4497 DWORD len;
4498 UINT r = ERROR_SUCCESS;
4500 static const WCHAR attrs_fmt[] = {
4501 'D','S','N','=','%','s',0 };
4503 desc = MSI_RecordGetString(rec, 3);
4504 driver = MSI_RecordGetString(rec, 4);
4505 registration = MSI_RecordGetInteger(rec, 5);
4507 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4508 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4510 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4511 attrs = msi_alloc(len * sizeof(WCHAR));
4512 if (!attrs)
4513 return ERROR_OUTOFMEMORY;
4515 sprintfW(attrs, attrs_fmt, desc);
4516 attrs[len - 1] = '\0';
4518 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4520 ERR("Failed to install SQL data source!\n");
4521 r = ERROR_FUNCTION_FAILED;
4524 msi_free(attrs);
4526 return r;
4529 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4531 UINT rc;
4532 MSIQUERY *view;
4534 static const WCHAR driver_query[] = {
4535 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4536 'O','D','B','C','D','r','i','v','e','r',0 };
4538 static const WCHAR translator_query[] = {
4539 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4540 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4542 static const WCHAR source_query[] = {
4543 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4544 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4546 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4547 if (rc != ERROR_SUCCESS)
4548 return ERROR_SUCCESS;
4550 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4551 msiobj_release(&view->hdr);
4553 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4554 if (rc != ERROR_SUCCESS)
4555 return ERROR_SUCCESS;
4557 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4558 msiobj_release(&view->hdr);
4560 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4561 if (rc != ERROR_SUCCESS)
4562 return ERROR_SUCCESS;
4564 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4565 msiobj_release(&view->hdr);
4567 return rc;
4570 #define ENV_ACT_SETALWAYS 0x1
4571 #define ENV_ACT_SETABSENT 0x2
4572 #define ENV_ACT_REMOVE 0x4
4573 #define ENV_ACT_REMOVEMATCH 0x8
4575 #define ENV_MOD_MACHINE 0x20000000
4576 #define ENV_MOD_APPEND 0x40000000
4577 #define ENV_MOD_PREFIX 0x80000000
4578 #define ENV_MOD_MASK 0xC0000000
4580 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4582 static LONG env_set_flags( LPCWSTR *name, LPWSTR *value, DWORD *flags )
4584 LPCWSTR cptr = *name;
4585 LPWSTR ptr = *value;
4587 static const WCHAR prefix[] = {'[','~',']',0};
4589 *flags = 0;
4590 while (*cptr && (*cptr == '=' || *cptr == '+' ||
4591 *cptr == '-' || *cptr == '!' || *cptr == '*'))
4593 switch (*cptr)
4595 case '=':
4596 *flags |= ENV_ACT_SETALWAYS;
4597 break;
4598 case '+':
4599 *flags |= ENV_ACT_SETABSENT;
4600 break;
4601 case '-':
4602 *flags |= ENV_ACT_REMOVE;
4603 break;
4604 case '!':
4605 *flags |= ENV_ACT_REMOVEMATCH;
4606 break;
4607 case '*':
4608 *flags |= ENV_MOD_MACHINE;
4609 break;
4610 default:
4611 ERR("Unknown Environment flag: %c\n", *cptr);
4612 return ERROR_FUNCTION_FAILED;
4615 cptr++;
4616 (*name)++;
4619 if (!*cptr)
4621 ERR("Missing environment variable\n");
4622 return ERROR_FUNCTION_FAILED;
4625 if (!strncmpW(ptr, prefix, lstrlenW(prefix)))
4627 *flags |= ENV_MOD_PREFIX;
4628 *value += lstrlenW(prefix);
4630 else
4632 ptr += lstrlenW(ptr) - lstrlenW(prefix) - 1;
4633 if (!lstrcmpW(ptr, prefix))
4635 *flags |= ENV_MOD_APPEND;
4636 *ptr = '\0';
4640 if (!*flags ||
4641 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
4642 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
4643 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
4644 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
4646 ERR("Invalid flags: %08x\n", *flags);
4647 return ERROR_FUNCTION_FAILED;
4650 return ERROR_SUCCESS;
4653 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
4655 MSIPACKAGE *package = param;
4656 LPCWSTR name, value, comp;
4657 LPWSTR data = NULL, newval = NULL;
4658 LPWSTR deformatted, ptr;
4659 DWORD flags, type, size;
4660 LONG res;
4661 HKEY env, root = HKEY_CURRENT_USER;
4663 static const WCHAR environment[] =
4664 {'S','y','s','t','e','m','\\',
4665 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4666 'C','o','n','t','r','o','l','\\',
4667 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4668 'E','n','v','i','r','o','n','m','e','n','t',0};
4669 static const WCHAR semicolon[] = {';',0};
4671 name = MSI_RecordGetString(rec, 2);
4672 value = MSI_RecordGetString(rec, 3);
4673 comp = MSI_RecordGetString(rec, 4);
4675 deformat_string(package, value, &deformatted);
4676 if (!deformatted)
4677 return ERROR_OUTOFMEMORY;
4679 res = env_set_flags(&name, &deformatted, &flags);
4680 if (res != ERROR_SUCCESS)
4681 goto done;
4683 value = deformatted;
4685 if (flags & ENV_MOD_MACHINE)
4686 root = HKEY_LOCAL_MACHINE;
4688 res = RegOpenKeyExW(root, environment, 0, KEY_ALL_ACCESS, &env);
4689 if (res != ERROR_SUCCESS)
4690 goto done;
4692 if (flags & ENV_ACT_REMOVE)
4693 FIXME("Not removing environment variable on uninstall!\n");
4695 size = 0;
4696 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
4697 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
4698 (res == ERROR_SUCCESS && type != REG_SZ))
4699 goto done;
4701 if (res != ERROR_FILE_NOT_FOUND)
4703 if (flags & ENV_ACT_SETABSENT)
4705 res = ERROR_SUCCESS;
4706 goto done;
4709 data = msi_alloc(size);
4710 if (!data)
4712 RegCloseKey(env);
4713 return ERROR_OUTOFMEMORY;
4716 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
4717 if (res != ERROR_SUCCESS)
4718 goto done;
4720 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
4722 res = RegDeleteKeyW(env, name);
4723 goto done;
4726 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
4727 newval = msi_alloc(size);
4728 ptr = newval;
4729 if (!newval)
4731 res = ERROR_OUTOFMEMORY;
4732 goto done;
4735 if (!(flags & ENV_MOD_MASK))
4736 lstrcpyW(newval, value);
4737 else
4739 if (flags & ENV_MOD_PREFIX)
4741 lstrcpyW(newval, value);
4742 lstrcatW(newval, semicolon);
4743 ptr = newval + lstrlenW(value) + 1;
4746 lstrcpyW(ptr, data);
4748 if (flags & ENV_MOD_APPEND)
4750 lstrcatW(newval, semicolon);
4751 lstrcatW(newval, value);
4755 else
4757 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
4758 newval = msi_alloc(size);
4759 if (!newval)
4761 res = ERROR_OUTOFMEMORY;
4762 goto done;
4765 lstrcpyW(newval, value);
4768 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
4769 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
4771 done:
4772 RegCloseKey(env);
4773 msi_free(deformatted);
4774 msi_free(data);
4775 msi_free(newval);
4776 return res;
4779 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
4781 UINT rc;
4782 MSIQUERY * view;
4783 static const WCHAR ExecSeqQuery[] =
4784 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4785 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
4786 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4787 if (rc != ERROR_SUCCESS)
4788 return ERROR_SUCCESS;
4790 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
4791 msiobj_release(&view->hdr);
4793 return rc;
4796 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
4797 LPCSTR action, LPCWSTR table )
4799 static const WCHAR query[] = {
4800 'S','E','L','E','C','T',' ','*',' ',
4801 'F','R','O','M',' ','`','%','s','`',0 };
4802 MSIQUERY *view = NULL;
4803 DWORD count = 0;
4804 UINT r;
4806 r = MSI_OpenQuery( package->db, &view, query, table );
4807 if (r == ERROR_SUCCESS)
4809 r = MSI_IterateRecords(view, &count, NULL, package);
4810 msiobj_release(&view->hdr);
4813 if (count)
4814 FIXME("%s -> %u ignored %s table values\n",
4815 action, count, debugstr_w(table));
4817 return ERROR_SUCCESS;
4820 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
4822 TRACE("%p\n", package);
4823 return ERROR_SUCCESS;
4826 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4828 static const WCHAR table[] =
4829 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
4830 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
4833 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
4835 static const WCHAR table[] = { 'M','o','v','e','F','i','l','e',0 };
4836 return msi_unimplemented_action_stub( package, "MoveFiles", table );
4839 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
4841 static const WCHAR table[] = { 'P','a','t','c','h',0 };
4842 return msi_unimplemented_action_stub( package, "PatchFiles", table );
4845 static UINT ACTION_BindImage( MSIPACKAGE *package )
4847 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
4848 return msi_unimplemented_action_stub( package, "BindImage", table );
4851 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
4853 static const WCHAR table[] = {
4854 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
4855 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
4858 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
4860 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
4861 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
4864 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4866 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
4867 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
4870 static UINT ACTION_StopServices( MSIPACKAGE *package )
4872 static const WCHAR table[] = {
4873 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4874 return msi_unimplemented_action_stub( package, "StopServices", table );
4877 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
4879 static const WCHAR table[] = {
4880 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4881 return msi_unimplemented_action_stub( package, "DeleteServices", table );
4883 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
4885 static const WCHAR table[] = {
4886 'P','r','o','d','u','c','t','I','D',0 };
4887 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
4890 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
4892 static const WCHAR table[] = {
4893 'E','n','v','i','r','o','n','m','e','n','t',0 };
4894 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
4897 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
4899 static const WCHAR table[] = {
4900 'M','s','i','A','s','s','e','m','b','l','y',0 };
4901 return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
4904 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
4906 static const WCHAR table[] = {
4907 'M','s','i','A','s','s','e','m','b','l','y',0 };
4908 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
4911 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
4913 static const WCHAR table[] = { 'F','o','n','t',0 };
4914 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
4917 static UINT ACTION_CCPSearch( MSIPACKAGE *package )
4919 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
4920 return msi_unimplemented_action_stub( package, "CCPSearch", table );
4923 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
4925 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
4926 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
4929 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
4931 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
4932 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
4935 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
4937 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
4938 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
4941 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
4943 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
4944 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
4947 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
4949 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
4950 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
4953 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
4955 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
4956 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
4959 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
4961 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
4962 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
4965 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
4967 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
4968 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
4971 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
4973 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
4974 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
4977 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4979 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
4980 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
4983 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
4985 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
4986 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
4989 static UINT ACTION_UnpublishFeatures( MSIPACKAGE *package )
4991 static const WCHAR table[] = { 'F','e','a','t','u','r','e','C','o','m','p','o','n','e','n','t','s',0 };
4992 return msi_unimplemented_action_stub( package, "UnpublishFeatures", table );
4995 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
4997 static const WCHAR table[] = { 'A','p','p','I','d',0 };
4998 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5001 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5003 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5004 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5007 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5009 static const WCHAR table[] = { 'M','I','M','E',0 };
5010 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5013 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5015 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5016 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5019 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5021 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5022 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5025 static const struct _actions StandardActions[] = {
5026 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5027 { szAppSearch, ACTION_AppSearch },
5028 { szBindImage, ACTION_BindImage },
5029 { szCCPSearch, ACTION_CCPSearch },
5030 { szCostFinalize, ACTION_CostFinalize },
5031 { szCostInitialize, ACTION_CostInitialize },
5032 { szCreateFolders, ACTION_CreateFolders },
5033 { szCreateShortcuts, ACTION_CreateShortcuts },
5034 { szDeleteServices, ACTION_DeleteServices },
5035 { szDisableRollback, NULL },
5036 { szDuplicateFiles, ACTION_DuplicateFiles },
5037 { szExecuteAction, ACTION_ExecuteAction },
5038 { szFileCost, ACTION_FileCost },
5039 { szFindRelatedProducts, ACTION_FindRelatedProducts },
5040 { szForceReboot, ACTION_ForceReboot },
5041 { szInstallAdminPackage, NULL },
5042 { szInstallExecute, ACTION_InstallExecute },
5043 { szInstallExecuteAgain, ACTION_InstallExecute },
5044 { szInstallFiles, ACTION_InstallFiles},
5045 { szInstallFinalize, ACTION_InstallFinalize },
5046 { szInstallInitialize, ACTION_InstallInitialize },
5047 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5048 { szInstallValidate, ACTION_InstallValidate },
5049 { szIsolateComponents, ACTION_IsolateComponents },
5050 { szLaunchConditions, ACTION_LaunchConditions },
5051 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5052 { szMoveFiles, ACTION_MoveFiles },
5053 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5054 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5055 { szInstallODBC, ACTION_InstallODBC },
5056 { szInstallServices, ACTION_InstallServices },
5057 { szPatchFiles, ACTION_PatchFiles },
5058 { szProcessComponents, ACTION_ProcessComponents },
5059 { szPublishComponents, ACTION_PublishComponents },
5060 { szPublishFeatures, ACTION_PublishFeatures },
5061 { szPublishProduct, ACTION_PublishProduct },
5062 { szRegisterClassInfo, ACTION_RegisterClassInfo },
5063 { szRegisterComPlus, ACTION_RegisterComPlus},
5064 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5065 { szRegisterFonts, ACTION_RegisterFonts },
5066 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5067 { szRegisterProduct, ACTION_RegisterProduct },
5068 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5069 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5070 { szRegisterUser, ACTION_RegisterUser },
5071 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5072 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5073 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5074 { szRemoveFiles, ACTION_RemoveFiles },
5075 { szRemoveFolders, ACTION_RemoveFolders },
5076 { szRemoveIniValues, ACTION_RemoveIniValues },
5077 { szRemoveODBC, ACTION_RemoveODBC },
5078 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5079 { szRemoveShortcuts, ACTION_RemoveShortcuts },
5080 { szResolveSource, ACTION_ResolveSource },
5081 { szRMCCPSearch, ACTION_RMCCPSearch },
5082 { szScheduleReboot, NULL },
5083 { szSelfRegModules, ACTION_SelfRegModules },
5084 { szSelfUnregModules, ACTION_SelfUnregModules },
5085 { szSetODBCFolders, NULL },
5086 { szStartServices, ACTION_StartServices },
5087 { szStopServices, ACTION_StopServices },
5088 { szUnpublishComponents, ACTION_UnpublishComponents },
5089 { szUnpublishFeatures, ACTION_UnpublishFeatures },
5090 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5091 { szUnregisterComPlus, ACTION_UnregisterComPlus },
5092 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5093 { szUnregisterFonts, ACTION_UnregisterFonts },
5094 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5095 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5096 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5097 { szValidateProductID, ACTION_ValidateProductID },
5098 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5099 { szWriteIniValues, ACTION_WriteIniValues },
5100 { szWriteRegistryValues, ACTION_WriteRegistryValues },
5101 { NULL, NULL },