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
31 #include "wine/debug.h"
40 #include "wine/unicode.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
51 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
52 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
53 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
54 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action, UINT* rc, BOOL force);
57 * consts and values used
59 static const WCHAR c_colon[] = {'C',':','\\',0};
61 static const WCHAR szCreateFolders[] =
62 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
63 static const WCHAR szCostFinalize[] =
64 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
65 const WCHAR szInstallFiles[] =
66 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
67 const WCHAR szDuplicateFiles[] =
68 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
69 static const WCHAR szWriteRegistryValues[] =
70 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
71 'V','a','l','u','e','s',0};
72 static const WCHAR szCostInitialize[] =
73 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
74 static const WCHAR szFileCost[] =
75 {'F','i','l','e','C','o','s','t',0};
76 static const WCHAR szInstallInitialize[] =
77 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
78 static const WCHAR szInstallValidate[] =
79 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
80 static const WCHAR szLaunchConditions[] =
81 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
82 static const WCHAR szProcessComponents[] =
83 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
84 static const WCHAR szRegisterTypeLibraries[] =
85 {'R','e','g','i','s','t','e','r','T','y','p','e',
86 'L','i','b','r','a','r','i','e','s',0};
87 const WCHAR szRegisterClassInfo[] =
88 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
89 const WCHAR szRegisterProgIdInfo[] =
90 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
91 static const WCHAR szCreateShortcuts[] =
92 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
93 static const WCHAR szPublishProduct[] =
94 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
95 static const WCHAR szWriteIniValues[] =
96 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
97 static const WCHAR szSelfRegModules[] =
98 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
99 static const WCHAR szPublishFeatures[] =
100 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
101 static const WCHAR szRegisterProduct[] =
102 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
103 static const WCHAR szInstallExecute[] =
104 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
105 static const WCHAR szInstallExecuteAgain[] =
106 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
107 'A','g','a','i','n',0};
108 static const WCHAR szInstallFinalize[] =
109 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
110 static const WCHAR szForceReboot[] =
111 {'F','o','r','c','e','R','e','b','o','o','t',0};
112 static const WCHAR szResolveSource[] =
113 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
114 static const WCHAR szAppSearch[] =
115 {'A','p','p','S','e','a','r','c','h',0};
116 static const WCHAR szAllocateRegistrySpace[] =
117 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
118 'S','p','a','c','e',0};
119 static const WCHAR szBindImage[] =
120 {'B','i','n','d','I','m','a','g','e',0};
121 static const WCHAR szCCPSearch[] =
122 {'C','C','P','S','e','a','r','c','h',0};
123 static const WCHAR szDeleteServices[] =
124 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
125 static const WCHAR szDisableRollback[] =
126 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
127 static const WCHAR szExecuteAction[] =
128 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
129 const WCHAR szFindRelatedProducts[] =
130 {'F','i','n','d','R','e','l','a','t','e','d',
131 'P','r','o','d','u','c','t','s',0};
132 static const WCHAR szInstallAdminPackage[] =
133 {'I','n','s','t','a','l','l','A','d','m','i','n',
134 'P','a','c','k','a','g','e',0};
135 static const WCHAR szInstallSFPCatalogFile[] =
136 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
138 static const WCHAR szIsolateComponents[] =
139 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
140 const WCHAR szMigrateFeatureStates[] =
141 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
142 'S','t','a','t','e','s',0};
143 const WCHAR szMoveFiles[] =
144 {'M','o','v','e','F','i','l','e','s',0};
145 static const WCHAR szMsiPublishAssemblies[] =
146 {'M','s','i','P','u','b','l','i','s','h',
147 'A','s','s','e','m','b','l','i','e','s',0};
148 static const WCHAR szMsiUnpublishAssemblies[] =
149 {'M','s','i','U','n','p','u','b','l','i','s','h',
150 'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szInstallODBC[] =
152 {'I','n','s','t','a','l','l','O','D','B','C',0};
153 static const WCHAR szInstallServices[] =
154 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
155 const WCHAR szPatchFiles[] =
156 {'P','a','t','c','h','F','i','l','e','s',0};
157 static const WCHAR szPublishComponents[] =
158 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
159 static const WCHAR szRegisterComPlus[] =
160 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
161 const WCHAR szRegisterExtensionInfo[] =
162 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
164 static const WCHAR szRegisterFonts[] =
165 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
166 const WCHAR szRegisterMIMEInfo[] =
167 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
168 static const WCHAR szRegisterUser[] =
169 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
170 const WCHAR szRemoveDuplicateFiles[] =
171 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
172 'F','i','l','e','s',0};
173 static const WCHAR szRemoveEnvironmentStrings[] =
174 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
175 'S','t','r','i','n','g','s',0};
176 const WCHAR szRemoveExistingProducts[] =
177 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
178 'P','r','o','d','u','c','t','s',0};
179 const WCHAR szRemoveFiles[] =
180 {'R','e','m','o','v','e','F','i','l','e','s',0};
181 static const WCHAR szRemoveFolders[] =
182 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
183 static const WCHAR szRemoveIniValues[] =
184 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
185 static const WCHAR szRemoveODBC[] =
186 {'R','e','m','o','v','e','O','D','B','C',0};
187 static const WCHAR szRemoveRegistryValues[] =
188 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
189 'V','a','l','u','e','s',0};
190 static const WCHAR szRemoveShortcuts[] =
191 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
192 static const WCHAR szRMCCPSearch[] =
193 {'R','M','C','C','P','S','e','a','r','c','h',0};
194 static const WCHAR szScheduleReboot[] =
195 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
196 static const WCHAR szSelfUnregModules[] =
197 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
198 static const WCHAR szSetODBCFolders[] =
199 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
200 static const WCHAR szStartServices[] =
201 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
202 static const WCHAR szStopServices[] =
203 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
204 static const WCHAR szUnpublishComponents[] =
205 {'U','n','p','u','b','l','i','s','h',
206 'C','o','m','p','o','n','e','n','t','s',0};
207 static const WCHAR szUnpublishFeatures[] =
208 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
209 const WCHAR szUnregisterClassInfo[] =
210 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
212 static const WCHAR szUnregisterComPlus[] =
213 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
214 const WCHAR szUnregisterExtensionInfo[] =
215 {'U','n','r','e','g','i','s','t','e','r',
216 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
217 static const WCHAR szUnregisterFonts[] =
218 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
219 const WCHAR szUnregisterMIMEInfo[] =
220 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
221 const WCHAR szUnregisterProgIdInfo[] =
222 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
224 static const WCHAR szUnregisterTypeLibraries[] =
225 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
226 'L','i','b','r','a','r','i','e','s',0};
227 static const WCHAR szValidateProductID[] =
228 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
229 static const WCHAR szWriteEnvironmentStrings[] =
230 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
231 'S','t','r','i','n','g','s',0};
233 /* action handlers */
234 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
238 STANDARDACTIONHANDLER handler;
242 /********************************************************
244 ********************************************************/
246 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
248 static const WCHAR Query_t[] =
249 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
250 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
251 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
252 ' ','\'','%','s','\'',0};
255 row = MSI_QueryGetRecord( package->db, Query_t, action );
258 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
259 msiobj_release(&row->hdr);
262 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
266 static const WCHAR template_s[]=
267 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
269 static const WCHAR template_e[]=
270 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
271 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
273 static const WCHAR format[] =
274 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
278 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
280 sprintfW(message,template_s,timet,action);
282 sprintfW(message,template_e,timet,action,rc);
284 row = MSI_CreateRecord(1);
285 MSI_RecordSetStringW(row,1,message);
287 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
288 msiobj_release(&row->hdr);
291 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
297 LPWSTR prop = NULL, val = NULL;
300 return ERROR_SUCCESS;
312 TRACE("Looking at %s\n",debugstr_w(ptr));
314 ptr2 = strchrW(ptr,'=');
317 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
324 prop = msi_alloc((len+1)*sizeof(WCHAR));
325 memcpy(prop,ptr,len*sizeof(WCHAR));
335 while (*ptr && (quote || (!quote && *ptr!=' ')))
348 val = msi_alloc((len+1)*sizeof(WCHAR));
349 memcpy(val,ptr2,len*sizeof(WCHAR));
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);
362 return ERROR_SUCCESS;
366 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
369 LPWSTR p, *ret = NULL;
375 /* count the number of substrings */
376 for ( pc = str, count = 0; pc; count++ )
378 pc = strchrW( pc, sep );
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) );
389 /* copy the string and set the pointers */
390 p = (LPWSTR) &ret[count+1];
392 for( count = 0; (ret[count] = p); count++ )
394 p = strchrW( p, sep );
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;
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 ) )
416 ret = ERROR_FUNCTION_FAILED;
418 msi_free( patch_product );
419 msi_free( prod_code );
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;
431 TRACE("%p %s\n", package, debugstr_w(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 );
442 ret = msi_check_transform_applicable( package, stg );
443 if (ret == ERROR_SUCCESS)
444 msi_table_apply_transform( package->db, stg );
446 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
447 IStorage_Release( stg );
450 ERR("failed to open substorage %s\n", debugstr_w(name));
452 return ERROR_SUCCESS;
455 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
457 static const WCHAR szProdCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
458 LPWSTR guid_list, *guids, product_code;
459 UINT i, ret = ERROR_FUNCTION_FAILED;
461 product_code = msi_dup_property( package, szProdCode );
464 /* FIXME: the property ProductCode should be written into the DB somewhere */
465 ERR("no product code to check\n");
466 return ERROR_SUCCESS;
469 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
470 guids = msi_split_string( guid_list, ';' );
471 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
473 if (!lstrcmpW( guids[i], product_code ))
477 msi_free( guid_list );
478 msi_free( product_code );
483 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
486 LPWSTR str, *substorage;
487 UINT i, r = ERROR_SUCCESS;
489 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
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 );
503 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
505 msiobj_release( &si->hdr );
510 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
512 MSIDATABASE *patch_db = NULL;
515 TRACE("%p %s\n", package, debugstr_w( file ) );
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 ) );
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] );
558 msi_free( patch_list );
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] );
578 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
582 msi_free( xform_list );
587 static BOOL ui_sequence_exists( MSIPACKAGE *package )
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);
611 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
614 LPWSTR source, check;
617 static const WCHAR szOriginalDatabase[] =
618 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
620 db = msi_dup_property( package, szOriginalDatabase );
622 return ERROR_OUTOFMEMORY;
624 p = strrchrW( db, '\\' );
627 p = strrchrW( db, '/' );
631 return ERROR_SUCCESS;
636 source = msi_alloc( len * sizeof(WCHAR) );
637 lstrcpynW( source, db, len );
639 check = msi_dup_property( package, cszSourceDir );
640 if (!check || replace)
641 MSI_SetPropertyW( package, cszSourceDir, source );
645 check = msi_dup_property( package, cszSOURCEDIR );
646 if (!check || replace)
647 MSI_SetPropertyW( package, cszSOURCEDIR, source );
653 return ERROR_SUCCESS;
656 static UINT msi_set_context(MSIPACKAGE *package)
663 static const WCHAR szOne[] = {'1',0};
664 static const WCHAR szAllUsers[] = {'A','L','L','U','S','E','R','S',0};
666 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
668 r = MSI_GetPropertyW(package, szAllUsers, val, &sz);
669 if (r == ERROR_SUCCESS)
672 if (num == 1 || num == 2)
673 package->Context = MSIINSTALLCONTEXT_MACHINE;
676 MSI_SetPropertyW(package, szAllUsers, szOne);
677 return ERROR_SUCCESS;
680 /****************************************************
681 * TOP level entry points
682 *****************************************************/
684 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
685 LPCWSTR szCommandLine )
688 BOOL ui = FALSE, ui_exists;
689 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
690 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
691 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
693 MSI_SetPropertyW(package, szAction, szInstall);
695 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
697 package->script->InWhatSequence = SEQUENCE_INSTALL;
704 dir = strdupW(szPackagePath);
705 p = strrchrW(dir, '\\');
709 file = szPackagePath + (p - dir);
714 dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
715 GetCurrentDirectoryW(MAX_PATH, dir);
716 lstrcatW(dir, cszbs);
717 file = szPackagePath;
720 msi_free( package->PackagePath );
721 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
722 if (!package->PackagePath)
725 return ERROR_OUTOFMEMORY;
728 lstrcpyW(package->PackagePath, dir);
729 lstrcatW(package->PackagePath, file);
732 msi_set_sourcedir_props(package, FALSE);
735 msi_parse_command_line( package, szCommandLine, FALSE );
737 msi_apply_transforms( package );
738 msi_apply_patches( package );
740 /* properties may have been added by a transform */
741 msi_clone_properties( package );
742 msi_set_context( package );
744 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
746 package->script->InWhatSequence |= SEQUENCE_UI;
747 rc = ACTION_ProcessUISequence(package);
749 ui_exists = ui_sequence_exists(package);
750 if (rc == ERROR_SUCCESS || !ui_exists)
752 package->script->InWhatSequence |= SEQUENCE_EXEC;
753 rc = ACTION_ProcessExecSequence(package,ui_exists);
757 rc = ACTION_ProcessExecSequence(package,FALSE);
759 package->script->CurrentlyScripting= FALSE;
761 /* process the ending type action */
762 if (rc == ERROR_SUCCESS)
763 ACTION_PerformActionSequence(package,-1,ui);
764 else if (rc == ERROR_INSTALL_USEREXIT)
765 ACTION_PerformActionSequence(package,-2,ui);
766 else if (rc == ERROR_INSTALL_SUSPEND)
767 ACTION_PerformActionSequence(package,-4,ui);
769 ACTION_PerformActionSequence(package,-3,ui);
771 /* finish up running custom actions */
772 ACTION_FinishCustomActions(package);
777 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
779 UINT rc = ERROR_SUCCESS;
781 static const WCHAR ExecSeqQuery[] =
782 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
783 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
784 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
785 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
787 static const WCHAR UISeqQuery[] =
788 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
789 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
790 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
791 ' ', '=',' ','%','i',0};
794 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
796 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
800 LPCWSTR action, cond;
802 TRACE("Running the actions\n");
804 /* check conditions */
805 cond = MSI_RecordGetString(row,2);
807 /* this is a hack to skip errors in the condition code */
808 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
811 action = MSI_RecordGetString(row,1);
814 ERR("failed to fetch action\n");
815 rc = ERROR_FUNCTION_FAILED;
820 rc = ACTION_PerformUIAction(package,action,-1);
822 rc = ACTION_PerformAction(package,action,-1,FALSE);
824 msiobj_release(&row->hdr);
835 } iterate_action_param;
837 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
839 iterate_action_param *iap= (iterate_action_param*)param;
841 LPCWSTR cond, action;
843 action = MSI_RecordGetString(row,1);
846 ERR("Error is retrieving action name\n");
847 return ERROR_FUNCTION_FAILED;
850 /* check conditions */
851 cond = MSI_RecordGetString(row,2);
853 /* this is a hack to skip errors in the condition code */
854 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
856 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
857 return ERROR_SUCCESS;
861 rc = ACTION_PerformUIAction(iap->package,action,-1);
863 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
865 msi_dialog_check_messages( NULL );
867 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
868 rc = iap->package->CurrentInstallState;
870 if (rc == ERROR_FUNCTION_NOT_CALLED)
873 if (rc != ERROR_SUCCESS)
874 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
879 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
883 static const WCHAR query[] =
884 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
886 ' ','W','H','E','R','E',' ',
887 '`','S','e','q','u','e','n','c','e','`',' ',
888 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
889 '`','S','e','q','u','e','n','c','e','`',0};
890 iterate_action_param iap;
893 * FIXME: probably should be checking UILevel in the
894 * ACTION_PerformUIAction/ACTION_PerformAction
895 * rather than saving the UI level here. Those
896 * two functions can be merged too.
898 iap.package = package;
901 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
903 r = MSI_OpenQuery( package->db, &view, query, szTable );
904 if (r == ERROR_SUCCESS)
906 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
907 msiobj_release(&view->hdr);
913 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
917 static const WCHAR ExecSeqQuery[] =
918 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
919 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
920 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
921 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
922 'O','R','D','E','R',' ', 'B','Y',' ',
923 '`','S','e','q','u','e','n','c','e','`',0 };
925 static const WCHAR IVQuery[] =
926 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
927 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
928 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
929 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
930 ' ','\'', 'I','n','s','t','a','l','l',
931 'V','a','l','i','d','a','t','e','\'', 0};
933 iterate_action_param iap;
935 iap.package = package;
938 if (package->script->ExecuteSequenceRun)
940 TRACE("Execute Sequence already Run\n");
941 return ERROR_SUCCESS;
944 package->script->ExecuteSequenceRun = TRUE;
946 /* get the sequence number */
949 row = MSI_QueryGetRecord(package->db, IVQuery);
951 return ERROR_FUNCTION_FAILED;
952 seq = MSI_RecordGetInteger(row,1);
953 msiobj_release(&row->hdr);
956 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
957 if (rc == ERROR_SUCCESS)
959 TRACE("Running the actions\n");
961 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
962 msiobj_release(&view->hdr);
968 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
972 static const WCHAR ExecSeqQuery [] =
973 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
974 '`','I','n','s','t','a','l','l',
975 'U','I','S','e','q','u','e','n','c','e','`',
976 ' ','W','H','E','R','E',' ',
977 '`','S','e','q','u','e','n','c','e','`',' ',
978 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
979 '`','S','e','q','u','e','n','c','e','`',0};
980 iterate_action_param iap;
982 iap.package = package;
985 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
987 if (rc == ERROR_SUCCESS)
989 TRACE("Running the actions\n");
991 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
992 msiobj_release(&view->hdr);
998 /********************************************************
999 * ACTION helper functions and functions that perform the actions
1000 *******************************************************/
1001 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
1002 UINT* rc, UINT script, BOOL force )
1007 arc = ACTION_CustomAction(package, action, script, force);
1009 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1018 * A lot of actions are really important even if they don't do anything
1019 * explicit... Lots of properties are set at the beginning of the installation
1020 * CostFinalize does a bunch of work to translate the directories and such
1022 * But until I get write access to the database that is hard, so I am going to
1023 * hack it to see if I can get something to run.
1025 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1027 UINT rc = ERROR_SUCCESS;
1030 TRACE("Performing action (%s)\n",debugstr_w(action));
1032 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1035 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1039 WARN("unhandled msi action %s\n",debugstr_w(action));
1040 rc = ERROR_FUNCTION_NOT_CALLED;
1046 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1048 UINT rc = ERROR_SUCCESS;
1049 BOOL handled = FALSE;
1051 TRACE("Performing action (%s)\n",debugstr_w(action));
1053 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1056 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1058 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1063 WARN("unhandled msi action %s\n",debugstr_w(action));
1064 rc = ERROR_FUNCTION_NOT_CALLED;
1072 * Actual Action Handlers
1075 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1077 MSIPACKAGE *package = (MSIPACKAGE*)param;
1083 dir = MSI_RecordGetString(row,1);
1086 ERR("Unable to get folder id\n");
1087 return ERROR_SUCCESS;
1090 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1093 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1094 return ERROR_SUCCESS;
1097 TRACE("Folder is %s\n",debugstr_w(full_path));
1100 uirow = MSI_CreateRecord(1);
1101 MSI_RecordSetStringW(uirow,1,full_path);
1102 ui_actiondata(package,szCreateFolders,uirow);
1103 msiobj_release( &uirow->hdr );
1105 if (folder->State == 0)
1106 create_full_pathW(full_path);
1110 msi_free(full_path);
1111 return ERROR_SUCCESS;
1114 /* FIXME: probably should merge this with the above function */
1115 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1117 UINT rc = ERROR_SUCCESS;
1119 LPWSTR install_path;
1121 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1123 return ERROR_FUNCTION_FAILED;
1125 /* create the path */
1126 if (folder->State == 0)
1128 create_full_pathW(install_path);
1131 msi_free(install_path);
1136 UINT msi_create_component_directories( MSIPACKAGE *package )
1140 /* create all the folders required by the components are going to install */
1141 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1143 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1145 msi_create_directory( package, comp->Directory );
1148 return ERROR_SUCCESS;
1152 * Also we cannot enable/disable components either, so for now I am just going
1153 * to do all the directories for all the components.
1155 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1157 static const WCHAR ExecSeqQuery[] =
1158 {'S','E','L','E','C','T',' ',
1159 '`','D','i','r','e','c','t','o','r','y','_','`',
1160 ' ','F','R','O','M',' ',
1161 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1165 /* create all the empty folders specified in the CreateFolder table */
1166 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1167 if (rc != ERROR_SUCCESS)
1168 return ERROR_SUCCESS;
1170 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1171 msiobj_release(&view->hdr);
1173 msi_create_component_directories( package );
1178 static UINT load_component( MSIRECORD *row, LPVOID param )
1180 MSIPACKAGE *package = param;
1183 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1185 return ERROR_FUNCTION_FAILED;
1187 list_add_tail( &package->components, &comp->entry );
1189 /* fill in the data */
1190 comp->Component = msi_dup_record_field( row, 1 );
1192 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1194 comp->ComponentId = msi_dup_record_field( row, 2 );
1195 comp->Directory = msi_dup_record_field( row, 3 );
1196 comp->Attributes = MSI_RecordGetInteger(row,4);
1197 comp->Condition = msi_dup_record_field( row, 5 );
1198 comp->KeyPath = msi_dup_record_field( row, 6 );
1200 comp->Installed = INSTALLSTATE_UNKNOWN;
1201 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1203 return ERROR_SUCCESS;
1206 static UINT load_all_components( MSIPACKAGE *package )
1208 static const WCHAR query[] = {
1209 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1210 '`','C','o','m','p','o','n','e','n','t','`',0 };
1214 if (!list_empty(&package->components))
1215 return ERROR_SUCCESS;
1217 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1218 if (r != ERROR_SUCCESS)
1221 r = MSI_IterateRecords(view, NULL, load_component, package);
1222 msiobj_release(&view->hdr);
1227 MSIPACKAGE *package;
1228 MSIFEATURE *feature;
1231 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1235 cl = msi_alloc( sizeof (*cl) );
1237 return ERROR_NOT_ENOUGH_MEMORY;
1238 cl->component = comp;
1239 list_add_tail( &feature->Components, &cl->entry );
1241 return ERROR_SUCCESS;
1244 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1248 fl = msi_alloc( sizeof(*fl) );
1250 return ERROR_NOT_ENOUGH_MEMORY;
1251 fl->feature = child;
1252 list_add_tail( &parent->Children, &fl->entry );
1254 return ERROR_SUCCESS;
1257 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1259 _ilfs* ilfs= (_ilfs*)param;
1263 component = MSI_RecordGetString(row,1);
1265 /* check to see if the component is already loaded */
1266 comp = get_loaded_component( ilfs->package, component );
1269 ERR("unknown component %s\n", debugstr_w(component));
1270 return ERROR_FUNCTION_FAILED;
1273 add_feature_component( ilfs->feature, comp );
1274 comp->Enabled = TRUE;
1276 return ERROR_SUCCESS;
1279 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1281 MSIFEATURE *feature;
1286 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1288 if ( !lstrcmpW( feature->Feature, name ) )
1295 static UINT load_feature(MSIRECORD * row, LPVOID param)
1297 MSIPACKAGE* package = (MSIPACKAGE*)param;
1298 MSIFEATURE* feature;
1299 static const WCHAR Query1[] =
1300 {'S','E','L','E','C','T',' ',
1301 '`','C','o','m','p','o','n','e','n','t','_','`',
1302 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1303 'C','o','m','p','o','n','e','n','t','s','`',' ',
1304 'W','H','E','R','E',' ',
1305 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1310 /* fill in the data */
1312 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1314 return ERROR_NOT_ENOUGH_MEMORY;
1316 list_init( &feature->Children );
1317 list_init( &feature->Components );
1319 feature->Feature = msi_dup_record_field( row, 1 );
1321 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1323 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1324 feature->Title = msi_dup_record_field( row, 3 );
1325 feature->Description = msi_dup_record_field( row, 4 );
1327 if (!MSI_RecordIsNull(row,5))
1328 feature->Display = MSI_RecordGetInteger(row,5);
1330 feature->Level= MSI_RecordGetInteger(row,6);
1331 feature->Directory = msi_dup_record_field( row, 7 );
1332 feature->Attributes = MSI_RecordGetInteger(row,8);
1334 feature->Installed = INSTALLSTATE_UNKNOWN;
1335 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1337 list_add_tail( &package->features, &feature->entry );
1339 /* load feature components */
1341 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1342 if (rc != ERROR_SUCCESS)
1343 return ERROR_SUCCESS;
1345 ilfs.package = package;
1346 ilfs.feature = feature;
1348 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1349 msiobj_release(&view->hdr);
1351 return ERROR_SUCCESS;
1354 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1356 MSIPACKAGE* package = (MSIPACKAGE*)param;
1357 MSIFEATURE *parent, *child;
1359 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1361 return ERROR_FUNCTION_FAILED;
1363 if (!child->Feature_Parent)
1364 return ERROR_SUCCESS;
1366 parent = find_feature_by_name( package, child->Feature_Parent );
1368 return ERROR_FUNCTION_FAILED;
1370 add_feature_child( parent, child );
1371 return ERROR_SUCCESS;
1374 static UINT load_all_features( MSIPACKAGE *package )
1376 static const WCHAR query[] = {
1377 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1378 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1379 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1383 if (!list_empty(&package->features))
1384 return ERROR_SUCCESS;
1386 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1387 if (r != ERROR_SUCCESS)
1390 r = MSI_IterateRecords( view, NULL, load_feature, package );
1391 if (r != ERROR_SUCCESS)
1394 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1395 msiobj_release( &view->hdr );
1400 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1411 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1413 static const WCHAR query[] = {
1414 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1415 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1416 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1417 MSIQUERY *view = NULL;
1418 MSIRECORD *row = NULL;
1421 TRACE("%s\n", debugstr_w(file->File));
1423 r = MSI_OpenQuery(package->db, &view, query, file->File);
1424 if (r != ERROR_SUCCESS)
1427 r = MSI_ViewExecute(view, NULL);
1428 if (r != ERROR_SUCCESS)
1431 r = MSI_ViewFetch(view, &row);
1432 if (r != ERROR_SUCCESS)
1435 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1436 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1437 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1438 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1439 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1442 if (view) msiobj_release(&view->hdr);
1443 if (row) msiobj_release(&row->hdr);
1447 static UINT load_file(MSIRECORD *row, LPVOID param)
1449 MSIPACKAGE* package = (MSIPACKAGE*)param;
1453 /* fill in the data */
1455 file = msi_alloc_zero( sizeof (MSIFILE) );
1457 return ERROR_NOT_ENOUGH_MEMORY;
1459 file->File = msi_dup_record_field( row, 1 );
1461 component = MSI_RecordGetString( row, 2 );
1462 file->Component = get_loaded_component( package, component );
1464 if (!file->Component)
1466 WARN("Component not found: %s\n", debugstr_w(component));
1467 msi_free(file->File);
1469 return ERROR_SUCCESS;
1472 file->FileName = msi_dup_record_field( row, 3 );
1473 reduce_to_longfilename( file->FileName );
1475 file->ShortName = msi_dup_record_field( row, 3 );
1476 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1478 file->FileSize = MSI_RecordGetInteger( row, 4 );
1479 file->Version = msi_dup_record_field( row, 5 );
1480 file->Language = msi_dup_record_field( row, 6 );
1481 file->Attributes = MSI_RecordGetInteger( row, 7 );
1482 file->Sequence = MSI_RecordGetInteger( row, 8 );
1484 file->state = msifs_invalid;
1486 /* if the compressed bits are not set in the file attributes,
1487 * then read the information from the package word count property
1489 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1491 file->IsCompressed = FALSE;
1493 else if (file->Attributes &
1494 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1496 file->IsCompressed = TRUE;
1498 else if (file->Attributes & msidbFileAttributesNoncompressed)
1500 file->IsCompressed = FALSE;
1504 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1507 load_file_hash(package, file);
1509 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1511 list_add_tail( &package->files, &file->entry );
1513 return ERROR_SUCCESS;
1516 static UINT load_all_files(MSIPACKAGE *package)
1520 static const WCHAR Query[] =
1521 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1522 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1523 '`','S','e','q','u','e','n','c','e','`', 0};
1525 if (!list_empty(&package->files))
1526 return ERROR_SUCCESS;
1528 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1529 if (rc != ERROR_SUCCESS)
1530 return ERROR_SUCCESS;
1532 rc = MSI_IterateRecords(view, NULL, load_file, package);
1533 msiobj_release(&view->hdr);
1535 return ERROR_SUCCESS;
1538 static UINT load_folder( MSIRECORD *row, LPVOID param )
1540 MSIPACKAGE *package = param;
1541 static const WCHAR szDot[] = { '.',0 };
1542 static WCHAR szEmpty[] = { 0 };
1543 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1546 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1548 return ERROR_NOT_ENOUGH_MEMORY;
1550 folder->Directory = msi_dup_record_field( row, 1 );
1552 TRACE("%s\n", debugstr_w(folder->Directory));
1554 p = msi_dup_record_field(row, 3);
1556 /* split src and target dir */
1558 src_short = folder_split_path( p, ':' );
1560 /* split the long and short paths */
1561 tgt_long = folder_split_path( tgt_short, '|' );
1562 src_long = folder_split_path( src_short, '|' );
1564 /* check for no-op dirs */
1565 if (!lstrcmpW(szDot, tgt_short))
1566 tgt_short = szEmpty;
1567 if (!lstrcmpW(szDot, src_short))
1568 src_short = szEmpty;
1571 tgt_long = tgt_short;
1574 src_short = tgt_short;
1575 src_long = tgt_long;
1579 src_long = src_short;
1581 /* FIXME: use the target short path too */
1582 folder->TargetDefault = strdupW(tgt_long);
1583 folder->SourceShortPath = strdupW(src_short);
1584 folder->SourceLongPath = strdupW(src_long);
1587 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1588 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1589 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1591 folder->Parent = msi_dup_record_field( row, 2 );
1593 folder->Property = msi_dup_property( package, folder->Directory );
1595 list_add_tail( &package->folders, &folder->entry );
1597 TRACE("returning %p\n", folder);
1599 return ERROR_SUCCESS;
1602 static UINT load_all_folders( MSIPACKAGE *package )
1604 static const WCHAR query[] = {
1605 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1606 '`','D','i','r','e','c','t','o','r','y','`',0 };
1610 if (!list_empty(&package->folders))
1611 return ERROR_SUCCESS;
1613 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1614 if (r != ERROR_SUCCESS)
1617 r = MSI_IterateRecords(view, NULL, load_folder, package);
1618 msiobj_release(&view->hdr);
1623 * I am not doing any of the costing functionality yet.
1624 * Mostly looking at doing the Component and Feature loading
1626 * The native MSI does A LOT of modification to tables here. Mostly adding
1627 * a lot of temporary columns to the Feature and Component tables.
1629 * note: Native msi also tracks the short filename. But I am only going to
1630 * track the long ones. Also looking at this directory table
1631 * it appears that the directory table does not get the parents
1632 * resolved base on property only based on their entries in the
1635 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1637 static const WCHAR szCosting[] =
1638 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1639 static const WCHAR szZero[] = { '0', 0 };
1641 MSI_SetPropertyW(package, szCosting, szZero);
1642 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1644 load_all_folders( package );
1645 load_all_components( package );
1646 load_all_features( package );
1647 load_all_files( package );
1649 return ERROR_SUCCESS;
1652 static UINT execute_script(MSIPACKAGE *package, UINT script )
1655 UINT rc = ERROR_SUCCESS;
1657 TRACE("Executing Script %i\n",script);
1659 if (!package->script)
1661 ERR("no script!\n");
1662 return ERROR_FUNCTION_FAILED;
1665 for (i = 0; i < package->script->ActionCount[script]; i++)
1668 action = package->script->Actions[script][i];
1669 ui_actionstart(package, action);
1670 TRACE("Executing Action (%s)\n",debugstr_w(action));
1671 rc = ACTION_PerformAction(package, action, script, TRUE);
1672 if (rc != ERROR_SUCCESS)
1675 msi_free_action_script(package, script);
1679 static UINT ACTION_FileCost(MSIPACKAGE *package)
1681 return ERROR_SUCCESS;
1684 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1690 state = MsiQueryProductStateW(package->ProductCode);
1692 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1694 if (!comp->ComponentId)
1697 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1698 comp->Installed = INSTALLSTATE_ABSENT;
1701 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1702 package->Context, comp->ComponentId,
1704 if (r != ERROR_SUCCESS)
1705 comp->Installed = INSTALLSTATE_ABSENT;
1710 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1712 MSIFEATURE *feature;
1715 state = MsiQueryProductStateW(package->ProductCode);
1717 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1719 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1720 feature->Installed = INSTALLSTATE_ABSENT;
1723 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1729 static BOOL process_state_property(MSIPACKAGE* package, int level,
1730 LPCWSTR property, INSTALLSTATE state)
1732 static const WCHAR all[]={'A','L','L',0};
1733 static const WCHAR remove[] = {'R','E','M','O','V','E',0};
1735 MSIFEATURE *feature;
1737 override = msi_dup_property( package, property );
1741 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1743 if (lstrcmpW(property, remove) &&
1744 (feature->Level <= 0 || feature->Level > level))
1747 if (strcmpiW(override,all)==0)
1748 msi_feature_set_state(package, feature, state);
1751 LPWSTR ptr = override;
1752 LPWSTR ptr2 = strchrW(override,',');
1756 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1757 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1759 msi_feature_set_state(package, feature, state);
1765 ptr2 = strchrW(ptr,',');
1777 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1780 static const WCHAR szlevel[] =
1781 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1782 static const WCHAR szAddLocal[] =
1783 {'A','D','D','L','O','C','A','L',0};
1784 static const WCHAR szAddSource[] =
1785 {'A','D','D','S','O','U','R','C','E',0};
1786 static const WCHAR szRemove[] =
1787 {'R','E','M','O','V','E',0};
1788 static const WCHAR szReinstall[] =
1789 {'R','E','I','N','S','T','A','L','L',0};
1790 BOOL override = FALSE;
1791 MSICOMPONENT* component;
1792 MSIFEATURE *feature;
1795 /* I do not know if this is where it should happen.. but */
1797 TRACE("Checking Install Level\n");
1799 level = msi_get_property_int(package, szlevel, 1);
1801 /* ok here is the _real_ rub
1802 * all these activation/deactivation things happen in order and things
1803 * later on the list override things earlier on the list.
1804 * 1) INSTALLLEVEL processing
1814 * 11) FILEADDDEFAULT
1816 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1817 * REMOVE are the big ones, since we don't handle administrative installs
1820 override |= process_state_property(package, level, szAddLocal, INSTALLSTATE_LOCAL);
1821 override |= process_state_property(package, level, szRemove, INSTALLSTATE_ABSENT);
1822 override |= process_state_property(package, level, szAddSource, INSTALLSTATE_SOURCE);
1823 override |= process_state_property(package, level, szReinstall, INSTALLSTATE_LOCAL);
1827 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1829 BOOL feature_state = ((feature->Level > 0) &&
1830 (feature->Level <= level));
1832 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1834 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1835 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1836 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1837 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1839 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1843 /* disable child features of unselected parent features */
1844 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1848 if (feature->Level > 0 && feature->Level <= level)
1851 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1852 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1857 /* set the Preselected Property */
1858 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1859 static const WCHAR szOne[] = { '1', 0 };
1861 MSI_SetPropertyW(package,szPreselected,szOne);
1865 * now we want to enable or disable components base on feature
1868 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1872 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1873 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1875 if (!feature->Level)
1878 /* features with components that have compressed files are made local */
1879 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1881 if (cl->component->Enabled &&
1882 cl->component->ForceLocalState &&
1883 feature->Action == INSTALLSTATE_SOURCE)
1885 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1890 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1892 component = cl->component;
1894 if (!component->Enabled)
1897 switch (feature->Action)
1899 case INSTALLSTATE_ABSENT:
1900 component->anyAbsent = 1;
1902 case INSTALLSTATE_ADVERTISED:
1903 component->hasAdvertiseFeature = 1;
1905 case INSTALLSTATE_SOURCE:
1906 component->hasSourceFeature = 1;
1908 case INSTALLSTATE_LOCAL:
1909 component->hasLocalFeature = 1;
1911 case INSTALLSTATE_DEFAULT:
1912 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1913 component->hasAdvertiseFeature = 1;
1914 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1915 component->hasSourceFeature = 1;
1917 component->hasLocalFeature = 1;
1925 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1927 /* if the component isn't enabled, leave it alone */
1928 if (!component->Enabled)
1931 /* check if it's local or source */
1932 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1933 (component->hasLocalFeature || component->hasSourceFeature))
1935 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1936 !component->ForceLocalState)
1937 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1939 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1943 /* if any feature is local, the component must be local too */
1944 if (component->hasLocalFeature)
1946 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1950 if (component->hasSourceFeature)
1952 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1956 if (component->hasAdvertiseFeature)
1958 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1962 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1963 if (component->anyAbsent)
1964 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1967 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1969 if (component->Action == INSTALLSTATE_DEFAULT)
1971 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1972 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1975 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1976 debugstr_w(component->Component), component->Installed, component->Action);
1980 return ERROR_SUCCESS;
1983 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1985 MSIPACKAGE *package = (MSIPACKAGE*)param;
1990 name = MSI_RecordGetString(row,1);
1992 f = get_loaded_folder(package, name);
1993 if (!f) return ERROR_SUCCESS;
1995 /* reset the ResolvedTarget */
1996 msi_free(f->ResolvedTarget);
1997 f->ResolvedTarget = NULL;
1999 /* This helper function now does ALL the work */
2000 TRACE("Dir %s ...\n",debugstr_w(name));
2001 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
2002 TRACE("resolves to %s\n",debugstr_w(path));
2005 return ERROR_SUCCESS;
2008 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2010 MSIPACKAGE *package = (MSIPACKAGE*)param;
2012 MSIFEATURE *feature;
2014 name = MSI_RecordGetString( row, 1 );
2016 feature = get_loaded_feature( package, name );
2018 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2022 Condition = MSI_RecordGetString(row,3);
2024 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2026 int level = MSI_RecordGetInteger(row,2);
2027 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2028 feature->Level = level;
2031 return ERROR_SUCCESS;
2034 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2036 static const WCHAR name_fmt[] =
2037 {'%','u','.','%','u','.','%','u','.','%','u',0};
2038 static const WCHAR name[] = {'\\',0};
2039 VS_FIXEDFILEINFO *lpVer;
2040 WCHAR filever[0x100];
2046 TRACE("%s\n", debugstr_w(filename));
2048 versize = GetFileVersionInfoSizeW( filename, &handle );
2052 version = msi_alloc( versize );
2053 GetFileVersionInfoW( filename, 0, versize, version );
2055 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2057 msi_free( version );
2061 sprintfW( filever, name_fmt,
2062 HIWORD(lpVer->dwFileVersionMS),
2063 LOWORD(lpVer->dwFileVersionMS),
2064 HIWORD(lpVer->dwFileVersionLS),
2065 LOWORD(lpVer->dwFileVersionLS));
2067 msi_free( version );
2069 return strdupW( filever );
2072 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2074 LPWSTR file_version;
2077 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2079 MSICOMPONENT* comp = file->Component;
2085 if (file->IsCompressed)
2086 comp->ForceLocalState = TRUE;
2088 /* calculate target */
2089 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2091 msi_free(file->TargetPath);
2093 TRACE("file %s is named %s\n",
2094 debugstr_w(file->File), debugstr_w(file->FileName));
2096 file->TargetPath = build_directory_name(2, p, file->FileName);
2100 TRACE("file %s resolves to %s\n",
2101 debugstr_w(file->File), debugstr_w(file->TargetPath));
2103 /* don't check files of components that aren't installed */
2104 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2105 comp->Installed == INSTALLSTATE_ABSENT)
2107 file->state = msifs_missing; /* assume files are missing */
2111 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2113 file->state = msifs_missing;
2114 comp->Cost += file->FileSize;
2118 if (file->Version &&
2119 (file_version = msi_get_disk_file_version( file->TargetPath )))
2121 TRACE("new %s old %s\n", debugstr_w(file->Version),
2122 debugstr_w(file_version));
2123 /* FIXME: seems like a bad way to compare version numbers */
2124 if (lstrcmpiW(file_version, file->Version)<0)
2126 file->state = msifs_overwrite;
2127 comp->Cost += file->FileSize;
2130 file->state = msifs_present;
2131 msi_free( file_version );
2134 file->state = msifs_present;
2137 return ERROR_SUCCESS;
2141 * A lot is done in this function aside from just the costing.
2142 * The costing needs to be implemented at some point but for now I am going
2143 * to focus on the directory building
2146 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2148 static const WCHAR ExecSeqQuery[] =
2149 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2150 '`','D','i','r','e','c','t','o','r','y','`',0};
2151 static const WCHAR ConditionQuery[] =
2152 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2153 '`','C','o','n','d','i','t','i','o','n','`',0};
2154 static const WCHAR szCosting[] =
2155 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2156 static const WCHAR szlevel[] =
2157 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2158 static const WCHAR szOutOfDiskSpace[] =
2159 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2160 static const WCHAR szOne[] = { '1', 0 };
2161 static const WCHAR szZero[] = { '0', 0 };
2167 TRACE("Building Directory properties\n");
2169 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2170 if (rc == ERROR_SUCCESS)
2172 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2174 msiobj_release(&view->hdr);
2177 /* read components states from the registry */
2178 ACTION_GetComponentInstallStates(package);
2179 ACTION_GetFeatureInstallStates(package);
2181 TRACE("File calculations\n");
2182 msi_check_file_install_states( package );
2184 TRACE("Evaluating Condition Table\n");
2186 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2187 if (rc == ERROR_SUCCESS)
2189 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2191 msiobj_release(&view->hdr);
2194 TRACE("Enabling or Disabling Components\n");
2195 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2197 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2199 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2200 comp->Enabled = FALSE;
2203 comp->Enabled = TRUE;
2206 MSI_SetPropertyW(package,szCosting,szOne);
2207 /* set default run level if not set */
2208 level = msi_dup_property( package, szlevel );
2210 MSI_SetPropertyW(package,szlevel, szOne);
2213 /* FIXME: check volume disk space */
2214 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2216 return MSI_SetFeatureStates(package);
2219 /* OK this value is "interpreted" and then formatted based on the
2220 first few characters */
2221 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2226 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2232 LPWSTR deformated = NULL;
2235 deformat_string(package, &value[2], &deformated);
2237 /* binary value type */
2241 *size = (strlenW(ptr)/2)+1;
2243 *size = strlenW(ptr)/2;
2245 data = msi_alloc(*size);
2251 /* if uneven pad with a zero in front */
2257 data[count] = (BYTE)strtol(byte,NULL,0);
2259 TRACE("Uneven byte count\n");
2267 data[count] = (BYTE)strtol(byte,NULL,0);
2270 msi_free(deformated);
2272 TRACE("Data %i bytes(%i)\n",*size,count);
2279 deformat_string(package, &value[1], &deformated);
2282 *size = sizeof(DWORD);
2283 data = msi_alloc(*size);
2289 if ( (*p < '0') || (*p > '9') )
2295 if (deformated[0] == '-')
2298 TRACE("DWORD %i\n",*(LPDWORD)data);
2300 msi_free(deformated);
2305 static const WCHAR szMulti[] = {'[','~',']',0};
2314 *type=REG_EXPAND_SZ;
2322 if (strstrW(value,szMulti))
2323 *type = REG_MULTI_SZ;
2325 /* remove initial delimiter */
2326 if (!strncmpW(value, szMulti, 3))
2329 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2331 /* add double NULL terminator */
2332 if (*type == REG_MULTI_SZ)
2334 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2335 data = msi_realloc_zero(data, *size);
2341 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2343 MSIPACKAGE *package = (MSIPACKAGE*)param;
2344 static const WCHAR szHCR[] =
2345 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2346 'R','O','O','T','\\',0};
2347 static const WCHAR szHCU[] =
2348 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2349 'U','S','E','R','\\',0};
2350 static const WCHAR szHLM[] =
2351 {'H','K','E','Y','_','L','O','C','A','L','_',
2352 'M','A','C','H','I','N','E','\\',0};
2353 static const WCHAR szHU[] =
2354 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2356 LPSTR value_data = NULL;
2357 HKEY root_key, hkey;
2360 LPCWSTR szRoot, component, name, key, value;
2365 BOOL check_first = FALSE;
2368 ui_progress(package,2,0,0,0);
2375 component = MSI_RecordGetString(row, 6);
2376 comp = get_loaded_component(package,component);
2378 return ERROR_SUCCESS;
2380 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2382 TRACE("Skipping write due to disabled component %s\n",
2383 debugstr_w(component));
2385 comp->Action = comp->Installed;
2387 return ERROR_SUCCESS;
2390 comp->Action = INSTALLSTATE_LOCAL;
2392 name = MSI_RecordGetString(row, 4);
2393 if( MSI_RecordIsNull(row,5) && name )
2395 /* null values can have special meanings */
2396 if (name[0]=='-' && name[1] == 0)
2397 return ERROR_SUCCESS;
2398 else if ((name[0]=='+' && name[1] == 0) ||
2399 (name[0] == '*' && name[1] == 0))
2404 root = MSI_RecordGetInteger(row,2);
2405 key = MSI_RecordGetString(row, 3);
2407 /* get the root key */
2412 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2413 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2414 if (all_users && all_users[0] == '1')
2416 root_key = HKEY_LOCAL_MACHINE;
2421 root_key = HKEY_CURRENT_USER;
2424 msi_free(all_users);
2427 case 0: root_key = HKEY_CLASSES_ROOT;
2430 case 1: root_key = HKEY_CURRENT_USER;
2433 case 2: root_key = HKEY_LOCAL_MACHINE;
2436 case 3: root_key = HKEY_USERS;
2440 ERR("Unknown root %i\n",root);
2446 return ERROR_SUCCESS;
2448 deformat_string(package, key , &deformated);
2449 size = strlenW(deformated) + strlenW(szRoot) + 1;
2450 uikey = msi_alloc(size*sizeof(WCHAR));
2451 strcpyW(uikey,szRoot);
2452 strcatW(uikey,deformated);
2454 if (RegCreateKeyW( root_key, deformated, &hkey))
2456 ERR("Could not create key %s\n",debugstr_w(deformated));
2457 msi_free(deformated);
2459 return ERROR_SUCCESS;
2461 msi_free(deformated);
2463 value = MSI_RecordGetString(row,5);
2465 value_data = parse_value(package, value, &type, &size);
2468 static const WCHAR szEmpty[] = {0};
2469 value_data = (LPSTR)strdupW(szEmpty);
2470 size = sizeof(szEmpty);
2474 deformat_string(package, name, &deformated);
2478 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2480 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2485 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2486 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2488 TRACE("value %s of %s checked already exists\n",
2489 debugstr_w(deformated), debugstr_w(uikey));
2493 TRACE("Checked and setting value %s of %s\n",
2494 debugstr_w(deformated), debugstr_w(uikey));
2495 if (deformated || size)
2496 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2501 uirow = MSI_CreateRecord(3);
2502 MSI_RecordSetStringW(uirow,2,deformated);
2503 MSI_RecordSetStringW(uirow,1,uikey);
2506 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2508 MSI_RecordSetStringW(uirow,3,value);
2510 ui_actiondata(package,szWriteRegistryValues,uirow);
2511 msiobj_release( &uirow->hdr );
2513 msi_free(value_data);
2514 msi_free(deformated);
2517 return ERROR_SUCCESS;
2520 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2524 static const WCHAR ExecSeqQuery[] =
2525 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2526 '`','R','e','g','i','s','t','r','y','`',0 };
2528 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2529 if (rc != ERROR_SUCCESS)
2530 return ERROR_SUCCESS;
2532 /* increment progress bar each time action data is sent */
2533 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2535 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2537 msiobj_release(&view->hdr);
2541 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2543 package->script->CurrentlyScripting = TRUE;
2545 return ERROR_SUCCESS;
2549 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2554 static const WCHAR q1[]=
2555 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2556 '`','R','e','g','i','s','t','r','y','`',0};
2559 MSIFEATURE *feature;
2562 TRACE("InstallValidate\n");
2564 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2565 if (rc == ERROR_SUCCESS)
2567 MSI_IterateRecords( view, &progress, NULL, package );
2568 msiobj_release( &view->hdr );
2569 total += progress * REG_PROGRESS_VALUE;
2572 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2573 total += COMPONENT_PROGRESS_VALUE;
2575 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2576 total += file->FileSize;
2578 ui_progress(package,0,total,0,0);
2580 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2582 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2583 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2584 feature->ActionRequest);
2587 return ERROR_SUCCESS;
2590 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2592 MSIPACKAGE* package = (MSIPACKAGE*)param;
2593 LPCWSTR cond = NULL;
2594 LPCWSTR message = NULL;
2597 static const WCHAR title[]=
2598 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2600 cond = MSI_RecordGetString(row,1);
2602 r = MSI_EvaluateConditionW(package,cond);
2603 if (r == MSICONDITION_FALSE)
2605 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2608 message = MSI_RecordGetString(row,2);
2609 deformat_string(package,message,&deformated);
2610 MessageBoxW(NULL,deformated,title,MB_OK);
2611 msi_free(deformated);
2614 return ERROR_INSTALL_FAILURE;
2617 return ERROR_SUCCESS;
2620 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2623 MSIQUERY * view = NULL;
2624 static const WCHAR ExecSeqQuery[] =
2625 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2626 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2628 TRACE("Checking launch conditions\n");
2630 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2631 if (rc != ERROR_SUCCESS)
2632 return ERROR_SUCCESS;
2634 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2635 msiobj_release(&view->hdr);
2640 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2644 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2646 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2648 MSIRECORD * row = 0;
2650 LPWSTR deformated,buffer,deformated_name;
2652 static const WCHAR ExecSeqQuery[] =
2653 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2654 '`','R','e','g','i','s','t','r','y','`',' ',
2655 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2656 ' ','=',' ' ,'\'','%','s','\'',0 };
2657 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2658 static const WCHAR fmt2[]=
2659 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2661 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2665 root = MSI_RecordGetInteger(row,2);
2666 key = MSI_RecordGetString(row, 3);
2667 name = MSI_RecordGetString(row, 4);
2668 deformat_string(package, key , &deformated);
2669 deformat_string(package, name, &deformated_name);
2671 len = strlenW(deformated) + 6;
2672 if (deformated_name)
2673 len+=strlenW(deformated_name);
2675 buffer = msi_alloc( len *sizeof(WCHAR));
2677 if (deformated_name)
2678 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2680 sprintfW(buffer,fmt,root,deformated);
2682 msi_free(deformated);
2683 msi_free(deformated_name);
2684 msiobj_release(&row->hdr);
2688 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2690 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2695 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2698 return strdupW( file->TargetPath );
2703 static HKEY openSharedDLLsKey(void)
2706 static const WCHAR path[] =
2707 {'S','o','f','t','w','a','r','e','\\',
2708 'M','i','c','r','o','s','o','f','t','\\',
2709 'W','i','n','d','o','w','s','\\',
2710 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2711 'S','h','a','r','e','d','D','L','L','s',0};
2713 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2717 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2722 DWORD sz = sizeof(count);
2725 hkey = openSharedDLLsKey();
2726 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2727 if (rc != ERROR_SUCCESS)
2733 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2737 hkey = openSharedDLLsKey();
2739 msi_reg_set_val_dword( hkey, path, count );
2741 RegDeleteValueW(hkey,path);
2747 * Return TRUE if the count should be written out and FALSE if not
2749 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2751 MSIFEATURE *feature;
2755 /* only refcount DLLs */
2756 if (comp->KeyPath == NULL ||
2757 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2758 comp->Attributes & msidbComponentAttributesODBCDataSource)
2762 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2763 write = (count > 0);
2765 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2769 /* increment counts */
2770 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2774 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2777 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2779 if ( cl->component == comp )
2784 /* decrement counts */
2785 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2789 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2792 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2794 if ( cl->component == comp )
2799 /* ref count all the files in the component */
2804 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2806 if (file->Component == comp)
2807 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2811 /* add a count for permanent */
2812 if (comp->Attributes & msidbComponentAttributesPermanent)
2815 comp->RefCount = count;
2818 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2821 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2823 WCHAR squished_pc[GUID_SIZE];
2824 WCHAR squished_cc[GUID_SIZE];
2831 squash_guid(package->ProductCode,squished_pc);
2832 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2834 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2838 ui_progress(package,2,0,0,0);
2839 if (!comp->ComponentId)
2842 squash_guid(comp->ComponentId,squished_cc);
2844 msi_free(comp->FullKeypath);
2845 comp->FullKeypath = resolve_keypath( package, comp );
2847 ACTION_RefCountComponent( package, comp );
2849 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2850 debugstr_w(comp->Component),
2851 debugstr_w(squished_cc),
2852 debugstr_w(comp->FullKeypath),
2855 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
2856 ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
2858 if (!comp->FullKeypath)
2861 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2862 rc = MSIREG_OpenLocalUserDataComponentKey(comp->ComponentId, &hkey, TRUE);
2864 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey, TRUE);
2866 if (rc != ERROR_SUCCESS)
2869 if (comp->Attributes & msidbComponentAttributesPermanent)
2871 static const WCHAR szPermKey[] =
2872 { '0','0','0','0','0','0','0','0','0','0','0','0',
2873 '0','0','0','0','0','0','0','0','0','0','0','0',
2874 '0','0','0','0','0','0','0','0',0 };
2876 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2879 if (comp->Action == INSTALLSTATE_LOCAL)
2880 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2886 WCHAR source[MAX_PATH];
2887 WCHAR base[MAX_PATH];
2890 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2891 static const WCHAR query[] = {
2892 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2893 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2894 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2895 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2896 '`','D','i','s','k','I','d','`',0};
2898 file = get_loaded_file(package, comp->KeyPath);
2902 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2903 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2904 ptr2 = strrchrW(source, '\\') + 1;
2905 msiobj_release(&row->hdr);
2907 lstrcpyW(base, package->PackagePath);
2908 ptr = strrchrW(base, '\\');
2911 sourcepath = resolve_file_source(package, file);
2912 ptr = sourcepath + lstrlenW(base);
2913 lstrcpyW(ptr2, ptr);
2914 msi_free(sourcepath);
2916 msi_reg_set_val_str(hkey, squished_pc, source);
2920 else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
2922 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2923 MSIREG_DeleteLocalUserDataComponentKey(comp->ComponentId);
2925 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2929 uirow = MSI_CreateRecord(3);
2930 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2931 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2932 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2933 ui_actiondata(package,szProcessComponents,uirow);
2934 msiobj_release( &uirow->hdr );
2937 return ERROR_SUCCESS;
2948 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2949 LPWSTR lpszName, LONG_PTR lParam)
2952 typelib_struct *tl_struct = (typelib_struct*) lParam;
2953 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2957 if (!IS_INTRESOURCE(lpszName))
2959 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2963 sz = strlenW(tl_struct->source)+4;
2964 sz *= sizeof(WCHAR);
2966 if ((INT_PTR)lpszName == 1)
2967 tl_struct->path = strdupW(tl_struct->source);
2970 tl_struct->path = msi_alloc(sz);
2971 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2974 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2975 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2978 msi_free(tl_struct->path);
2979 tl_struct->path = NULL;
2984 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2985 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2987 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2991 msi_free(tl_struct->path);
2992 tl_struct->path = NULL;
2994 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2995 ITypeLib_Release(tl_struct->ptLib);
3000 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3002 MSIPACKAGE* package = (MSIPACKAGE*)param;
3006 typelib_struct tl_struct;
3011 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3013 component = MSI_RecordGetString(row,3);
3014 comp = get_loaded_component(package,component);
3016 return ERROR_SUCCESS;
3018 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3020 TRACE("Skipping typelib reg due to disabled component\n");
3022 comp->Action = comp->Installed;
3024 return ERROR_SUCCESS;
3027 comp->Action = INSTALLSTATE_LOCAL;
3029 file = get_loaded_file( package, comp->KeyPath );
3031 return ERROR_SUCCESS;
3033 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3037 guid = MSI_RecordGetString(row,1);
3038 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3039 tl_struct.source = strdupW( file->TargetPath );
3040 tl_struct.path = NULL;
3042 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3043 (LONG_PTR)&tl_struct);
3051 helpid = MSI_RecordGetString(row,6);
3054 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3055 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3059 ERR("Failed to register type library %s\n",
3060 debugstr_w(tl_struct.path));
3063 ui_actiondata(package,szRegisterTypeLibraries,row);
3065 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3068 ITypeLib_Release(tl_struct.ptLib);
3069 msi_free(tl_struct.path);
3072 ERR("Failed to load type library %s\n",
3073 debugstr_w(tl_struct.source));
3075 FreeLibrary(module);
3076 msi_free(tl_struct.source);
3080 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3083 ERR("Failed to load type library: %08x\n", hr);
3084 return ERROR_FUNCTION_FAILED;
3087 ITypeLib_Release(tlib);
3090 return ERROR_SUCCESS;
3093 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3096 * OK this is a bit confusing.. I am given a _Component key and I believe
3097 * that the file that is being registered as a type library is the "key file
3098 * of that component" which I interpret to mean "The file in the KeyPath of
3103 static const WCHAR Query[] =
3104 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3105 '`','T','y','p','e','L','i','b','`',0};
3107 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3108 if (rc != ERROR_SUCCESS)
3109 return ERROR_SUCCESS;
3111 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3112 msiobj_release(&view->hdr);
3116 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3118 MSIPACKAGE *package = (MSIPACKAGE*)param;
3119 LPWSTR target_file, target_folder, filename;
3120 LPCWSTR buffer, extension;
3122 static const WCHAR szlnk[]={'.','l','n','k',0};
3123 IShellLinkW *sl = NULL;
3124 IPersistFile *pf = NULL;
3127 buffer = MSI_RecordGetString(row,4);
3128 comp = get_loaded_component(package,buffer);
3130 return ERROR_SUCCESS;
3132 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3134 TRACE("Skipping shortcut creation due to disabled component\n");
3136 comp->Action = comp->Installed;
3138 return ERROR_SUCCESS;
3141 comp->Action = INSTALLSTATE_LOCAL;
3143 ui_actiondata(package,szCreateShortcuts,row);
3145 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3146 &IID_IShellLinkW, (LPVOID *) &sl );
3150 ERR("CLSID_ShellLink not available\n");
3154 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3157 ERR("QueryInterface(IID_IPersistFile) failed\n");
3161 buffer = MSI_RecordGetString(row,2);
3162 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3164 /* may be needed because of a bug somewhere else */
3165 create_full_pathW(target_folder);
3167 filename = msi_dup_record_field( row, 3 );
3168 reduce_to_longfilename(filename);
3170 extension = strchrW(filename,'.');
3171 if (!extension || strcmpiW(extension,szlnk))
3173 int len = strlenW(filename);
3174 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3175 memcpy(filename + len, szlnk, sizeof(szlnk));
3177 target_file = build_directory_name(2, target_folder, filename);
3178 msi_free(target_folder);
3181 buffer = MSI_RecordGetString(row,5);
3182 if (strchrW(buffer,'['))
3185 deformat_string(package,buffer,&deformated);
3186 IShellLinkW_SetPath(sl,deformated);
3187 msi_free(deformated);
3191 FIXME("poorly handled shortcut format, advertised shortcut\n");
3192 IShellLinkW_SetPath(sl,comp->FullKeypath);
3195 if (!MSI_RecordIsNull(row,6))
3198 buffer = MSI_RecordGetString(row,6);
3199 deformat_string(package,buffer,&deformated);
3200 IShellLinkW_SetArguments(sl,deformated);
3201 msi_free(deformated);
3204 if (!MSI_RecordIsNull(row,7))
3206 buffer = MSI_RecordGetString(row,7);
3207 IShellLinkW_SetDescription(sl,buffer);
3210 if (!MSI_RecordIsNull(row,8))
3211 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3213 if (!MSI_RecordIsNull(row,9))
3218 buffer = MSI_RecordGetString(row,9);
3220 Path = build_icon_path(package,buffer);
3221 index = MSI_RecordGetInteger(row,10);
3223 /* no value means 0 */
3224 if (index == MSI_NULL_INTEGER)
3227 IShellLinkW_SetIconLocation(sl,Path,index);
3231 if (!MSI_RecordIsNull(row,11))
3232 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3234 if (!MSI_RecordIsNull(row,12))
3237 buffer = MSI_RecordGetString(row,12);
3238 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3240 IShellLinkW_SetWorkingDirectory(sl,Path);
3244 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3245 IPersistFile_Save(pf,target_file,FALSE);
3247 msi_free(target_file);
3251 IPersistFile_Release( pf );
3253 IShellLinkW_Release( sl );
3255 return ERROR_SUCCESS;
3258 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3263 static const WCHAR Query[] =
3264 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3265 '`','S','h','o','r','t','c','u','t','`',0};
3267 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3268 if (rc != ERROR_SUCCESS)
3269 return ERROR_SUCCESS;
3271 res = CoInitialize( NULL );
3274 ERR("CoInitialize failed\n");
3275 return ERROR_FUNCTION_FAILED;
3278 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3279 msiobj_release(&view->hdr);
3286 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3288 MSIPACKAGE* package = (MSIPACKAGE*)param;
3297 FileName = MSI_RecordGetString(row,1);
3300 ERR("Unable to get FileName\n");
3301 return ERROR_SUCCESS;
3304 FilePath = build_icon_path(package,FileName);
3306 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3308 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3309 FILE_ATTRIBUTE_NORMAL, NULL);
3311 if (the_file == INVALID_HANDLE_VALUE)
3313 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3315 return ERROR_SUCCESS;
3322 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3323 if (rc != ERROR_SUCCESS)
3325 ERR("Failed to get stream\n");
3326 CloseHandle(the_file);
3327 DeleteFileW(FilePath);
3330 WriteFile(the_file,buffer,sz,&write,NULL);
3331 } while (sz == 1024);
3335 CloseHandle(the_file);
3337 uirow = MSI_CreateRecord(1);
3338 MSI_RecordSetStringW(uirow,1,FileName);
3339 ui_actiondata(package,szPublishProduct,uirow);
3340 msiobj_release( &uirow->hdr );
3342 return ERROR_SUCCESS;
3345 static UINT msi_publish_icons(MSIPACKAGE *package)
3350 static const WCHAR query[]= {
3351 'S','E','L','E','C','T',' ','*',' ',
3352 'F','R','O','M',' ','`','I','c','o','n','`',0};
3354 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3355 if (r == ERROR_SUCCESS)
3357 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3358 msiobj_release(&view->hdr);
3361 return ERROR_SUCCESS;
3364 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3370 MSISOURCELISTINFO *info;
3372 static const WCHAR szEmpty[] = {0};
3373 static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0};
3375 r = RegCreateKeyW(hkey, szSourceList, &source);
3376 if (r != ERROR_SUCCESS)
3379 RegCloseKey(source);
3381 buffer = strrchrW(package->PackagePath, '\\') + 1;
3382 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3383 package->Context, MSICODE_PRODUCT,
3384 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3385 if (r != ERROR_SUCCESS)
3388 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3389 package->Context, MSICODE_PRODUCT,
3390 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3391 if (r != ERROR_SUCCESS)
3394 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3395 package->Context, MSICODE_PRODUCT,
3396 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3397 if (r != ERROR_SUCCESS)
3400 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3402 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3403 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3404 info->options, info->value);
3406 MsiSourceListSetInfoW(package->ProductCode, NULL,
3407 info->context, info->options,
3408 info->property, info->value);
3411 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3413 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3414 disk->context, disk->options,
3415 disk->disk_id, disk->volume_label, disk->disk_prompt);
3418 return ERROR_SUCCESS;
3421 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3423 MSIHANDLE hdb, suminfo;
3424 WCHAR guids[MAX_PATH];
3425 WCHAR packcode[SQUISH_GUID_SIZE];
3432 static const WCHAR szProductLanguage[] =
3433 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3434 static const WCHAR szARPProductIcon[] =
3435 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3436 static const WCHAR szProductVersion[] =
3437 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3438 static const WCHAR szAssignment[] =
3439 {'A','s','s','i','g','n','m','e','n','t',0};
3440 static const WCHAR szAdvertiseFlags[] =
3441 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3442 static const WCHAR szClients[] =
3443 {'C','l','i','e','n','t','s',0};
3444 static const WCHAR szColon[] = {':',0};
3446 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3447 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3450 langid = msi_get_property_int(package, szProductLanguage, 0);
3451 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3453 ptr = strrchrW(package->PackagePath, '\\' ) + 1;
3454 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGENAMEW, ptr);
3457 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3459 buffer = msi_dup_property(package, szARPProductIcon);
3462 LPWSTR path = build_icon_path(package,buffer);
3463 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3468 buffer = msi_dup_property(package, szProductVersion);
3471 DWORD verdword = msi_version_str_to_dword(buffer);
3472 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3476 msi_reg_set_val_dword(hkey, szAssignment, 0);
3477 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3478 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3479 msi_reg_set_val_str(hkey, szClients, szColon);
3481 hdb = alloc_msihandle(&package->db->hdr);
3483 return ERROR_NOT_ENOUGH_MEMORY;
3485 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3486 MsiCloseHandle(hdb);
3487 if (r != ERROR_SUCCESS)
3491 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3492 NULL, guids, &size);
3493 if (r != ERROR_SUCCESS)
3496 ptr = strchrW(guids, ';');
3498 squash_guid(guids, packcode);
3499 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3502 MsiCloseHandle(suminfo);
3503 return ERROR_SUCCESS;
3506 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3511 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3513 static const WCHAR szUpgradeCode[] =
3514 {'U','p','g','r','a','d','e','C','o','d','e',0};
3516 upgrade = msi_dup_property(package, szUpgradeCode);
3518 return ERROR_SUCCESS;
3520 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3522 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3523 if (r != ERROR_SUCCESS)
3528 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3529 if (r != ERROR_SUCCESS)
3533 squash_guid(package->ProductCode, squashed_pc);
3534 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3543 static BOOL msi_check_publish(MSIPACKAGE *package)
3545 MSIFEATURE *feature;
3547 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3549 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3556 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3558 MSIFEATURE *feature;
3560 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3562 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3570 * 99% of the work done here is only done for
3571 * advertised installs. However this is where the
3572 * Icon table is processed and written out
3573 * so that is what I am going to do here.
3575 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3581 /* FIXME: also need to publish if the product is in advertise mode */
3582 if (!msi_check_publish(package))
3583 return ERROR_SUCCESS;
3585 rc = MSIREG_OpenProductKey(package->ProductCode, package->Context,
3587 if (rc != ERROR_SUCCESS)
3590 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3592 rc = MSIREG_OpenLocalUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3593 if (rc != ERROR_SUCCESS)
3598 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3599 if (rc != ERROR_SUCCESS)
3603 rc = msi_publish_upgrade_code(package);
3604 if (rc != ERROR_SUCCESS)
3607 rc = msi_publish_product_properties(package, hukey);
3608 if (rc != ERROR_SUCCESS)
3611 rc = msi_publish_sourcelist(package, hukey);
3612 if (rc != ERROR_SUCCESS)
3615 rc = msi_publish_icons(package);
3619 RegCloseKey(hudkey);
3624 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3626 MSIPACKAGE *package = (MSIPACKAGE*)param;
3627 LPCWSTR component, section, key, value, identifier, dirproperty;
3628 LPWSTR deformated_section, deformated_key, deformated_value;
3629 LPWSTR folder, filename, fullname = NULL;
3630 LPCWSTR filenameptr;
3634 static const WCHAR szWindowsFolder[] =
3635 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3637 component = MSI_RecordGetString(row, 8);
3638 comp = get_loaded_component(package,component);
3640 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3642 TRACE("Skipping ini file due to disabled component %s\n",
3643 debugstr_w(component));
3645 comp->Action = comp->Installed;
3647 return ERROR_SUCCESS;
3650 comp->Action = INSTALLSTATE_LOCAL;
3652 identifier = MSI_RecordGetString(row,1);
3653 dirproperty = MSI_RecordGetString(row,3);
3654 section = MSI_RecordGetString(row,4);
3655 key = MSI_RecordGetString(row,5);
3656 value = MSI_RecordGetString(row,6);
3657 action = MSI_RecordGetInteger(row,7);
3659 deformat_string(package,section,&deformated_section);
3660 deformat_string(package,key,&deformated_key);
3661 deformat_string(package,value,&deformated_value);
3663 filename = msi_dup_record_field(row, 2);
3664 if (filename && (filenameptr = strchrW(filename, '|')))
3667 filenameptr = filename;
3671 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3673 folder = msi_dup_property( package, dirproperty );
3676 folder = msi_dup_property( package, szWindowsFolder );
3680 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3684 fullname = build_directory_name(2, folder, filenameptr);
3688 TRACE("Adding value %s to section %s in %s\n",
3689 debugstr_w(deformated_key), debugstr_w(deformated_section),
3690 debugstr_w(fullname));
3691 WritePrivateProfileStringW(deformated_section, deformated_key,
3692 deformated_value, fullname);
3694 else if (action == 1)
3697 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3698 returned, 10, fullname);
3699 if (returned[0] == 0)
3701 TRACE("Adding value %s to section %s in %s\n",
3702 debugstr_w(deformated_key), debugstr_w(deformated_section),
3703 debugstr_w(fullname));
3705 WritePrivateProfileStringW(deformated_section, deformated_key,
3706 deformated_value, fullname);
3709 else if (action == 3)
3710 FIXME("Append to existing section not yet implemented\n");
3712 uirow = MSI_CreateRecord(4);
3713 MSI_RecordSetStringW(uirow,1,identifier);
3714 MSI_RecordSetStringW(uirow,2,deformated_section);
3715 MSI_RecordSetStringW(uirow,3,deformated_key);
3716 MSI_RecordSetStringW(uirow,4,deformated_value);
3717 ui_actiondata(package,szWriteIniValues,uirow);
3718 msiobj_release( &uirow->hdr );
3724 msi_free(deformated_key);
3725 msi_free(deformated_value);
3726 msi_free(deformated_section);
3727 return ERROR_SUCCESS;
3730 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3734 static const WCHAR ExecSeqQuery[] =
3735 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3736 '`','I','n','i','F','i','l','e','`',0};
3738 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3739 if (rc != ERROR_SUCCESS)
3741 TRACE("no IniFile table\n");
3742 return ERROR_SUCCESS;
3745 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3746 msiobj_release(&view->hdr);
3750 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3752 MSIPACKAGE *package = (MSIPACKAGE*)param;
3757 static const WCHAR ExeStr[] =
3758 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3759 static const WCHAR close[] = {'\"',0};
3761 PROCESS_INFORMATION info;
3766 memset(&si,0,sizeof(STARTUPINFOW));
3768 filename = MSI_RecordGetString(row,1);
3769 file = get_loaded_file( package, filename );
3773 ERR("Unable to find file id %s\n",debugstr_w(filename));
3774 return ERROR_SUCCESS;
3777 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3779 FullName = msi_alloc(len*sizeof(WCHAR));
3780 strcpyW(FullName,ExeStr);
3781 strcatW( FullName, file->TargetPath );
3782 strcatW(FullName,close);
3784 TRACE("Registering %s\n",debugstr_w(FullName));
3785 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3790 CloseHandle(info.hThread);
3791 msi_dialog_check_messages(info.hProcess);
3792 CloseHandle(info.hProcess);
3798 uirow = MSI_CreateRecord( 2 );
3799 uipath = strdupW( file->TargetPath );
3800 p = strrchrW(uipath,'\\');
3803 MSI_RecordSetStringW( uirow, 1, &p[1] );
3804 MSI_RecordSetStringW( uirow, 2, uipath);
3805 ui_actiondata( package, szSelfRegModules, uirow);
3806 msiobj_release( &uirow->hdr );
3808 /* FIXME: call ui_progress? */
3810 return ERROR_SUCCESS;
3813 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3817 static const WCHAR ExecSeqQuery[] =
3818 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3819 '`','S','e','l','f','R','e','g','`',0};
3821 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3822 if (rc != ERROR_SUCCESS)
3824 TRACE("no SelfReg table\n");
3825 return ERROR_SUCCESS;
3828 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3829 msiobj_release(&view->hdr);
3831 return ERROR_SUCCESS;
3834 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3836 MSIFEATURE *feature;
3839 HKEY userdata = NULL;
3841 if (!msi_check_publish(package))
3842 return ERROR_SUCCESS;
3844 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3846 if (rc != ERROR_SUCCESS)
3849 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3851 if (rc != ERROR_SUCCESS)
3854 /* here the guids are base 85 encoded */
3855 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3861 BOOL absent = FALSE;
3864 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3865 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3866 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3870 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3874 if (feature->Feature_Parent)
3875 size += strlenW( feature->Feature_Parent )+2;
3877 data = msi_alloc(size * sizeof(WCHAR));
3880 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3882 MSICOMPONENT* component = cl->component;
3886 if (component->ComponentId)
3888 TRACE("From %s\n",debugstr_w(component->ComponentId));
3889 CLSIDFromString(component->ComponentId, &clsid);
3890 encode_base85_guid(&clsid,buf);
3891 TRACE("to %s\n",debugstr_w(buf));
3896 if (feature->Feature_Parent)
3898 static const WCHAR sep[] = {'\2',0};
3900 strcatW(data,feature->Feature_Parent);
3903 msi_reg_set_val_str( userdata, feature->Feature, data );
3907 if (feature->Feature_Parent)
3908 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3911 static const WCHAR emptyW[] = {0};
3912 size += sizeof(WCHAR);
3913 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3914 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : emptyW),size);
3918 size += 2*sizeof(WCHAR);
3919 data = msi_alloc(size);
3922 if (feature->Feature_Parent)
3923 strcpyW( &data[1], feature->Feature_Parent );
3924 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3930 uirow = MSI_CreateRecord( 1 );
3931 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3932 ui_actiondata( package, szPublishFeatures, uirow);
3933 msiobj_release( &uirow->hdr );
3934 /* FIXME: call ui_progress? */
3939 RegCloseKey(userdata);
3943 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3948 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3950 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3952 if (r == ERROR_SUCCESS)
3954 RegDeleteValueW(hkey, feature->Feature);
3958 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3960 if (r == ERROR_SUCCESS)
3962 RegDeleteValueW(hkey, feature->Feature);
3966 return ERROR_SUCCESS;
3969 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3971 MSIFEATURE *feature;
3973 if (!msi_check_unpublish(package))
3974 return ERROR_SUCCESS;
3976 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3978 msi_unpublish_feature(package, feature);
3981 return ERROR_SUCCESS;
3984 static UINT msi_get_local_package_name( LPWSTR path )
3986 static const WCHAR szInstaller[] = {
3987 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3988 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3992 time = GetTickCount();
3993 GetWindowsDirectoryW( path, MAX_PATH );
3994 lstrcatW( path, szInstaller );
3995 CreateDirectoryW( path, NULL );
3997 len = lstrlenW(path);
3998 for (i=0; i<0x10000; i++)
4000 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
4001 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
4002 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
4003 if (handle != INVALID_HANDLE_VALUE)
4005 CloseHandle(handle);
4008 if (GetLastError() != ERROR_FILE_EXISTS &&
4009 GetLastError() != ERROR_SHARING_VIOLATION)
4010 return ERROR_FUNCTION_FAILED;
4013 return ERROR_SUCCESS;
4016 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
4018 WCHAR packagefile[MAX_PATH];
4021 r = msi_get_local_package_name( packagefile );
4022 if (r != ERROR_SUCCESS)
4025 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
4027 r = CopyFileW( package->db->path, packagefile, FALSE);
4031 ERR("Unable to copy package (%s -> %s) (error %d)\n",
4032 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
4033 return ERROR_FUNCTION_FAILED;
4036 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
4038 return ERROR_SUCCESS;
4041 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4043 LPWSTR prop, val, key;
4049 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4050 static const WCHAR szWindowsInstaller[] =
4051 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4052 static const WCHAR modpath_fmt[] =
4053 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4054 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4055 static const WCHAR szModifyPath[] =
4056 {'M','o','d','i','f','y','P','a','t','h',0};
4057 static const WCHAR szUninstallString[] =
4058 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4059 static const WCHAR szEstimatedSize[] =
4060 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4061 static const WCHAR szProductLanguage[] =
4062 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4063 static const WCHAR szProductVersion[] =
4064 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4065 static const WCHAR szProductName[] =
4066 {'P','r','o','d','u','c','t','N','a','m','e',0};
4067 static const WCHAR szDisplayName[] =
4068 {'D','i','s','p','l','a','y','N','a','m','e',0};
4069 static const WCHAR szDisplayVersion[] =
4070 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4071 static const WCHAR szManufacturer[] =
4072 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4074 static const LPCSTR propval[] = {
4075 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4076 "ARPCONTACT", "Contact",
4077 "ARPCOMMENTS", "Comments",
4078 "ProductName", "DisplayName",
4079 "ProductVersion", "DisplayVersion",
4080 "ARPHELPLINK", "HelpLink",
4081 "ARPHELPTELEPHONE", "HelpTelephone",
4082 "ARPINSTALLLOCATION", "InstallLocation",
4083 "SourceDir", "InstallSource",
4084 "Manufacturer", "Publisher",
4085 "ARPREADME", "Readme",
4087 "ARPURLINFOABOUT", "URLInfoAbout",
4088 "ARPURLUPDATEINFO", "URLUpdateInfo",
4091 const LPCSTR *p = propval;
4095 prop = strdupAtoW(*p++);
4096 key = strdupAtoW(*p++);
4097 val = msi_dup_property(package, prop);
4098 msi_reg_set_val_str(hkey, key, val);
4104 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4106 size = deformat_string(package, modpath_fmt, &buffer);
4107 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4108 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4111 /* FIXME: Write real Estimated Size when we have it */
4112 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4114 buffer = msi_dup_property(package, szProductName);
4115 msi_reg_set_val_str(hkey, szDisplayName, buffer);
4118 buffer = msi_dup_property(package, cszSourceDir);
4119 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4122 buffer = msi_dup_property(package, szManufacturer);
4123 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4126 GetLocalTime(&systime);
4127 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4128 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4130 langid = msi_get_property_int(package, szProductLanguage, 0);
4131 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4133 buffer = msi_dup_property(package, szProductVersion);
4134 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4137 DWORD verdword = msi_version_str_to_dword(buffer);
4139 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4140 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4141 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4145 return ERROR_SUCCESS;
4148 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4150 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4151 LPWSTR upgrade_code;
4156 static const WCHAR szUpgradeCode[] = {
4157 'U','p','g','r','a','d','e','C','o','d','e',0};
4159 /* FIXME: also need to publish if the product is in advertise mode */
4160 if (!msi_check_publish(package))
4161 return ERROR_SUCCESS;
4163 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4164 if (rc != ERROR_SUCCESS)
4167 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4169 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &props, TRUE);
4170 if (rc != ERROR_SUCCESS)
4175 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
4176 if (rc != ERROR_SUCCESS)
4180 msi_make_package_local(package, props);
4182 rc = msi_publish_install_properties(package, hkey);
4183 if (rc != ERROR_SUCCESS)
4186 rc = msi_publish_install_properties(package, props);
4187 if (rc != ERROR_SUCCESS)
4190 upgrade_code = msi_dup_property(package, szUpgradeCode);
4193 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4194 squash_guid(package->ProductCode, squashed_pc);
4195 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4196 RegCloseKey(upgrade);
4197 msi_free(upgrade_code);
4203 return ERROR_SUCCESS;
4206 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4208 return execute_script(package,INSTALL_SCRIPT);
4211 static UINT msi_unpublish_product(MSIPACKAGE *package)
4214 LPWSTR remove = NULL;
4215 LPWSTR *features = NULL;
4216 BOOL full_uninstall = TRUE;
4217 MSIFEATURE *feature;
4219 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4220 static const WCHAR szAll[] = {'A','L','L',0};
4221 static const WCHAR szUpgradeCode[] =
4222 {'U','p','g','r','a','d','e','C','o','d','e',0};
4224 remove = msi_dup_property(package, szRemove);
4226 return ERROR_SUCCESS;
4228 features = msi_split_string(remove, ',');
4232 ERR("REMOVE feature list is empty!\n");
4233 return ERROR_FUNCTION_FAILED;
4236 if (!lstrcmpW(features[0], szAll))
4237 full_uninstall = TRUE;
4240 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4242 if (feature->Action != INSTALLSTATE_ABSENT)
4243 full_uninstall = FALSE;
4247 if (!full_uninstall)
4250 MSIREG_DeleteProductKey(package->ProductCode);
4251 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4252 MSIREG_DeleteUninstallKey(package->ProductCode);
4254 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4256 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4257 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4261 MSIREG_DeleteUserProductKey(package->ProductCode);
4262 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4265 upgrade = msi_dup_property(package, szUpgradeCode);
4268 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4275 return ERROR_SUCCESS;
4278 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4282 rc = msi_unpublish_product(package);
4283 if (rc != ERROR_SUCCESS)
4286 /* turn off scheduling */
4287 package->script->CurrentlyScripting= FALSE;
4289 /* first do the same as an InstallExecute */
4290 rc = ACTION_InstallExecute(package);
4291 if (rc != ERROR_SUCCESS)
4294 /* then handle Commit Actions */
4295 rc = execute_script(package,COMMIT_SCRIPT);
4300 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4302 static const WCHAR RunOnce[] = {
4303 'S','o','f','t','w','a','r','e','\\',
4304 'M','i','c','r','o','s','o','f','t','\\',
4305 'W','i','n','d','o','w','s','\\',
4306 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4307 'R','u','n','O','n','c','e',0};
4308 static const WCHAR InstallRunOnce[] = {
4309 'S','o','f','t','w','a','r','e','\\',
4310 'M','i','c','r','o','s','o','f','t','\\',
4311 'W','i','n','d','o','w','s','\\',
4312 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4313 'I','n','s','t','a','l','l','e','r','\\',
4314 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4316 static const WCHAR msiexec_fmt[] = {
4318 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4319 '\"','%','s','\"',0};
4320 static const WCHAR install_fmt[] = {
4321 '/','I',' ','\"','%','s','\"',' ',
4322 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4323 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4324 WCHAR buffer[256], sysdir[MAX_PATH];
4326 WCHAR squished_pc[100];
4328 squash_guid(package->ProductCode,squished_pc);
4330 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4331 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4332 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4335 msi_reg_set_val_str( hkey, squished_pc, buffer );
4338 TRACE("Reboot command %s\n",debugstr_w(buffer));
4340 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4341 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4343 msi_reg_set_val_str( hkey, squished_pc, buffer );
4346 return ERROR_INSTALL_SUSPEND;
4349 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4355 * We are currently doing what should be done here in the top level Install
4356 * however for Administrative and uninstalls this step will be needed
4358 if (!package->PackagePath)
4359 return ERROR_SUCCESS;
4361 msi_set_sourcedir_props(package, TRUE);
4363 attrib = GetFileAttributesW(package->db->path);
4364 if (attrib == INVALID_FILE_ATTRIBUTES)
4370 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4371 package->Context, MSICODE_PRODUCT,
4372 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4373 if (rc == ERROR_MORE_DATA)
4375 prompt = msi_alloc(size * sizeof(WCHAR));
4376 MsiSourceListGetInfoW(package->ProductCode, NULL,
4377 package->Context, MSICODE_PRODUCT,
4378 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4381 prompt = strdupW(package->db->path);
4383 msg = generate_error_string(package,1302,1,prompt);
4384 while(attrib == INVALID_FILE_ATTRIBUTES)
4386 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4389 rc = ERROR_INSTALL_USEREXIT;
4392 attrib = GetFileAttributesW(package->db->path);
4398 return ERROR_SUCCESS;
4403 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4410 static const WCHAR szPropKeys[][80] =
4412 {'P','r','o','d','u','c','t','I','D',0},
4413 {'U','S','E','R','N','A','M','E',0},
4414 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4418 static const WCHAR szRegKeys[][80] =
4420 {'P','r','o','d','u','c','t','I','D',0},
4421 {'R','e','g','O','w','n','e','r',0},
4422 {'R','e','g','C','o','m','p','a','n','y',0},
4426 if (msi_check_unpublish(package))
4428 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4429 return ERROR_SUCCESS;
4432 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4434 return ERROR_SUCCESS;
4436 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4437 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &hkey, TRUE);
4439 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &hkey, TRUE);
4441 if (rc != ERROR_SUCCESS)
4444 for( i = 0; szPropKeys[i][0]; i++ )
4446 buffer = msi_dup_property( package, szPropKeys[i] );
4447 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4452 msi_free(productid);
4455 /* FIXME: call ui_actiondata */
4461 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4465 package->script->InWhatSequence |= SEQUENCE_EXEC;
4466 rc = ACTION_ProcessExecSequence(package,FALSE);
4471 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4473 MSIPACKAGE *package = (MSIPACKAGE*)param;
4474 LPCWSTR compgroupid=NULL;
4475 LPCWSTR feature=NULL;
4476 LPCWSTR text = NULL;
4477 LPCWSTR qualifier = NULL;
4478 LPCWSTR component = NULL;
4479 LPWSTR advertise = NULL;
4480 LPWSTR output = NULL;
4482 UINT rc = ERROR_SUCCESS;
4487 component = MSI_RecordGetString(rec,3);
4488 comp = get_loaded_component(package,component);
4490 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4491 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4492 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4494 TRACE("Skipping: Component %s not scheduled for install\n",
4495 debugstr_w(component));
4497 return ERROR_SUCCESS;
4500 compgroupid = MSI_RecordGetString(rec,1);
4501 qualifier = MSI_RecordGetString(rec,2);
4503 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4504 if (rc != ERROR_SUCCESS)
4507 text = MSI_RecordGetString(rec,4);
4508 feature = MSI_RecordGetString(rec,5);
4510 advertise = create_component_advertise_string(package, comp, feature);
4512 sz = strlenW(advertise);
4515 sz += lstrlenW(text);
4518 sz *= sizeof(WCHAR);
4520 output = msi_alloc_zero(sz);
4521 strcpyW(output,advertise);
4522 msi_free(advertise);
4525 strcatW(output,text);
4527 msi_reg_set_val_multi_str( hkey, qualifier, output );
4534 uirow = MSI_CreateRecord( 2 );
4535 MSI_RecordSetStringW( uirow, 1, compgroupid );
4536 MSI_RecordSetStringW( uirow, 2, qualifier);
4537 ui_actiondata( package, szPublishComponents, uirow);
4538 msiobj_release( &uirow->hdr );
4539 /* FIXME: call ui_progress? */
4545 * At present I am ignorning the advertised components part of this and only
4546 * focusing on the qualified component sets
4548 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4552 static const WCHAR ExecSeqQuery[] =
4553 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4554 '`','P','u','b','l','i','s','h',
4555 'C','o','m','p','o','n','e','n','t','`',0};
4557 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4558 if (rc != ERROR_SUCCESS)
4559 return ERROR_SUCCESS;
4561 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4562 msiobj_release(&view->hdr);
4567 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4569 MSIPACKAGE *package = (MSIPACKAGE*)param;
4572 SC_HANDLE hscm, service = NULL;
4573 LPCWSTR comp, depends, pass;
4574 LPWSTR name = NULL, disp = NULL;
4575 LPCWSTR load_order, serv_name, key;
4576 DWORD serv_type, start_type;
4579 static const WCHAR query[] =
4580 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4581 '`','C','o','m','p','o','n','e','n','t','`',' ',
4582 'W','H','E','R','E',' ',
4583 '`','C','o','m','p','o','n','e','n','t','`',' ',
4584 '=','\'','%','s','\'',0};
4586 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4589 ERR("Failed to open the SC Manager!\n");
4593 start_type = MSI_RecordGetInteger(rec, 5);
4594 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4597 depends = MSI_RecordGetString(rec, 8);
4598 if (depends && *depends)
4599 FIXME("Dependency list unhandled!\n");
4601 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4602 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4603 serv_type = MSI_RecordGetInteger(rec, 4);
4604 err_control = MSI_RecordGetInteger(rec, 6);
4605 load_order = MSI_RecordGetString(rec, 7);
4606 serv_name = MSI_RecordGetString(rec, 9);
4607 pass = MSI_RecordGetString(rec, 10);
4608 comp = MSI_RecordGetString(rec, 12);
4610 /* fetch the service path */
4611 row = MSI_QueryGetRecord(package->db, query, comp);
4614 ERR("Control query failed!\n");
4618 key = MSI_RecordGetString(row, 6);
4620 file = get_loaded_file(package, key);
4621 msiobj_release(&row->hdr);
4624 ERR("Failed to load the service file\n");
4628 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4629 start_type, err_control, file->TargetPath,
4630 load_order, NULL, NULL, serv_name, pass);
4633 if (GetLastError() != ERROR_SERVICE_EXISTS)
4634 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4638 CloseServiceHandle(service);
4639 CloseServiceHandle(hscm);
4643 return ERROR_SUCCESS;
4646 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4650 static const WCHAR ExecSeqQuery[] =
4651 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4652 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4654 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4655 if (rc != ERROR_SUCCESS)
4656 return ERROR_SUCCESS;
4658 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4659 msiobj_release(&view->hdr);
4664 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4665 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4667 LPCWSTR *vector, *temp_vector;
4671 static const WCHAR separator[] = {'[','~',']',0};
4674 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4679 vector = msi_alloc(sizeof(LPWSTR));
4687 vector[*numargs - 1] = p;
4689 if ((q = strstrW(p, separator)))
4693 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4699 vector = temp_vector;
4708 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4710 MSIPACKAGE *package = (MSIPACKAGE *)param;
4712 SC_HANDLE scm, service = NULL;
4713 LPCWSTR name, *vector = NULL;
4715 DWORD event, numargs;
4716 UINT r = ERROR_FUNCTION_FAILED;
4718 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4719 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4720 return ERROR_SUCCESS;
4722 name = MSI_RecordGetString(rec, 2);
4723 event = MSI_RecordGetInteger(rec, 3);
4724 args = strdupW(MSI_RecordGetString(rec, 4));
4726 if (!(event & msidbServiceControlEventStart))
4727 return ERROR_SUCCESS;
4729 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4732 ERR("Failed to open the service control manager\n");
4736 service = OpenServiceW(scm, name, SERVICE_START);
4739 ERR("Failed to open service %s\n", debugstr_w(name));
4743 vector = msi_service_args_to_vector(args, &numargs);
4745 if (!StartServiceW(service, numargs, vector))
4747 ERR("Failed to start service %s\n", debugstr_w(name));
4754 CloseServiceHandle(service);
4755 CloseServiceHandle(scm);
4762 static UINT ACTION_StartServices( MSIPACKAGE *package )
4767 static const WCHAR query[] = {
4768 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4769 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4771 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4772 if (rc != ERROR_SUCCESS)
4773 return ERROR_SUCCESS;
4775 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4776 msiobj_release(&view->hdr);
4781 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4783 DWORD i, needed, count;
4784 ENUM_SERVICE_STATUSW *dependencies;
4788 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4789 0, &needed, &count))
4792 if (GetLastError() != ERROR_MORE_DATA)
4795 dependencies = msi_alloc(needed);
4799 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4800 needed, &needed, &count))
4803 for (i = 0; i < count; i++)
4805 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4806 SERVICE_STOP | SERVICE_QUERY_STATUS);
4810 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4817 msi_free(dependencies);
4821 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4823 MSIPACKAGE *package = (MSIPACKAGE *)param;
4825 SERVICE_STATUS status;
4826 SERVICE_STATUS_PROCESS ssp;
4827 SC_HANDLE scm = NULL, service = NULL;
4829 DWORD event, needed;
4831 event = MSI_RecordGetInteger(rec, 3);
4832 if (!(event & msidbServiceControlEventStop))
4833 return ERROR_SUCCESS;
4835 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4836 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4837 return ERROR_SUCCESS;
4839 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4840 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4841 args = strdupW(MSI_RecordGetString(rec, 4));
4843 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4846 WARN("Failed to open the SCM: %d\n", GetLastError());
4850 service = OpenServiceW(scm, name,
4852 SERVICE_QUERY_STATUS |
4853 SERVICE_ENUMERATE_DEPENDENTS);
4856 WARN("Failed to open service (%s): %d\n",
4857 debugstr_w(name), GetLastError());
4861 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4862 sizeof(SERVICE_STATUS_PROCESS), &needed))
4864 WARN("Failed to query service status (%s): %d\n",
4865 debugstr_w(name), GetLastError());
4869 if (ssp.dwCurrentState == SERVICE_STOPPED)
4872 stop_service_dependents(scm, service);
4874 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4875 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4878 CloseServiceHandle(service);
4879 CloseServiceHandle(scm);
4883 return ERROR_SUCCESS;
4886 static UINT ACTION_StopServices( MSIPACKAGE *package )
4891 static const WCHAR query[] = {
4892 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4893 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4895 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4896 if (rc != ERROR_SUCCESS)
4897 return ERROR_SUCCESS;
4899 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4900 msiobj_release(&view->hdr);
4905 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4909 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4911 if (!lstrcmpW(file->File, filename))
4918 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4920 MSIPACKAGE *package = (MSIPACKAGE*)param;
4921 LPWSTR driver, driver_path, ptr;
4922 WCHAR outpath[MAX_PATH];
4923 MSIFILE *driver_file, *setup_file;
4926 UINT r = ERROR_SUCCESS;
4928 static const WCHAR driver_fmt[] = {
4929 'D','r','i','v','e','r','=','%','s',0};
4930 static const WCHAR setup_fmt[] = {
4931 'S','e','t','u','p','=','%','s',0};
4932 static const WCHAR usage_fmt[] = {
4933 'F','i','l','e','U','s','a','g','e','=','1',0};
4935 desc = MSI_RecordGetString(rec, 3);
4937 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4938 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4940 if (!driver_file || !setup_file)
4942 ERR("ODBC Driver entry not found!\n");
4943 return ERROR_FUNCTION_FAILED;
4946 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4947 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4948 lstrlenW(usage_fmt) + 1;
4949 driver = msi_alloc(len * sizeof(WCHAR));
4951 return ERROR_OUTOFMEMORY;
4954 lstrcpyW(ptr, desc);
4955 ptr += lstrlenW(ptr) + 1;
4957 sprintfW(ptr, driver_fmt, driver_file->FileName);
4958 ptr += lstrlenW(ptr) + 1;
4960 sprintfW(ptr, setup_fmt, setup_file->FileName);
4961 ptr += lstrlenW(ptr) + 1;
4963 lstrcpyW(ptr, usage_fmt);
4964 ptr += lstrlenW(ptr) + 1;
4967 driver_path = strdupW(driver_file->TargetPath);
4968 ptr = strrchrW(driver_path, '\\');
4969 if (ptr) *ptr = '\0';
4971 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4972 NULL, ODBC_INSTALL_COMPLETE, &usage))
4974 ERR("Failed to install SQL driver!\n");
4975 r = ERROR_FUNCTION_FAILED;
4979 msi_free(driver_path);
4984 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4986 MSIPACKAGE *package = (MSIPACKAGE*)param;
4987 LPWSTR translator, translator_path, ptr;
4988 WCHAR outpath[MAX_PATH];
4989 MSIFILE *translator_file, *setup_file;
4992 UINT r = ERROR_SUCCESS;
4994 static const WCHAR translator_fmt[] = {
4995 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4996 static const WCHAR setup_fmt[] = {
4997 'S','e','t','u','p','=','%','s',0};
4999 desc = MSI_RecordGetString(rec, 3);
5001 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5002 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5004 if (!translator_file || !setup_file)
5006 ERR("ODBC Translator entry not found!\n");
5007 return ERROR_FUNCTION_FAILED;
5010 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
5011 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
5012 translator = msi_alloc(len * sizeof(WCHAR));
5014 return ERROR_OUTOFMEMORY;
5017 lstrcpyW(ptr, desc);
5018 ptr += lstrlenW(ptr) + 1;
5020 sprintfW(ptr, translator_fmt, translator_file->FileName);
5021 ptr += lstrlenW(ptr) + 1;
5023 sprintfW(ptr, setup_fmt, setup_file->FileName);
5024 ptr += lstrlenW(ptr) + 1;
5027 translator_path = strdupW(translator_file->TargetPath);
5028 ptr = strrchrW(translator_path, '\\');
5029 if (ptr) *ptr = '\0';
5031 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5032 NULL, ODBC_INSTALL_COMPLETE, &usage))
5034 ERR("Failed to install SQL translator!\n");
5035 r = ERROR_FUNCTION_FAILED;
5038 msi_free(translator);
5039 msi_free(translator_path);
5044 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5047 LPCWSTR desc, driver;
5048 WORD request = ODBC_ADD_SYS_DSN;
5051 UINT r = ERROR_SUCCESS;
5053 static const WCHAR attrs_fmt[] = {
5054 'D','S','N','=','%','s',0 };
5056 desc = MSI_RecordGetString(rec, 3);
5057 driver = MSI_RecordGetString(rec, 4);
5058 registration = MSI_RecordGetInteger(rec, 5);
5060 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5061 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5063 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5064 attrs = msi_alloc(len * sizeof(WCHAR));
5066 return ERROR_OUTOFMEMORY;
5068 sprintfW(attrs, attrs_fmt, desc);
5069 attrs[len - 1] = '\0';
5071 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5073 ERR("Failed to install SQL data source!\n");
5074 r = ERROR_FUNCTION_FAILED;
5082 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5087 static const WCHAR driver_query[] = {
5088 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5089 'O','D','B','C','D','r','i','v','e','r',0 };
5091 static const WCHAR translator_query[] = {
5092 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5093 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5095 static const WCHAR source_query[] = {
5096 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5097 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5099 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5100 if (rc != ERROR_SUCCESS)
5101 return ERROR_SUCCESS;
5103 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5104 msiobj_release(&view->hdr);
5106 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5107 if (rc != ERROR_SUCCESS)
5108 return ERROR_SUCCESS;
5110 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5111 msiobj_release(&view->hdr);
5113 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5114 if (rc != ERROR_SUCCESS)
5115 return ERROR_SUCCESS;
5117 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5118 msiobj_release(&view->hdr);
5123 #define ENV_ACT_SETALWAYS 0x1
5124 #define ENV_ACT_SETABSENT 0x2
5125 #define ENV_ACT_REMOVE 0x4
5126 #define ENV_ACT_REMOVEMATCH 0x8
5128 #define ENV_MOD_MACHINE 0x20000000
5129 #define ENV_MOD_APPEND 0x40000000
5130 #define ENV_MOD_PREFIX 0x80000000
5131 #define ENV_MOD_MASK 0xC0000000
5133 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5135 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5137 LPCWSTR cptr = *name;
5138 LPCWSTR ptr = *value;
5140 static const WCHAR prefix[] = {'[','~',']',0};
5141 static const int prefix_len = 3;
5147 *flags |= ENV_ACT_SETALWAYS;
5148 else if (*cptr == '+')
5149 *flags |= ENV_ACT_SETABSENT;
5150 else if (*cptr == '-')
5151 *flags |= ENV_ACT_REMOVE;
5152 else if (*cptr == '!')
5153 *flags |= ENV_ACT_REMOVEMATCH;
5154 else if (*cptr == '*')
5155 *flags |= ENV_MOD_MACHINE;
5165 ERR("Missing environment variable\n");
5166 return ERROR_FUNCTION_FAILED;
5169 if (!strncmpW(ptr, prefix, prefix_len))
5171 *flags |= ENV_MOD_APPEND;
5172 *value += lstrlenW(prefix);
5174 else if (lstrlenW(*value) >= prefix_len)
5176 ptr += lstrlenW(ptr) - prefix_len;
5177 if (!lstrcmpW(ptr, prefix))
5179 *flags |= ENV_MOD_PREFIX;
5180 /* the "[~]" will be removed by deformat_string */;
5185 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5186 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5187 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5188 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5190 ERR("Invalid flags: %08x\n", *flags);
5191 return ERROR_FUNCTION_FAILED;
5194 return ERROR_SUCCESS;
5197 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5199 MSIPACKAGE *package = param;
5200 LPCWSTR name, value;
5201 LPWSTR data = NULL, newval = NULL;
5202 LPWSTR deformatted = NULL, ptr;
5203 DWORD flags, type, size;
5205 HKEY env = NULL, root;
5206 LPCWSTR environment;
5208 static const WCHAR user_env[] =
5209 {'E','n','v','i','r','o','n','m','e','n','t',0};
5210 static const WCHAR machine_env[] =
5211 {'S','y','s','t','e','m','\\',
5212 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5213 'C','o','n','t','r','o','l','\\',
5214 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5215 'E','n','v','i','r','o','n','m','e','n','t',0};
5216 static const WCHAR semicolon[] = {';',0};
5218 name = MSI_RecordGetString(rec, 2);
5219 value = MSI_RecordGetString(rec, 3);
5221 res = env_set_flags(&name, &value, &flags);
5222 if (res != ERROR_SUCCESS)
5225 deformat_string(package, value, &deformatted);
5228 res = ERROR_OUTOFMEMORY;
5232 value = deformatted;
5234 if (flags & ENV_MOD_MACHINE)
5236 environment = machine_env;
5237 root = HKEY_LOCAL_MACHINE;
5241 environment = user_env;
5242 root = HKEY_CURRENT_USER;
5245 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5246 KEY_ALL_ACCESS, NULL, &env, NULL);
5247 if (res != ERROR_SUCCESS)
5250 if (flags & ENV_ACT_REMOVE)
5251 FIXME("Not removing environment variable on uninstall!\n");
5254 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5255 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5256 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5259 if (res != ERROR_FILE_NOT_FOUND)
5261 if (flags & ENV_ACT_SETABSENT)
5263 res = ERROR_SUCCESS;
5267 data = msi_alloc(size);
5271 return ERROR_OUTOFMEMORY;
5274 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5275 if (res != ERROR_SUCCESS)
5278 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5280 res = RegDeleteKeyW(env, name);
5284 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5285 newval = msi_alloc(size);
5289 res = ERROR_OUTOFMEMORY;
5293 if (!(flags & ENV_MOD_MASK))
5294 lstrcpyW(newval, value);
5297 if (flags & ENV_MOD_PREFIX)
5299 lstrcpyW(newval, value);
5300 lstrcatW(newval, semicolon);
5301 ptr = newval + lstrlenW(value) + 1;
5304 lstrcpyW(ptr, data);
5306 if (flags & ENV_MOD_APPEND)
5308 lstrcatW(newval, semicolon);
5309 lstrcatW(newval, value);
5315 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5316 newval = msi_alloc(size);
5319 res = ERROR_OUTOFMEMORY;
5323 lstrcpyW(newval, value);
5326 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5327 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5330 if (env) RegCloseKey(env);
5331 msi_free(deformatted);
5337 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5341 static const WCHAR ExecSeqQuery[] =
5342 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5343 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5344 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5345 if (rc != ERROR_SUCCESS)
5346 return ERROR_SUCCESS;
5348 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5349 msiobj_release(&view->hdr);
5354 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5365 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5369 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5370 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5372 WARN("Source or dest is directory, not moving\n");
5376 if (options == msidbMoveFileOptionsMove)
5378 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5379 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5382 WARN("MoveFile failed: %d\n", GetLastError());
5388 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5389 ret = CopyFileW(source, dest, FALSE);
5392 WARN("CopyFile failed: %d\n", GetLastError());
5400 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5403 DWORD dirlen, pathlen;
5405 ptr = strrchrW(wildcard, '\\');
5406 dirlen = ptr - wildcard + 1;
5408 pathlen = dirlen + lstrlenW(filename) + 1;
5409 path = msi_alloc(pathlen * sizeof(WCHAR));
5411 lstrcpynW(path, wildcard, dirlen + 1);
5412 lstrcatW(path, filename);
5417 static void free_file_entry(FILE_LIST *file)
5419 msi_free(file->source);
5420 msi_free(file->dest);
5424 static void free_list(FILE_LIST *list)
5426 while (!list_empty(&list->entry))
5428 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5430 list_remove(&file->entry);
5431 free_file_entry(file);
5435 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5437 FILE_LIST *new, *file;
5438 LPWSTR ptr, filename;
5441 new = msi_alloc_zero(sizeof(FILE_LIST));
5445 new->source = strdupW(source);
5446 ptr = strrchrW(dest, '\\') + 1;
5447 filename = strrchrW(new->source, '\\') + 1;
5449 new->sourcename = filename;
5452 new->destname = ptr;
5454 new->destname = new->sourcename;
5456 size = (ptr - dest) + lstrlenW(filename) + 1;
5457 new->dest = msi_alloc(size * sizeof(WCHAR));
5460 free_file_entry(new);
5464 lstrcpynW(new->dest, dest, ptr - dest + 1);
5465 lstrcatW(new->dest, filename);
5467 if (list_empty(&files->entry))
5469 list_add_head(&files->entry, &new->entry);
5473 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5475 if (lstrcmpW(source, file->source) < 0)
5477 list_add_before(&file->entry, &new->entry);
5482 list_add_after(&file->entry, &new->entry);
5486 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5488 WIN32_FIND_DATAW wfd;
5492 FILE_LIST files, *file;
5495 hfile = FindFirstFileW(source, &wfd);
5496 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5498 list_init(&files.entry);
5500 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5502 if (is_dot_dir(wfd.cFileName)) continue;
5504 path = wildcard_to_file(source, wfd.cFileName);
5511 add_wildcard(&files, path, dest);
5515 /* no files match the wildcard */
5516 if (list_empty(&files.entry))
5519 /* only the first wildcard match gets renamed to dest */
5520 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5521 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5522 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5529 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5531 while (!list_empty(&files.entry))
5533 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5535 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5537 list_remove(&file->entry);
5538 free_file_entry(file);
5549 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5551 MSIPACKAGE *package = param;
5554 LPWSTR destname = NULL;
5555 LPWSTR sourcedir = NULL, destdir = NULL;
5556 LPWSTR source = NULL, dest = NULL;
5559 BOOL ret, wildcards;
5561 static const WCHAR backslash[] = {'\\',0};
5563 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5564 if (!comp || !comp->Enabled ||
5565 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5567 TRACE("Component not set for install, not moving file\n");
5568 return ERROR_SUCCESS;
5571 sourcename = MSI_RecordGetString(rec, 3);
5572 options = MSI_RecordGetInteger(rec, 7);
5574 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5578 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5584 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5587 source = strdupW(sourcedir);
5593 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5594 source = msi_alloc(size * sizeof(WCHAR));
5598 lstrcpyW(source, sourcedir);
5599 if (source[lstrlenW(source) - 1] != '\\')
5600 lstrcatW(source, backslash);
5601 lstrcatW(source, sourcename);
5604 wildcards = strchrW(source, '*') || strchrW(source, '?');
5606 if (MSI_RecordIsNull(rec, 4))
5610 destname = strdupW(sourcename);
5617 destname = strdupW(MSI_RecordGetString(rec, 4));
5619 reduce_to_longfilename(destname);
5624 size = lstrlenW(destname);
5626 size += lstrlenW(destdir) + 2;
5627 dest = msi_alloc(size * sizeof(WCHAR));
5631 lstrcpyW(dest, destdir);
5632 if (dest[lstrlenW(dest) - 1] != '\\')
5633 lstrcatW(dest, backslash);
5636 lstrcatW(dest, destname);
5638 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5640 ret = CreateDirectoryW(destdir, NULL);
5643 WARN("CreateDirectory failed: %d\n", GetLastError());
5644 return ERROR_SUCCESS;
5649 msi_move_file(source, dest, options);
5651 move_files_wildcard(source, dest, options);
5654 msi_free(sourcedir);
5660 return ERROR_SUCCESS;
5663 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5668 static const WCHAR ExecSeqQuery[] =
5669 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5670 '`','M','o','v','e','F','i','l','e','`',0};
5672 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5673 if (rc != ERROR_SUCCESS)
5674 return ERROR_SUCCESS;
5676 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5677 msiobj_release(&view->hdr);
5682 typedef struct tagMSIASSEMBLY
5685 MSICOMPONENT *component;
5686 MSIFEATURE *feature;
5694 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5696 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5697 LPVOID pvReserved, HMODULE *phModDll);
5699 static BOOL init_functionpointers(void)
5705 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5707 hmscoree = LoadLibraryA("mscoree.dll");
5710 WARN("mscoree.dll not available\n");
5714 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5715 if (!pLoadLibraryShim)
5717 WARN("LoadLibraryShim not available\n");
5718 FreeLibrary(hmscoree);
5722 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5725 WARN("fusion.dll not available\n");
5726 FreeLibrary(hmscoree);
5730 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5732 FreeLibrary(hmscoree);
5736 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5739 IAssemblyCache *cache;
5741 UINT r = ERROR_FUNCTION_FAILED;
5743 TRACE("installing assembly: %s\n", debugstr_w(path));
5745 if (assembly->feature)
5746 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5748 if (assembly->manifest)
5749 FIXME("Manifest unhandled\n");
5751 if (assembly->application)
5753 FIXME("Assembly should be privately installed\n");
5754 return ERROR_SUCCESS;
5757 if (assembly->attributes == msidbAssemblyAttributesWin32)
5759 FIXME("Win32 assemblies not handled\n");
5760 return ERROR_SUCCESS;
5763 hr = pCreateAssemblyCache(&cache, 0);
5767 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5769 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5774 IAssemblyCache_Release(cache);
5778 typedef struct tagASSEMBLY_LIST
5780 MSIPACKAGE *package;
5781 IAssemblyCache *cache;
5782 struct list *assemblies;
5785 typedef struct tagASSEMBLY_NAME
5793 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
5795 ASSEMBLY_NAME *asmname = (ASSEMBLY_NAME *)param;
5796 LPCWSTR name = MSI_RecordGetString(rec, 2);
5797 LPWSTR val = msi_dup_record_field(rec, 3);
5799 static const WCHAR Name[] = {'N','a','m','e',0};
5800 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
5801 static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
5802 static const WCHAR PublicKeyToken[] = {
5803 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
5805 if (!lstrcmpW(name, Name))
5806 asmname->name = val;
5807 else if (!lstrcmpW(name, Version))
5808 asmname->version = val;
5809 else if (!lstrcmpW(name, Culture))
5810 asmname->culture = val;
5811 else if (!lstrcmpW(name, PublicKeyToken))
5812 asmname->pubkeytoken = val;
5816 return ERROR_SUCCESS;
5819 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
5823 *size = lstrlenW(append) + 1;
5824 *str = msi_alloc((*size) * sizeof(WCHAR));
5825 lstrcpyW(*str, append);
5829 (*size) += lstrlenW(append);
5830 *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
5831 lstrcatW(*str, append);
5834 static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
5837 ASSEMBLY_INFO asminfo;
5845 static const WCHAR separator[] = {',',' ',0};
5846 static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
5847 static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
5848 static const WCHAR PublicKeyToken[] = {
5849 'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
5850 static const WCHAR query[] = {
5851 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5852 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
5853 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
5854 '=','\'','%','s','\'',0};
5858 ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
5859 ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
5861 r = MSI_OpenQuery(db, &view, query, comp->Component);
5862 if (r != ERROR_SUCCESS)
5863 return ERROR_SUCCESS;
5865 MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
5866 msiobj_release(&view->hdr);
5870 ERR("No assembly name specified!\n");
5874 append_str(&disp, &size, name.name);
5878 append_str(&disp, &size, separator);
5879 append_str(&disp, &size, Version);
5880 append_str(&disp, &size, name.version);
5885 append_str(&disp, &size, separator);
5886 append_str(&disp, &size, Culture);
5887 append_str(&disp, &size, name.culture);
5890 if (name.pubkeytoken)
5892 append_str(&disp, &size, separator);
5893 append_str(&disp, &size, PublicKeyToken);
5894 append_str(&disp, &size, name.pubkeytoken);
5897 asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
5898 IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
5900 found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
5903 msiobj_release(&view->hdr);
5905 msi_free(name.name);
5906 msi_free(name.version);
5907 msi_free(name.culture);
5908 msi_free(name.pubkeytoken);
5913 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
5915 ASSEMBLY_LIST *list = (ASSEMBLY_LIST *)param;
5916 MSIASSEMBLY *assembly;
5918 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
5920 return ERROR_OUTOFMEMORY;
5922 assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
5924 if (!assembly->component || !assembly->component->Enabled ||
5925 !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5927 TRACE("Component not set for install, not publishing assembly\n");
5929 return ERROR_SUCCESS;
5932 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
5933 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
5935 if (!assembly->file)
5937 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
5938 return ERROR_FUNCTION_FAILED;
5941 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
5942 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
5943 assembly->attributes = MSI_RecordGetInteger(rec, 5);
5944 assembly->installed = check_assembly_installed(list->package->db,
5946 assembly->component);
5948 list_add_head(list->assemblies, &assembly->entry);
5949 return ERROR_SUCCESS;
5952 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
5954 IAssemblyCache *cache = NULL;
5960 static const WCHAR query[] =
5961 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5962 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5964 r = MSI_DatabaseOpenViewW(package->db, query, &view);
5965 if (r != ERROR_SUCCESS)
5966 return ERROR_SUCCESS;
5968 hr = pCreateAssemblyCache(&cache, 0);
5970 return ERROR_FUNCTION_FAILED;
5972 list.package = package;
5974 list.assemblies = assemblies;
5976 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
5977 msiobj_release(&view->hdr);
5979 IAssemblyCache_Release(cache);
5984 static void free_assemblies(struct list *assemblies)
5986 struct list *item, *cursor;
5988 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
5990 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
5992 list_remove(&assembly->entry);
5993 msi_free(assembly->application);
5994 msi_free(assembly->manifest);
5999 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6001 MSIASSEMBLY *assembly;
6003 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6005 if (!lstrcmpW(assembly->file->File, file))
6015 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6016 LPWSTR *path, DWORD *attrs, PVOID user)
6018 MSIASSEMBLY *assembly;
6019 WCHAR temppath[MAX_PATH];
6020 struct list *assemblies = (struct list *)user;
6023 if (!find_assembly(assemblies, file, &assembly))
6026 GetTempPathW(MAX_PATH, temppath);
6027 PathAddBackslashW(temppath);
6028 lstrcatW(temppath, assembly->file->FileName);
6030 if (action == MSICABEXTRACT_BEGINEXTRACT)
6032 if (assembly->installed)
6035 *path = strdupW(temppath);
6036 *attrs = assembly->file->Attributes;
6038 else if (action == MSICABEXTRACT_FILEEXTRACTED)
6040 assembly->installed = TRUE;
6042 r = install_assembly(package, assembly, temppath);
6043 if (r != ERROR_SUCCESS)
6044 ERR("Failed to install assembly\n");
6050 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6053 struct list assemblies = LIST_INIT(assemblies);
6054 MSIASSEMBLY *assembly;
6057 if (!init_functionpointers() || !pCreateAssemblyCache)
6058 return ERROR_FUNCTION_FAILED;
6060 r = load_assemblies(package, &assemblies);
6061 if (r != ERROR_SUCCESS)
6064 if (list_empty(&assemblies))
6067 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6070 r = ERROR_OUTOFMEMORY;
6074 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6076 if (assembly->installed && !mi->is_continuous)
6079 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6080 (assembly->file->IsCompressed && !mi->is_extracted))
6084 r = ready_media(package, assembly->file, mi);
6085 if (r != ERROR_SUCCESS)
6087 ERR("Failed to ready media\n");
6092 data.package = package;
6093 data.cb = installassembly_cb;
6094 data.user = &assemblies;
6096 if (assembly->file->IsCompressed &&
6097 !msi_cabextract(package, mi, &data))
6099 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6100 r = ERROR_FUNCTION_FAILED;
6105 if (!assembly->file->IsCompressed)
6107 LPWSTR source = resolve_file_source(package, assembly->file);
6109 r = install_assembly(package, assembly, source);
6110 if (r != ERROR_SUCCESS)
6111 ERR("Failed to install assembly\n");
6116 /* FIXME: write Installer assembly reg values */
6120 free_assemblies(&assemblies);
6124 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
6125 LPCSTR action, LPCWSTR table )
6127 static const WCHAR query[] = {
6128 'S','E','L','E','C','T',' ','*',' ',
6129 'F','R','O','M',' ','`','%','s','`',0 };
6130 MSIQUERY *view = NULL;
6134 r = MSI_OpenQuery( package->db, &view, query, table );
6135 if (r == ERROR_SUCCESS)
6137 r = MSI_IterateRecords(view, &count, NULL, package);
6138 msiobj_release(&view->hdr);
6142 FIXME("%s -> %u ignored %s table values\n",
6143 action, count, debugstr_w(table));
6145 return ERROR_SUCCESS;
6148 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6150 TRACE("%p\n", package);
6151 return ERROR_SUCCESS;
6154 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6156 static const WCHAR table[] =
6157 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6158 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6161 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6163 static const WCHAR table[] = { 'P','a','t','c','h',0 };
6164 return msi_unimplemented_action_stub( package, "PatchFiles", table );
6167 static UINT ACTION_BindImage( MSIPACKAGE *package )
6169 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6170 return msi_unimplemented_action_stub( package, "BindImage", table );
6173 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6175 static const WCHAR table[] = {
6176 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6177 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6180 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6182 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6183 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6186 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
6188 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
6189 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
6192 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6194 static const WCHAR table[] = {
6195 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6196 return msi_unimplemented_action_stub( package, "DeleteServices", table );
6198 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6200 static const WCHAR table[] = {
6201 'P','r','o','d','u','c','t','I','D',0 };
6202 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
6205 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6207 static const WCHAR table[] = {
6208 'E','n','v','i','r','o','n','m','e','n','t',0 };
6209 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6212 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6214 static const WCHAR table[] = {
6215 'M','s','i','A','s','s','e','m','b','l','y',0 };
6216 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6219 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
6221 static const WCHAR table[] = { 'F','o','n','t',0 };
6222 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
6225 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6227 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6228 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6231 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6233 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6234 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6237 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6239 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6240 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6243 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6245 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6246 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6249 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6251 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6252 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6255 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6257 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6258 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6261 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
6263 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
6264 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
6267 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6269 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
6270 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
6273 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6275 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6276 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6279 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
6281 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
6282 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
6285 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6287 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6288 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6291 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6293 static const WCHAR table[] = { 'A','p','p','I','d',0 };
6294 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6297 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6299 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6300 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6303 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6305 static const WCHAR table[] = { 'M','I','M','E',0 };
6306 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6309 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6311 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6312 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6315 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
6317 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
6318 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6321 static const struct _actions StandardActions[] = {
6322 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6323 { szAppSearch, ACTION_AppSearch },
6324 { szBindImage, ACTION_BindImage },
6325 { szCCPSearch, ACTION_CCPSearch },
6326 { szCostFinalize, ACTION_CostFinalize },
6327 { szCostInitialize, ACTION_CostInitialize },
6328 { szCreateFolders, ACTION_CreateFolders },
6329 { szCreateShortcuts, ACTION_CreateShortcuts },
6330 { szDeleteServices, ACTION_DeleteServices },
6331 { szDisableRollback, NULL },
6332 { szDuplicateFiles, ACTION_DuplicateFiles },
6333 { szExecuteAction, ACTION_ExecuteAction },
6334 { szFileCost, ACTION_FileCost },
6335 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6336 { szForceReboot, ACTION_ForceReboot },
6337 { szInstallAdminPackage, NULL },
6338 { szInstallExecute, ACTION_InstallExecute },
6339 { szInstallExecuteAgain, ACTION_InstallExecute },
6340 { szInstallFiles, ACTION_InstallFiles},
6341 { szInstallFinalize, ACTION_InstallFinalize },
6342 { szInstallInitialize, ACTION_InstallInitialize },
6343 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6344 { szInstallValidate, ACTION_InstallValidate },
6345 { szIsolateComponents, ACTION_IsolateComponents },
6346 { szLaunchConditions, ACTION_LaunchConditions },
6347 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6348 { szMoveFiles, ACTION_MoveFiles },
6349 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6350 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6351 { szInstallODBC, ACTION_InstallODBC },
6352 { szInstallServices, ACTION_InstallServices },
6353 { szPatchFiles, ACTION_PatchFiles },
6354 { szProcessComponents, ACTION_ProcessComponents },
6355 { szPublishComponents, ACTION_PublishComponents },
6356 { szPublishFeatures, ACTION_PublishFeatures },
6357 { szPublishProduct, ACTION_PublishProduct },
6358 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6359 { szRegisterComPlus, ACTION_RegisterComPlus},
6360 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6361 { szRegisterFonts, ACTION_RegisterFonts },
6362 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6363 { szRegisterProduct, ACTION_RegisterProduct },
6364 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6365 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6366 { szRegisterUser, ACTION_RegisterUser },
6367 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6368 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6369 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6370 { szRemoveFiles, ACTION_RemoveFiles },
6371 { szRemoveFolders, ACTION_RemoveFolders },
6372 { szRemoveIniValues, ACTION_RemoveIniValues },
6373 { szRemoveODBC, ACTION_RemoveODBC },
6374 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6375 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6376 { szResolveSource, ACTION_ResolveSource },
6377 { szRMCCPSearch, ACTION_RMCCPSearch },
6378 { szScheduleReboot, NULL },
6379 { szSelfRegModules, ACTION_SelfRegModules },
6380 { szSelfUnregModules, ACTION_SelfUnregModules },
6381 { szSetODBCFolders, NULL },
6382 { szStartServices, ACTION_StartServices },
6383 { szStopServices, ACTION_StopServices },
6384 { szUnpublishComponents, ACTION_UnpublishComponents },
6385 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6386 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6387 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6388 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6389 { szUnregisterFonts, ACTION_UnregisterFonts },
6390 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6391 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6392 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6393 { szValidateProductID, ACTION_ValidateProductID },
6394 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6395 { szWriteIniValues, ACTION_WriteIniValues },
6396 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6400 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6401 UINT* rc, BOOL force )
6407 if (!run && !package->script->CurrentlyScripting)
6412 if (strcmpW(action,szInstallFinalize) == 0 ||
6413 strcmpW(action,szInstallExecute) == 0 ||
6414 strcmpW(action,szInstallExecuteAgain) == 0)
6419 while (StandardActions[i].action != NULL)
6421 if (strcmpW(StandardActions[i].action, action)==0)
6425 ui_actioninfo(package, action, TRUE, 0);
6426 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6427 ui_actioninfo(package, action, FALSE, *rc);
6431 ui_actionstart(package, action);
6432 if (StandardActions[i].handler)
6434 *rc = StandardActions[i].handler(package);
6438 FIXME("unhandled standard action %s\n",debugstr_w(action));
6439 *rc = ERROR_SUCCESS;