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_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
2865 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
2868 if (rc != ERROR_SUCCESS)
2871 if (comp->Attributes & msidbComponentAttributesPermanent)
2873 static const WCHAR szPermKey[] =
2874 { '0','0','0','0','0','0','0','0','0','0','0','0',
2875 '0','0','0','0','0','0','0','0','0','0','0','0',
2876 '0','0','0','0','0','0','0','0',0 };
2878 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2881 if (comp->Action == INSTALLSTATE_LOCAL)
2882 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2888 WCHAR source[MAX_PATH];
2889 WCHAR base[MAX_PATH];
2892 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2893 static const WCHAR query[] = {
2894 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2895 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2896 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2897 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2898 '`','D','i','s','k','I','d','`',0};
2900 file = get_loaded_file(package, comp->KeyPath);
2904 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2905 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2906 ptr2 = strrchrW(source, '\\') + 1;
2907 msiobj_release(&row->hdr);
2909 lstrcpyW(base, package->PackagePath);
2910 ptr = strrchrW(base, '\\');
2913 sourcepath = resolve_file_source(package, file);
2914 ptr = sourcepath + lstrlenW(base);
2915 lstrcpyW(ptr2, ptr);
2916 msi_free(sourcepath);
2918 msi_reg_set_val_str(hkey, squished_pc, source);
2922 else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
2924 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2925 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
2927 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
2931 uirow = MSI_CreateRecord(3);
2932 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2933 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2934 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2935 ui_actiondata(package,szProcessComponents,uirow);
2936 msiobj_release( &uirow->hdr );
2939 return ERROR_SUCCESS;
2950 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2951 LPWSTR lpszName, LONG_PTR lParam)
2954 typelib_struct *tl_struct = (typelib_struct*) lParam;
2955 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2959 if (!IS_INTRESOURCE(lpszName))
2961 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2965 sz = strlenW(tl_struct->source)+4;
2966 sz *= sizeof(WCHAR);
2968 if ((INT_PTR)lpszName == 1)
2969 tl_struct->path = strdupW(tl_struct->source);
2972 tl_struct->path = msi_alloc(sz);
2973 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2976 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2977 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2980 msi_free(tl_struct->path);
2981 tl_struct->path = NULL;
2986 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2987 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2989 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2993 msi_free(tl_struct->path);
2994 tl_struct->path = NULL;
2996 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2997 ITypeLib_Release(tl_struct->ptLib);
3002 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3004 MSIPACKAGE* package = (MSIPACKAGE*)param;
3008 typelib_struct tl_struct;
3013 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3015 component = MSI_RecordGetString(row,3);
3016 comp = get_loaded_component(package,component);
3018 return ERROR_SUCCESS;
3020 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3022 TRACE("Skipping typelib reg due to disabled component\n");
3024 comp->Action = comp->Installed;
3026 return ERROR_SUCCESS;
3029 comp->Action = INSTALLSTATE_LOCAL;
3031 file = get_loaded_file( package, comp->KeyPath );
3033 return ERROR_SUCCESS;
3035 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3039 guid = MSI_RecordGetString(row,1);
3040 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3041 tl_struct.source = strdupW( file->TargetPath );
3042 tl_struct.path = NULL;
3044 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3045 (LONG_PTR)&tl_struct);
3053 helpid = MSI_RecordGetString(row,6);
3056 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3057 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3061 ERR("Failed to register type library %s\n",
3062 debugstr_w(tl_struct.path));
3065 ui_actiondata(package,szRegisterTypeLibraries,row);
3067 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3070 ITypeLib_Release(tl_struct.ptLib);
3071 msi_free(tl_struct.path);
3074 ERR("Failed to load type library %s\n",
3075 debugstr_w(tl_struct.source));
3077 FreeLibrary(module);
3078 msi_free(tl_struct.source);
3082 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3085 ERR("Failed to load type library: %08x\n", hr);
3086 return ERROR_FUNCTION_FAILED;
3089 ITypeLib_Release(tlib);
3092 return ERROR_SUCCESS;
3095 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3098 * OK this is a bit confusing.. I am given a _Component key and I believe
3099 * that the file that is being registered as a type library is the "key file
3100 * of that component" which I interpret to mean "The file in the KeyPath of
3105 static const WCHAR Query[] =
3106 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3107 '`','T','y','p','e','L','i','b','`',0};
3109 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3110 if (rc != ERROR_SUCCESS)
3111 return ERROR_SUCCESS;
3113 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3114 msiobj_release(&view->hdr);
3118 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3120 MSIPACKAGE *package = (MSIPACKAGE*)param;
3121 LPWSTR target_file, target_folder, filename;
3122 LPCWSTR buffer, extension;
3124 static const WCHAR szlnk[]={'.','l','n','k',0};
3125 IShellLinkW *sl = NULL;
3126 IPersistFile *pf = NULL;
3129 buffer = MSI_RecordGetString(row,4);
3130 comp = get_loaded_component(package,buffer);
3132 return ERROR_SUCCESS;
3134 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3136 TRACE("Skipping shortcut creation due to disabled component\n");
3138 comp->Action = comp->Installed;
3140 return ERROR_SUCCESS;
3143 comp->Action = INSTALLSTATE_LOCAL;
3145 ui_actiondata(package,szCreateShortcuts,row);
3147 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3148 &IID_IShellLinkW, (LPVOID *) &sl );
3152 ERR("CLSID_ShellLink not available\n");
3156 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3159 ERR("QueryInterface(IID_IPersistFile) failed\n");
3163 buffer = MSI_RecordGetString(row,2);
3164 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3166 /* may be needed because of a bug somewhere else */
3167 create_full_pathW(target_folder);
3169 filename = msi_dup_record_field( row, 3 );
3170 reduce_to_longfilename(filename);
3172 extension = strchrW(filename,'.');
3173 if (!extension || strcmpiW(extension,szlnk))
3175 int len = strlenW(filename);
3176 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3177 memcpy(filename + len, szlnk, sizeof(szlnk));
3179 target_file = build_directory_name(2, target_folder, filename);
3180 msi_free(target_folder);
3183 buffer = MSI_RecordGetString(row,5);
3184 if (strchrW(buffer,'['))
3187 deformat_string(package,buffer,&deformated);
3188 IShellLinkW_SetPath(sl,deformated);
3189 msi_free(deformated);
3193 FIXME("poorly handled shortcut format, advertised shortcut\n");
3194 IShellLinkW_SetPath(sl,comp->FullKeypath);
3197 if (!MSI_RecordIsNull(row,6))
3200 buffer = MSI_RecordGetString(row,6);
3201 deformat_string(package,buffer,&deformated);
3202 IShellLinkW_SetArguments(sl,deformated);
3203 msi_free(deformated);
3206 if (!MSI_RecordIsNull(row,7))
3208 buffer = MSI_RecordGetString(row,7);
3209 IShellLinkW_SetDescription(sl,buffer);
3212 if (!MSI_RecordIsNull(row,8))
3213 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3215 if (!MSI_RecordIsNull(row,9))
3220 buffer = MSI_RecordGetString(row,9);
3222 Path = build_icon_path(package,buffer);
3223 index = MSI_RecordGetInteger(row,10);
3225 /* no value means 0 */
3226 if (index == MSI_NULL_INTEGER)
3229 IShellLinkW_SetIconLocation(sl,Path,index);
3233 if (!MSI_RecordIsNull(row,11))
3234 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3236 if (!MSI_RecordIsNull(row,12))
3239 buffer = MSI_RecordGetString(row,12);
3240 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3242 IShellLinkW_SetWorkingDirectory(sl,Path);
3246 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3247 IPersistFile_Save(pf,target_file,FALSE);
3249 msi_free(target_file);
3253 IPersistFile_Release( pf );
3255 IShellLinkW_Release( sl );
3257 return ERROR_SUCCESS;
3260 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3265 static const WCHAR Query[] =
3266 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3267 '`','S','h','o','r','t','c','u','t','`',0};
3269 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3270 if (rc != ERROR_SUCCESS)
3271 return ERROR_SUCCESS;
3273 res = CoInitialize( NULL );
3276 ERR("CoInitialize failed\n");
3277 return ERROR_FUNCTION_FAILED;
3280 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3281 msiobj_release(&view->hdr);
3288 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3290 MSIPACKAGE* package = (MSIPACKAGE*)param;
3299 FileName = MSI_RecordGetString(row,1);
3302 ERR("Unable to get FileName\n");
3303 return ERROR_SUCCESS;
3306 FilePath = build_icon_path(package,FileName);
3308 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3310 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3311 FILE_ATTRIBUTE_NORMAL, NULL);
3313 if (the_file == INVALID_HANDLE_VALUE)
3315 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3317 return ERROR_SUCCESS;
3324 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3325 if (rc != ERROR_SUCCESS)
3327 ERR("Failed to get stream\n");
3328 CloseHandle(the_file);
3329 DeleteFileW(FilePath);
3332 WriteFile(the_file,buffer,sz,&write,NULL);
3333 } while (sz == 1024);
3337 CloseHandle(the_file);
3339 uirow = MSI_CreateRecord(1);
3340 MSI_RecordSetStringW(uirow,1,FileName);
3341 ui_actiondata(package,szPublishProduct,uirow);
3342 msiobj_release( &uirow->hdr );
3344 return ERROR_SUCCESS;
3347 static UINT msi_publish_icons(MSIPACKAGE *package)
3352 static const WCHAR query[]= {
3353 'S','E','L','E','C','T',' ','*',' ',
3354 'F','R','O','M',' ','`','I','c','o','n','`',0};
3356 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3357 if (r == ERROR_SUCCESS)
3359 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3360 msiobj_release(&view->hdr);
3363 return ERROR_SUCCESS;
3366 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3372 MSISOURCELISTINFO *info;
3374 static const WCHAR szEmpty[] = {0};
3375 static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0};
3377 r = RegCreateKeyW(hkey, szSourceList, &source);
3378 if (r != ERROR_SUCCESS)
3381 RegCloseKey(source);
3383 buffer = strrchrW(package->PackagePath, '\\') + 1;
3384 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3385 package->Context, MSICODE_PRODUCT,
3386 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3387 if (r != ERROR_SUCCESS)
3390 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3391 package->Context, MSICODE_PRODUCT,
3392 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3393 if (r != ERROR_SUCCESS)
3396 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3397 package->Context, MSICODE_PRODUCT,
3398 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3399 if (r != ERROR_SUCCESS)
3402 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3404 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3405 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3406 info->options, info->value);
3408 MsiSourceListSetInfoW(package->ProductCode, NULL,
3409 info->context, info->options,
3410 info->property, info->value);
3413 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3415 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3416 disk->context, disk->options,
3417 disk->disk_id, disk->volume_label, disk->disk_prompt);
3420 return ERROR_SUCCESS;
3423 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3425 MSIHANDLE hdb, suminfo;
3426 WCHAR guids[MAX_PATH];
3427 WCHAR packcode[SQUISH_GUID_SIZE];
3434 static const WCHAR szProductLanguage[] =
3435 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3436 static const WCHAR szARPProductIcon[] =
3437 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3438 static const WCHAR szProductVersion[] =
3439 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3440 static const WCHAR szAssignment[] =
3441 {'A','s','s','i','g','n','m','e','n','t',0};
3442 static const WCHAR szAdvertiseFlags[] =
3443 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3444 static const WCHAR szClients[] =
3445 {'C','l','i','e','n','t','s',0};
3446 static const WCHAR szColon[] = {':',0};
3448 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3449 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3452 langid = msi_get_property_int(package, szProductLanguage, 0);
3453 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3455 ptr = strrchrW(package->PackagePath, '\\' ) + 1;
3456 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGENAMEW, ptr);
3459 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3461 buffer = msi_dup_property(package, szARPProductIcon);
3464 LPWSTR path = build_icon_path(package,buffer);
3465 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3470 buffer = msi_dup_property(package, szProductVersion);
3473 DWORD verdword = msi_version_str_to_dword(buffer);
3474 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3478 msi_reg_set_val_dword(hkey, szAssignment, 0);
3479 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3480 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3481 msi_reg_set_val_str(hkey, szClients, szColon);
3483 hdb = alloc_msihandle(&package->db->hdr);
3485 return ERROR_NOT_ENOUGH_MEMORY;
3487 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3488 MsiCloseHandle(hdb);
3489 if (r != ERROR_SUCCESS)
3493 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3494 NULL, guids, &size);
3495 if (r != ERROR_SUCCESS)
3498 ptr = strchrW(guids, ';');
3500 squash_guid(guids, packcode);
3501 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3504 MsiCloseHandle(suminfo);
3505 return ERROR_SUCCESS;
3508 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3513 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3515 static const WCHAR szUpgradeCode[] =
3516 {'U','p','g','r','a','d','e','C','o','d','e',0};
3518 upgrade = msi_dup_property(package, szUpgradeCode);
3520 return ERROR_SUCCESS;
3522 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3524 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3525 if (r != ERROR_SUCCESS)
3530 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3531 if (r != ERROR_SUCCESS)
3535 squash_guid(package->ProductCode, squashed_pc);
3536 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3545 static BOOL msi_check_publish(MSIPACKAGE *package)
3547 MSIFEATURE *feature;
3549 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3551 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3558 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3560 MSIFEATURE *feature;
3562 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3564 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3572 * 99% of the work done here is only done for
3573 * advertised installs. However this is where the
3574 * Icon table is processed and written out
3575 * so that is what I am going to do here.
3577 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3583 /* FIXME: also need to publish if the product is in advertise mode */
3584 if (!msi_check_publish(package))
3585 return ERROR_SUCCESS;
3587 rc = MSIREG_OpenProductKey(package->ProductCode, package->Context,
3589 if (rc != ERROR_SUCCESS)
3592 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3594 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, szLocalSid,
3596 if (rc != ERROR_SUCCESS)
3601 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, NULL,
3603 if (rc != ERROR_SUCCESS)
3607 rc = msi_publish_upgrade_code(package);
3608 if (rc != ERROR_SUCCESS)
3611 rc = msi_publish_product_properties(package, hukey);
3612 if (rc != ERROR_SUCCESS)
3615 rc = msi_publish_sourcelist(package, hukey);
3616 if (rc != ERROR_SUCCESS)
3619 rc = msi_publish_icons(package);
3623 RegCloseKey(hudkey);
3628 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3630 MSIPACKAGE *package = (MSIPACKAGE*)param;
3631 LPCWSTR component, section, key, value, identifier, dirproperty;
3632 LPWSTR deformated_section, deformated_key, deformated_value;
3633 LPWSTR folder, filename, fullname = NULL;
3634 LPCWSTR filenameptr;
3638 static const WCHAR szWindowsFolder[] =
3639 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3641 component = MSI_RecordGetString(row, 8);
3642 comp = get_loaded_component(package,component);
3644 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3646 TRACE("Skipping ini file due to disabled component %s\n",
3647 debugstr_w(component));
3649 comp->Action = comp->Installed;
3651 return ERROR_SUCCESS;
3654 comp->Action = INSTALLSTATE_LOCAL;
3656 identifier = MSI_RecordGetString(row,1);
3657 dirproperty = MSI_RecordGetString(row,3);
3658 section = MSI_RecordGetString(row,4);
3659 key = MSI_RecordGetString(row,5);
3660 value = MSI_RecordGetString(row,6);
3661 action = MSI_RecordGetInteger(row,7);
3663 deformat_string(package,section,&deformated_section);
3664 deformat_string(package,key,&deformated_key);
3665 deformat_string(package,value,&deformated_value);
3667 filename = msi_dup_record_field(row, 2);
3668 if (filename && (filenameptr = strchrW(filename, '|')))
3671 filenameptr = filename;
3675 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3677 folder = msi_dup_property( package, dirproperty );
3680 folder = msi_dup_property( package, szWindowsFolder );
3684 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3688 fullname = build_directory_name(2, folder, filenameptr);
3692 TRACE("Adding value %s to section %s in %s\n",
3693 debugstr_w(deformated_key), debugstr_w(deformated_section),
3694 debugstr_w(fullname));
3695 WritePrivateProfileStringW(deformated_section, deformated_key,
3696 deformated_value, fullname);
3698 else if (action == 1)
3701 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3702 returned, 10, fullname);
3703 if (returned[0] == 0)
3705 TRACE("Adding value %s to section %s in %s\n",
3706 debugstr_w(deformated_key), debugstr_w(deformated_section),
3707 debugstr_w(fullname));
3709 WritePrivateProfileStringW(deformated_section, deformated_key,
3710 deformated_value, fullname);
3713 else if (action == 3)
3714 FIXME("Append to existing section not yet implemented\n");
3716 uirow = MSI_CreateRecord(4);
3717 MSI_RecordSetStringW(uirow,1,identifier);
3718 MSI_RecordSetStringW(uirow,2,deformated_section);
3719 MSI_RecordSetStringW(uirow,3,deformated_key);
3720 MSI_RecordSetStringW(uirow,4,deformated_value);
3721 ui_actiondata(package,szWriteIniValues,uirow);
3722 msiobj_release( &uirow->hdr );
3728 msi_free(deformated_key);
3729 msi_free(deformated_value);
3730 msi_free(deformated_section);
3731 return ERROR_SUCCESS;
3734 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3738 static const WCHAR ExecSeqQuery[] =
3739 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3740 '`','I','n','i','F','i','l','e','`',0};
3742 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3743 if (rc != ERROR_SUCCESS)
3745 TRACE("no IniFile table\n");
3746 return ERROR_SUCCESS;
3749 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3750 msiobj_release(&view->hdr);
3754 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3756 MSIPACKAGE *package = (MSIPACKAGE*)param;
3761 static const WCHAR ExeStr[] =
3762 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3763 static const WCHAR close[] = {'\"',0};
3765 PROCESS_INFORMATION info;
3770 memset(&si,0,sizeof(STARTUPINFOW));
3772 filename = MSI_RecordGetString(row,1);
3773 file = get_loaded_file( package, filename );
3777 ERR("Unable to find file id %s\n",debugstr_w(filename));
3778 return ERROR_SUCCESS;
3781 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3783 FullName = msi_alloc(len*sizeof(WCHAR));
3784 strcpyW(FullName,ExeStr);
3785 strcatW( FullName, file->TargetPath );
3786 strcatW(FullName,close);
3788 TRACE("Registering %s\n",debugstr_w(FullName));
3789 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3794 CloseHandle(info.hThread);
3795 msi_dialog_check_messages(info.hProcess);
3796 CloseHandle(info.hProcess);
3802 uirow = MSI_CreateRecord( 2 );
3803 uipath = strdupW( file->TargetPath );
3804 p = strrchrW(uipath,'\\');
3807 MSI_RecordSetStringW( uirow, 1, &p[1] );
3808 MSI_RecordSetStringW( uirow, 2, uipath);
3809 ui_actiondata( package, szSelfRegModules, uirow);
3810 msiobj_release( &uirow->hdr );
3812 /* FIXME: call ui_progress? */
3814 return ERROR_SUCCESS;
3817 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3821 static const WCHAR ExecSeqQuery[] =
3822 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3823 '`','S','e','l','f','R','e','g','`',0};
3825 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3826 if (rc != ERROR_SUCCESS)
3828 TRACE("no SelfReg table\n");
3829 return ERROR_SUCCESS;
3832 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3833 msiobj_release(&view->hdr);
3835 return ERROR_SUCCESS;
3838 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3840 MSIFEATURE *feature;
3843 HKEY userdata = NULL;
3845 if (!msi_check_publish(package))
3846 return ERROR_SUCCESS;
3848 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3850 if (rc != ERROR_SUCCESS)
3853 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3855 if (rc != ERROR_SUCCESS)
3858 /* here the guids are base 85 encoded */
3859 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3865 BOOL absent = FALSE;
3868 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3869 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3870 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3874 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3878 if (feature->Feature_Parent)
3879 size += strlenW( feature->Feature_Parent )+2;
3881 data = msi_alloc(size * sizeof(WCHAR));
3884 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3886 MSICOMPONENT* component = cl->component;
3890 if (component->ComponentId)
3892 TRACE("From %s\n",debugstr_w(component->ComponentId));
3893 CLSIDFromString(component->ComponentId, &clsid);
3894 encode_base85_guid(&clsid,buf);
3895 TRACE("to %s\n",debugstr_w(buf));
3900 if (feature->Feature_Parent)
3902 static const WCHAR sep[] = {'\2',0};
3904 strcatW(data,feature->Feature_Parent);
3907 msi_reg_set_val_str( userdata, feature->Feature, data );
3911 if (feature->Feature_Parent)
3912 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3915 static const WCHAR emptyW[] = {0};
3916 size += sizeof(WCHAR);
3917 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3918 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : emptyW),size);
3922 size += 2*sizeof(WCHAR);
3923 data = msi_alloc(size);
3926 if (feature->Feature_Parent)
3927 strcpyW( &data[1], feature->Feature_Parent );
3928 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3934 uirow = MSI_CreateRecord( 1 );
3935 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3936 ui_actiondata( package, szPublishFeatures, uirow);
3937 msiobj_release( &uirow->hdr );
3938 /* FIXME: call ui_progress? */
3943 RegCloseKey(userdata);
3947 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3952 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3954 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3956 if (r == ERROR_SUCCESS)
3958 RegDeleteValueW(hkey, feature->Feature);
3962 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3964 if (r == ERROR_SUCCESS)
3966 RegDeleteValueW(hkey, feature->Feature);
3970 return ERROR_SUCCESS;
3973 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3975 MSIFEATURE *feature;
3977 if (!msi_check_unpublish(package))
3978 return ERROR_SUCCESS;
3980 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3982 msi_unpublish_feature(package, feature);
3985 return ERROR_SUCCESS;
3988 static UINT msi_get_local_package_name( LPWSTR path )
3990 static const WCHAR szInstaller[] = {
3991 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3992 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3996 time = GetTickCount();
3997 GetWindowsDirectoryW( path, MAX_PATH );
3998 lstrcatW( path, szInstaller );
3999 CreateDirectoryW( path, NULL );
4001 len = lstrlenW(path);
4002 for (i=0; i<0x10000; i++)
4004 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
4005 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
4006 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
4007 if (handle != INVALID_HANDLE_VALUE)
4009 CloseHandle(handle);
4012 if (GetLastError() != ERROR_FILE_EXISTS &&
4013 GetLastError() != ERROR_SHARING_VIOLATION)
4014 return ERROR_FUNCTION_FAILED;
4017 return ERROR_SUCCESS;
4020 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
4022 WCHAR packagefile[MAX_PATH];
4025 r = msi_get_local_package_name( packagefile );
4026 if (r != ERROR_SUCCESS)
4029 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
4031 r = CopyFileW( package->db->path, packagefile, FALSE);
4035 ERR("Unable to copy package (%s -> %s) (error %d)\n",
4036 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
4037 return ERROR_FUNCTION_FAILED;
4040 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
4042 return ERROR_SUCCESS;
4045 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4047 LPWSTR prop, val, key;
4053 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4054 static const WCHAR szWindowsInstaller[] =
4055 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4056 static const WCHAR modpath_fmt[] =
4057 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4058 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4059 static const WCHAR szModifyPath[] =
4060 {'M','o','d','i','f','y','P','a','t','h',0};
4061 static const WCHAR szUninstallString[] =
4062 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4063 static const WCHAR szEstimatedSize[] =
4064 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4065 static const WCHAR szProductLanguage[] =
4066 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4067 static const WCHAR szProductVersion[] =
4068 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4069 static const WCHAR szProductName[] =
4070 {'P','r','o','d','u','c','t','N','a','m','e',0};
4071 static const WCHAR szDisplayName[] =
4072 {'D','i','s','p','l','a','y','N','a','m','e',0};
4073 static const WCHAR szDisplayVersion[] =
4074 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4075 static const WCHAR szManufacturer[] =
4076 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4078 static const LPCSTR propval[] = {
4079 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4080 "ARPCONTACT", "Contact",
4081 "ARPCOMMENTS", "Comments",
4082 "ProductName", "DisplayName",
4083 "ProductVersion", "DisplayVersion",
4084 "ARPHELPLINK", "HelpLink",
4085 "ARPHELPTELEPHONE", "HelpTelephone",
4086 "ARPINSTALLLOCATION", "InstallLocation",
4087 "SourceDir", "InstallSource",
4088 "Manufacturer", "Publisher",
4089 "ARPREADME", "Readme",
4091 "ARPURLINFOABOUT", "URLInfoAbout",
4092 "ARPURLUPDATEINFO", "URLUpdateInfo",
4095 const LPCSTR *p = propval;
4099 prop = strdupAtoW(*p++);
4100 key = strdupAtoW(*p++);
4101 val = msi_dup_property(package, prop);
4102 msi_reg_set_val_str(hkey, key, val);
4108 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4110 size = deformat_string(package, modpath_fmt, &buffer);
4111 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4112 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4115 /* FIXME: Write real Estimated Size when we have it */
4116 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4118 buffer = msi_dup_property(package, szProductName);
4119 msi_reg_set_val_str(hkey, szDisplayName, buffer);
4122 buffer = msi_dup_property(package, cszSourceDir);
4123 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4126 buffer = msi_dup_property(package, szManufacturer);
4127 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4130 GetLocalTime(&systime);
4131 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4132 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4134 langid = msi_get_property_int(package, szProductLanguage, 0);
4135 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4137 buffer = msi_dup_property(package, szProductVersion);
4138 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4141 DWORD verdword = msi_version_str_to_dword(buffer);
4143 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4144 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4145 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4149 return ERROR_SUCCESS;
4152 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4154 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4155 LPWSTR upgrade_code;
4160 static const WCHAR szUpgradeCode[] = {
4161 'U','p','g','r','a','d','e','C','o','d','e',0};
4163 /* FIXME: also need to publish if the product is in advertise mode */
4164 if (!msi_check_publish(package))
4165 return ERROR_SUCCESS;
4167 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4168 if (rc != ERROR_SUCCESS)
4171 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4173 rc = MSIREG_OpenInstallProps(package->ProductCode, szLocalSid,
4175 if (rc != ERROR_SUCCESS)
4180 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
4181 if (rc != ERROR_SUCCESS)
4185 msi_make_package_local(package, props);
4187 rc = msi_publish_install_properties(package, hkey);
4188 if (rc != ERROR_SUCCESS)
4191 rc = msi_publish_install_properties(package, props);
4192 if (rc != ERROR_SUCCESS)
4195 upgrade_code = msi_dup_property(package, szUpgradeCode);
4198 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4199 squash_guid(package->ProductCode, squashed_pc);
4200 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4201 RegCloseKey(upgrade);
4202 msi_free(upgrade_code);
4208 return ERROR_SUCCESS;
4211 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4213 return execute_script(package,INSTALL_SCRIPT);
4216 static UINT msi_unpublish_product(MSIPACKAGE *package)
4219 LPWSTR remove = NULL;
4220 LPWSTR *features = NULL;
4221 BOOL full_uninstall = TRUE;
4222 MSIFEATURE *feature;
4224 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4225 static const WCHAR szAll[] = {'A','L','L',0};
4226 static const WCHAR szUpgradeCode[] =
4227 {'U','p','g','r','a','d','e','C','o','d','e',0};
4229 remove = msi_dup_property(package, szRemove);
4231 return ERROR_SUCCESS;
4233 features = msi_split_string(remove, ',');
4237 ERR("REMOVE feature list is empty!\n");
4238 return ERROR_FUNCTION_FAILED;
4241 if (!lstrcmpW(features[0], szAll))
4242 full_uninstall = TRUE;
4245 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4247 if (feature->Action != INSTALLSTATE_ABSENT)
4248 full_uninstall = FALSE;
4252 if (!full_uninstall)
4255 MSIREG_DeleteProductKey(package->ProductCode);
4256 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4257 MSIREG_DeleteUninstallKey(package->ProductCode);
4259 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4261 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4262 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4266 MSIREG_DeleteUserProductKey(package->ProductCode);
4267 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4270 upgrade = msi_dup_property(package, szUpgradeCode);
4273 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4280 return ERROR_SUCCESS;
4283 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4287 rc = msi_unpublish_product(package);
4288 if (rc != ERROR_SUCCESS)
4291 /* turn off scheduling */
4292 package->script->CurrentlyScripting= FALSE;
4294 /* first do the same as an InstallExecute */
4295 rc = ACTION_InstallExecute(package);
4296 if (rc != ERROR_SUCCESS)
4299 /* then handle Commit Actions */
4300 rc = execute_script(package,COMMIT_SCRIPT);
4305 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4307 static const WCHAR RunOnce[] = {
4308 'S','o','f','t','w','a','r','e','\\',
4309 'M','i','c','r','o','s','o','f','t','\\',
4310 'W','i','n','d','o','w','s','\\',
4311 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4312 'R','u','n','O','n','c','e',0};
4313 static const WCHAR InstallRunOnce[] = {
4314 'S','o','f','t','w','a','r','e','\\',
4315 'M','i','c','r','o','s','o','f','t','\\',
4316 'W','i','n','d','o','w','s','\\',
4317 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4318 'I','n','s','t','a','l','l','e','r','\\',
4319 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4321 static const WCHAR msiexec_fmt[] = {
4323 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4324 '\"','%','s','\"',0};
4325 static const WCHAR install_fmt[] = {
4326 '/','I',' ','\"','%','s','\"',' ',
4327 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4328 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4329 WCHAR buffer[256], sysdir[MAX_PATH];
4331 WCHAR squished_pc[100];
4333 squash_guid(package->ProductCode,squished_pc);
4335 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4336 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4337 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4340 msi_reg_set_val_str( hkey, squished_pc, buffer );
4343 TRACE("Reboot command %s\n",debugstr_w(buffer));
4345 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4346 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4348 msi_reg_set_val_str( hkey, squished_pc, buffer );
4351 return ERROR_INSTALL_SUSPEND;
4354 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4360 * We are currently doing what should be done here in the top level Install
4361 * however for Administrative and uninstalls this step will be needed
4363 if (!package->PackagePath)
4364 return ERROR_SUCCESS;
4366 msi_set_sourcedir_props(package, TRUE);
4368 attrib = GetFileAttributesW(package->db->path);
4369 if (attrib == INVALID_FILE_ATTRIBUTES)
4375 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4376 package->Context, MSICODE_PRODUCT,
4377 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4378 if (rc == ERROR_MORE_DATA)
4380 prompt = msi_alloc(size * sizeof(WCHAR));
4381 MsiSourceListGetInfoW(package->ProductCode, NULL,
4382 package->Context, MSICODE_PRODUCT,
4383 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4386 prompt = strdupW(package->db->path);
4388 msg = generate_error_string(package,1302,1,prompt);
4389 while(attrib == INVALID_FILE_ATTRIBUTES)
4391 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4394 rc = ERROR_INSTALL_USEREXIT;
4397 attrib = GetFileAttributesW(package->db->path);
4403 return ERROR_SUCCESS;
4408 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4415 static const WCHAR szPropKeys[][80] =
4417 {'P','r','o','d','u','c','t','I','D',0},
4418 {'U','S','E','R','N','A','M','E',0},
4419 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4423 static const WCHAR szRegKeys[][80] =
4425 {'P','r','o','d','u','c','t','I','D',0},
4426 {'R','e','g','O','w','n','e','r',0},
4427 {'R','e','g','C','o','m','p','a','n','y',0},
4431 if (msi_check_unpublish(package))
4433 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4434 return ERROR_SUCCESS;
4437 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4439 return ERROR_SUCCESS;
4441 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4442 rc = MSIREG_OpenInstallProps(package->ProductCode, szLocalSid, &hkey, TRUE);
4444 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &hkey, TRUE);
4446 if (rc != ERROR_SUCCESS)
4449 for( i = 0; szPropKeys[i][0]; i++ )
4451 buffer = msi_dup_property( package, szPropKeys[i] );
4452 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4457 msi_free(productid);
4460 /* FIXME: call ui_actiondata */
4466 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4470 package->script->InWhatSequence |= SEQUENCE_EXEC;
4471 rc = ACTION_ProcessExecSequence(package,FALSE);
4476 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4478 MSIPACKAGE *package = (MSIPACKAGE*)param;
4479 LPCWSTR compgroupid=NULL;
4480 LPCWSTR feature=NULL;
4481 LPCWSTR text = NULL;
4482 LPCWSTR qualifier = NULL;
4483 LPCWSTR component = NULL;
4484 LPWSTR advertise = NULL;
4485 LPWSTR output = NULL;
4487 UINT rc = ERROR_SUCCESS;
4492 component = MSI_RecordGetString(rec,3);
4493 comp = get_loaded_component(package,component);
4495 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4496 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4497 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4499 TRACE("Skipping: Component %s not scheduled for install\n",
4500 debugstr_w(component));
4502 return ERROR_SUCCESS;
4505 compgroupid = MSI_RecordGetString(rec,1);
4506 qualifier = MSI_RecordGetString(rec,2);
4508 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4509 if (rc != ERROR_SUCCESS)
4512 text = MSI_RecordGetString(rec,4);
4513 feature = MSI_RecordGetString(rec,5);
4515 advertise = create_component_advertise_string(package, comp, feature);
4517 sz = strlenW(advertise);
4520 sz += lstrlenW(text);
4523 sz *= sizeof(WCHAR);
4525 output = msi_alloc_zero(sz);
4526 strcpyW(output,advertise);
4527 msi_free(advertise);
4530 strcatW(output,text);
4532 msi_reg_set_val_multi_str( hkey, qualifier, output );
4539 uirow = MSI_CreateRecord( 2 );
4540 MSI_RecordSetStringW( uirow, 1, compgroupid );
4541 MSI_RecordSetStringW( uirow, 2, qualifier);
4542 ui_actiondata( package, szPublishComponents, uirow);
4543 msiobj_release( &uirow->hdr );
4544 /* FIXME: call ui_progress? */
4550 * At present I am ignorning the advertised components part of this and only
4551 * focusing on the qualified component sets
4553 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4557 static const WCHAR ExecSeqQuery[] =
4558 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4559 '`','P','u','b','l','i','s','h',
4560 'C','o','m','p','o','n','e','n','t','`',0};
4562 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4563 if (rc != ERROR_SUCCESS)
4564 return ERROR_SUCCESS;
4566 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4567 msiobj_release(&view->hdr);
4572 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4574 MSIPACKAGE *package = (MSIPACKAGE*)param;
4577 SC_HANDLE hscm, service = NULL;
4578 LPCWSTR comp, depends, pass;
4579 LPWSTR name = NULL, disp = NULL;
4580 LPCWSTR load_order, serv_name, key;
4581 DWORD serv_type, start_type;
4584 static const WCHAR query[] =
4585 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4586 '`','C','o','m','p','o','n','e','n','t','`',' ',
4587 'W','H','E','R','E',' ',
4588 '`','C','o','m','p','o','n','e','n','t','`',' ',
4589 '=','\'','%','s','\'',0};
4591 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4594 ERR("Failed to open the SC Manager!\n");
4598 start_type = MSI_RecordGetInteger(rec, 5);
4599 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4602 depends = MSI_RecordGetString(rec, 8);
4603 if (depends && *depends)
4604 FIXME("Dependency list unhandled!\n");
4606 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4607 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4608 serv_type = MSI_RecordGetInteger(rec, 4);
4609 err_control = MSI_RecordGetInteger(rec, 6);
4610 load_order = MSI_RecordGetString(rec, 7);
4611 serv_name = MSI_RecordGetString(rec, 9);
4612 pass = MSI_RecordGetString(rec, 10);
4613 comp = MSI_RecordGetString(rec, 12);
4615 /* fetch the service path */
4616 row = MSI_QueryGetRecord(package->db, query, comp);
4619 ERR("Control query failed!\n");
4623 key = MSI_RecordGetString(row, 6);
4625 file = get_loaded_file(package, key);
4626 msiobj_release(&row->hdr);
4629 ERR("Failed to load the service file\n");
4633 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4634 start_type, err_control, file->TargetPath,
4635 load_order, NULL, NULL, serv_name, pass);
4638 if (GetLastError() != ERROR_SERVICE_EXISTS)
4639 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4643 CloseServiceHandle(service);
4644 CloseServiceHandle(hscm);
4648 return ERROR_SUCCESS;
4651 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4655 static const WCHAR ExecSeqQuery[] =
4656 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4657 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4659 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4660 if (rc != ERROR_SUCCESS)
4661 return ERROR_SUCCESS;
4663 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4664 msiobj_release(&view->hdr);
4669 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4670 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4672 LPCWSTR *vector, *temp_vector;
4676 static const WCHAR separator[] = {'[','~',']',0};
4679 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4684 vector = msi_alloc(sizeof(LPWSTR));
4692 vector[*numargs - 1] = p;
4694 if ((q = strstrW(p, separator)))
4698 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4704 vector = temp_vector;
4713 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4715 MSIPACKAGE *package = (MSIPACKAGE *)param;
4717 SC_HANDLE scm, service = NULL;
4718 LPCWSTR name, *vector = NULL;
4720 DWORD event, numargs;
4721 UINT r = ERROR_FUNCTION_FAILED;
4723 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4724 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4725 return ERROR_SUCCESS;
4727 name = MSI_RecordGetString(rec, 2);
4728 event = MSI_RecordGetInteger(rec, 3);
4729 args = strdupW(MSI_RecordGetString(rec, 4));
4731 if (!(event & msidbServiceControlEventStart))
4732 return ERROR_SUCCESS;
4734 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4737 ERR("Failed to open the service control manager\n");
4741 service = OpenServiceW(scm, name, SERVICE_START);
4744 ERR("Failed to open service %s\n", debugstr_w(name));
4748 vector = msi_service_args_to_vector(args, &numargs);
4750 if (!StartServiceW(service, numargs, vector))
4752 ERR("Failed to start service %s\n", debugstr_w(name));
4759 CloseServiceHandle(service);
4760 CloseServiceHandle(scm);
4767 static UINT ACTION_StartServices( MSIPACKAGE *package )
4772 static const WCHAR query[] = {
4773 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4774 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4776 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4777 if (rc != ERROR_SUCCESS)
4778 return ERROR_SUCCESS;
4780 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4781 msiobj_release(&view->hdr);
4786 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4788 DWORD i, needed, count;
4789 ENUM_SERVICE_STATUSW *dependencies;
4793 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4794 0, &needed, &count))
4797 if (GetLastError() != ERROR_MORE_DATA)
4800 dependencies = msi_alloc(needed);
4804 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4805 needed, &needed, &count))
4808 for (i = 0; i < count; i++)
4810 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4811 SERVICE_STOP | SERVICE_QUERY_STATUS);
4815 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4822 msi_free(dependencies);
4826 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4828 MSIPACKAGE *package = (MSIPACKAGE *)param;
4830 SERVICE_STATUS status;
4831 SERVICE_STATUS_PROCESS ssp;
4832 SC_HANDLE scm = NULL, service = NULL;
4834 DWORD event, needed;
4836 event = MSI_RecordGetInteger(rec, 3);
4837 if (!(event & msidbServiceControlEventStop))
4838 return ERROR_SUCCESS;
4840 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4841 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4842 return ERROR_SUCCESS;
4844 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4845 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4846 args = strdupW(MSI_RecordGetString(rec, 4));
4848 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4851 WARN("Failed to open the SCM: %d\n", GetLastError());
4855 service = OpenServiceW(scm, name,
4857 SERVICE_QUERY_STATUS |
4858 SERVICE_ENUMERATE_DEPENDENTS);
4861 WARN("Failed to open service (%s): %d\n",
4862 debugstr_w(name), GetLastError());
4866 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4867 sizeof(SERVICE_STATUS_PROCESS), &needed))
4869 WARN("Failed to query service status (%s): %d\n",
4870 debugstr_w(name), GetLastError());
4874 if (ssp.dwCurrentState == SERVICE_STOPPED)
4877 stop_service_dependents(scm, service);
4879 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4880 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4883 CloseServiceHandle(service);
4884 CloseServiceHandle(scm);
4888 return ERROR_SUCCESS;
4891 static UINT ACTION_StopServices( MSIPACKAGE *package )
4896 static const WCHAR query[] = {
4897 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4898 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4900 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4901 if (rc != ERROR_SUCCESS)
4902 return ERROR_SUCCESS;
4904 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4905 msiobj_release(&view->hdr);
4910 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4914 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4916 if (!lstrcmpW(file->File, filename))
4923 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4925 MSIPACKAGE *package = (MSIPACKAGE*)param;
4926 LPWSTR driver, driver_path, ptr;
4927 WCHAR outpath[MAX_PATH];
4928 MSIFILE *driver_file, *setup_file;
4931 UINT r = ERROR_SUCCESS;
4933 static const WCHAR driver_fmt[] = {
4934 'D','r','i','v','e','r','=','%','s',0};
4935 static const WCHAR setup_fmt[] = {
4936 'S','e','t','u','p','=','%','s',0};
4937 static const WCHAR usage_fmt[] = {
4938 'F','i','l','e','U','s','a','g','e','=','1',0};
4940 desc = MSI_RecordGetString(rec, 3);
4942 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4943 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4945 if (!driver_file || !setup_file)
4947 ERR("ODBC Driver entry not found!\n");
4948 return ERROR_FUNCTION_FAILED;
4951 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4952 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4953 lstrlenW(usage_fmt) + 1;
4954 driver = msi_alloc(len * sizeof(WCHAR));
4956 return ERROR_OUTOFMEMORY;
4959 lstrcpyW(ptr, desc);
4960 ptr += lstrlenW(ptr) + 1;
4962 sprintfW(ptr, driver_fmt, driver_file->FileName);
4963 ptr += lstrlenW(ptr) + 1;
4965 sprintfW(ptr, setup_fmt, setup_file->FileName);
4966 ptr += lstrlenW(ptr) + 1;
4968 lstrcpyW(ptr, usage_fmt);
4969 ptr += lstrlenW(ptr) + 1;
4972 driver_path = strdupW(driver_file->TargetPath);
4973 ptr = strrchrW(driver_path, '\\');
4974 if (ptr) *ptr = '\0';
4976 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4977 NULL, ODBC_INSTALL_COMPLETE, &usage))
4979 ERR("Failed to install SQL driver!\n");
4980 r = ERROR_FUNCTION_FAILED;
4984 msi_free(driver_path);
4989 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4991 MSIPACKAGE *package = (MSIPACKAGE*)param;
4992 LPWSTR translator, translator_path, ptr;
4993 WCHAR outpath[MAX_PATH];
4994 MSIFILE *translator_file, *setup_file;
4997 UINT r = ERROR_SUCCESS;
4999 static const WCHAR translator_fmt[] = {
5000 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5001 static const WCHAR setup_fmt[] = {
5002 'S','e','t','u','p','=','%','s',0};
5004 desc = MSI_RecordGetString(rec, 3);
5006 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5007 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5009 if (!translator_file || !setup_file)
5011 ERR("ODBC Translator entry not found!\n");
5012 return ERROR_FUNCTION_FAILED;
5015 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
5016 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
5017 translator = msi_alloc(len * sizeof(WCHAR));
5019 return ERROR_OUTOFMEMORY;
5022 lstrcpyW(ptr, desc);
5023 ptr += lstrlenW(ptr) + 1;
5025 sprintfW(ptr, translator_fmt, translator_file->FileName);
5026 ptr += lstrlenW(ptr) + 1;
5028 sprintfW(ptr, setup_fmt, setup_file->FileName);
5029 ptr += lstrlenW(ptr) + 1;
5032 translator_path = strdupW(translator_file->TargetPath);
5033 ptr = strrchrW(translator_path, '\\');
5034 if (ptr) *ptr = '\0';
5036 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5037 NULL, ODBC_INSTALL_COMPLETE, &usage))
5039 ERR("Failed to install SQL translator!\n");
5040 r = ERROR_FUNCTION_FAILED;
5043 msi_free(translator);
5044 msi_free(translator_path);
5049 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5052 LPCWSTR desc, driver;
5053 WORD request = ODBC_ADD_SYS_DSN;
5056 UINT r = ERROR_SUCCESS;
5058 static const WCHAR attrs_fmt[] = {
5059 'D','S','N','=','%','s',0 };
5061 desc = MSI_RecordGetString(rec, 3);
5062 driver = MSI_RecordGetString(rec, 4);
5063 registration = MSI_RecordGetInteger(rec, 5);
5065 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5066 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5068 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5069 attrs = msi_alloc(len * sizeof(WCHAR));
5071 return ERROR_OUTOFMEMORY;
5073 sprintfW(attrs, attrs_fmt, desc);
5074 attrs[len - 1] = '\0';
5076 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5078 ERR("Failed to install SQL data source!\n");
5079 r = ERROR_FUNCTION_FAILED;
5087 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5092 static const WCHAR driver_query[] = {
5093 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5094 'O','D','B','C','D','r','i','v','e','r',0 };
5096 static const WCHAR translator_query[] = {
5097 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5098 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5100 static const WCHAR source_query[] = {
5101 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5102 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5104 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5105 if (rc != ERROR_SUCCESS)
5106 return ERROR_SUCCESS;
5108 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5109 msiobj_release(&view->hdr);
5111 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5112 if (rc != ERROR_SUCCESS)
5113 return ERROR_SUCCESS;
5115 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5116 msiobj_release(&view->hdr);
5118 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5119 if (rc != ERROR_SUCCESS)
5120 return ERROR_SUCCESS;
5122 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5123 msiobj_release(&view->hdr);
5128 #define ENV_ACT_SETALWAYS 0x1
5129 #define ENV_ACT_SETABSENT 0x2
5130 #define ENV_ACT_REMOVE 0x4
5131 #define ENV_ACT_REMOVEMATCH 0x8
5133 #define ENV_MOD_MACHINE 0x20000000
5134 #define ENV_MOD_APPEND 0x40000000
5135 #define ENV_MOD_PREFIX 0x80000000
5136 #define ENV_MOD_MASK 0xC0000000
5138 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5140 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5142 LPCWSTR cptr = *name;
5143 LPCWSTR ptr = *value;
5145 static const WCHAR prefix[] = {'[','~',']',0};
5146 static const int prefix_len = 3;
5152 *flags |= ENV_ACT_SETALWAYS;
5153 else if (*cptr == '+')
5154 *flags |= ENV_ACT_SETABSENT;
5155 else if (*cptr == '-')
5156 *flags |= ENV_ACT_REMOVE;
5157 else if (*cptr == '!')
5158 *flags |= ENV_ACT_REMOVEMATCH;
5159 else if (*cptr == '*')
5160 *flags |= ENV_MOD_MACHINE;
5170 ERR("Missing environment variable\n");
5171 return ERROR_FUNCTION_FAILED;
5174 if (!strncmpW(ptr, prefix, prefix_len))
5176 *flags |= ENV_MOD_APPEND;
5177 *value += lstrlenW(prefix);
5179 else if (lstrlenW(*value) >= prefix_len)
5181 ptr += lstrlenW(ptr) - prefix_len;
5182 if (!lstrcmpW(ptr, prefix))
5184 *flags |= ENV_MOD_PREFIX;
5185 /* the "[~]" will be removed by deformat_string */;
5190 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5191 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5192 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5193 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5195 ERR("Invalid flags: %08x\n", *flags);
5196 return ERROR_FUNCTION_FAILED;
5199 return ERROR_SUCCESS;
5202 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5204 MSIPACKAGE *package = param;
5205 LPCWSTR name, value;
5206 LPWSTR data = NULL, newval = NULL;
5207 LPWSTR deformatted = NULL, ptr;
5208 DWORD flags, type, size;
5210 HKEY env = NULL, root;
5211 LPCWSTR environment;
5213 static const WCHAR user_env[] =
5214 {'E','n','v','i','r','o','n','m','e','n','t',0};
5215 static const WCHAR machine_env[] =
5216 {'S','y','s','t','e','m','\\',
5217 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5218 'C','o','n','t','r','o','l','\\',
5219 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5220 'E','n','v','i','r','o','n','m','e','n','t',0};
5221 static const WCHAR semicolon[] = {';',0};
5223 name = MSI_RecordGetString(rec, 2);
5224 value = MSI_RecordGetString(rec, 3);
5226 res = env_set_flags(&name, &value, &flags);
5227 if (res != ERROR_SUCCESS)
5230 deformat_string(package, value, &deformatted);
5233 res = ERROR_OUTOFMEMORY;
5237 value = deformatted;
5239 if (flags & ENV_MOD_MACHINE)
5241 environment = machine_env;
5242 root = HKEY_LOCAL_MACHINE;
5246 environment = user_env;
5247 root = HKEY_CURRENT_USER;
5250 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5251 KEY_ALL_ACCESS, NULL, &env, NULL);
5252 if (res != ERROR_SUCCESS)
5255 if (flags & ENV_ACT_REMOVE)
5256 FIXME("Not removing environment variable on uninstall!\n");
5259 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5260 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5261 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5264 if (res != ERROR_FILE_NOT_FOUND)
5266 if (flags & ENV_ACT_SETABSENT)
5268 res = ERROR_SUCCESS;
5272 data = msi_alloc(size);
5276 return ERROR_OUTOFMEMORY;
5279 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5280 if (res != ERROR_SUCCESS)
5283 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5285 res = RegDeleteKeyW(env, name);
5289 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5290 newval = msi_alloc(size);
5294 res = ERROR_OUTOFMEMORY;
5298 if (!(flags & ENV_MOD_MASK))
5299 lstrcpyW(newval, value);
5302 if (flags & ENV_MOD_PREFIX)
5304 lstrcpyW(newval, value);
5305 lstrcatW(newval, semicolon);
5306 ptr = newval + lstrlenW(value) + 1;
5309 lstrcpyW(ptr, data);
5311 if (flags & ENV_MOD_APPEND)
5313 lstrcatW(newval, semicolon);
5314 lstrcatW(newval, value);
5320 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5321 newval = msi_alloc(size);
5324 res = ERROR_OUTOFMEMORY;
5328 lstrcpyW(newval, value);
5331 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5332 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5335 if (env) RegCloseKey(env);
5336 msi_free(deformatted);
5342 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5346 static const WCHAR ExecSeqQuery[] =
5347 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5348 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5349 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5350 if (rc != ERROR_SUCCESS)
5351 return ERROR_SUCCESS;
5353 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5354 msiobj_release(&view->hdr);
5359 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5370 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5374 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5375 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5377 WARN("Source or dest is directory, not moving\n");
5381 if (options == msidbMoveFileOptionsMove)
5383 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5384 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5387 WARN("MoveFile failed: %d\n", GetLastError());
5393 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5394 ret = CopyFileW(source, dest, FALSE);
5397 WARN("CopyFile failed: %d\n", GetLastError());
5405 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5408 DWORD dirlen, pathlen;
5410 ptr = strrchrW(wildcard, '\\');
5411 dirlen = ptr - wildcard + 1;
5413 pathlen = dirlen + lstrlenW(filename) + 1;
5414 path = msi_alloc(pathlen * sizeof(WCHAR));
5416 lstrcpynW(path, wildcard, dirlen + 1);
5417 lstrcatW(path, filename);
5422 static void free_file_entry(FILE_LIST *file)
5424 msi_free(file->source);
5425 msi_free(file->dest);
5429 static void free_list(FILE_LIST *list)
5431 while (!list_empty(&list->entry))
5433 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5435 list_remove(&file->entry);
5436 free_file_entry(file);
5440 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5442 FILE_LIST *new, *file;
5443 LPWSTR ptr, filename;
5446 new = msi_alloc_zero(sizeof(FILE_LIST));
5450 new->source = strdupW(source);
5451 ptr = strrchrW(dest, '\\') + 1;
5452 filename = strrchrW(new->source, '\\') + 1;
5454 new->sourcename = filename;
5457 new->destname = ptr;
5459 new->destname = new->sourcename;
5461 size = (ptr - dest) + lstrlenW(filename) + 1;
5462 new->dest = msi_alloc(size * sizeof(WCHAR));
5465 free_file_entry(new);
5469 lstrcpynW(new->dest, dest, ptr - dest + 1);
5470 lstrcatW(new->dest, filename);
5472 if (list_empty(&files->entry))
5474 list_add_head(&files->entry, &new->entry);
5478 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5480 if (lstrcmpW(source, file->source) < 0)
5482 list_add_before(&file->entry, &new->entry);
5487 list_add_after(&file->entry, &new->entry);
5491 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5493 WIN32_FIND_DATAW wfd;
5497 FILE_LIST files, *file;
5500 hfile = FindFirstFileW(source, &wfd);
5501 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5503 list_init(&files.entry);
5505 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5507 if (is_dot_dir(wfd.cFileName)) continue;
5509 path = wildcard_to_file(source, wfd.cFileName);
5516 add_wildcard(&files, path, dest);
5520 /* no files match the wildcard */
5521 if (list_empty(&files.entry))
5524 /* only the first wildcard match gets renamed to dest */
5525 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5526 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5527 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5534 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5536 while (!list_empty(&files.entry))
5538 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5540 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5542 list_remove(&file->entry);
5543 free_file_entry(file);
5554 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5556 MSIPACKAGE *package = param;
5559 LPWSTR destname = NULL;
5560 LPWSTR sourcedir = NULL, destdir = NULL;
5561 LPWSTR source = NULL, dest = NULL;
5564 BOOL ret, wildcards;
5566 static const WCHAR backslash[] = {'\\',0};
5568 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5569 if (!comp || !comp->Enabled ||
5570 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5572 TRACE("Component not set for install, not moving file\n");
5573 return ERROR_SUCCESS;
5576 sourcename = MSI_RecordGetString(rec, 3);
5577 options = MSI_RecordGetInteger(rec, 7);
5579 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5583 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5589 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5592 source = strdupW(sourcedir);
5598 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5599 source = msi_alloc(size * sizeof(WCHAR));
5603 lstrcpyW(source, sourcedir);
5604 if (source[lstrlenW(source) - 1] != '\\')
5605 lstrcatW(source, backslash);
5606 lstrcatW(source, sourcename);
5609 wildcards = strchrW(source, '*') || strchrW(source, '?');
5611 if (MSI_RecordIsNull(rec, 4))
5615 destname = strdupW(sourcename);
5622 destname = strdupW(MSI_RecordGetString(rec, 4));
5624 reduce_to_longfilename(destname);
5629 size = lstrlenW(destname);
5631 size += lstrlenW(destdir) + 2;
5632 dest = msi_alloc(size * sizeof(WCHAR));
5636 lstrcpyW(dest, destdir);
5637 if (dest[lstrlenW(dest) - 1] != '\\')
5638 lstrcatW(dest, backslash);
5641 lstrcatW(dest, destname);
5643 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5645 ret = CreateDirectoryW(destdir, NULL);
5648 WARN("CreateDirectory failed: %d\n", GetLastError());
5649 return ERROR_SUCCESS;
5654 msi_move_file(source, dest, options);
5656 move_files_wildcard(source, dest, options);
5659 msi_free(sourcedir);
5665 return ERROR_SUCCESS;
5668 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5673 static const WCHAR ExecSeqQuery[] =
5674 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5675 '`','M','o','v','e','F','i','l','e','`',0};
5677 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5678 if (rc != ERROR_SUCCESS)
5679 return ERROR_SUCCESS;
5681 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5682 msiobj_release(&view->hdr);
5687 typedef struct tagMSIASSEMBLY
5690 MSICOMPONENT *component;
5691 MSIFEATURE *feature;
5699 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5701 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5702 LPVOID pvReserved, HMODULE *phModDll);
5704 static BOOL init_functionpointers(void)
5710 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5712 hmscoree = LoadLibraryA("mscoree.dll");
5715 WARN("mscoree.dll not available\n");
5719 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5720 if (!pLoadLibraryShim)
5722 WARN("LoadLibraryShim not available\n");
5723 FreeLibrary(hmscoree);
5727 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5730 WARN("fusion.dll not available\n");
5731 FreeLibrary(hmscoree);
5735 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5737 FreeLibrary(hmscoree);
5741 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5744 IAssemblyCache *cache;
5746 UINT r = ERROR_FUNCTION_FAILED;
5748 TRACE("installing assembly: %s\n", debugstr_w(path));
5750 if (assembly->feature)
5751 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5753 if (assembly->manifest)
5754 FIXME("Manifest unhandled\n");
5756 if (assembly->application)
5758 FIXME("Assembly should be privately installed\n");
5759 return ERROR_SUCCESS;
5762 if (assembly->attributes == msidbAssemblyAttributesWin32)
5764 FIXME("Win32 assemblies not handled\n");
5765 return ERROR_SUCCESS;
5768 hr = pCreateAssemblyCache(&cache, 0);
5772 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5774 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5779 IAssemblyCache_Release(cache);
5783 typedef struct tagASSEMBLY_LIST
5785 MSIPACKAGE *package;
5786 IAssemblyCache *cache;
5787 struct list *assemblies;
5790 typedef struct tagASSEMBLY_NAME
5798 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
5800 ASSEMBLY_NAME *asmname = (ASSEMBLY_NAME *)param;
5801 LPCWSTR name = MSI_RecordGetString(rec, 2);
5802 LPWSTR val = msi_dup_record_field(rec, 3);
5804 static const WCHAR Name[] = {'N','a','m','e',0};
5805 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
5806 static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
5807 static const WCHAR PublicKeyToken[] = {
5808 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
5810 if (!lstrcmpW(name, Name))
5811 asmname->name = val;
5812 else if (!lstrcmpW(name, Version))
5813 asmname->version = val;
5814 else if (!lstrcmpW(name, Culture))
5815 asmname->culture = val;
5816 else if (!lstrcmpW(name, PublicKeyToken))
5817 asmname->pubkeytoken = val;
5821 return ERROR_SUCCESS;
5824 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
5828 *size = lstrlenW(append) + 1;
5829 *str = msi_alloc((*size) * sizeof(WCHAR));
5830 lstrcpyW(*str, append);
5834 (*size) += lstrlenW(append);
5835 *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
5836 lstrcatW(*str, append);
5839 static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
5842 ASSEMBLY_INFO asminfo;
5850 static const WCHAR separator[] = {',',' ',0};
5851 static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
5852 static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
5853 static const WCHAR PublicKeyToken[] = {
5854 'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
5855 static const WCHAR query[] = {
5856 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5857 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
5858 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
5859 '=','\'','%','s','\'',0};
5863 ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
5864 ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
5866 r = MSI_OpenQuery(db, &view, query, comp->Component);
5867 if (r != ERROR_SUCCESS)
5868 return ERROR_SUCCESS;
5870 MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
5871 msiobj_release(&view->hdr);
5875 ERR("No assembly name specified!\n");
5879 append_str(&disp, &size, name.name);
5883 append_str(&disp, &size, separator);
5884 append_str(&disp, &size, Version);
5885 append_str(&disp, &size, name.version);
5890 append_str(&disp, &size, separator);
5891 append_str(&disp, &size, Culture);
5892 append_str(&disp, &size, name.culture);
5895 if (name.pubkeytoken)
5897 append_str(&disp, &size, separator);
5898 append_str(&disp, &size, PublicKeyToken);
5899 append_str(&disp, &size, name.pubkeytoken);
5902 asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
5903 IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
5905 found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
5908 msiobj_release(&view->hdr);
5910 msi_free(name.name);
5911 msi_free(name.version);
5912 msi_free(name.culture);
5913 msi_free(name.pubkeytoken);
5918 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
5920 ASSEMBLY_LIST *list = (ASSEMBLY_LIST *)param;
5921 MSIASSEMBLY *assembly;
5923 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
5925 return ERROR_OUTOFMEMORY;
5927 assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
5929 if (!assembly->component || !assembly->component->Enabled ||
5930 !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5932 TRACE("Component not set for install, not publishing assembly\n");
5934 return ERROR_SUCCESS;
5937 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
5938 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
5940 if (!assembly->file)
5942 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
5943 return ERROR_FUNCTION_FAILED;
5946 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
5947 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
5948 assembly->attributes = MSI_RecordGetInteger(rec, 5);
5949 assembly->installed = check_assembly_installed(list->package->db,
5951 assembly->component);
5953 list_add_head(list->assemblies, &assembly->entry);
5954 return ERROR_SUCCESS;
5957 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
5959 IAssemblyCache *cache = NULL;
5965 static const WCHAR query[] =
5966 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5967 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5969 r = MSI_DatabaseOpenViewW(package->db, query, &view);
5970 if (r != ERROR_SUCCESS)
5971 return ERROR_SUCCESS;
5973 hr = pCreateAssemblyCache(&cache, 0);
5975 return ERROR_FUNCTION_FAILED;
5977 list.package = package;
5979 list.assemblies = assemblies;
5981 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
5982 msiobj_release(&view->hdr);
5984 IAssemblyCache_Release(cache);
5989 static void free_assemblies(struct list *assemblies)
5991 struct list *item, *cursor;
5993 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
5995 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
5997 list_remove(&assembly->entry);
5998 msi_free(assembly->application);
5999 msi_free(assembly->manifest);
6004 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6006 MSIASSEMBLY *assembly;
6008 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6010 if (!lstrcmpW(assembly->file->File, file))
6020 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6021 LPWSTR *path, DWORD *attrs, PVOID user)
6023 MSIASSEMBLY *assembly;
6024 WCHAR temppath[MAX_PATH];
6025 struct list *assemblies = (struct list *)user;
6028 if (!find_assembly(assemblies, file, &assembly))
6031 GetTempPathW(MAX_PATH, temppath);
6032 PathAddBackslashW(temppath);
6033 lstrcatW(temppath, assembly->file->FileName);
6035 if (action == MSICABEXTRACT_BEGINEXTRACT)
6037 if (assembly->installed)
6040 *path = strdupW(temppath);
6041 *attrs = assembly->file->Attributes;
6043 else if (action == MSICABEXTRACT_FILEEXTRACTED)
6045 assembly->installed = TRUE;
6047 r = install_assembly(package, assembly, temppath);
6048 if (r != ERROR_SUCCESS)
6049 ERR("Failed to install assembly\n");
6055 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6058 struct list assemblies = LIST_INIT(assemblies);
6059 MSIASSEMBLY *assembly;
6062 if (!init_functionpointers() || !pCreateAssemblyCache)
6063 return ERROR_FUNCTION_FAILED;
6065 r = load_assemblies(package, &assemblies);
6066 if (r != ERROR_SUCCESS)
6069 if (list_empty(&assemblies))
6072 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6075 r = ERROR_OUTOFMEMORY;
6079 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6081 if (assembly->installed && !mi->is_continuous)
6084 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6085 (assembly->file->IsCompressed && !mi->is_extracted))
6089 r = ready_media(package, assembly->file, mi);
6090 if (r != ERROR_SUCCESS)
6092 ERR("Failed to ready media\n");
6097 data.package = package;
6098 data.cb = installassembly_cb;
6099 data.user = &assemblies;
6101 if (assembly->file->IsCompressed &&
6102 !msi_cabextract(package, mi, &data))
6104 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6105 r = ERROR_FUNCTION_FAILED;
6110 if (!assembly->file->IsCompressed)
6112 LPWSTR source = resolve_file_source(package, assembly->file);
6114 r = install_assembly(package, assembly, source);
6115 if (r != ERROR_SUCCESS)
6116 ERR("Failed to install assembly\n");
6121 /* FIXME: write Installer assembly reg values */
6125 free_assemblies(&assemblies);
6129 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
6130 LPCSTR action, LPCWSTR table )
6132 static const WCHAR query[] = {
6133 'S','E','L','E','C','T',' ','*',' ',
6134 'F','R','O','M',' ','`','%','s','`',0 };
6135 MSIQUERY *view = NULL;
6139 r = MSI_OpenQuery( package->db, &view, query, table );
6140 if (r == ERROR_SUCCESS)
6142 r = MSI_IterateRecords(view, &count, NULL, package);
6143 msiobj_release(&view->hdr);
6147 FIXME("%s -> %u ignored %s table values\n",
6148 action, count, debugstr_w(table));
6150 return ERROR_SUCCESS;
6153 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6155 TRACE("%p\n", package);
6156 return ERROR_SUCCESS;
6159 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6161 static const WCHAR table[] =
6162 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6163 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6166 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6168 static const WCHAR table[] = { 'P','a','t','c','h',0 };
6169 return msi_unimplemented_action_stub( package, "PatchFiles", table );
6172 static UINT ACTION_BindImage( MSIPACKAGE *package )
6174 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6175 return msi_unimplemented_action_stub( package, "BindImage", table );
6178 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6180 static const WCHAR table[] = {
6181 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6182 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6185 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6187 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6188 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6191 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
6193 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
6194 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
6197 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6199 static const WCHAR table[] = {
6200 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6201 return msi_unimplemented_action_stub( package, "DeleteServices", table );
6203 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6205 static const WCHAR table[] = {
6206 'P','r','o','d','u','c','t','I','D',0 };
6207 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
6210 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6212 static const WCHAR table[] = {
6213 'E','n','v','i','r','o','n','m','e','n','t',0 };
6214 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6217 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6219 static const WCHAR table[] = {
6220 'M','s','i','A','s','s','e','m','b','l','y',0 };
6221 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6224 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
6226 static const WCHAR table[] = { 'F','o','n','t',0 };
6227 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
6230 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6232 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6233 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6236 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6238 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6239 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6242 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6244 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6245 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6248 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6250 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6251 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6254 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6256 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6257 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6260 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6262 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6263 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6266 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
6268 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
6269 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
6272 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6274 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
6275 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
6278 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6280 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6281 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6284 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
6286 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
6287 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
6290 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6292 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6293 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6296 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6298 static const WCHAR table[] = { 'A','p','p','I','d',0 };
6299 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6302 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6304 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6305 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6308 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6310 static const WCHAR table[] = { 'M','I','M','E',0 };
6311 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6314 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6316 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6317 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6320 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
6322 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
6323 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6326 static const struct _actions StandardActions[] = {
6327 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6328 { szAppSearch, ACTION_AppSearch },
6329 { szBindImage, ACTION_BindImage },
6330 { szCCPSearch, ACTION_CCPSearch },
6331 { szCostFinalize, ACTION_CostFinalize },
6332 { szCostInitialize, ACTION_CostInitialize },
6333 { szCreateFolders, ACTION_CreateFolders },
6334 { szCreateShortcuts, ACTION_CreateShortcuts },
6335 { szDeleteServices, ACTION_DeleteServices },
6336 { szDisableRollback, NULL },
6337 { szDuplicateFiles, ACTION_DuplicateFiles },
6338 { szExecuteAction, ACTION_ExecuteAction },
6339 { szFileCost, ACTION_FileCost },
6340 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6341 { szForceReboot, ACTION_ForceReboot },
6342 { szInstallAdminPackage, NULL },
6343 { szInstallExecute, ACTION_InstallExecute },
6344 { szInstallExecuteAgain, ACTION_InstallExecute },
6345 { szInstallFiles, ACTION_InstallFiles},
6346 { szInstallFinalize, ACTION_InstallFinalize },
6347 { szInstallInitialize, ACTION_InstallInitialize },
6348 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6349 { szInstallValidate, ACTION_InstallValidate },
6350 { szIsolateComponents, ACTION_IsolateComponents },
6351 { szLaunchConditions, ACTION_LaunchConditions },
6352 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6353 { szMoveFiles, ACTION_MoveFiles },
6354 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6355 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6356 { szInstallODBC, ACTION_InstallODBC },
6357 { szInstallServices, ACTION_InstallServices },
6358 { szPatchFiles, ACTION_PatchFiles },
6359 { szProcessComponents, ACTION_ProcessComponents },
6360 { szPublishComponents, ACTION_PublishComponents },
6361 { szPublishFeatures, ACTION_PublishFeatures },
6362 { szPublishProduct, ACTION_PublishProduct },
6363 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6364 { szRegisterComPlus, ACTION_RegisterComPlus},
6365 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6366 { szRegisterFonts, ACTION_RegisterFonts },
6367 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6368 { szRegisterProduct, ACTION_RegisterProduct },
6369 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6370 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6371 { szRegisterUser, ACTION_RegisterUser },
6372 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6373 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6374 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6375 { szRemoveFiles, ACTION_RemoveFiles },
6376 { szRemoveFolders, ACTION_RemoveFolders },
6377 { szRemoveIniValues, ACTION_RemoveIniValues },
6378 { szRemoveODBC, ACTION_RemoveODBC },
6379 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6380 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6381 { szResolveSource, ACTION_ResolveSource },
6382 { szRMCCPSearch, ACTION_RMCCPSearch },
6383 { szScheduleReboot, NULL },
6384 { szSelfRegModules, ACTION_SelfRegModules },
6385 { szSelfUnregModules, ACTION_SelfUnregModules },
6386 { szSetODBCFolders, NULL },
6387 { szStartServices, ACTION_StartServices },
6388 { szStopServices, ACTION_StopServices },
6389 { szUnpublishComponents, ACTION_UnpublishComponents },
6390 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6391 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6392 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6393 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6394 { szUnregisterFonts, ACTION_UnregisterFonts },
6395 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6396 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6397 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6398 { szValidateProductID, ACTION_ValidateProductID },
6399 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6400 { szWriteIniValues, ACTION_WriteIniValues },
6401 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6405 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6406 UINT* rc, BOOL force )
6412 if (!run && !package->script->CurrentlyScripting)
6417 if (strcmpW(action,szInstallFinalize) == 0 ||
6418 strcmpW(action,szInstallExecute) == 0 ||
6419 strcmpW(action,szInstallExecuteAgain) == 0)
6424 while (StandardActions[i].action != NULL)
6426 if (strcmpW(StandardActions[i].action, action)==0)
6430 ui_actioninfo(package, action, TRUE, 0);
6431 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6432 ui_actioninfo(package, action, FALSE, *rc);
6436 ui_actionstart(package, action);
6437 if (StandardActions[i].handler)
6439 *rc = StandardActions[i].handler(package);
6443 FIXME("unhandled standard action %s\n",debugstr_w(action));
6444 *rc = ERROR_SUCCESS;