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
24 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
26 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
38 #include "wine/debug.h"
43 #include "wine/unicode.h"
46 #define REG_PROGRESS_VALUE 13200
47 #define COMPONENT_PROGRESS_VALUE 24000
49 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
54 static UINT
ACTION_ProcessExecSequence(MSIPACKAGE
*package
, BOOL UIran
);
55 static UINT
ACTION_ProcessUISequence(MSIPACKAGE
*package
);
56 static UINT
ACTION_PerformActionSequence(MSIPACKAGE
*package
, UINT seq
, BOOL UI
);
59 * consts and values used
61 static const WCHAR c_colon
[] = {'C',':','\\',0};
63 static const WCHAR szCreateFolders
[] =
64 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
65 static const WCHAR szCostFinalize
[] =
66 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
67 const WCHAR szInstallFiles
[] =
68 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
69 const WCHAR szDuplicateFiles
[] =
70 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
71 static const WCHAR szWriteRegistryValues
[] =
72 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
73 'V','a','l','u','e','s',0};
74 static const WCHAR szCostInitialize
[] =
75 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
76 static const WCHAR szFileCost
[] =
77 {'F','i','l','e','C','o','s','t',0};
78 static const WCHAR szInstallInitialize
[] =
79 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
80 static const WCHAR szInstallValidate
[] =
81 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
82 static const WCHAR szLaunchConditions
[] =
83 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
84 static const WCHAR szProcessComponents
[] =
85 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
86 static const WCHAR szRegisterTypeLibraries
[] =
87 {'R','e','g','i','s','t','e','r','T','y','p','e',
88 'L','i','b','r','a','r','i','e','s',0};
89 const WCHAR szRegisterClassInfo
[] =
90 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
91 const WCHAR szRegisterProgIdInfo
[] =
92 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
93 static const WCHAR szCreateShortcuts
[] =
94 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
95 static const WCHAR szPublishProduct
[] =
96 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
97 static const WCHAR szWriteIniValues
[] =
98 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
99 static const WCHAR szSelfRegModules
[] =
100 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
101 static const WCHAR szPublishFeatures
[] =
102 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
103 static const WCHAR szRegisterProduct
[] =
104 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
105 static const WCHAR szInstallExecute
[] =
106 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
107 static const WCHAR szInstallExecuteAgain
[] =
108 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
109 'A','g','a','i','n',0};
110 static const WCHAR szInstallFinalize
[] =
111 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
112 static const WCHAR szForceReboot
[] =
113 {'F','o','r','c','e','R','e','b','o','o','t',0};
114 static const WCHAR szResolveSource
[] =
115 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
116 static const WCHAR szAppSearch
[] =
117 {'A','p','p','S','e','a','r','c','h',0};
118 static const WCHAR szAllocateRegistrySpace
[] =
119 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
120 'S','p','a','c','e',0};
121 static const WCHAR szBindImage
[] =
122 {'B','i','n','d','I','m','a','g','e',0};
123 static const WCHAR szCCPSearch
[] =
124 {'C','C','P','S','e','a','r','c','h',0};
125 static const WCHAR szDeleteServices
[] =
126 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
127 static const WCHAR szDisableRollback
[] =
128 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
129 static const WCHAR szExecuteAction
[] =
130 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
131 const WCHAR szFindRelatedProducts
[] =
132 {'F','i','n','d','R','e','l','a','t','e','d',
133 'P','r','o','d','u','c','t','s',0};
134 static const WCHAR szInstallAdminPackage
[] =
135 {'I','n','s','t','a','l','l','A','d','m','i','n',
136 'P','a','c','k','a','g','e',0};
137 static const WCHAR szInstallSFPCatalogFile
[] =
138 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
140 static const WCHAR szIsolateComponents
[] =
141 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
142 const WCHAR szMigrateFeatureStates
[] =
143 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
144 'S','t','a','t','e','s',0};
145 const WCHAR szMoveFiles
[] =
146 {'M','o','v','e','F','i','l','e','s',0};
147 static const WCHAR szMsiPublishAssemblies
[] =
148 {'M','s','i','P','u','b','l','i','s','h',
149 'A','s','s','e','m','b','l','i','e','s',0};
150 static const WCHAR szMsiUnpublishAssemblies
[] =
151 {'M','s','i','U','n','p','u','b','l','i','s','h',
152 'A','s','s','e','m','b','l','i','e','s',0};
153 static const WCHAR szInstallODBC
[] =
154 {'I','n','s','t','a','l','l','O','D','B','C',0};
155 static const WCHAR szInstallServices
[] =
156 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
157 const WCHAR szPatchFiles
[] =
158 {'P','a','t','c','h','F','i','l','e','s',0};
159 static const WCHAR szPublishComponents
[] =
160 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
161 static const WCHAR szRegisterComPlus
[] =
162 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
163 const WCHAR szRegisterExtensionInfo
[] =
164 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
166 static const WCHAR szRegisterFonts
[] =
167 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
168 const WCHAR szRegisterMIMEInfo
[] =
169 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
170 static const WCHAR szRegisterUser
[] =
171 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
172 const WCHAR szRemoveDuplicateFiles
[] =
173 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
174 'F','i','l','e','s',0};
175 static const WCHAR szRemoveEnvironmentStrings
[] =
176 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
177 'S','t','r','i','n','g','s',0};
178 const WCHAR szRemoveExistingProducts
[] =
179 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
180 'P','r','o','d','u','c','t','s',0};
181 const WCHAR szRemoveFiles
[] =
182 {'R','e','m','o','v','e','F','i','l','e','s',0};
183 static const WCHAR szRemoveFolders
[] =
184 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
185 static const WCHAR szRemoveIniValues
[] =
186 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
187 static const WCHAR szRemoveODBC
[] =
188 {'R','e','m','o','v','e','O','D','B','C',0};
189 static const WCHAR szRemoveRegistryValues
[] =
190 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
191 'V','a','l','u','e','s',0};
192 static const WCHAR szRemoveShortcuts
[] =
193 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
194 static const WCHAR szRMCCPSearch
[] =
195 {'R','M','C','C','P','S','e','a','r','c','h',0};
196 static const WCHAR szScheduleReboot
[] =
197 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
198 static const WCHAR szSelfUnregModules
[] =
199 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
200 static const WCHAR szSetODBCFolders
[] =
201 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
202 static const WCHAR szStartServices
[] =
203 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
204 static const WCHAR szStopServices
[] =
205 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
206 static const WCHAR szUnpublishComponents
[] =
207 {'U','n','p','u','b','l','i','s','h',
208 'C','o','m','p','o','n','e','n','t','s',0};
209 static const WCHAR szUnpublishFeatures
[] =
210 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
211 const WCHAR szUnregisterClassInfo
[] =
212 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
214 static const WCHAR szUnregisterComPlus
[] =
215 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
216 const WCHAR szUnregisterExtensionInfo
[] =
217 {'U','n','r','e','g','i','s','t','e','r',
218 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
219 static const WCHAR szUnregisterFonts
[] =
220 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
221 const WCHAR szUnregisterMIMEInfo
[] =
222 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
223 const WCHAR szUnregisterProgIdInfo
[] =
224 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
226 static const WCHAR szUnregisterTypeLibraries
[] =
227 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
228 'L','i','b','r','a','r','i','e','s',0};
229 static const WCHAR szValidateProductID
[] =
230 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
231 static const WCHAR szWriteEnvironmentStrings
[] =
232 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
233 'S','t','r','i','n','g','s',0};
235 /* action handlers */
236 typedef UINT (*STANDARDACTIONHANDLER
)(MSIPACKAGE
*);
240 STANDARDACTIONHANDLER handler
;
243 static struct _actions StandardActions
[];
246 /********************************************************
248 ********************************************************/
250 static void ui_actionstart(MSIPACKAGE
*package
, LPCWSTR action
)
252 static const WCHAR Query_t
[] =
253 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
254 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
255 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
256 ' ','\'','%','s','\'',0};
259 row
= MSI_QueryGetRecord( package
->db
, Query_t
, action
);
262 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONSTART
, row
);
263 msiobj_release(&row
->hdr
);
266 static void ui_actioninfo(MSIPACKAGE
*package
, LPCWSTR action
, BOOL start
,
270 static const WCHAR template_s
[]=
271 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
273 static const WCHAR template_e
[]=
274 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
275 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
277 static const WCHAR format
[] =
278 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
282 GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, NULL
, format
, timet
, 0x100);
284 sprintfW(message
,template_s
,timet
,action
);
286 sprintfW(message
,template_e
,timet
,action
,rc
);
288 row
= MSI_CreateRecord(1);
289 MSI_RecordSetStringW(row
,1,message
);
291 MSI_ProcessMessage(package
, INSTALLMESSAGE_INFO
, row
);
292 msiobj_release(&row
->hdr
);
295 static UINT
msi_parse_command_line( MSIPACKAGE
*package
, LPCWSTR szCommandLine
)
300 LPWSTR prop
= NULL
, val
= NULL
;
303 return ERROR_SUCCESS
;
315 TRACE("Looking at %s\n",debugstr_w(ptr
));
317 ptr2
= strchrW(ptr
,'=');
320 ERR("command line contains unknown string : %s\n", debugstr_w(ptr
));
327 prop
= msi_alloc((len
+1)*sizeof(WCHAR
));
328 memcpy(prop
,ptr
,len
*sizeof(WCHAR
));
334 while (*ptr
&& (quote
|| (!quote
&& *ptr
!=' ')))
347 val
= msi_alloc((len
+1)*sizeof(WCHAR
));
348 memcpy(val
,ptr2
,len
*sizeof(WCHAR
));
351 if (lstrlenW(prop
) > 0)
353 TRACE("Found commandline property (%s) = (%s)\n",
354 debugstr_w(prop
), debugstr_w(val
));
355 MSI_SetPropertyW(package
,prop
,val
);
361 return ERROR_SUCCESS
;
365 static LPWSTR
* msi_split_string( LPCWSTR str
, WCHAR sep
)
368 LPWSTR p
, *ret
= NULL
;
374 /* count the number of substrings */
375 for ( pc
= str
, count
= 0; pc
; count
++ )
377 pc
= strchrW( pc
, sep
);
382 /* allocate space for an array of substring pointers and the substrings */
383 ret
= msi_alloc( (count
+1) * sizeof (LPWSTR
) +
384 (lstrlenW(str
)+1) * sizeof(WCHAR
) );
388 /* copy the string and set the pointers */
389 p
= (LPWSTR
) &ret
[count
+1];
391 for( count
= 0; (ret
[count
] = p
); count
++ )
393 p
= strchrW( p
, sep
);
401 static UINT
msi_check_transform_applicable( MSIPACKAGE
*package
, IStorage
*patch
)
403 WCHAR szProductCode
[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
404 LPWSTR prod_code
, patch_product
;
407 prod_code
= msi_dup_property( package
, szProductCode
);
408 patch_product
= msi_get_suminfo_product( patch
);
410 TRACE("db = %s patch = %s\n", debugstr_w(prod_code
), debugstr_w(patch_product
));
412 if ( strstrW( patch_product
, prod_code
) )
415 ret
= ERROR_FUNCTION_FAILED
;
417 msi_free( patch_product
);
418 msi_free( prod_code
);
423 static UINT
msi_apply_substorage_transform( MSIPACKAGE
*package
,
424 MSIDATABASE
*patch_db
, LPCWSTR name
)
426 UINT ret
= ERROR_FUNCTION_FAILED
;
427 IStorage
*stg
= NULL
;
430 TRACE("%p %s\n", package
, debugstr_w(name
) );
434 ERR("expected a colon in %s\n", debugstr_w(name
));
435 return ERROR_FUNCTION_FAILED
;
438 r
= IStorage_OpenStorage( patch_db
->storage
, name
, NULL
, STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
441 ret
= msi_check_transform_applicable( package
, stg
);
442 if (ret
== ERROR_SUCCESS
)
443 msi_table_apply_transform( package
->db
, stg
);
445 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name
));
446 IStorage_Release( stg
);
449 ERR("failed to open substorage %s\n", debugstr_w(name
));
451 return ERROR_SUCCESS
;
454 static UINT
msi_check_patch_applicable( MSIPACKAGE
*package
, MSISUMMARYINFO
*si
)
456 static const WCHAR szProdID
[] = { 'P','r','o','d','u','c','t','I','D',0 };
457 LPWSTR guid_list
, *guids
, product_id
;
458 UINT i
, ret
= ERROR_FUNCTION_FAILED
;
460 product_id
= msi_dup_property( package
, szProdID
);
463 /* FIXME: the property ProductID should be written into the DB somewhere */
464 ERR("no product ID to check\n");
465 return ERROR_SUCCESS
;
468 guid_list
= msi_suminfo_dup_string( si
, PID_TEMPLATE
);
469 guids
= msi_split_string( guid_list
, ';' );
470 for ( i
= 0; guids
[i
] && ret
!= ERROR_SUCCESS
; i
++ )
472 if (!lstrcmpW( guids
[i
], product_id
))
476 msi_free( guid_list
);
477 msi_free( product_id
);
482 static UINT
msi_parse_patch_summary( MSIPACKAGE
*package
, MSIDATABASE
*patch_db
)
485 LPWSTR str
, *substorage
;
486 UINT i
, r
= ERROR_SUCCESS
;
488 si
= MSI_GetSummaryInformationW( patch_db
->storage
, 0 );
490 return ERROR_FUNCTION_FAILED
;
492 msi_check_patch_applicable( package
, si
);
494 /* enumerate the substorage */
495 str
= msi_suminfo_dup_string( si
, PID_LASTAUTHOR
);
496 substorage
= msi_split_string( str
, ';' );
497 for ( i
= 0; substorage
&& substorage
[i
] && r
== ERROR_SUCCESS
; i
++ )
498 r
= msi_apply_substorage_transform( package
, patch_db
, substorage
[i
] );
499 msi_free( substorage
);
502 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
504 msiobj_release( &si
->hdr
);
509 static UINT
msi_apply_patch_package( MSIPACKAGE
*package
, LPCWSTR file
)
511 MSIDATABASE
*patch_db
= NULL
;
514 TRACE("%p %s\n", package
, debugstr_w( file
) );
517 * We probably want to make sure we only open a patch collection here.
518 * Patch collections (.msp) and databases (.msi) have different GUIDs
519 * but currently MSI_OpenDatabaseW will accept both.
521 r
= MSI_OpenDatabaseW( file
, MSIDBOPEN_READONLY
, &patch_db
);
522 if ( r
!= ERROR_SUCCESS
)
524 ERR("failed to open patch collection %s\n", debugstr_w( file
) );
528 msi_parse_patch_summary( package
, patch_db
);
531 * There might be a CAB file in the patch package,
532 * so append it to the list of storage to search for streams.
534 append_storage_to_db( package
->db
, patch_db
->storage
);
536 msiobj_release( &patch_db
->hdr
);
538 return ERROR_SUCCESS
;
541 /* get the PATCH property, and apply all the patches it specifies */
542 static UINT
msi_apply_patches( MSIPACKAGE
*package
)
544 static const WCHAR szPatch
[] = { 'P','A','T','C','H',0 };
545 LPWSTR patch_list
, *patches
;
546 UINT i
, r
= ERROR_SUCCESS
;
548 patch_list
= msi_dup_property( package
, szPatch
);
550 TRACE("patches to be applied: %s\n", debugstr_w( patch_list
) );
552 patches
= msi_split_string( patch_list
, ';' );
553 for( i
=0; patches
&& patches
[i
] && r
== ERROR_SUCCESS
; i
++ )
554 r
= msi_apply_patch_package( package
, patches
[i
] );
557 msi_free( patch_list
);
562 static UINT
msi_apply_transforms( MSIPACKAGE
*package
)
564 static const WCHAR szTransforms
[] = {
565 'T','R','A','N','S','F','O','R','M','S',0 };
566 LPWSTR xform_list
, *xforms
;
567 UINT i
, r
= ERROR_SUCCESS
;
569 xform_list
= msi_dup_property( package
, szTransforms
);
570 xforms
= msi_split_string( xform_list
, ';' );
572 for( i
=0; xforms
&& xforms
[i
] && r
== ERROR_SUCCESS
; i
++ )
574 if (xforms
[i
][0] == ':')
575 r
= msi_apply_substorage_transform( package
, package
->db
, &xforms
[i
][1] );
577 r
= MSI_DatabaseApplyTransformW( package
->db
, xforms
[i
], 0 );
581 msi_free( xform_list
);
586 /****************************************************
587 * TOP level entry points
588 *****************************************************/
590 UINT
MSI_InstallPackage( MSIPACKAGE
*package
, LPCWSTR szPackagePath
,
591 LPCWSTR szCommandLine
)
595 static const WCHAR szUILevel
[] = {'U','I','L','e','v','e','l',0};
596 static const WCHAR szAction
[] = {'A','C','T','I','O','N',0};
597 static const WCHAR szInstall
[] = {'I','N','S','T','A','L','L',0};
599 MSI_SetPropertyW(package
, szAction
, szInstall
);
601 package
->script
= msi_alloc_zero(sizeof(MSISCRIPT
));
603 package
->script
->InWhatSequence
= SEQUENCE_INSTALL
;
607 LPWSTR p
, check
, path
;
609 path
= strdupW(szPackagePath
);
610 p
= strrchrW(path
,'\\');
619 path
= msi_alloc(MAX_PATH
*sizeof(WCHAR
));
620 GetCurrentDirectoryW(MAX_PATH
,path
);
624 check
= msi_dup_property( package
, cszSourceDir
);
626 MSI_SetPropertyW(package
, cszSourceDir
, path
);
628 check
= msi_dup_property( package
, cszSOURCEDIR
);
630 MSI_SetPropertyW(package
, cszSOURCEDIR
, path
);
632 msi_free( package
->PackagePath
);
633 package
->PackagePath
= path
;
638 msi_parse_command_line( package
, szCommandLine
);
640 msi_apply_transforms( package
);
641 msi_apply_patches( package
);
643 if ( msi_get_property_int(package
, szUILevel
, 0) >= INSTALLUILEVEL_REDUCED
)
645 package
->script
->InWhatSequence
|= SEQUENCE_UI
;
646 rc
= ACTION_ProcessUISequence(package
);
648 if (rc
== ERROR_SUCCESS
)
650 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
651 rc
= ACTION_ProcessExecSequence(package
,TRUE
);
655 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
659 /* install was halted but should be considered a success */
663 package
->script
->CurrentlyScripting
= FALSE
;
665 /* process the ending type action */
666 if (rc
== ERROR_SUCCESS
)
667 ACTION_PerformActionSequence(package
,-1,ui
);
668 else if (rc
== ERROR_INSTALL_USEREXIT
)
669 ACTION_PerformActionSequence(package
,-2,ui
);
670 else if (rc
== ERROR_INSTALL_SUSPEND
)
671 ACTION_PerformActionSequence(package
,-4,ui
);
673 ACTION_PerformActionSequence(package
,-3,ui
);
675 /* finish up running custom actions */
676 ACTION_FinishCustomActions(package
);
681 static UINT
ACTION_PerformActionSequence(MSIPACKAGE
*package
, UINT seq
, BOOL UI
)
683 UINT rc
= ERROR_SUCCESS
;
685 static const WCHAR ExecSeqQuery
[] =
686 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
687 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
688 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
689 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
691 static const WCHAR UISeqQuery
[] =
692 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
693 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
694 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
695 ' ', '=',' ','%','i',0};
698 row
= MSI_QueryGetRecord(package
->db
, UISeqQuery
, seq
);
700 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
, seq
);
704 LPCWSTR action
, cond
;
706 TRACE("Running the actions\n");
708 /* check conditions */
709 cond
= MSI_RecordGetString(row
,2);
711 /* this is a hack to skip errors in the condition code */
712 if (MSI_EvaluateConditionW(package
, cond
) == MSICONDITION_FALSE
)
715 action
= MSI_RecordGetString(row
,1);
718 ERR("failed to fetch action\n");
719 rc
= ERROR_FUNCTION_FAILED
;
724 rc
= ACTION_PerformUIAction(package
,action
);
726 rc
= ACTION_PerformAction(package
,action
,FALSE
);
728 msiobj_release(&row
->hdr
);
739 } iterate_action_param
;
741 static UINT
ITERATE_Actions(MSIRECORD
*row
, LPVOID param
)
743 iterate_action_param
*iap
= (iterate_action_param
*)param
;
745 LPCWSTR cond
, action
;
747 action
= MSI_RecordGetString(row
,1);
750 ERR("Error is retrieving action name\n");
751 return ERROR_FUNCTION_FAILED
;
754 /* check conditions */
755 cond
= MSI_RecordGetString(row
,2);
757 /* this is a hack to skip errors in the condition code */
758 if (MSI_EvaluateConditionW(iap
->package
, cond
) == MSICONDITION_FALSE
)
760 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action
));
761 return ERROR_SUCCESS
;
765 rc
= ACTION_PerformUIAction(iap
->package
,action
);
767 rc
= ACTION_PerformAction(iap
->package
,action
,FALSE
);
769 msi_dialog_check_messages( NULL
);
771 if (iap
->package
->CurrentInstallState
!= ERROR_SUCCESS
)
772 rc
= iap
->package
->CurrentInstallState
;
774 if (rc
== ERROR_FUNCTION_NOT_CALLED
)
777 if (rc
!= ERROR_SUCCESS
)
778 ERR("Execution halted, action %s returned %i\n", debugstr_w(action
), rc
);
783 UINT
MSI_Sequence( MSIPACKAGE
*package
, LPCWSTR szTable
, INT iSequenceMode
)
787 static const WCHAR query
[] =
788 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
790 ' ','W','H','E','R','E',' ',
791 '`','S','e','q','u','e','n','c','e','`',' ',
792 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
793 '`','S','e','q','u','e','n','c','e','`',0};
794 iterate_action_param iap
;
797 * FIXME: probably should be checking UILevel in the
798 * ACTION_PerformUIAction/ACTION_PerformAction
799 * rather than saving the UI level here. Those
800 * two functions can be merged too.
802 iap
.package
= package
;
805 TRACE("%p %s %i\n", package
, debugstr_w(szTable
), iSequenceMode
);
807 r
= MSI_OpenQuery( package
->db
, &view
, query
, szTable
);
808 if (r
== ERROR_SUCCESS
)
810 r
= MSI_IterateRecords( view
, NULL
, ITERATE_Actions
, &iap
);
811 msiobj_release(&view
->hdr
);
817 static UINT
ACTION_ProcessExecSequence(MSIPACKAGE
*package
, BOOL UIran
)
821 static const WCHAR ExecSeqQuery
[] =
822 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
823 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
824 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
825 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
826 'O','R','D','E','R',' ', 'B','Y',' ',
827 '`','S','e','q','u','e','n','c','e','`',0 };
829 static const WCHAR IVQuery
[] =
830 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
831 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
832 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
833 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
834 ' ','\'', 'I','n','s','t','a','l','l',
835 'V','a','l','i','d','a','t','e','\'', 0};
837 iterate_action_param iap
;
839 iap
.package
= package
;
842 if (package
->script
->ExecuteSequenceRun
)
844 TRACE("Execute Sequence already Run\n");
845 return ERROR_SUCCESS
;
848 package
->script
->ExecuteSequenceRun
= TRUE
;
850 /* get the sequence number */
853 row
= MSI_QueryGetRecord(package
->db
, IVQuery
);
855 return ERROR_FUNCTION_FAILED
;
856 seq
= MSI_RecordGetInteger(row
,1);
857 msiobj_release(&row
->hdr
);
860 rc
= MSI_OpenQuery(package
->db
, &view
, ExecSeqQuery
, seq
);
861 if (rc
== ERROR_SUCCESS
)
863 TRACE("Running the actions\n");
865 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, &iap
);
866 msiobj_release(&view
->hdr
);
872 static UINT
ACTION_ProcessUISequence(MSIPACKAGE
*package
)
876 static const WCHAR ExecSeqQuery
[] =
877 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
878 '`','I','n','s','t','a','l','l',
879 'U','I','S','e','q','u','e','n','c','e','`',
880 ' ','W','H','E','R','E',' ',
881 '`','S','e','q','u','e','n','c','e','`',' ',
882 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
883 '`','S','e','q','u','e','n','c','e','`',0};
884 iterate_action_param iap
;
886 iap
.package
= package
;
889 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
891 if (rc
== ERROR_SUCCESS
)
893 TRACE("Running the actions\n");
895 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, &iap
);
896 msiobj_release(&view
->hdr
);
902 /********************************************************
903 * ACTION helper functions and functions that perform the actions
904 *******************************************************/
905 static BOOL
ACTION_HandleStandardAction(MSIPACKAGE
*package
, LPCWSTR action
,
906 UINT
* rc
, BOOL force
)
912 if (!run
&& !package
->script
->CurrentlyScripting
)
917 if (strcmpW(action
,szInstallFinalize
) == 0 ||
918 strcmpW(action
,szInstallExecute
) == 0 ||
919 strcmpW(action
,szInstallExecuteAgain
) == 0)
924 while (StandardActions
[i
].action
!= NULL
)
926 if (strcmpW(StandardActions
[i
].action
, action
)==0)
930 ui_actioninfo(package
, action
, TRUE
, 0);
931 *rc
= schedule_action(package
,INSTALL_SCRIPT
,action
);
932 ui_actioninfo(package
, action
, FALSE
, *rc
);
936 ui_actionstart(package
, action
);
937 if (StandardActions
[i
].handler
)
939 *rc
= StandardActions
[i
].handler(package
);
943 FIXME("unhandled standard action %s\n",debugstr_w(action
));
955 static BOOL
ACTION_HandleCustomAction( MSIPACKAGE
* package
, LPCWSTR action
,
956 UINT
* rc
, BOOL force
)
961 arc
= ACTION_CustomAction(package
,action
, force
);
963 if (arc
!= ERROR_CALL_NOT_IMPLEMENTED
)
972 * A lot of actions are really important even if they don't do anything
973 * explicit... Lots of properties are set at the beginning of the installation
974 * CostFinalize does a bunch of work to translate the directories and such
976 * But until I get write access to the database that is hard, so I am going to
977 * hack it to see if I can get something to run.
979 UINT
ACTION_PerformAction(MSIPACKAGE
*package
, const WCHAR
*action
, BOOL force
)
981 UINT rc
= ERROR_SUCCESS
;
984 TRACE("Performing action (%s)\n",debugstr_w(action
));
986 handled
= ACTION_HandleStandardAction(package
, action
, &rc
, force
);
989 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, force
);
993 FIXME("unhandled msi action %s\n",debugstr_w(action
));
994 rc
= ERROR_FUNCTION_NOT_CALLED
;
1000 UINT
ACTION_PerformUIAction(MSIPACKAGE
*package
, const WCHAR
*action
)
1002 UINT rc
= ERROR_SUCCESS
;
1003 BOOL handled
= FALSE
;
1005 TRACE("Performing action (%s)\n",debugstr_w(action
));
1007 handled
= ACTION_HandleStandardAction(package
, action
, &rc
,TRUE
);
1010 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, FALSE
);
1012 if( !handled
&& ACTION_DialogBox(package
,action
) == ERROR_SUCCESS
)
1017 FIXME("unhandled msi action %s\n",debugstr_w(action
));
1018 rc
= ERROR_FUNCTION_NOT_CALLED
;
1026 * Actual Action Handlers
1029 static UINT
ITERATE_CreateFolders(MSIRECORD
*row
, LPVOID param
)
1031 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1037 dir
= MSI_RecordGetString(row
,1);
1040 ERR("Unable to get folder id\n");
1041 return ERROR_SUCCESS
;
1044 full_path
= resolve_folder(package
,dir
,FALSE
,FALSE
,&folder
);
1047 ERR("Unable to resolve folder id %s\n",debugstr_w(dir
));
1048 return ERROR_SUCCESS
;
1051 TRACE("Folder is %s\n",debugstr_w(full_path
));
1054 uirow
= MSI_CreateRecord(1);
1055 MSI_RecordSetStringW(uirow
,1,full_path
);
1056 ui_actiondata(package
,szCreateFolders
,uirow
);
1057 msiobj_release( &uirow
->hdr
);
1059 if (folder
->State
== 0)
1060 create_full_pathW(full_path
);
1064 msi_free(full_path
);
1065 return ERROR_SUCCESS
;
1068 /* FIXME: probably should merge this with the above function */
1069 static UINT
msi_create_directory( MSIPACKAGE
* package
, LPCWSTR dir
)
1071 UINT rc
= ERROR_SUCCESS
;
1073 LPWSTR install_path
;
1075 install_path
= resolve_folder(package
, dir
, FALSE
, FALSE
, &folder
);
1077 return ERROR_FUNCTION_FAILED
;
1079 /* create the path */
1080 if (folder
->State
== 0)
1082 create_full_pathW(install_path
);
1085 msi_free(install_path
);
1090 UINT
msi_create_component_directories( MSIPACKAGE
*package
)
1094 /* create all the folders required by the components are going to install */
1095 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1097 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
1099 msi_create_directory( package
, comp
->Directory
);
1102 return ERROR_SUCCESS
;
1106 * Also we cannot enable/disable components either, so for now I am just going
1107 * to do all the directories for all the components.
1109 static UINT
ACTION_CreateFolders(MSIPACKAGE
*package
)
1111 static const WCHAR ExecSeqQuery
[] =
1112 {'S','E','L','E','C','T',' ',
1113 '`','D','i','r','e','c','t','o','r','y','_','`',
1114 ' ','F','R','O','M',' ',
1115 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1119 /* create all the empty folders specified in the CreateFolder table */
1120 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
1121 if (rc
!= ERROR_SUCCESS
)
1122 return ERROR_SUCCESS
;
1124 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateFolders
, package
);
1125 msiobj_release(&view
->hdr
);
1127 msi_create_component_directories( package
);
1132 static UINT
load_component( MSIRECORD
*row
, LPVOID param
)
1134 MSIPACKAGE
*package
= param
;
1137 comp
= msi_alloc_zero( sizeof(MSICOMPONENT
) );
1139 return ERROR_FUNCTION_FAILED
;
1141 list_add_tail( &package
->components
, &comp
->entry
);
1143 /* fill in the data */
1144 comp
->Component
= msi_dup_record_field( row
, 1 );
1146 TRACE("Loading Component %s\n", debugstr_w(comp
->Component
));
1148 comp
->ComponentId
= msi_dup_record_field( row
, 2 );
1149 comp
->Directory
= msi_dup_record_field( row
, 3 );
1150 comp
->Attributes
= MSI_RecordGetInteger(row
,4);
1151 comp
->Condition
= msi_dup_record_field( row
, 5 );
1152 comp
->KeyPath
= msi_dup_record_field( row
, 6 );
1154 comp
->Installed
= INSTALLSTATE_UNKNOWN
;
1155 msi_component_set_state( comp
, INSTALLSTATE_UNKNOWN
);
1157 return ERROR_SUCCESS
;
1160 static UINT
load_all_components( MSIPACKAGE
*package
)
1162 static const WCHAR query
[] = {
1163 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1164 '`','C','o','m','p','o','n','e','n','t','`',0 };
1168 if (!list_empty(&package
->components
))
1169 return ERROR_SUCCESS
;
1171 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1172 if (r
!= ERROR_SUCCESS
)
1175 r
= MSI_IterateRecords(view
, NULL
, load_component
, package
);
1176 msiobj_release(&view
->hdr
);
1181 MSIPACKAGE
*package
;
1182 MSIFEATURE
*feature
;
1185 static UINT
add_feature_component( MSIFEATURE
*feature
, MSICOMPONENT
*comp
)
1189 cl
= msi_alloc( sizeof (*cl
) );
1191 return ERROR_NOT_ENOUGH_MEMORY
;
1192 cl
->component
= comp
;
1193 list_add_tail( &feature
->Components
, &cl
->entry
);
1195 return ERROR_SUCCESS
;
1198 static UINT
add_feature_child( MSIFEATURE
*parent
, MSIFEATURE
*child
)
1202 fl
= msi_alloc( sizeof(*fl
) );
1204 return ERROR_NOT_ENOUGH_MEMORY
;
1205 fl
->feature
= child
;
1206 list_add_tail( &parent
->Children
, &fl
->entry
);
1208 return ERROR_SUCCESS
;
1211 static UINT
iterate_load_featurecomponents(MSIRECORD
*row
, LPVOID param
)
1213 _ilfs
* ilfs
= (_ilfs
*)param
;
1217 component
= MSI_RecordGetString(row
,1);
1219 /* check to see if the component is already loaded */
1220 comp
= get_loaded_component( ilfs
->package
, component
);
1223 ERR("unknown component %s\n", debugstr_w(component
));
1224 return ERROR_FUNCTION_FAILED
;
1227 add_feature_component( ilfs
->feature
, comp
);
1228 comp
->Enabled
= TRUE
;
1230 return ERROR_SUCCESS
;
1233 static MSIFEATURE
*find_feature_by_name( MSIPACKAGE
*package
, LPCWSTR name
)
1235 MSIFEATURE
*feature
;
1237 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1239 if ( !lstrcmpW( feature
->Feature
, name
) )
1246 static UINT
load_feature(MSIRECORD
* row
, LPVOID param
)
1248 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1249 MSIFEATURE
* feature
;
1250 static const WCHAR Query1
[] =
1251 {'S','E','L','E','C','T',' ',
1252 '`','C','o','m','p','o','n','e','n','t','_','`',
1253 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1254 'C','o','m','p','o','n','e','n','t','s','`',' ',
1255 'W','H','E','R','E',' ',
1256 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1261 /* fill in the data */
1263 feature
= msi_alloc_zero( sizeof (MSIFEATURE
) );
1265 return ERROR_NOT_ENOUGH_MEMORY
;
1267 list_init( &feature
->Children
);
1268 list_init( &feature
->Components
);
1270 feature
->Feature
= msi_dup_record_field( row
, 1 );
1272 TRACE("Loading feature %s\n",debugstr_w(feature
->Feature
));
1274 feature
->Feature_Parent
= msi_dup_record_field( row
, 2 );
1275 feature
->Title
= msi_dup_record_field( row
, 3 );
1276 feature
->Description
= msi_dup_record_field( row
, 4 );
1278 if (!MSI_RecordIsNull(row
,5))
1279 feature
->Display
= MSI_RecordGetInteger(row
,5);
1281 feature
->Level
= MSI_RecordGetInteger(row
,6);
1282 feature
->Directory
= msi_dup_record_field( row
, 7 );
1283 feature
->Attributes
= MSI_RecordGetInteger(row
,8);
1285 feature
->Installed
= INSTALLSTATE_UNKNOWN
;
1286 msi_feature_set_state( feature
, INSTALLSTATE_UNKNOWN
);
1288 list_add_tail( &package
->features
, &feature
->entry
);
1290 /* load feature components */
1292 rc
= MSI_OpenQuery( package
->db
, &view
, Query1
, feature
->Feature
);
1293 if (rc
!= ERROR_SUCCESS
)
1294 return ERROR_SUCCESS
;
1296 ilfs
.package
= package
;
1297 ilfs
.feature
= feature
;
1299 MSI_IterateRecords(view
, NULL
, iterate_load_featurecomponents
, &ilfs
);
1300 msiobj_release(&view
->hdr
);
1302 return ERROR_SUCCESS
;
1305 static UINT
find_feature_children(MSIRECORD
* row
, LPVOID param
)
1307 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1308 MSIFEATURE
*parent
, *child
;
1310 child
= find_feature_by_name( package
, MSI_RecordGetString( row
, 1 ) );
1312 return ERROR_FUNCTION_FAILED
;
1314 if (!child
->Feature_Parent
)
1315 return ERROR_SUCCESS
;
1317 parent
= find_feature_by_name( package
, child
->Feature_Parent
);
1319 return ERROR_FUNCTION_FAILED
;
1321 add_feature_child( parent
, child
);
1322 return ERROR_SUCCESS
;
1325 static UINT
load_all_features( MSIPACKAGE
*package
)
1327 static const WCHAR query
[] = {
1328 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1329 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1330 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1334 if (!list_empty(&package
->features
))
1335 return ERROR_SUCCESS
;
1337 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1338 if (r
!= ERROR_SUCCESS
)
1341 r
= MSI_IterateRecords( view
, NULL
, load_feature
, package
);
1342 if (r
!= ERROR_SUCCESS
)
1345 r
= MSI_IterateRecords( view
, NULL
, find_feature_children
, package
);
1346 msiobj_release( &view
->hdr
);
1351 static LPWSTR
folder_split_path(LPWSTR p
, WCHAR ch
)
1362 static UINT
load_file(MSIRECORD
*row
, LPVOID param
)
1364 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1368 /* fill in the data */
1370 file
= msi_alloc_zero( sizeof (MSIFILE
) );
1372 return ERROR_NOT_ENOUGH_MEMORY
;
1374 file
->File
= msi_dup_record_field( row
, 1 );
1376 component
= MSI_RecordGetString( row
, 2 );
1377 file
->Component
= get_loaded_component( package
, component
);
1379 if (!file
->Component
)
1380 ERR("Unfound Component %s\n",debugstr_w(component
));
1382 file
->FileName
= msi_dup_record_field( row
, 3 );
1383 reduce_to_longfilename( file
->FileName
);
1385 file
->ShortName
= msi_dup_record_field( row
, 3 );
1386 file
->LongName
= strdupW( folder_split_path(file
->ShortName
, '|'));
1388 file
->FileSize
= MSI_RecordGetInteger( row
, 4 );
1389 file
->Version
= msi_dup_record_field( row
, 5 );
1390 file
->Language
= msi_dup_record_field( row
, 6 );
1391 file
->Attributes
= MSI_RecordGetInteger( row
, 7 );
1392 file
->Sequence
= MSI_RecordGetInteger( row
, 8 );
1394 file
->state
= msifs_invalid
;
1396 /* if the compressed bits are not set in the file attributes,
1397 * then read the information from the package word count property
1399 if (file
->Attributes
& msidbFileAttributesCompressed
)
1401 file
->IsCompressed
= TRUE
;
1403 else if (file
->Attributes
& msidbFileAttributesNoncompressed
)
1405 file
->IsCompressed
= FALSE
;
1409 file
->IsCompressed
= package
->WordCount
& MSIWORDCOUNT_COMPRESSED
;
1412 TRACE("File Loaded (%s)\n",debugstr_w(file
->File
));
1414 list_add_tail( &package
->files
, &file
->entry
);
1416 return ERROR_SUCCESS
;
1419 static UINT
load_all_files(MSIPACKAGE
*package
)
1423 static const WCHAR Query
[] =
1424 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1425 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1426 '`','S','e','q','u','e','n','c','e','`', 0};
1428 if (!list_empty(&package
->files
))
1429 return ERROR_SUCCESS
;
1431 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
1432 if (rc
!= ERROR_SUCCESS
)
1433 return ERROR_SUCCESS
;
1435 rc
= MSI_IterateRecords(view
, NULL
, load_file
, package
);
1436 msiobj_release(&view
->hdr
);
1438 return ERROR_SUCCESS
;
1443 * I am not doing any of the costing functionality yet.
1444 * Mostly looking at doing the Component and Feature loading
1446 * The native MSI does A LOT of modification to tables here. Mostly adding
1447 * a lot of temporary columns to the Feature and Component tables.
1449 * note: Native msi also tracks the short filename. But I am only going to
1450 * track the long ones. Also looking at this directory table
1451 * it appears that the directory table does not get the parents
1452 * resolved base on property only based on their entries in the
1455 static UINT
ACTION_CostInitialize(MSIPACKAGE
*package
)
1457 static const WCHAR szCosting
[] =
1458 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1459 static const WCHAR szZero
[] = { '0', 0 };
1461 if ( 1 == msi_get_property_int( package
, szCosting
, 0 ) )
1462 return ERROR_SUCCESS
;
1464 MSI_SetPropertyW(package
, szCosting
, szZero
);
1465 MSI_SetPropertyW(package
, cszRootDrive
, c_colon
);
1467 load_all_components( package
);
1468 load_all_features( package
);
1469 load_all_files( package
);
1471 return ERROR_SUCCESS
;
1474 static UINT
execute_script(MSIPACKAGE
*package
, UINT script
)
1477 UINT rc
= ERROR_SUCCESS
;
1479 TRACE("Executing Script %i\n",script
);
1481 if (!package
->script
)
1483 ERR("no script!\n");
1484 return ERROR_FUNCTION_FAILED
;
1487 for (i
= 0; i
< package
->script
->ActionCount
[script
]; i
++)
1490 action
= package
->script
->Actions
[script
][i
];
1491 ui_actionstart(package
, action
);
1492 TRACE("Executing Action (%s)\n",debugstr_w(action
));
1493 rc
= ACTION_PerformAction(package
, action
, TRUE
);
1494 if (rc
!= ERROR_SUCCESS
)
1497 msi_free_action_script(package
, script
);
1501 static UINT
ACTION_FileCost(MSIPACKAGE
*package
)
1503 return ERROR_SUCCESS
;
1506 static MSIFOLDER
*load_folder( MSIPACKAGE
*package
, LPCWSTR dir
)
1508 static const WCHAR Query
[] =
1509 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1510 '`','D','i','r','e','c', 't','o','r','y','`',' ',
1511 'W','H','E','R','E',' ', '`', 'D','i','r','e','c','t', 'o','r','y','`',
1512 ' ','=',' ','\'','%','s','\'',
1514 static const WCHAR szDot
[] = { '.',0 };
1515 static WCHAR szEmpty
[] = { 0 };
1516 LPWSTR p
, tgt_short
, tgt_long
, src_short
, src_long
;
1521 TRACE("Looking for dir %s\n",debugstr_w(dir
));
1523 folder
= get_loaded_folder( package
, dir
);
1527 TRACE("Working to load %s\n",debugstr_w(dir
));
1529 folder
= msi_alloc_zero( sizeof (MSIFOLDER
) );
1533 folder
->Directory
= strdupW(dir
);
1535 row
= MSI_QueryGetRecord(package
->db
, Query
, dir
);
1539 p
= msi_dup_record_field(row
, 3);
1541 /* split src and target dir */
1543 src_short
= folder_split_path( p
, ':' );
1545 /* split the long and short paths */
1546 tgt_long
= folder_split_path( tgt_short
, '|' );
1547 src_long
= folder_split_path( src_short
, '|' );
1549 /* check for no-op dirs */
1550 if (!lstrcmpW(szDot
, tgt_short
))
1551 tgt_short
= szEmpty
;
1552 if (!lstrcmpW(szDot
, src_short
))
1553 src_short
= szEmpty
;
1556 tgt_long
= tgt_short
;
1559 src_short
= tgt_short
;
1560 src_long
= tgt_long
;
1564 src_long
= src_short
;
1566 /* FIXME: use the target short path too */
1567 folder
->TargetDefault
= strdupW(tgt_long
);
1568 folder
->SourceShortPath
= strdupW(src_short
);
1569 folder
->SourceLongPath
= strdupW(src_long
);
1572 TRACE("TargetDefault = %s\n",debugstr_w( folder
->TargetDefault
));
1573 TRACE("SourceLong = %s\n", debugstr_w( folder
->SourceLongPath
));
1574 TRACE("SourceShort = %s\n", debugstr_w( folder
->SourceShortPath
));
1576 parent
= MSI_RecordGetString(row
, 2);
1579 folder
->Parent
= load_folder( package
, parent
);
1580 if ( folder
->Parent
)
1581 TRACE("loaded parent %p %s\n", folder
->Parent
,
1582 debugstr_w(folder
->Parent
->Directory
));
1584 ERR("failed to load parent folder %s\n", debugstr_w(parent
));
1587 folder
->Property
= msi_dup_property( package
, dir
);
1589 msiobj_release(&row
->hdr
);
1591 list_add_tail( &package
->folders
, &folder
->entry
);
1593 TRACE("%s returning %p\n",debugstr_w(dir
),folder
);
1598 static void ACTION_GetComponentInstallStates(MSIPACKAGE
*package
)
1602 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1606 if (!comp
->ComponentId
)
1609 res
= MsiGetComponentPathW( package
->ProductCode
,
1610 comp
->ComponentId
, NULL
, NULL
);
1612 res
= INSTALLSTATE_ABSENT
;
1613 comp
->Installed
= res
;
1617 /* scan for and update current install states */
1618 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE
*package
)
1621 MSIFEATURE
*feature
;
1623 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1626 INSTALLSTATE res
= INSTALLSTATE_ABSENT
;
1628 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1630 comp
= cl
->component
;
1632 if (!comp
->ComponentId
)
1634 res
= INSTALLSTATE_ABSENT
;
1638 if (res
== INSTALLSTATE_ABSENT
)
1639 res
= comp
->Installed
;
1642 if (res
== comp
->Installed
)
1645 if (res
!= INSTALLSTATE_DEFAULT
|| res
!= INSTALLSTATE_LOCAL
||
1646 res
!= INSTALLSTATE_SOURCE
)
1648 res
= INSTALLSTATE_INCOMPLETE
;
1652 feature
->Installed
= res
;
1656 static BOOL
process_state_property (MSIPACKAGE
* package
, LPCWSTR property
,
1659 static const WCHAR all
[]={'A','L','L',0};
1661 MSIFEATURE
*feature
;
1663 override
= msi_dup_property( package
, property
);
1667 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1669 if (strcmpiW(override
,all
)==0)
1670 msi_feature_set_state( feature
, state
);
1673 LPWSTR ptr
= override
;
1674 LPWSTR ptr2
= strchrW(override
,',');
1678 if ((ptr2
&& strncmpW(ptr
,feature
->Feature
, ptr2
-ptr
)==0)
1679 || (!ptr2
&& strcmpW(ptr
,feature
->Feature
)==0))
1681 msi_feature_set_state( feature
, state
);
1687 ptr2
= strchrW(ptr
,',');
1699 UINT
MSI_SetFeatureStates(MSIPACKAGE
*package
)
1702 static const WCHAR szlevel
[] =
1703 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1704 static const WCHAR szAddLocal
[] =
1705 {'A','D','D','L','O','C','A','L',0};
1706 static const WCHAR szRemove
[] =
1707 {'R','E','M','O','V','E',0};
1708 static const WCHAR szReinstall
[] =
1709 {'R','E','I','N','S','T','A','L','L',0};
1710 BOOL override
= FALSE
;
1711 MSICOMPONENT
* component
;
1712 MSIFEATURE
*feature
;
1715 /* I do not know if this is where it should happen.. but */
1717 TRACE("Checking Install Level\n");
1719 install_level
= msi_get_property_int( package
, szlevel
, 1 );
1721 /* ok here is the _real_ rub
1722 * all these activation/deactivation things happen in order and things
1723 * later on the list override things earlier on the list.
1724 * 1) INSTALLLEVEL processing
1734 * 11) FILEADDDEFAULT
1735 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1736 * ignored for all the features. seems strange, especially since it is not
1737 * documented anywhere, but it is how it works.
1739 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1740 * REMOVE are the big ones, since we don't handle administrative installs
1743 override
|= process_state_property(package
,szAddLocal
,INSTALLSTATE_LOCAL
);
1744 override
|= process_state_property(package
,szRemove
,INSTALLSTATE_ABSENT
);
1745 override
|= process_state_property(package
,szReinstall
,INSTALLSTATE_LOCAL
);
1749 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1751 BOOL feature_state
= ((feature
->Level
> 0) &&
1752 (feature
->Level
<= install_level
));
1754 if ((feature_state
) && (feature
->Action
== INSTALLSTATE_UNKNOWN
))
1756 if (feature
->Attributes
& msidbFeatureAttributesFavorSource
)
1757 msi_feature_set_state( feature
, INSTALLSTATE_SOURCE
);
1758 else if (feature
->Attributes
& msidbFeatureAttributesFavorAdvertise
)
1759 msi_feature_set_state( feature
, INSTALLSTATE_ADVERTISED
);
1761 msi_feature_set_state( feature
, INSTALLSTATE_LOCAL
);
1765 /* disable child features of unselected parent features */
1766 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1770 if (feature
->Level
> 0 && feature
->Level
<= install_level
)
1773 LIST_FOR_EACH_ENTRY( fl
, &feature
->Children
, FeatureList
, entry
)
1774 msi_feature_set_state( fl
->feature
, INSTALLSTATE_UNKNOWN
);
1779 /* set the Preselected Property */
1780 static const WCHAR szPreselected
[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1781 static const WCHAR szOne
[] = { '1', 0 };
1783 MSI_SetPropertyW(package
,szPreselected
,szOne
);
1787 * now we want to enable or disable components base on feature
1790 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1794 TRACE("Examining Feature %s (Installed %i, Action %i, Request %i)\n",
1795 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
,
1796 feature
->ActionRequest
);
1798 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1800 component
= cl
->component
;
1802 switch (component
->Attributes
)
1804 case msidbComponentAttributesLocalOnly
:
1805 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1807 case msidbComponentAttributesSourceOnly
:
1808 msi_component_set_state( component
, INSTALLSTATE_SOURCE
);
1810 case msidbComponentAttributesOptional
:
1811 msi_component_set_state( component
, INSTALLSTATE_DEFAULT
);
1814 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1817 if (component
->ForceLocalState
)
1818 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1820 if (!component
->Enabled
)
1821 msi_component_set_state( component
, INSTALLSTATE_UNKNOWN
);
1822 else if (feature
->Attributes
== msidbFeatureAttributesFavorLocal
)
1824 if (!(component
->Attributes
& msidbComponentAttributesSourceOnly
))
1825 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1827 else if (feature
->Attributes
== msidbFeatureAttributesFavorSource
)
1829 if ((component
->Action
== INSTALLSTATE_UNKNOWN
) ||
1830 (component
->Action
== INSTALLSTATE_ABSENT
) ||
1831 (component
->Action
== INSTALLSTATE_ADVERTISED
) ||
1832 (component
->Action
== INSTALLSTATE_DEFAULT
))
1833 msi_component_set_state( component
, INSTALLSTATE_SOURCE
);
1835 else if (feature
->ActionRequest
== INSTALLSTATE_ADVERTISED
)
1837 if ((component
->Action
== INSTALLSTATE_UNKNOWN
) ||
1838 (component
->Action
== INSTALLSTATE_ABSENT
))
1839 msi_component_set_state( component
, INSTALLSTATE_ADVERTISED
);
1841 else if (feature
->ActionRequest
== INSTALLSTATE_ABSENT
)
1843 if (component
->Action
== INSTALLSTATE_UNKNOWN
)
1844 msi_component_set_state( component
, INSTALLSTATE_ABSENT
);
1846 else if (feature
->ActionRequest
== INSTALLSTATE_UNKNOWN
)
1847 msi_component_set_state( component
, INSTALLSTATE_UNKNOWN
);
1849 if (component
->ForceLocalState
&& feature
->Action
== INSTALLSTATE_SOURCE
)
1850 msi_feature_set_state( feature
, INSTALLSTATE_LOCAL
);
1854 LIST_FOR_EACH_ENTRY( component
, &package
->components
, MSICOMPONENT
, entry
)
1856 TRACE("Result: Component %s (Installed %i, Action %i, Request %i)\n",
1857 debugstr_w(component
->Component
), component
->Installed
,
1858 component
->Action
, component
->ActionRequest
);
1862 return ERROR_SUCCESS
;
1865 static UINT
ITERATE_CostFinalizeDirectories(MSIRECORD
*row
, LPVOID param
)
1867 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1871 name
= MSI_RecordGetString(row
,1);
1873 /* This helper function now does ALL the work */
1874 TRACE("Dir %s ...\n",debugstr_w(name
));
1875 load_folder(package
,name
);
1876 path
= resolve_folder(package
,name
,FALSE
,TRUE
,NULL
);
1877 TRACE("resolves to %s\n",debugstr_w(path
));
1880 return ERROR_SUCCESS
;
1883 static UINT
ITERATE_CostFinalizeConditions(MSIRECORD
*row
, LPVOID param
)
1885 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1887 MSIFEATURE
*feature
;
1889 name
= MSI_RecordGetString( row
, 1 );
1891 feature
= get_loaded_feature( package
, name
);
1893 ERR("FAILED to find loaded feature %s\n",debugstr_w(name
));
1897 Condition
= MSI_RecordGetString(row
,3);
1899 if (MSI_EvaluateConditionW(package
,Condition
) == MSICONDITION_TRUE
)
1901 int level
= MSI_RecordGetInteger(row
,2);
1902 TRACE("Reseting feature %s to level %i\n", debugstr_w(name
), level
);
1903 feature
->Level
= level
;
1906 return ERROR_SUCCESS
;
1909 LPWSTR
msi_get_disk_file_version( LPCWSTR filename
)
1911 static const WCHAR name_fmt
[] =
1912 {'%','u','.','%','u','.','%','u','.','%','u',0};
1913 static WCHAR name
[] = {'\\',0};
1914 VS_FIXEDFILEINFO
*lpVer
;
1915 WCHAR filever
[0x100];
1921 TRACE("%s\n", debugstr_w(filename
));
1923 versize
= GetFileVersionInfoSizeW( filename
, &handle
);
1927 version
= msi_alloc( versize
);
1928 GetFileVersionInfoW( filename
, 0, versize
, version
);
1930 VerQueryValueW( version
, name
, (LPVOID
*)&lpVer
, &sz
);
1931 msi_free( version
);
1933 sprintfW( filever
, name_fmt
,
1934 HIWORD(lpVer
->dwFileVersionMS
),
1935 LOWORD(lpVer
->dwFileVersionMS
),
1936 HIWORD(lpVer
->dwFileVersionLS
),
1937 LOWORD(lpVer
->dwFileVersionLS
));
1939 return strdupW( filever
);
1943 * A lot is done in this function aside from just the costing.
1944 * The costing needs to be implemented at some point but for now I am going
1945 * to focus on the directory building
1948 static UINT
ACTION_CostFinalize(MSIPACKAGE
*package
)
1950 static const WCHAR ExecSeqQuery
[] =
1951 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1952 '`','D','i','r','e','c','t','o','r','y','`',0};
1953 static const WCHAR ConditionQuery
[] =
1954 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1955 '`','C','o','n','d','i','t','i','o','n','`',0};
1956 static const WCHAR szCosting
[] =
1957 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1958 static const WCHAR szlevel
[] =
1959 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1960 static const WCHAR szOne
[] = { '1', 0 };
1965 LPWSTR level
, file_version
;
1967 if ( 1 == msi_get_property_int( package
, szCosting
, 0 ) )
1968 return ERROR_SUCCESS
;
1970 TRACE("Building Directory properties\n");
1972 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
1973 if (rc
== ERROR_SUCCESS
)
1975 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CostFinalizeDirectories
,
1977 msiobj_release(&view
->hdr
);
1980 /* read components states from the registry */
1981 ACTION_GetComponentInstallStates(package
);
1983 TRACE("File calculations\n");
1985 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
1987 MSICOMPONENT
* comp
= file
->Component
;
1993 if (file
->IsCompressed
)
1994 comp
->ForceLocalState
= TRUE
;
1996 /* calculate target */
1997 p
= resolve_folder(package
, comp
->Directory
, FALSE
, FALSE
, NULL
);
1999 msi_free(file
->TargetPath
);
2001 TRACE("file %s is named %s\n",
2002 debugstr_w(file
->File
), debugstr_w(file
->FileName
));
2004 file
->TargetPath
= build_directory_name(2, p
, file
->FileName
);
2008 TRACE("file %s resolves to %s\n",
2009 debugstr_w(file
->File
), debugstr_w(file
->TargetPath
));
2011 /* don't check files of components that aren't installed */
2012 if (comp
->Installed
== INSTALLSTATE_UNKNOWN
||
2013 comp
->Installed
== INSTALLSTATE_ABSENT
)
2015 file
->state
= msifs_missing
; /* assume files are missing */
2019 if (GetFileAttributesW(file
->TargetPath
) == INVALID_FILE_ATTRIBUTES
)
2021 file
->state
= msifs_missing
;
2022 comp
->Cost
+= file
->FileSize
;
2023 comp
->Installed
= INSTALLSTATE_INCOMPLETE
;
2027 if (file
->Version
&&
2028 (file_version
= msi_get_disk_file_version( file
->TargetPath
)))
2030 TRACE("new %s old %s\n", debugstr_w(file
->Version
),
2031 debugstr_w(file_version
));
2032 /* FIXME: seems like a bad way to compare version numbers */
2033 if (lstrcmpiW(file_version
, file
->Version
)<0)
2035 file
->state
= msifs_overwrite
;
2036 comp
->Cost
+= file
->FileSize
;
2037 comp
->Installed
= INSTALLSTATE_INCOMPLETE
;
2040 file
->state
= msifs_present
;
2041 msi_free( file_version
);
2044 file
->state
= msifs_present
;
2047 TRACE("Evaluating Condition Table\n");
2049 rc
= MSI_DatabaseOpenViewW(package
->db
, ConditionQuery
, &view
);
2050 if (rc
== ERROR_SUCCESS
)
2052 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CostFinalizeConditions
,
2054 msiobj_release(&view
->hdr
);
2057 TRACE("Enabling or Disabling Components\n");
2058 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2060 if (MSI_EvaluateConditionW(package
, comp
->Condition
) == MSICONDITION_FALSE
)
2062 TRACE("Disabling component %s\n", debugstr_w(comp
->Component
));
2063 comp
->Enabled
= FALSE
;
2067 MSI_SetPropertyW(package
,szCosting
,szOne
);
2068 /* set default run level if not set */
2069 level
= msi_dup_property( package
, szlevel
);
2071 MSI_SetPropertyW(package
,szlevel
, szOne
);
2074 ACTION_UpdateFeatureInstallStates(package
);
2076 return MSI_SetFeatureStates(package
);
2079 /* OK this value is "interpreted" and then formatted based on the
2080 first few characters */
2081 static LPSTR
parse_value(MSIPACKAGE
*package
, LPCWSTR value
, DWORD
*type
,
2085 if (value
[0]=='#' && value
[1]!='#' && value
[1]!='%')
2091 LPWSTR deformated
= NULL
;
2094 deformat_string(package
, &value
[2], &deformated
);
2096 /* binary value type */
2100 *size
= (strlenW(ptr
)/2)+1;
2102 *size
= strlenW(ptr
)/2;
2104 data
= msi_alloc(*size
);
2110 /* if uneven pad with a zero in front */
2116 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2118 TRACE("Uneven byte count\n");
2126 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2129 msi_free(deformated
);
2131 TRACE("Data %i bytes(%i)\n",*size
,count
);
2138 deformat_string(package
, &value
[1], &deformated
);
2141 *size
= sizeof(DWORD
);
2142 data
= msi_alloc(*size
);
2148 if ( (*p
< '0') || (*p
> '9') )
2154 if (deformated
[0] == '-')
2157 TRACE("DWORD %i\n",*(LPDWORD
)data
);
2159 msi_free(deformated
);
2164 static const WCHAR szMulti
[] = {'[','~',']',0};
2173 *type
=REG_EXPAND_SZ
;
2181 if (strstrW(value
,szMulti
))
2182 *type
= REG_MULTI_SZ
;
2184 *size
= deformat_string(package
, ptr
,(LPWSTR
*)&data
);
2189 static UINT
ITERATE_WriteRegistryValues(MSIRECORD
*row
, LPVOID param
)
2191 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
2192 static const WCHAR szHCR
[] =
2193 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2194 'R','O','O','T','\\',0};
2195 static const WCHAR szHCU
[] =
2196 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2197 'U','S','E','R','\\',0};
2198 static const WCHAR szHLM
[] =
2199 {'H','K','E','Y','_','L','O','C','A','L','_',
2200 'M','A','C','H','I','N','E','\\',0};
2201 static const WCHAR szHU
[] =
2202 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2204 LPSTR value_data
= NULL
;
2205 HKEY root_key
, hkey
;
2208 LPCWSTR szRoot
, component
, name
, key
, value
;
2213 BOOL check_first
= FALSE
;
2216 ui_progress(package
,2,0,0,0);
2223 component
= MSI_RecordGetString(row
, 6);
2224 comp
= get_loaded_component(package
,component
);
2226 return ERROR_SUCCESS
;
2228 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2230 TRACE("Skipping write due to disabled component %s\n",
2231 debugstr_w(component
));
2233 comp
->Action
= comp
->Installed
;
2235 return ERROR_SUCCESS
;
2238 comp
->Action
= INSTALLSTATE_LOCAL
;
2240 name
= MSI_RecordGetString(row
, 4);
2241 if( MSI_RecordIsNull(row
,5) && name
)
2243 /* null values can have special meanings */
2244 if (name
[0]=='-' && name
[1] == 0)
2245 return ERROR_SUCCESS
;
2246 else if ((name
[0]=='+' && name
[1] == 0) ||
2247 (name
[0] == '*' && name
[1] == 0))
2252 root
= MSI_RecordGetInteger(row
,2);
2253 key
= MSI_RecordGetString(row
, 3);
2255 /* get the root key */
2260 static const WCHAR szALLUSER
[] = {'A','L','L','U','S','E','R','S',0};
2261 LPWSTR all_users
= msi_dup_property( package
, szALLUSER
);
2262 if (all_users
&& all_users
[0] == '1')
2264 root_key
= HKEY_LOCAL_MACHINE
;
2269 root_key
= HKEY_CURRENT_USER
;
2272 msi_free(all_users
);
2275 case 0: root_key
= HKEY_CLASSES_ROOT
;
2278 case 1: root_key
= HKEY_CURRENT_USER
;
2281 case 2: root_key
= HKEY_LOCAL_MACHINE
;
2284 case 3: root_key
= HKEY_USERS
;
2288 ERR("Unknown root %i\n",root
);
2294 return ERROR_SUCCESS
;
2296 deformat_string(package
, key
, &deformated
);
2297 size
= strlenW(deformated
) + strlenW(szRoot
) + 1;
2298 uikey
= msi_alloc(size
*sizeof(WCHAR
));
2299 strcpyW(uikey
,szRoot
);
2300 strcatW(uikey
,deformated
);
2302 if (RegCreateKeyW( root_key
, deformated
, &hkey
))
2304 ERR("Could not create key %s\n",debugstr_w(deformated
));
2305 msi_free(deformated
);
2307 return ERROR_SUCCESS
;
2309 msi_free(deformated
);
2311 value
= MSI_RecordGetString(row
,5);
2313 value_data
= parse_value(package
, value
, &type
, &size
);
2316 static const WCHAR szEmpty
[] = {0};
2317 value_data
= (LPSTR
)strdupW(szEmpty
);
2322 deformat_string(package
, name
, &deformated
);
2324 /* get the double nulls to terminate SZ_MULTI */
2325 if (type
== REG_MULTI_SZ
)
2326 size
+=sizeof(WCHAR
);
2330 TRACE("Setting value %s of %s\n",debugstr_w(deformated
),
2332 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
)value_data
, size
);
2337 rc
= RegQueryValueExW(hkey
, deformated
, NULL
, NULL
, NULL
, &sz
);
2338 if (rc
== ERROR_SUCCESS
|| rc
== ERROR_MORE_DATA
)
2340 TRACE("value %s of %s checked already exists\n",
2341 debugstr_w(deformated
), debugstr_w(uikey
));
2345 TRACE("Checked and setting value %s of %s\n",
2346 debugstr_w(deformated
), debugstr_w(uikey
));
2347 if (deformated
|| size
)
2348 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
) value_data
, size
);
2353 uirow
= MSI_CreateRecord(3);
2354 MSI_RecordSetStringW(uirow
,2,deformated
);
2355 MSI_RecordSetStringW(uirow
,1,uikey
);
2358 MSI_RecordSetStringW(uirow
,3,(LPWSTR
)value_data
);
2360 MSI_RecordSetStringW(uirow
,3,value
);
2362 ui_actiondata(package
,szWriteRegistryValues
,uirow
);
2363 msiobj_release( &uirow
->hdr
);
2365 msi_free(value_data
);
2366 msi_free(deformated
);
2369 return ERROR_SUCCESS
;
2372 static UINT
ACTION_WriteRegistryValues(MSIPACKAGE
*package
)
2376 static const WCHAR ExecSeqQuery
[] =
2377 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2378 '`','R','e','g','i','s','t','r','y','`',0 };
2380 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2381 if (rc
!= ERROR_SUCCESS
)
2382 return ERROR_SUCCESS
;
2384 /* increment progress bar each time action data is sent */
2385 ui_progress(package
,1,REG_PROGRESS_VALUE
,1,0);
2387 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteRegistryValues
, package
);
2389 msiobj_release(&view
->hdr
);
2393 static UINT
ACTION_InstallInitialize(MSIPACKAGE
*package
)
2395 package
->script
->CurrentlyScripting
= TRUE
;
2397 return ERROR_SUCCESS
;
2401 static UINT
ACTION_InstallValidate(MSIPACKAGE
*package
)
2406 static const WCHAR q1
[]=
2407 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2408 '`','R','e','g','i','s','t','r','y','`',0};
2411 MSIFEATURE
*feature
;
2414 TRACE("InstallValidate\n");
2416 rc
= MSI_DatabaseOpenViewW(package
->db
, q1
, &view
);
2417 if (rc
== ERROR_SUCCESS
)
2419 MSI_IterateRecords( view
, &progress
, NULL
, package
);
2420 msiobj_release( &view
->hdr
);
2421 total
+= progress
* REG_PROGRESS_VALUE
;
2424 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2425 total
+= COMPONENT_PROGRESS_VALUE
;
2427 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2428 total
+= file
->FileSize
;
2430 ui_progress(package
,0,total
,0,0);
2432 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2434 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2435 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
,
2436 feature
->ActionRequest
);
2439 return ERROR_SUCCESS
;
2442 static UINT
ITERATE_LaunchConditions(MSIRECORD
*row
, LPVOID param
)
2444 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
2445 LPCWSTR cond
= NULL
;
2446 LPCWSTR message
= NULL
;
2447 static const WCHAR title
[]=
2448 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2450 cond
= MSI_RecordGetString(row
,1);
2452 if (MSI_EvaluateConditionW(package
,cond
) != MSICONDITION_TRUE
)
2455 message
= MSI_RecordGetString(row
,2);
2456 deformat_string(package
,message
,&deformated
);
2457 MessageBoxW(NULL
,deformated
,title
,MB_OK
);
2458 msi_free(deformated
);
2459 return ERROR_FUNCTION_FAILED
;
2462 return ERROR_SUCCESS
;
2465 static UINT
ACTION_LaunchConditions(MSIPACKAGE
*package
)
2468 MSIQUERY
* view
= NULL
;
2469 static const WCHAR ExecSeqQuery
[] =
2470 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2471 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2473 TRACE("Checking launch conditions\n");
2475 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2476 if (rc
!= ERROR_SUCCESS
)
2477 return ERROR_SUCCESS
;
2479 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_LaunchConditions
, package
);
2480 msiobj_release(&view
->hdr
);
2485 static LPWSTR
resolve_keypath( MSIPACKAGE
* package
, MSICOMPONENT
*cmp
)
2489 return resolve_folder(package
,cmp
->Directory
,FALSE
,FALSE
,NULL
);
2491 if (cmp
->Attributes
& msidbComponentAttributesRegistryKeyPath
)
2493 MSIRECORD
* row
= 0;
2495 LPWSTR deformated
,buffer
,deformated_name
;
2497 static const WCHAR ExecSeqQuery
[] =
2498 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2499 '`','R','e','g','i','s','t','r','y','`',' ',
2500 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2501 ' ','=',' ' ,'\'','%','s','\'',0 };
2502 static const WCHAR fmt
[]={'%','0','2','i',':','\\','%','s','\\',0};
2503 static const WCHAR fmt2
[]=
2504 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2506 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
,cmp
->KeyPath
);
2510 root
= MSI_RecordGetInteger(row
,2);
2511 key
= MSI_RecordGetString(row
, 3);
2512 name
= MSI_RecordGetString(row
, 4);
2513 deformat_string(package
, key
, &deformated
);
2514 deformat_string(package
, name
, &deformated_name
);
2516 len
= strlenW(deformated
) + 6;
2517 if (deformated_name
)
2518 len
+=strlenW(deformated_name
);
2520 buffer
= msi_alloc( len
*sizeof(WCHAR
));
2522 if (deformated_name
)
2523 sprintfW(buffer
,fmt2
,root
,deformated
,deformated_name
);
2525 sprintfW(buffer
,fmt
,root
,deformated
);
2527 msi_free(deformated
);
2528 msi_free(deformated_name
);
2529 msiobj_release(&row
->hdr
);
2533 else if (cmp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2535 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2540 MSIFILE
*file
= get_loaded_file( package
, cmp
->KeyPath
);
2543 return strdupW( file
->TargetPath
);
2548 static HKEY
openSharedDLLsKey(void)
2551 static const WCHAR path
[] =
2552 {'S','o','f','t','w','a','r','e','\\',
2553 'M','i','c','r','o','s','o','f','t','\\',
2554 'W','i','n','d','o','w','s','\\',
2555 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2556 'S','h','a','r','e','d','D','L','L','s',0};
2558 RegCreateKeyW(HKEY_LOCAL_MACHINE
,path
,&hkey
);
2562 static UINT
ACTION_GetSharedDLLsCount(LPCWSTR dll
)
2567 DWORD sz
= sizeof(count
);
2570 hkey
= openSharedDLLsKey();
2571 rc
= RegQueryValueExW(hkey
, dll
, NULL
, &type
, (LPBYTE
)&count
, &sz
);
2572 if (rc
!= ERROR_SUCCESS
)
2578 static UINT
ACTION_WriteSharedDLLsCount(LPCWSTR path
, UINT count
)
2582 hkey
= openSharedDLLsKey();
2584 msi_reg_set_val_dword( hkey
, path
, count
);
2586 RegDeleteValueW(hkey
,path
);
2592 * Return TRUE if the count should be written out and FALSE if not
2594 static void ACTION_RefCountComponent( MSIPACKAGE
* package
, MSICOMPONENT
*comp
)
2596 MSIFEATURE
*feature
;
2600 /* only refcount DLLs */
2601 if (comp
->KeyPath
== NULL
||
2602 comp
->Attributes
& msidbComponentAttributesRegistryKeyPath
||
2603 comp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2607 count
= ACTION_GetSharedDLLsCount( comp
->FullKeypath
);
2608 write
= (count
> 0);
2610 if (comp
->Attributes
& msidbComponentAttributesSharedDllRefCount
)
2614 /* increment counts */
2615 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2619 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
))
2622 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2624 if ( cl
->component
== comp
)
2629 /* decrement counts */
2630 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2634 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ABSENT
))
2637 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2639 if ( cl
->component
== comp
)
2644 /* ref count all the files in the component */
2649 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2651 if (file
->Component
== comp
)
2652 ACTION_WriteSharedDLLsCount( file
->TargetPath
, count
);
2656 /* add a count for permenent */
2657 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2660 comp
->RefCount
= count
;
2663 ACTION_WriteSharedDLLsCount( comp
->FullKeypath
, comp
->RefCount
);
2667 * Ok further analysis makes me think that this work is
2668 * actually done in the PublishComponents and PublishFeatures
2669 * step, and not here. It appears like the keypath and all that is
2670 * resolved in this step, however actually written in the Publish steps.
2671 * But we will leave it here for now because it is unclear
2673 static UINT
ACTION_ProcessComponents(MSIPACKAGE
*package
)
2675 WCHAR squished_pc
[GUID_SIZE
];
2676 WCHAR squished_cc
[GUID_SIZE
];
2679 HKEY hkey
=0,hkey2
=0;
2681 /* writes the Component and Features values to the registry */
2683 rc
= MSIREG_OpenComponents(&hkey
);
2684 if (rc
!= ERROR_SUCCESS
)
2687 squash_guid(package
->ProductCode
,squished_pc
);
2688 ui_progress(package
,1,COMPONENT_PROGRESS_VALUE
,1,0);
2690 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2694 ui_progress(package
,2,0,0,0);
2695 if (!comp
->ComponentId
)
2698 squash_guid(comp
->ComponentId
,squished_cc
);
2700 msi_free(comp
->FullKeypath
);
2701 comp
->FullKeypath
= resolve_keypath( package
, comp
);
2703 /* do the refcounting */
2704 ACTION_RefCountComponent( package
, comp
);
2706 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2707 debugstr_w(comp
->Component
),
2708 debugstr_w(squished_cc
),
2709 debugstr_w(comp
->FullKeypath
),
2712 * Write the keypath out if the component is to be registered
2713 * and delete the key if the component is to be deregistered
2715 if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2717 rc
= RegCreateKeyW(hkey
,squished_cc
,&hkey2
);
2718 if (rc
!= ERROR_SUCCESS
)
2721 if (!comp
->FullKeypath
)
2724 msi_reg_set_val_str( hkey2
, squished_pc
, comp
->FullKeypath
);
2726 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2728 static const WCHAR szPermKey
[] =
2729 { '0','0','0','0','0','0','0','0','0','0','0','0',
2730 '0','0','0','0','0','0','0','0','0','0','0','0',
2731 '0','0','0','0','0','0','0','0',0 };
2733 msi_reg_set_val_str( hkey2
, szPermKey
, comp
->FullKeypath
);
2739 uirow
= MSI_CreateRecord(3);
2740 MSI_RecordSetStringW(uirow
,1,package
->ProductCode
);
2741 MSI_RecordSetStringW(uirow
,2,comp
->ComponentId
);
2742 MSI_RecordSetStringW(uirow
,3,comp
->FullKeypath
);
2743 ui_actiondata(package
,szProcessComponents
,uirow
);
2744 msiobj_release( &uirow
->hdr
);
2746 else if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ABSENT
))
2750 rc
= RegOpenKeyW(hkey
,squished_cc
,&hkey2
);
2751 if (rc
!= ERROR_SUCCESS
)
2754 RegDeleteValueW(hkey2
,squished_pc
);
2756 /* if the key is empty delete it */
2757 res
= RegEnumKeyExW(hkey2
,0,NULL
,0,0,NULL
,0,NULL
);
2759 if (res
== ERROR_NO_MORE_ITEMS
)
2760 RegDeleteKeyW(hkey
,squished_cc
);
2763 uirow
= MSI_CreateRecord(2);
2764 MSI_RecordSetStringW(uirow
,1,package
->ProductCode
);
2765 MSI_RecordSetStringW(uirow
,2,comp
->ComponentId
);
2766 ui_actiondata(package
,szProcessComponents
,uirow
);
2767 msiobj_release( &uirow
->hdr
);
2782 static BOOL CALLBACK
Typelib_EnumResNameProc( HMODULE hModule
, LPCWSTR lpszType
,
2783 LPWSTR lpszName
, LONG_PTR lParam
)
2786 typelib_struct
*tl_struct
= (typelib_struct
*) lParam
;
2787 static const WCHAR fmt
[] = {'%','s','\\','%','i',0};
2791 if (!IS_INTRESOURCE(lpszName
))
2793 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName
));
2797 sz
= strlenW(tl_struct
->source
)+4;
2798 sz
*= sizeof(WCHAR
);
2800 if ((INT_PTR
)lpszName
== 1)
2801 tl_struct
->path
= strdupW(tl_struct
->source
);
2804 tl_struct
->path
= msi_alloc(sz
);
2805 sprintfW(tl_struct
->path
,fmt
,tl_struct
->source
, lpszName
);
2808 TRACE("trying %s\n", debugstr_w(tl_struct
->path
));
2809 res
= LoadTypeLib(tl_struct
->path
,&tl_struct
->ptLib
);
2810 if (!SUCCEEDED(res
))
2812 msi_free(tl_struct
->path
);
2813 tl_struct
->path
= NULL
;
2818 ITypeLib_GetLibAttr(tl_struct
->ptLib
, &attr
);
2819 if (IsEqualGUID(&(tl_struct
->clsid
),&(attr
->guid
)))
2821 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2825 msi_free(tl_struct
->path
);
2826 tl_struct
->path
= NULL
;
2828 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2829 ITypeLib_Release(tl_struct
->ptLib
);
2834 static UINT
ITERATE_RegisterTypeLibraries(MSIRECORD
*row
, LPVOID param
)
2836 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
2840 typelib_struct tl_struct
;
2842 static const WCHAR szTYPELIB
[] = {'T','Y','P','E','L','I','B',0};
2844 component
= MSI_RecordGetString(row
,3);
2845 comp
= get_loaded_component(package
,component
);
2847 return ERROR_SUCCESS
;
2849 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2851 TRACE("Skipping typelib reg due to disabled component\n");
2853 comp
->Action
= comp
->Installed
;
2855 return ERROR_SUCCESS
;
2858 comp
->Action
= INSTALLSTATE_LOCAL
;
2860 file
= get_loaded_file( package
, comp
->KeyPath
);
2862 return ERROR_SUCCESS
;
2864 module
= LoadLibraryExW( file
->TargetPath
, NULL
, LOAD_LIBRARY_AS_DATAFILE
);
2868 guid
= MSI_RecordGetString(row
,1);
2869 CLSIDFromString((LPWSTR
)guid
, &tl_struct
.clsid
);
2870 tl_struct
.source
= strdupW( file
->TargetPath
);
2871 tl_struct
.path
= NULL
;
2873 EnumResourceNamesW(module
, szTYPELIB
, Typelib_EnumResNameProc
,
2874 (LONG_PTR
)&tl_struct
);
2882 helpid
= MSI_RecordGetString(row
,6);
2885 help
= resolve_folder(package
,helpid
,FALSE
,FALSE
,NULL
);
2886 res
= RegisterTypeLib(tl_struct
.ptLib
,tl_struct
.path
,help
);
2889 if (!SUCCEEDED(res
))
2890 ERR("Failed to register type library %s\n",
2891 debugstr_w(tl_struct
.path
));
2894 ui_actiondata(package
,szRegisterTypeLibraries
,row
);
2896 TRACE("Registered %s\n", debugstr_w(tl_struct
.path
));
2899 ITypeLib_Release(tl_struct
.ptLib
);
2900 msi_free(tl_struct
.path
);
2903 ERR("Failed to load type library %s\n",
2904 debugstr_w(tl_struct
.source
));
2906 FreeLibrary(module
);
2907 msi_free(tl_struct
.source
);
2910 ERR("Could not load file! %s\n", debugstr_w(file
->TargetPath
));
2912 return ERROR_SUCCESS
;
2915 static UINT
ACTION_RegisterTypeLibraries(MSIPACKAGE
*package
)
2918 * OK this is a bit confusing.. I am given a _Component key and I believe
2919 * that the file that is being registered as a type library is the "key file
2920 * of that component" which I interpret to mean "The file in the KeyPath of
2925 static const WCHAR Query
[] =
2926 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2927 '`','T','y','p','e','L','i','b','`',0};
2929 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
2930 if (rc
!= ERROR_SUCCESS
)
2931 return ERROR_SUCCESS
;
2933 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_RegisterTypeLibraries
, package
);
2934 msiobj_release(&view
->hdr
);
2938 static UINT
ITERATE_CreateShortcuts(MSIRECORD
*row
, LPVOID param
)
2940 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
2941 LPWSTR target_file
, target_folder
, filename
;
2942 LPCWSTR buffer
, extension
;
2944 static const WCHAR szlnk
[]={'.','l','n','k',0};
2945 IShellLinkW
*sl
= NULL
;
2946 IPersistFile
*pf
= NULL
;
2949 buffer
= MSI_RecordGetString(row
,4);
2950 comp
= get_loaded_component(package
,buffer
);
2952 return ERROR_SUCCESS
;
2954 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2956 TRACE("Skipping shortcut creation due to disabled component\n");
2958 comp
->Action
= comp
->Installed
;
2960 return ERROR_SUCCESS
;
2963 comp
->Action
= INSTALLSTATE_LOCAL
;
2965 ui_actiondata(package
,szCreateShortcuts
,row
);
2967 res
= CoCreateInstance( &CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
,
2968 &IID_IShellLinkW
, (LPVOID
*) &sl
);
2972 ERR("CLSID_ShellLink not available\n");
2976 res
= IShellLinkW_QueryInterface( sl
, &IID_IPersistFile
,(LPVOID
*) &pf
);
2979 ERR("QueryInterface(IID_IPersistFile) failed\n");
2983 buffer
= MSI_RecordGetString(row
,2);
2984 target_folder
= resolve_folder(package
, buffer
,FALSE
,FALSE
,NULL
);
2986 /* may be needed because of a bug somehwere else */
2987 create_full_pathW(target_folder
);
2989 filename
= msi_dup_record_field( row
, 3 );
2990 reduce_to_longfilename(filename
);
2992 extension
= strchrW(filename
,'.');
2993 if (!extension
|| strcmpiW(extension
,szlnk
))
2995 int len
= strlenW(filename
);
2996 filename
= msi_realloc(filename
, len
* sizeof(WCHAR
) + sizeof(szlnk
));
2997 memcpy(filename
+ len
, szlnk
, sizeof(szlnk
));
2999 target_file
= build_directory_name(2, target_folder
, filename
);
3000 msi_free(target_folder
);
3003 buffer
= MSI_RecordGetString(row
,5);
3004 if (strchrW(buffer
,'['))
3007 deformat_string(package
,buffer
,&deformated
);
3008 IShellLinkW_SetPath(sl
,deformated
);
3009 msi_free(deformated
);
3013 FIXME("poorly handled shortcut format, advertised shortcut\n");
3014 IShellLinkW_SetPath(sl
,comp
->FullKeypath
);
3017 if (!MSI_RecordIsNull(row
,6))
3020 buffer
= MSI_RecordGetString(row
,6);
3021 deformat_string(package
,buffer
,&deformated
);
3022 IShellLinkW_SetArguments(sl
,deformated
);
3023 msi_free(deformated
);
3026 if (!MSI_RecordIsNull(row
,7))
3028 buffer
= MSI_RecordGetString(row
,7);
3029 IShellLinkW_SetDescription(sl
,buffer
);
3032 if (!MSI_RecordIsNull(row
,8))
3033 IShellLinkW_SetHotkey(sl
,MSI_RecordGetInteger(row
,8));
3035 if (!MSI_RecordIsNull(row
,9))
3040 buffer
= MSI_RecordGetString(row
,9);
3042 Path
= build_icon_path(package
,buffer
);
3043 index
= MSI_RecordGetInteger(row
,10);
3045 /* no value means 0 */
3046 if (index
== MSI_NULL_INTEGER
)
3049 IShellLinkW_SetIconLocation(sl
,Path
,index
);
3053 if (!MSI_RecordIsNull(row
,11))
3054 IShellLinkW_SetShowCmd(sl
,MSI_RecordGetInteger(row
,11));
3056 if (!MSI_RecordIsNull(row
,12))
3059 buffer
= MSI_RecordGetString(row
,12);
3060 Path
= resolve_folder(package
, buffer
, FALSE
, FALSE
, NULL
);
3062 IShellLinkW_SetWorkingDirectory(sl
,Path
);
3066 TRACE("Writing shortcut to %s\n",debugstr_w(target_file
));
3067 IPersistFile_Save(pf
,target_file
,FALSE
);
3069 msi_free(target_file
);
3073 IPersistFile_Release( pf
);
3075 IShellLinkW_Release( sl
);
3077 return ERROR_SUCCESS
;
3080 static UINT
ACTION_CreateShortcuts(MSIPACKAGE
*package
)
3085 static const WCHAR Query
[] =
3086 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3087 '`','S','h','o','r','t','c','u','t','`',0};
3089 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3090 if (rc
!= ERROR_SUCCESS
)
3091 return ERROR_SUCCESS
;
3093 res
= CoInitialize( NULL
);
3096 ERR("CoInitialize failed\n");
3097 return ERROR_FUNCTION_FAILED
;
3100 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateShortcuts
, package
);
3101 msiobj_release(&view
->hdr
);
3108 static UINT
ITERATE_PublishProduct(MSIRECORD
*row
, LPVOID param
)
3110 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
3119 FileName
= MSI_RecordGetString(row
,1);
3122 ERR("Unable to get FileName\n");
3123 return ERROR_SUCCESS
;
3126 FilePath
= build_icon_path(package
,FileName
);
3128 TRACE("Creating icon file at %s\n",debugstr_w(FilePath
));
3130 the_file
= CreateFileW(FilePath
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
,
3131 FILE_ATTRIBUTE_NORMAL
, NULL
);
3133 if (the_file
== INVALID_HANDLE_VALUE
)
3135 ERR("Unable to create file %s\n",debugstr_w(FilePath
));
3137 return ERROR_SUCCESS
;
3144 rc
= MSI_RecordReadStream(row
,2,buffer
,&sz
);
3145 if (rc
!= ERROR_SUCCESS
)
3147 ERR("Failed to get stream\n");
3148 CloseHandle(the_file
);
3149 DeleteFileW(FilePath
);
3152 WriteFile(the_file
,buffer
,sz
,&write
,NULL
);
3153 } while (sz
== 1024);
3157 CloseHandle(the_file
);
3159 uirow
= MSI_CreateRecord(1);
3160 MSI_RecordSetStringW(uirow
,1,FileName
);
3161 ui_actiondata(package
,szPublishProduct
,uirow
);
3162 msiobj_release( &uirow
->hdr
);
3164 return ERROR_SUCCESS
;
3168 * 99% of the work done here is only done for
3169 * advertised installs. However this is where the
3170 * Icon table is processed and written out
3171 * so that is what I am going to do here.
3173 static UINT
ACTION_PublishProduct(MSIPACKAGE
*package
)
3177 static const WCHAR Query
[]=
3178 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3179 '`','I','c','o','n','`',0};
3180 /* for registry stuff */
3183 static const WCHAR szProductLanguage
[] =
3184 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3185 static const WCHAR szARPProductIcon
[] =
3186 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3187 static const WCHAR szProductVersion
[] =
3188 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3192 MSIHANDLE hDb
, hSumInfo
;
3194 /* write out icon files */
3196 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3197 if (rc
== ERROR_SUCCESS
)
3199 MSI_IterateRecords(view
, NULL
, ITERATE_PublishProduct
, package
);
3200 msiobj_release(&view
->hdr
);
3203 /* ok there is a lot more done here but i need to figure out what */
3205 rc
= MSIREG_OpenProductsKey(package
->ProductCode
,&hkey
,TRUE
);
3206 if (rc
!= ERROR_SUCCESS
)
3209 rc
= MSIREG_OpenUserProductsKey(package
->ProductCode
,&hukey
,TRUE
);
3210 if (rc
!= ERROR_SUCCESS
)
3214 buffer
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTNAMEW
);
3215 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PRODUCTNAMEW
, buffer
);
3218 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
3219 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
3221 buffer
= msi_dup_property( package
, szARPProductIcon
);
3224 LPWSTR path
= build_icon_path(package
,buffer
);
3225 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PRODUCTICONW
, path
);
3230 buffer
= msi_dup_property( package
, szProductVersion
);
3233 DWORD verdword
= msi_version_str_to_dword(buffer
);
3234 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
3238 /* FIXME: Need to write more keys to the user registry */
3240 hDb
= alloc_msihandle( &package
->db
->hdr
);
3242 rc
= ERROR_NOT_ENOUGH_MEMORY
;
3245 rc
= MsiGetSummaryInformationW(hDb
, NULL
, 0, &hSumInfo
);
3246 MsiCloseHandle(hDb
);
3247 if (rc
== ERROR_SUCCESS
)
3249 WCHAR guidbuffer
[0x200];
3251 rc
= MsiSummaryInfoGetPropertyW(hSumInfo
, 9, NULL
, NULL
, NULL
,
3253 if (rc
== ERROR_SUCCESS
)
3255 WCHAR squashed
[GUID_SIZE
];
3256 /* for now we only care about the first guid */
3257 LPWSTR ptr
= strchrW(guidbuffer
,';');
3259 squash_guid(guidbuffer
,squashed
);
3260 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PACKAGECODEW
, squashed
);
3264 ERR("Unable to query Revision_Number...\n");
3267 MsiCloseHandle(hSumInfo
);
3271 ERR("Unable to open Summary Information\n");
3283 static UINT
ITERATE_WriteIniValues(MSIRECORD
*row
, LPVOID param
)
3285 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3286 LPCWSTR component
,section
,key
,value
,identifier
,filename
,dirproperty
;
3287 LPWSTR deformated_section
, deformated_key
, deformated_value
;
3288 LPWSTR folder
, fullname
= NULL
;
3292 static const WCHAR szWindowsFolder
[] =
3293 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3295 component
= MSI_RecordGetString(row
, 8);
3296 comp
= get_loaded_component(package
,component
);
3298 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3300 TRACE("Skipping ini file due to disabled component %s\n",
3301 debugstr_w(component
));
3303 comp
->Action
= comp
->Installed
;
3305 return ERROR_SUCCESS
;
3308 comp
->Action
= INSTALLSTATE_LOCAL
;
3310 identifier
= MSI_RecordGetString(row
,1);
3311 filename
= MSI_RecordGetString(row
,2);
3312 dirproperty
= MSI_RecordGetString(row
,3);
3313 section
= MSI_RecordGetString(row
,4);
3314 key
= MSI_RecordGetString(row
,5);
3315 value
= MSI_RecordGetString(row
,6);
3316 action
= MSI_RecordGetInteger(row
,7);
3318 deformat_string(package
,section
,&deformated_section
);
3319 deformat_string(package
,key
,&deformated_key
);
3320 deformat_string(package
,value
,&deformated_value
);
3324 folder
= resolve_folder(package
, dirproperty
, FALSE
, FALSE
, NULL
);
3326 folder
= msi_dup_property( package
, dirproperty
);
3329 folder
= msi_dup_property( package
, szWindowsFolder
);
3333 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty
));
3337 fullname
= build_directory_name(2, folder
, filename
);
3341 TRACE("Adding value %s to section %s in %s\n",
3342 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3343 debugstr_w(fullname
));
3344 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3345 deformated_value
, fullname
);
3347 else if (action
== 1)
3350 GetPrivateProfileStringW(deformated_section
, deformated_key
, NULL
,
3351 returned
, 10, fullname
);
3352 if (returned
[0] == 0)
3354 TRACE("Adding value %s to section %s in %s\n",
3355 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3356 debugstr_w(fullname
));
3358 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3359 deformated_value
, fullname
);
3362 else if (action
== 3)
3363 FIXME("Append to existing section not yet implemented\n");
3365 uirow
= MSI_CreateRecord(4);
3366 MSI_RecordSetStringW(uirow
,1,identifier
);
3367 MSI_RecordSetStringW(uirow
,2,deformated_section
);
3368 MSI_RecordSetStringW(uirow
,3,deformated_key
);
3369 MSI_RecordSetStringW(uirow
,4,deformated_value
);
3370 ui_actiondata(package
,szWriteIniValues
,uirow
);
3371 msiobj_release( &uirow
->hdr
);
3375 msi_free(deformated_key
);
3376 msi_free(deformated_value
);
3377 msi_free(deformated_section
);
3378 return ERROR_SUCCESS
;
3381 static UINT
ACTION_WriteIniValues(MSIPACKAGE
*package
)
3385 static const WCHAR ExecSeqQuery
[] =
3386 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3387 '`','I','n','i','F','i','l','e','`',0};
3389 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3390 if (rc
!= ERROR_SUCCESS
)
3392 TRACE("no IniFile table\n");
3393 return ERROR_SUCCESS
;
3396 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteIniValues
, package
);
3397 msiobj_release(&view
->hdr
);
3401 static UINT
ITERATE_SelfRegModules(MSIRECORD
*row
, LPVOID param
)
3403 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3408 static const WCHAR ExeStr
[] =
3409 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3410 static const WCHAR close
[] = {'\"',0};
3412 PROCESS_INFORMATION info
;
3417 memset(&si
,0,sizeof(STARTUPINFOW
));
3419 filename
= MSI_RecordGetString(row
,1);
3420 file
= get_loaded_file( package
, filename
);
3424 ERR("Unable to find file id %s\n",debugstr_w(filename
));
3425 return ERROR_SUCCESS
;
3428 len
= strlenW(ExeStr
) + strlenW( file
->TargetPath
) + 2;
3430 FullName
= msi_alloc(len
*sizeof(WCHAR
));
3431 strcpyW(FullName
,ExeStr
);
3432 strcatW( FullName
, file
->TargetPath
);
3433 strcatW(FullName
,close
);
3435 TRACE("Registering %s\n",debugstr_w(FullName
));
3436 brc
= CreateProcessW(NULL
, FullName
, NULL
, NULL
, FALSE
, 0, NULL
, c_colon
,
3440 msi_dialog_check_messages(info
.hProcess
);
3445 uirow
= MSI_CreateRecord( 2 );
3446 uipath
= strdupW( file
->TargetPath
);
3447 p
= strrchrW(uipath
,'\\');
3450 MSI_RecordSetStringW( uirow
, 1, &p
[2] );
3451 MSI_RecordSetStringW( uirow
, 2, uipath
);
3452 ui_actiondata( package
, szSelfRegModules
, uirow
);
3453 msiobj_release( &uirow
->hdr
);
3455 /* FIXME: call ui_progress? */
3457 return ERROR_SUCCESS
;
3460 static UINT
ACTION_SelfRegModules(MSIPACKAGE
*package
)
3464 static const WCHAR ExecSeqQuery
[] =
3465 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3466 '`','S','e','l','f','R','e','g','`',0};
3468 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3469 if (rc
!= ERROR_SUCCESS
)
3471 TRACE("no SelfReg table\n");
3472 return ERROR_SUCCESS
;
3475 MSI_IterateRecords(view
, NULL
, ITERATE_SelfRegModules
, package
);
3476 msiobj_release(&view
->hdr
);
3478 return ERROR_SUCCESS
;
3481 static UINT
ACTION_PublishFeatures(MSIPACKAGE
*package
)
3483 MSIFEATURE
*feature
;
3488 rc
= MSIREG_OpenFeaturesKey(package
->ProductCode
,&hkey
,TRUE
);
3489 if (rc
!= ERROR_SUCCESS
)
3492 rc
= MSIREG_OpenUserFeaturesKey(package
->ProductCode
,&hukey
,TRUE
);
3493 if (rc
!= ERROR_SUCCESS
)
3496 /* here the guids are base 85 encoded */
3497 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
3503 BOOL absent
= FALSE
;
3506 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
) &&
3507 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_SOURCE
) &&
3508 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ADVERTISED
))
3512 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3516 if (feature
->Feature_Parent
)
3517 size
+= strlenW( feature
->Feature_Parent
)+2;
3519 data
= msi_alloc(size
* sizeof(WCHAR
));
3522 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3524 MSICOMPONENT
* component
= cl
->component
;
3528 if (component
->ComponentId
)
3530 TRACE("From %s\n",debugstr_w(component
->ComponentId
));
3531 CLSIDFromString(component
->ComponentId
, &clsid
);
3532 encode_base85_guid(&clsid
,buf
);
3533 TRACE("to %s\n",debugstr_w(buf
));
3537 if (feature
->Feature_Parent
)
3539 static const WCHAR sep
[] = {'\2',0};
3541 strcatW(data
,feature
->Feature_Parent
);
3544 msi_reg_set_val_str( hkey
, feature
->Feature
, data
);
3548 if (feature
->Feature_Parent
)
3549 size
= strlenW(feature
->Feature_Parent
)*sizeof(WCHAR
);
3552 RegSetValueExW(hukey
,feature
->Feature
,0,REG_SZ
,
3553 (LPBYTE
)feature
->Feature_Parent
,size
);
3557 size
+= 2*sizeof(WCHAR
);
3558 data
= msi_alloc(size
);
3561 if (feature
->Feature_Parent
)
3562 strcpyW( &data
[1], feature
->Feature_Parent
);
3563 RegSetValueExW(hukey
,feature
->Feature
,0,REG_SZ
,
3569 uirow
= MSI_CreateRecord( 1 );
3570 MSI_RecordSetStringW( uirow
, 1, feature
->Feature
);
3571 ui_actiondata( package
, szPublishFeatures
, uirow
);
3572 msiobj_release( &uirow
->hdr
);
3573 /* FIXME: call ui_progress? */
3582 static UINT
msi_get_local_package_name( LPWSTR path
)
3584 static const WCHAR szInstaller
[] = {
3585 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3586 static const WCHAR fmt
[] = { '%','x','.','m','s','i',0};
3590 time
= GetTickCount();
3591 GetWindowsDirectoryW( path
, MAX_PATH
);
3592 lstrcatW( path
, szInstaller
);
3593 CreateDirectoryW( path
, NULL
);
3595 len
= lstrlenW(path
);
3596 for (i
=0; i
<0x10000; i
++)
3598 snprintfW( &path
[len
], MAX_PATH
- len
, fmt
, (time
+i
)&0xffff );
3599 handle
= CreateFileW( path
, GENERIC_WRITE
, 0, NULL
,
3600 CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, 0 );
3601 if (handle
!= INVALID_HANDLE_VALUE
)
3603 CloseHandle(handle
);
3606 if (GetLastError() != ERROR_FILE_EXISTS
&&
3607 GetLastError() != ERROR_SHARING_VIOLATION
)
3608 return ERROR_FUNCTION_FAILED
;
3611 return ERROR_SUCCESS
;
3614 static UINT
msi_make_package_local( MSIPACKAGE
*package
, HKEY hkey
)
3616 static const WCHAR szOriginalDatabase
[] =
3617 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3618 WCHAR packagefile
[MAX_PATH
];
3622 r
= msi_get_local_package_name( packagefile
);
3623 if (r
!= ERROR_SUCCESS
)
3626 TRACE("Copying to local package %s\n",debugstr_w(packagefile
));
3628 msiFilePath
= msi_dup_property( package
, szOriginalDatabase
);
3629 r
= CopyFileW( msiFilePath
, packagefile
, FALSE
);
3630 msi_free( msiFilePath
);
3634 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3635 debugstr_w(msiFilePath
), debugstr_w(packagefile
), GetLastError());
3636 return ERROR_FUNCTION_FAILED
;
3639 /* FIXME: maybe set this key in ACTION_RegisterProduct instead */
3640 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_LOCALPACKAGEW
, packagefile
);
3641 return ERROR_SUCCESS
;
3644 static UINT
msi_write_uninstall_property_vals( MSIPACKAGE
*package
, HKEY hkey
)
3646 LPWSTR prop
, val
, key
;
3647 static const LPCSTR propval
[] = {
3648 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3649 "ARPCONTACT", "Contact",
3650 "ARPCOMMENTS", "Comments",
3651 "ProductName", "DisplayName",
3652 "ProductVersion", "DisplayVersion",
3653 "ARPHELPLINK", "HelpLink",
3654 "ARPHELPTELEPHONE", "HelpTelephone",
3655 "ARPINSTALLLOCATION", "InstallLocation",
3656 "SourceDir", "InstallSource",
3657 "Manufacturer", "Publisher",
3658 "ARPREADME", "Readme",
3660 "ARPURLINFOABOUT", "URLInfoAbout",
3661 "ARPURLUPDATEINFO", "URLUpdateInfo",
3664 const LPCSTR
*p
= propval
;
3668 prop
= strdupAtoW( *p
++ );
3669 key
= strdupAtoW( *p
++ );
3670 val
= msi_dup_property( package
, prop
);
3671 msi_reg_set_val_str( hkey
, key
, val
);
3676 return ERROR_SUCCESS
;
3679 static UINT
ACTION_RegisterProduct(MSIPACKAGE
*package
)
3682 LPWSTR buffer
= NULL
;
3685 static const WCHAR szWindowsInstaller
[] =
3686 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3687 static const WCHAR szUpgradeCode
[] =
3688 {'U','p','g','r','a','d','e','C','o','d','e',0};
3689 static const WCHAR modpath_fmt
[] =
3690 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3691 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3692 static const WCHAR szModifyPath
[] =
3693 {'M','o','d','i','f','y','P','a','t','h',0};
3694 static const WCHAR szUninstallString
[] =
3695 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3696 static const WCHAR szEstimatedSize
[] =
3697 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3698 static const WCHAR szProductLanguage
[] =
3699 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3700 static const WCHAR szProductVersion
[] =
3701 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3704 static const WCHAR date_fmt
[] = {'%','i','%','i','%','i',0};
3705 LPWSTR upgrade_code
;
3708 rc
= MSIREG_OpenUninstallKey(package
->ProductCode
,&hkey
,TRUE
);
3709 if (rc
!= ERROR_SUCCESS
)
3712 /* dump all the info i can grab */
3713 /* FIXME: Flesh out more information */
3715 msi_write_uninstall_property_vals( package
, hkey
);
3717 msi_reg_set_val_dword( hkey
, szWindowsInstaller
, 1 );
3719 msi_make_package_local( package
, hkey
);
3721 /* do ModifyPath and UninstallString */
3722 size
= deformat_string(package
,modpath_fmt
,&buffer
);
3723 RegSetValueExW(hkey
,szModifyPath
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
3724 RegSetValueExW(hkey
,szUninstallString
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
3727 /* FIXME: Write real Estimated Size when we have it */
3728 msi_reg_set_val_dword( hkey
, szEstimatedSize
, 0 );
3730 GetLocalTime(&systime
);
3731 sprintfW(szDate
,date_fmt
,systime
.wYear
,systime
.wMonth
,systime
.wDay
);
3732 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_INSTALLDATEW
, szDate
);
3734 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
3735 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
3737 buffer
= msi_dup_property( package
, szProductVersion
);
3740 DWORD verdword
= msi_version_str_to_dword(buffer
);
3742 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
3743 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMAJORW
, verdword
>>24 );
3744 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMINORW
, (verdword
>>16)&0x00FF );
3748 /* Handle Upgrade Codes */
3749 upgrade_code
= msi_dup_property( package
, szUpgradeCode
);
3754 MSIREG_OpenUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
3755 squash_guid(package
->ProductCode
,squashed
);
3756 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
3758 MSIREG_OpenUserUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
3759 squash_guid(package
->ProductCode
,squashed
);
3760 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
3763 msi_free(upgrade_code
);
3768 /* FIXME: call ui_actiondata */
3770 return ERROR_SUCCESS
;
3773 static UINT
ACTION_InstallExecute(MSIPACKAGE
*package
)
3775 return execute_script(package
,INSTALL_SCRIPT
);
3778 static UINT
ACTION_InstallFinalize(MSIPACKAGE
*package
)
3782 /* turn off scheduleing */
3783 package
->script
->CurrentlyScripting
= FALSE
;
3785 /* first do the same as an InstallExecute */
3786 rc
= ACTION_InstallExecute(package
);
3787 if (rc
!= ERROR_SUCCESS
)
3790 /* then handle Commit Actions */
3791 rc
= execute_script(package
,COMMIT_SCRIPT
);
3796 static UINT
ACTION_ForceReboot(MSIPACKAGE
*package
)
3798 static const WCHAR RunOnce
[] = {
3799 'S','o','f','t','w','a','r','e','\\',
3800 'M','i','c','r','o','s','o','f','t','\\',
3801 'W','i','n','d','o','w','s','\\',
3802 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3803 'R','u','n','O','n','c','e',0};
3804 static const WCHAR InstallRunOnce
[] = {
3805 'S','o','f','t','w','a','r','e','\\',
3806 'M','i','c','r','o','s','o','f','t','\\',
3807 'W','i','n','d','o','w','s','\\',
3808 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3809 'I','n','s','t','a','l','l','e','r','\\',
3810 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
3812 static const WCHAR msiexec_fmt
[] = {
3814 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
3815 '\"','%','s','\"',0};
3816 static const WCHAR install_fmt
[] = {
3817 '/','I',' ','\"','%','s','\"',' ',
3818 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
3819 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
3820 WCHAR buffer
[256], sysdir
[MAX_PATH
];
3822 WCHAR squished_pc
[100];
3824 squash_guid(package
->ProductCode
,squished_pc
);
3826 GetSystemDirectoryW(sysdir
, sizeof(sysdir
)/sizeof(sysdir
[0]));
3827 RegCreateKeyW(HKEY_LOCAL_MACHINE
,RunOnce
,&hkey
);
3828 snprintfW(buffer
,sizeof(buffer
)/sizeof(buffer
[0]),msiexec_fmt
,sysdir
,
3831 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
3834 TRACE("Reboot command %s\n",debugstr_w(buffer
));
3836 RegCreateKeyW(HKEY_LOCAL_MACHINE
,InstallRunOnce
,&hkey
);
3837 sprintfW(buffer
,install_fmt
,package
->ProductCode
,squished_pc
);
3839 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
3842 return ERROR_INSTALL_SUSPEND
;
3845 static UINT
ACTION_ResolveSource(MSIPACKAGE
* package
)
3852 * we are currently doing what should be done here in the top level Install
3853 * however for Adminastrative and uninstalls this step will be needed
3855 if (!package
->PackagePath
)
3856 return ERROR_SUCCESS
;
3858 ptr
= strrchrW(package
->PackagePath
, '\\');
3860 return ERROR_SUCCESS
;
3862 len
= ptr
- package
->PackagePath
+ 2;
3863 source
= msi_alloc(len
* sizeof(WCHAR
));
3864 lstrcpynW(source
, package
->PackagePath
, len
);
3866 MSI_SetPropertyW(package
, cszSourceDir
, source
);
3867 MSI_SetPropertyW(package
, cszSOURCEDIR
, source
);
3871 attrib
= GetFileAttributesW(package
->PackagePath
);
3872 if (attrib
== INVALID_FILE_ATTRIBUTES
)
3878 rc
= MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
3879 MSIINSTALLCONTEXT_USERMANAGED
, MSICODE_PRODUCT
,
3880 INSTALLPROPERTY_DISKPROMPTW
,NULL
,&size
);
3881 if (rc
== ERROR_MORE_DATA
)
3883 prompt
= msi_alloc(size
* sizeof(WCHAR
));
3884 MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
3885 MSIINSTALLCONTEXT_USERMANAGED
, MSICODE_PRODUCT
,
3886 INSTALLPROPERTY_DISKPROMPTW
,prompt
,&size
);
3889 prompt
= strdupW(package
->PackagePath
);
3891 msg
= generate_error_string(package
,1302,1,prompt
);
3892 while(attrib
== INVALID_FILE_ATTRIBUTES
)
3894 rc
= MessageBoxW(NULL
,msg
,NULL
,MB_OKCANCEL
);
3897 rc
= ERROR_INSTALL_USEREXIT
;
3900 attrib
= GetFileAttributesW(package
->PackagePath
);
3906 return ERROR_SUCCESS
;
3911 static UINT
ACTION_RegisterUser(MSIPACKAGE
*package
)
3918 static const WCHAR szPropKeys
[][80] =
3920 {'P','r','o','d','u','c','t','I','D',0},
3921 {'U','S','E','R','N','A','M','E',0},
3922 {'C','O','M','P','A','N','Y','N','A','M','E',0},
3926 static const WCHAR szRegKeys
[][80] =
3928 {'P','r','o','d','u','c','t','I','D',0},
3929 {'R','e','g','O','w','n','e','r',0},
3930 {'R','e','g','C','o','m','p','a','n','y',0},
3934 productid
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTIDW
);
3936 return ERROR_SUCCESS
;
3938 rc
= MSIREG_OpenUninstallKey(package
->ProductCode
,&hkey
,TRUE
);
3939 if (rc
!= ERROR_SUCCESS
)
3942 for( i
= 0; szPropKeys
[i
][0]; i
++ )
3944 buffer
= msi_dup_property( package
, szPropKeys
[i
] );
3945 msi_reg_set_val_str( hkey
, szRegKeys
[i
], buffer
);
3950 msi_free(productid
);
3953 /* FIXME: call ui_actiondata */
3955 return ERROR_SUCCESS
;
3959 static UINT
ACTION_ExecuteAction(MSIPACKAGE
*package
)
3963 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
3964 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
3969 static UINT
ITERATE_PublishComponent(MSIRECORD
*rec
, LPVOID param
)
3971 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3972 LPCWSTR compgroupid
=NULL
;
3973 LPCWSTR feature
=NULL
;
3974 LPCWSTR text
= NULL
;
3975 LPCWSTR qualifier
= NULL
;
3976 LPCWSTR component
= NULL
;
3977 LPWSTR advertise
= NULL
;
3978 LPWSTR output
= NULL
;
3980 UINT rc
= ERROR_SUCCESS
;
3985 component
= MSI_RecordGetString(rec
,3);
3986 comp
= get_loaded_component(package
,component
);
3988 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
) &&
3989 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_SOURCE
) &&
3990 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ADVERTISED
))
3992 TRACE("Skipping: Component %s not scheduled for install\n",
3993 debugstr_w(component
));
3995 return ERROR_SUCCESS
;
3998 compgroupid
= MSI_RecordGetString(rec
,1);
3999 qualifier
= MSI_RecordGetString(rec
,2);
4001 rc
= MSIREG_OpenUserComponentsKey(compgroupid
, &hkey
, TRUE
);
4002 if (rc
!= ERROR_SUCCESS
)
4005 text
= MSI_RecordGetString(rec
,4);
4006 feature
= MSI_RecordGetString(rec
,5);
4008 advertise
= create_component_advertise_string(package
, comp
, feature
);
4010 sz
= strlenW(advertise
);
4013 sz
+= lstrlenW(text
);
4016 sz
*= sizeof(WCHAR
);
4018 output
= msi_alloc_zero(sz
);
4019 strcpyW(output
,advertise
);
4020 msi_free(advertise
);
4023 strcatW(output
,text
);
4025 msi_reg_set_val_multi_str( hkey
, qualifier
, output
);
4032 uirow
= MSI_CreateRecord( 2 );
4033 MSI_RecordSetStringW( uirow
, 1, compgroupid
);
4034 MSI_RecordSetStringW( uirow
, 2, qualifier
);
4035 ui_actiondata( package
, szPublishComponents
, uirow
);
4036 msiobj_release( &uirow
->hdr
);
4037 /* FIXME: call ui_progress? */
4043 * At present I am ignorning the advertised components part of this and only
4044 * focusing on the qualified component sets
4046 static UINT
ACTION_PublishComponents(MSIPACKAGE
*package
)
4050 static const WCHAR ExecSeqQuery
[] =
4051 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4052 '`','P','u','b','l','i','s','h',
4053 'C','o','m','p','o','n','e','n','t','`',0};
4055 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4056 if (rc
!= ERROR_SUCCESS
)
4057 return ERROR_SUCCESS
;
4059 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_PublishComponent
, package
);
4060 msiobj_release(&view
->hdr
);
4065 static UINT
ITERATE_InstallService(MSIRECORD
*rec
, LPVOID param
)
4067 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4070 SC_HANDLE hscm
, service
= NULL
;
4071 LPCWSTR name
, disp
, comp
, depends
, pass
;
4072 LPCWSTR load_order
, serv_name
, key
;
4073 DWORD serv_type
, start_type
;
4076 static const WCHAR query
[] =
4077 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4078 '`','C','o','m','p','o','n','e','n','t','`',' ',
4079 'W','H','E','R','E',' ',
4080 '`','C','o','m','p','o','n','e','n','t','`',' ',
4081 '=','\'','%','s','\'',0};
4083 hscm
= OpenSCManagerW(NULL
, SERVICES_ACTIVE_DATABASEW
, GENERIC_WRITE
);
4086 ERR("Failed to open the SC Manager!\n");
4090 start_type
= MSI_RecordGetInteger(rec
, 5);
4091 if (start_type
== SERVICE_BOOT_START
|| start_type
== SERVICE_SYSTEM_START
)
4094 depends
= MSI_RecordGetString(rec
, 8);
4095 if (depends
&& *depends
)
4096 FIXME("Dependency list unhandled!\n");
4098 name
= MSI_RecordGetString(rec
, 2);
4099 disp
= MSI_RecordGetString(rec
, 3);
4100 serv_type
= MSI_RecordGetInteger(rec
, 4);
4101 err_control
= MSI_RecordGetInteger(rec
, 6);
4102 load_order
= MSI_RecordGetString(rec
, 7);
4103 serv_name
= MSI_RecordGetString(rec
, 9);
4104 pass
= MSI_RecordGetString(rec
, 10);
4105 comp
= MSI_RecordGetString(rec
, 12);
4107 /* fetch the service path */
4108 row
= MSI_QueryGetRecord(package
->db
, query
, comp
);
4111 ERR("Control query failed!\n");
4115 key
= MSI_RecordGetString(row
, 6);
4116 msiobj_release(&row
->hdr
);
4118 file
= get_loaded_file(package
, key
);
4121 ERR("Failed to load the service file\n");
4125 service
= CreateServiceW(hscm
, name
, disp
, GENERIC_ALL
, serv_type
,
4126 start_type
, err_control
, file
->TargetPath
,
4127 load_order
, NULL
, NULL
, serv_name
, pass
);
4130 if (GetLastError() != ERROR_SERVICE_EXISTS
)
4131 ERR("Failed to create service %s: %d\n", debugstr_w(name
), GetLastError());
4135 CloseServiceHandle(service
);
4136 CloseServiceHandle(hscm
);
4138 return ERROR_SUCCESS
;
4141 static UINT
ACTION_InstallServices( MSIPACKAGE
*package
)
4145 static const WCHAR ExecSeqQuery
[] =
4146 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4147 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4149 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4150 if (rc
!= ERROR_SUCCESS
)
4151 return ERROR_SUCCESS
;
4153 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallService
, package
);
4154 msiobj_release(&view
->hdr
);
4159 static UINT
msi_unimplemented_action_stub( MSIPACKAGE
*package
,
4160 LPCSTR action
, LPCWSTR table
)
4162 static const WCHAR query
[] = {
4163 'S','E','L','E','C','T',' ','*',' ',
4164 'F','R','O','M',' ','`','%','s','`',0 };
4165 MSIQUERY
*view
= NULL
;
4169 r
= MSI_OpenQuery( package
->db
, &view
, query
, table
);
4170 if (r
== ERROR_SUCCESS
)
4172 r
= MSI_IterateRecords(view
, &count
, NULL
, package
);
4173 msiobj_release(&view
->hdr
);
4177 FIXME("%s -> %u ignored %s table values\n",
4178 action
, count
, debugstr_w(table
));
4180 return ERROR_SUCCESS
;
4183 static UINT
ACTION_AllocateRegistrySpace( MSIPACKAGE
*package
)
4185 TRACE("%p\n", package
);
4186 return ERROR_SUCCESS
;
4189 static UINT
ACTION_RemoveIniValues( MSIPACKAGE
*package
)
4191 static const WCHAR table
[] =
4192 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
4193 return msi_unimplemented_action_stub( package
, "RemoveIniValues", table
);
4196 static UINT
ACTION_MoveFiles( MSIPACKAGE
*package
)
4198 static const WCHAR table
[] = { 'M','o','v','e','F','i','l','e',0 };
4199 return msi_unimplemented_action_stub( package
, "MoveFiles", table
);
4202 static UINT
ACTION_PatchFiles( MSIPACKAGE
*package
)
4204 static const WCHAR table
[] = { 'P','a','t','c','h',0 };
4205 return msi_unimplemented_action_stub( package
, "PatchFiles", table
);
4208 static UINT
ACTION_BindImage( MSIPACKAGE
*package
)
4210 static const WCHAR table
[] = { 'B','i','n','d','I','m','a','g','e',0 };
4211 return msi_unimplemented_action_stub( package
, "BindImage", table
);
4214 static UINT
ACTION_IsolateComponents( MSIPACKAGE
*package
)
4216 static const WCHAR table
[] = {
4217 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
4218 return msi_unimplemented_action_stub( package
, "IsolateComponents", table
);
4221 static UINT
ACTION_MigrateFeatureStates( MSIPACKAGE
*package
)
4223 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
4224 return msi_unimplemented_action_stub( package
, "MigrateFeatureStates", table
);
4227 static UINT
ACTION_SelfUnregModules( MSIPACKAGE
*package
)
4229 static const WCHAR table
[] = { 'S','e','l','f','R','e','g',0 };
4230 return msi_unimplemented_action_stub( package
, "SelfUnregModules", table
);
4233 static UINT
ACTION_StartServices( MSIPACKAGE
*package
)
4235 static const WCHAR table
[] = {
4236 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4237 return msi_unimplemented_action_stub( package
, "StartServices", table
);
4240 static UINT
ACTION_StopServices( MSIPACKAGE
*package
)
4242 static const WCHAR table
[] = {
4243 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4244 return msi_unimplemented_action_stub( package
, "StopServices", table
);
4247 static UINT
ACTION_DeleteServices( MSIPACKAGE
*package
)
4249 static const WCHAR table
[] = {
4250 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4251 return msi_unimplemented_action_stub( package
, "DeleteServices", table
);
4254 static UINT
ACTION_WriteEnvironmentStrings( MSIPACKAGE
*package
)
4256 static const WCHAR table
[] = {
4257 'E','n','v','i','r','o','n','m','e','n','t',0 };
4258 return msi_unimplemented_action_stub( package
, "WriteEnvironmentStrings", table
);
4261 static UINT
ACTION_RemoveEnvironmentStrings( MSIPACKAGE
*package
)
4263 static const WCHAR table
[] = {
4264 'E','n','v','i','r','o','n','m','e','n','t',0 };
4265 return msi_unimplemented_action_stub( package
, "RemoveEnvironmentStrings", table
);
4268 static UINT
ACTION_MsiPublishAssemblies( MSIPACKAGE
*package
)
4270 static const WCHAR table
[] = {
4271 'M','s','i','A','s','s','e','m','b','l','y',0 };
4272 return msi_unimplemented_action_stub( package
, "MsiPublishAssemblies", table
);
4275 static UINT
ACTION_MsiUnpublishAssemblies( MSIPACKAGE
*package
)
4277 static const WCHAR table
[] = {
4278 'M','s','i','A','s','s','e','m','b','l','y',0 };
4279 return msi_unimplemented_action_stub( package
, "MsiUnpublishAssemblies", table
);
4282 static UINT
ACTION_UnregisterFonts( MSIPACKAGE
*package
)
4284 static const WCHAR table
[] = { 'F','o','n','t',0 };
4285 return msi_unimplemented_action_stub( package
, "UnregisterFonts", table
);
4288 static UINT
ACTION_CCPSearch( MSIPACKAGE
*package
)
4290 static const WCHAR table
[] = { 'C','C','P','S','e','a','r','c','h',0 };
4291 return msi_unimplemented_action_stub( package
, "CCPSearch", table
);
4294 static UINT
ACTION_RMCCPSearch( MSIPACKAGE
*package
)
4296 static const WCHAR table
[] = { 'C','C','P','S','e','a','r','c','h',0 };
4297 return msi_unimplemented_action_stub( package
, "RMCCPSearch", table
);
4300 static UINT
ACTION_RegisterComPlus( MSIPACKAGE
*package
)
4302 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
4303 return msi_unimplemented_action_stub( package
, "RegisterComPlus", table
);
4306 static UINT
ACTION_UnregisterComPlus( MSIPACKAGE
*package
)
4308 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
4309 return msi_unimplemented_action_stub( package
, "UnregisterComPlus", table
);
4312 static struct _actions StandardActions
[] = {
4313 { szAllocateRegistrySpace
, ACTION_AllocateRegistrySpace
},
4314 { szAppSearch
, ACTION_AppSearch
},
4315 { szBindImage
, ACTION_BindImage
},
4316 { szCCPSearch
, ACTION_CCPSearch
},
4317 { szCostFinalize
, ACTION_CostFinalize
},
4318 { szCostInitialize
, ACTION_CostInitialize
},
4319 { szCreateFolders
, ACTION_CreateFolders
},
4320 { szCreateShortcuts
, ACTION_CreateShortcuts
},
4321 { szDeleteServices
, ACTION_DeleteServices
},
4322 { szDisableRollback
, NULL
},
4323 { szDuplicateFiles
, ACTION_DuplicateFiles
},
4324 { szExecuteAction
, ACTION_ExecuteAction
},
4325 { szFileCost
, ACTION_FileCost
},
4326 { szFindRelatedProducts
, ACTION_FindRelatedProducts
},
4327 { szForceReboot
, ACTION_ForceReboot
},
4328 { szInstallAdminPackage
, NULL
},
4329 { szInstallExecute
, ACTION_InstallExecute
},
4330 { szInstallExecuteAgain
, ACTION_InstallExecute
},
4331 { szInstallFiles
, ACTION_InstallFiles
},
4332 { szInstallFinalize
, ACTION_InstallFinalize
},
4333 { szInstallInitialize
, ACTION_InstallInitialize
},
4334 { szInstallSFPCatalogFile
, NULL
},
4335 { szInstallValidate
, ACTION_InstallValidate
},
4336 { szIsolateComponents
, ACTION_IsolateComponents
},
4337 { szLaunchConditions
, ACTION_LaunchConditions
},
4338 { szMigrateFeatureStates
, ACTION_MigrateFeatureStates
},
4339 { szMoveFiles
, ACTION_MoveFiles
},
4340 { szMsiPublishAssemblies
, ACTION_MsiPublishAssemblies
},
4341 { szMsiUnpublishAssemblies
, ACTION_MsiUnpublishAssemblies
},
4342 { szInstallODBC
, NULL
},
4343 { szInstallServices
, ACTION_InstallServices
},
4344 { szPatchFiles
, ACTION_PatchFiles
},
4345 { szProcessComponents
, ACTION_ProcessComponents
},
4346 { szPublishComponents
, ACTION_PublishComponents
},
4347 { szPublishFeatures
, ACTION_PublishFeatures
},
4348 { szPublishProduct
, ACTION_PublishProduct
},
4349 { szRegisterClassInfo
, ACTION_RegisterClassInfo
},
4350 { szRegisterComPlus
, ACTION_RegisterComPlus
},
4351 { szRegisterExtensionInfo
, ACTION_RegisterExtensionInfo
},
4352 { szRegisterFonts
, ACTION_RegisterFonts
},
4353 { szRegisterMIMEInfo
, ACTION_RegisterMIMEInfo
},
4354 { szRegisterProduct
, ACTION_RegisterProduct
},
4355 { szRegisterProgIdInfo
, ACTION_RegisterProgIdInfo
},
4356 { szRegisterTypeLibraries
, ACTION_RegisterTypeLibraries
},
4357 { szRegisterUser
, ACTION_RegisterUser
},
4358 { szRemoveDuplicateFiles
, NULL
},
4359 { szRemoveEnvironmentStrings
, ACTION_RemoveEnvironmentStrings
},
4360 { szRemoveExistingProducts
, NULL
},
4361 { szRemoveFiles
, ACTION_RemoveFiles
},
4362 { szRemoveFolders
, NULL
},
4363 { szRemoveIniValues
, ACTION_RemoveIniValues
},
4364 { szRemoveODBC
, NULL
},
4365 { szRemoveRegistryValues
, NULL
},
4366 { szRemoveShortcuts
, NULL
},
4367 { szResolveSource
, ACTION_ResolveSource
},
4368 { szRMCCPSearch
, ACTION_RMCCPSearch
},
4369 { szScheduleReboot
, NULL
},
4370 { szSelfRegModules
, ACTION_SelfRegModules
},
4371 { szSelfUnregModules
, ACTION_SelfUnregModules
},
4372 { szSetODBCFolders
, NULL
},
4373 { szStartServices
, ACTION_StartServices
},
4374 { szStopServices
, ACTION_StopServices
},
4375 { szUnpublishComponents
, NULL
},
4376 { szUnpublishFeatures
, NULL
},
4377 { szUnregisterClassInfo
, NULL
},
4378 { szUnregisterComPlus
, ACTION_UnregisterComPlus
},
4379 { szUnregisterExtensionInfo
, NULL
},
4380 { szUnregisterFonts
, ACTION_UnregisterFonts
},
4381 { szUnregisterMIMEInfo
, NULL
},
4382 { szUnregisterProgIdInfo
, NULL
},
4383 { szUnregisterTypeLibraries
, NULL
},
4384 { szValidateProductID
, NULL
},
4385 { szWriteEnvironmentStrings
, ACTION_WriteEnvironmentStrings
},
4386 { szWriteIniValues
, ACTION_WriteIniValues
},
4387 { szWriteRegistryValues
, ACTION_WriteRegistryValues
},