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
37 #include "wine/debug.h"
42 #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
)
367 LPWSTR p
, *ret
= NULL
;
373 /* count the number of substrings */
374 for ( p
= (LPWSTR
)str
, count
= 0; p
; count
++ )
376 p
= strchrW( p
, sep
);
381 /* allocate space for an array of substring pointers and the substrings */
382 ret
= msi_alloc( (count
+1) * sizeof (LPWSTR
) +
383 (lstrlenW(str
)+1) * sizeof(WCHAR
) );
387 /* copy the string and set the pointers */
388 p
= (LPWSTR
) &ret
[count
+1];
390 for( count
= 0; (ret
[count
] = p
); count
++ )
392 p
= strchrW( p
, sep
);
400 static UINT
msi_apply_substorage_transform( MSIPACKAGE
*package
,
401 MSIDATABASE
*patch_db
, LPCWSTR name
)
403 UINT ret
= ERROR_FUNCTION_FAILED
;
404 IStorage
*stg
= NULL
;
407 TRACE("%p %s\n", package
, debugstr_w(name
) );
411 ERR("expected a colon in %s\n", debugstr_w(name
));
412 return ERROR_FUNCTION_FAILED
;
415 r
= IStorage_OpenStorage( patch_db
->storage
, name
, NULL
, STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
418 ret
= msi_table_apply_transform( package
->db
, stg
);
419 IStorage_Release( stg
);
423 ERR("failed to open substorage %s\n", debugstr_w(name
));
428 static UINT
msi_check_patch_applicable( MSIPACKAGE
*package
, MSISUMMARYINFO
*si
)
430 static const WCHAR szProdID
[] = { 'P','r','o','d','u','c','t','I','D',0 };
431 LPWSTR guid_list
, *guids
, product_id
;
432 UINT i
, ret
= ERROR_FUNCTION_FAILED
;
434 product_id
= msi_dup_property( package
, szProdID
);
437 /* FIXME: the property ProductID should be written into the DB somewhere */
438 ERR("no product ID to check\n");
439 return ERROR_SUCCESS
;
442 guid_list
= msi_suminfo_dup_string( si
, PID_TEMPLATE
);
443 guids
= msi_split_string( guid_list
, ';' );
444 for ( i
= 0; guids
[i
] && ret
!= ERROR_SUCCESS
; i
++ )
446 if (!lstrcmpW( guids
[i
], product_id
))
450 msi_free( guid_list
);
451 msi_free( product_id
);
456 static UINT
msi_parse_patch_summary( MSIPACKAGE
*package
, MSIDATABASE
*patch_db
)
459 LPWSTR str
, *substorage
;
460 UINT i
, r
= ERROR_SUCCESS
;
462 si
= MSI_GetSummaryInformationW( patch_db
, 0 );
464 return ERROR_FUNCTION_FAILED
;
466 msi_check_patch_applicable( package
, si
);
468 /* enumerate the substorage */
469 str
= msi_suminfo_dup_string( si
, PID_LASTAUTHOR
);
470 substorage
= msi_split_string( str
, ';' );
471 for ( i
= 0; substorage
&& substorage
[i
] && r
== ERROR_SUCCESS
; i
++ )
472 r
= msi_apply_substorage_transform( package
, patch_db
, substorage
[i
] );
473 msi_free( substorage
);
476 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
478 msiobj_release( &si
->hdr
);
483 static UINT
msi_apply_patch_package( MSIPACKAGE
*package
, LPCWSTR file
)
485 MSIDATABASE
*patch_db
= NULL
;
488 TRACE("%p %s\n", package
, debugstr_w( file
) );
491 * We probably want to make sure we only open a patch collection here.
492 * Patch collections (.msp) and databases (.msi) have different GUIDs
493 * but currently MSI_OpenDatabaseW will accept both.
495 r
= MSI_OpenDatabaseW( file
, MSIDBOPEN_READONLY
, &patch_db
);
496 if ( r
!= ERROR_SUCCESS
)
498 ERR("failed to open patch collection %s\n", debugstr_w( file
) );
502 msi_parse_patch_summary( package
, patch_db
);
503 msiobj_release( &patch_db
->hdr
);
505 return ERROR_SUCCESS
;
508 /* get the PATCH property, and apply all the patches it specifies */
509 static UINT
msi_apply_patches( MSIPACKAGE
*package
)
511 static const WCHAR szPatch
[] = { 'P','A','T','C','H',0 };
512 LPWSTR patch_list
, *patches
;
513 UINT i
, r
= ERROR_SUCCESS
;
515 patch_list
= msi_dup_property( package
, szPatch
);
517 TRACE("patches to be applied: %s\n", debugstr_w( patch_list
) );
519 patches
= msi_split_string( patch_list
, ';' );
520 for( i
=0; patches
&& patches
[i
] && r
== ERROR_SUCCESS
; i
++ )
521 r
= msi_apply_patch_package( package
, patches
[i
] );
524 msi_free( patch_list
);
529 static UINT
msi_apply_transforms( MSIPACKAGE
*package
)
531 static const WCHAR szTransforms
[] = {
532 'T','R','A','N','S','F','O','R','M','S',0 };
533 LPWSTR xform_list
, *xforms
;
534 UINT i
, r
= ERROR_SUCCESS
;
536 xform_list
= msi_dup_property( package
, szTransforms
);
537 xforms
= msi_split_string( xform_list
, ';' );
539 for( i
=0; xforms
&& xforms
[i
] && r
== ERROR_SUCCESS
; i
++ )
541 if (xforms
[i
][0] == ':')
542 r
= msi_apply_substorage_transform( package
, package
->db
, &xforms
[i
][1] );
544 r
= MSI_DatabaseApplyTransformW( package
->db
, xforms
[i
], 0 );
548 msi_free( xform_list
);
553 /****************************************************
554 * TOP level entry points
555 *****************************************************/
557 UINT
MSI_InstallPackage( MSIPACKAGE
*package
, LPCWSTR szPackagePath
,
558 LPCWSTR szCommandLine
)
562 static const WCHAR szUILevel
[] = {'U','I','L','e','v','e','l',0};
563 static const WCHAR szAction
[] = {'A','C','T','I','O','N',0};
564 static const WCHAR szInstall
[] = {'I','N','S','T','A','L','L',0};
566 MSI_SetPropertyW(package
, szAction
, szInstall
);
568 package
->script
= msi_alloc_zero(sizeof(MSISCRIPT
));
570 package
->script
->InWhatSequence
= SEQUENCE_INSTALL
;
574 LPWSTR p
, check
, path
;
576 package
->PackagePath
= strdupW(szPackagePath
);
577 path
= strdupW(szPackagePath
);
578 p
= strrchrW(path
,'\\');
587 path
= msi_alloc(MAX_PATH
*sizeof(WCHAR
));
588 GetCurrentDirectoryW(MAX_PATH
,path
);
592 check
= msi_dup_property( package
, cszSourceDir
);
594 MSI_SetPropertyW(package
, cszSourceDir
, path
);
599 msi_parse_command_line( package
, szCommandLine
);
601 msi_apply_transforms( package
);
602 msi_apply_patches( package
);
604 if ( msi_get_property_int(package
, szUILevel
, 0) >= INSTALLUILEVEL_REDUCED
)
606 package
->script
->InWhatSequence
|= SEQUENCE_UI
;
607 rc
= ACTION_ProcessUISequence(package
);
609 if (rc
== ERROR_SUCCESS
)
611 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
612 rc
= ACTION_ProcessExecSequence(package
,TRUE
);
616 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
620 /* install was halted but should be considered a success */
624 package
->script
->CurrentlyScripting
= FALSE
;
626 /* process the ending type action */
627 if (rc
== ERROR_SUCCESS
)
628 ACTION_PerformActionSequence(package
,-1,ui
);
629 else if (rc
== ERROR_INSTALL_USEREXIT
)
630 ACTION_PerformActionSequence(package
,-2,ui
);
631 else if (rc
== ERROR_INSTALL_SUSPEND
)
632 ACTION_PerformActionSequence(package
,-4,ui
);
634 ACTION_PerformActionSequence(package
,-3,ui
);
636 /* finish up running custom actions */
637 ACTION_FinishCustomActions(package
);
642 static UINT
ACTION_PerformActionSequence(MSIPACKAGE
*package
, UINT seq
, BOOL UI
)
644 UINT rc
= ERROR_SUCCESS
;
646 static const WCHAR ExecSeqQuery
[] =
647 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
648 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
649 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
650 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
652 static const WCHAR UISeqQuery
[] =
653 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
654 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
655 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
656 ' ', '=',' ','%','i',0};
659 row
= MSI_QueryGetRecord(package
->db
, UISeqQuery
, seq
);
661 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
, seq
);
665 LPCWSTR action
, cond
;
667 TRACE("Running the actions\n");
669 /* check conditions */
670 cond
= MSI_RecordGetString(row
,2);
673 /* this is a hack to skip errors in the condition code */
674 if (MSI_EvaluateConditionW(package
, cond
) == MSICONDITION_FALSE
)
678 action
= MSI_RecordGetString(row
,1);
681 ERR("failed to fetch action\n");
682 rc
= ERROR_FUNCTION_FAILED
;
687 rc
= ACTION_PerformUIAction(package
,action
);
689 rc
= ACTION_PerformAction(package
,action
,FALSE
);
691 msiobj_release(&row
->hdr
);
702 } iterate_action_param
;
704 static UINT
ITERATE_Actions(MSIRECORD
*row
, LPVOID param
)
706 iterate_action_param
*iap
= (iterate_action_param
*)param
;
708 LPCWSTR cond
, action
;
710 action
= MSI_RecordGetString(row
,1);
713 ERR("Error is retrieving action name\n");
714 return ERROR_FUNCTION_FAILED
;
717 /* check conditions */
718 cond
= MSI_RecordGetString(row
,2);
721 /* this is a hack to skip errors in the condition code */
722 if (MSI_EvaluateConditionW(iap
->package
, cond
) == MSICONDITION_FALSE
)
724 TRACE("Skipping action: %s (condition is false)\n",
726 return ERROR_SUCCESS
;
731 rc
= ACTION_PerformUIAction(iap
->package
,action
);
733 rc
= ACTION_PerformAction(iap
->package
,action
,FALSE
);
735 msi_dialog_check_messages( NULL
);
737 if (iap
->package
->CurrentInstallState
!= ERROR_SUCCESS
)
738 rc
= iap
->package
->CurrentInstallState
;
740 if (rc
== ERROR_FUNCTION_NOT_CALLED
)
743 if (rc
!= ERROR_SUCCESS
)
744 ERR("Execution halted, action %s returned %i\n", debugstr_w(action
), rc
);
749 UINT
MSI_Sequence( MSIPACKAGE
*package
, LPCWSTR szTable
, INT iSequenceMode
)
753 static const WCHAR query
[] =
754 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
756 ' ','W','H','E','R','E',' ',
757 '`','S','e','q','u','e','n','c','e','`',' ',
758 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
759 '`','S','e','q','u','e','n','c','e','`',0};
760 iterate_action_param iap
;
763 * FIXME: probably should be checking UILevel in the
764 * ACTION_PerformUIAction/ACTION_PerformAction
765 * rather than saving the UI level here. Those
766 * two functions can be merged too.
768 iap
.package
= package
;
771 TRACE("%p %s %i\n", package
, debugstr_w(szTable
), iSequenceMode
);
773 r
= MSI_OpenQuery( package
->db
, &view
, query
, szTable
);
774 if (r
== ERROR_SUCCESS
)
776 r
= MSI_IterateRecords( view
, NULL
, ITERATE_Actions
, &iap
);
777 msiobj_release(&view
->hdr
);
783 static UINT
ACTION_ProcessExecSequence(MSIPACKAGE
*package
, BOOL UIran
)
787 static const WCHAR ExecSeqQuery
[] =
788 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
789 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
790 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
791 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
792 'O','R','D','E','R',' ', 'B','Y',' ',
793 '`','S','e','q','u','e','n','c','e','`',0 };
795 static const WCHAR IVQuery
[] =
796 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
797 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
798 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
799 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
800 ' ','\'', 'I','n','s','t','a','l','l',
801 'V','a','l','i','d','a','t','e','\'', 0};
803 iterate_action_param iap
;
805 iap
.package
= package
;
808 if (package
->script
->ExecuteSequenceRun
)
810 TRACE("Execute Sequence already Run\n");
811 return ERROR_SUCCESS
;
814 package
->script
->ExecuteSequenceRun
= TRUE
;
816 /* get the sequence number */
819 row
= MSI_QueryGetRecord(package
->db
, IVQuery
);
821 return ERROR_FUNCTION_FAILED
;
822 seq
= MSI_RecordGetInteger(row
,1);
823 msiobj_release(&row
->hdr
);
826 rc
= MSI_OpenQuery(package
->db
, &view
, ExecSeqQuery
, seq
);
827 if (rc
== ERROR_SUCCESS
)
829 TRACE("Running the actions\n");
831 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, &iap
);
832 msiobj_release(&view
->hdr
);
838 static UINT
ACTION_ProcessUISequence(MSIPACKAGE
*package
)
842 static const WCHAR ExecSeqQuery
[] =
843 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
844 '`','I','n','s','t','a','l','l',
845 'U','I','S','e','q','u','e','n','c','e','`',
846 ' ','W','H','E','R','E',' ',
847 '`','S','e','q','u','e','n','c','e','`',' ',
848 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
849 '`','S','e','q','u','e','n','c','e','`',0};
850 iterate_action_param iap
;
852 iap
.package
= package
;
855 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
857 if (rc
== ERROR_SUCCESS
)
859 TRACE("Running the actions\n");
861 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, &iap
);
862 msiobj_release(&view
->hdr
);
868 /********************************************************
869 * ACTION helper functions and functions that perform the actions
870 *******************************************************/
871 static BOOL
ACTION_HandleStandardAction(MSIPACKAGE
*package
, LPCWSTR action
,
872 UINT
* rc
, BOOL force
)
878 if (!run
&& !package
->script
->CurrentlyScripting
)
883 if (strcmpW(action
,szInstallFinalize
) == 0 ||
884 strcmpW(action
,szInstallExecute
) == 0 ||
885 strcmpW(action
,szInstallExecuteAgain
) == 0)
890 while (StandardActions
[i
].action
!= NULL
)
892 if (strcmpW(StandardActions
[i
].action
, action
)==0)
896 ui_actioninfo(package
, action
, TRUE
, 0);
897 *rc
= schedule_action(package
,INSTALL_SCRIPT
,action
);
898 ui_actioninfo(package
, action
, FALSE
, *rc
);
902 ui_actionstart(package
, action
);
903 if (StandardActions
[i
].handler
)
905 *rc
= StandardActions
[i
].handler(package
);
909 FIXME("unhandled standard action %s\n",debugstr_w(action
));
921 static BOOL
ACTION_HandleCustomAction( MSIPACKAGE
* package
, LPCWSTR action
,
922 UINT
* rc
, BOOL force
)
927 arc
= ACTION_CustomAction(package
,action
, force
);
929 if (arc
!= ERROR_CALL_NOT_IMPLEMENTED
)
938 * A lot of actions are really important even if they don't do anything
939 * explicit... Lots of properties are set at the beginning of the installation
940 * CostFinalize does a bunch of work to translate the directories and such
942 * But until I get write access to the database that is hard, so I am going to
943 * hack it to see if I can get something to run.
945 UINT
ACTION_PerformAction(MSIPACKAGE
*package
, const WCHAR
*action
, BOOL force
)
947 UINT rc
= ERROR_SUCCESS
;
950 TRACE("Performing action (%s)\n",debugstr_w(action
));
952 handled
= ACTION_HandleStandardAction(package
, action
, &rc
, force
);
955 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, force
);
959 FIXME("unhandled msi action %s\n",debugstr_w(action
));
960 rc
= ERROR_FUNCTION_NOT_CALLED
;
966 UINT
ACTION_PerformUIAction(MSIPACKAGE
*package
, const WCHAR
*action
)
968 UINT rc
= ERROR_SUCCESS
;
969 BOOL handled
= FALSE
;
971 TRACE("Performing action (%s)\n",debugstr_w(action
));
973 handled
= ACTION_HandleStandardAction(package
, action
, &rc
,TRUE
);
976 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, FALSE
);
978 if( !handled
&& ACTION_DialogBox(package
,action
) == ERROR_SUCCESS
)
983 FIXME("unhandled msi action %s\n",debugstr_w(action
));
984 rc
= ERROR_FUNCTION_NOT_CALLED
;
992 * Actual Action Handlers
995 static UINT
ITERATE_CreateFolders(MSIRECORD
*row
, LPVOID param
)
997 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1003 dir
= MSI_RecordGetString(row
,1);
1006 ERR("Unable to get folder id\n");
1007 return ERROR_SUCCESS
;
1010 full_path
= resolve_folder(package
,dir
,FALSE
,FALSE
,&folder
);
1013 ERR("Unable to resolve folder id %s\n",debugstr_w(dir
));
1014 return ERROR_SUCCESS
;
1017 TRACE("Folder is %s\n",debugstr_w(full_path
));
1020 uirow
= MSI_CreateRecord(1);
1021 MSI_RecordSetStringW(uirow
,1,full_path
);
1022 ui_actiondata(package
,szCreateFolders
,uirow
);
1023 msiobj_release( &uirow
->hdr
);
1025 if (folder
->State
== 0)
1026 create_full_pathW(full_path
);
1030 msi_free(full_path
);
1031 return ERROR_SUCCESS
;
1034 /* FIXME: probably should merge this with the above function */
1035 static UINT
msi_create_directory( MSIPACKAGE
* package
, LPCWSTR dir
)
1037 UINT rc
= ERROR_SUCCESS
;
1039 LPWSTR install_path
;
1041 install_path
= resolve_folder(package
, dir
, FALSE
, FALSE
, &folder
);
1043 return ERROR_FUNCTION_FAILED
;
1045 /* create the path */
1046 if (folder
->State
== 0)
1048 create_full_pathW(install_path
);
1051 msi_free(install_path
);
1056 UINT
msi_create_component_directories( MSIPACKAGE
*package
)
1060 /* create all the folders required by the components are going to install */
1061 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1063 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
1065 msi_create_directory( package
, comp
->Directory
);
1068 return ERROR_SUCCESS
;
1072 * Also we cannot enable/disable components either, so for now I am just going
1073 * to do all the directories for all the components.
1075 static UINT
ACTION_CreateFolders(MSIPACKAGE
*package
)
1077 static const WCHAR ExecSeqQuery
[] =
1078 {'S','E','L','E','C','T',' ',
1079 '`','D','i','r','e','c','t','o','r','y','_','`',
1080 ' ','F','R','O','M',' ',
1081 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1085 /* create all the empty folders specified in the CreateFolder table */
1086 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
1087 if (rc
!= ERROR_SUCCESS
)
1088 return ERROR_SUCCESS
;
1090 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateFolders
, package
);
1091 msiobj_release(&view
->hdr
);
1093 msi_create_component_directories( package
);
1098 static UINT
load_component( MSIRECORD
*row
, LPVOID param
)
1100 MSIPACKAGE
*package
= param
;
1103 comp
= msi_alloc_zero( sizeof(MSICOMPONENT
) );
1105 return ERROR_FUNCTION_FAILED
;
1107 list_add_tail( &package
->components
, &comp
->entry
);
1109 /* fill in the data */
1110 comp
->Component
= msi_dup_record_field( row
, 1 );
1112 TRACE("Loading Component %s\n", debugstr_w(comp
->Component
));
1114 comp
->ComponentId
= msi_dup_record_field( row
, 2 );
1115 comp
->Directory
= msi_dup_record_field( row
, 3 );
1116 comp
->Attributes
= MSI_RecordGetInteger(row
,4);
1117 comp
->Condition
= msi_dup_record_field( row
, 5 );
1118 comp
->KeyPath
= msi_dup_record_field( row
, 6 );
1120 comp
->Installed
= INSTALLSTATE_ABSENT
;
1122 switch (comp
->Attributes
)
1124 case msidbComponentAttributesLocalOnly
:
1125 comp
->Action
= INSTALLSTATE_LOCAL
;
1126 comp
->ActionRequest
= INSTALLSTATE_LOCAL
;
1128 case msidbComponentAttributesSourceOnly
:
1129 comp
->Action
= INSTALLSTATE_SOURCE
;
1130 comp
->ActionRequest
= INSTALLSTATE_SOURCE
;
1132 case msidbComponentAttributesOptional
:
1133 comp
->Action
= INSTALLSTATE_DEFAULT
;
1134 comp
->ActionRequest
= INSTALLSTATE_DEFAULT
;
1137 comp
->Action
= INSTALLSTATE_LOCAL
;
1138 comp
->ActionRequest
= INSTALLSTATE_LOCAL
;
1141 return ERROR_SUCCESS
;
1144 static UINT
load_all_components( MSIPACKAGE
*package
)
1146 static const WCHAR query
[] = {
1147 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1148 '`','C','o','m','p','o','n','e','n','t','`',0 };
1152 if (!list_empty(&package
->components
))
1153 return ERROR_SUCCESS
;
1155 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1156 if (r
!= ERROR_SUCCESS
)
1159 r
= MSI_IterateRecords(view
, NULL
, load_component
, package
);
1160 msiobj_release(&view
->hdr
);
1165 MSIPACKAGE
*package
;
1166 MSIFEATURE
*feature
;
1169 static UINT
add_feature_component( MSIFEATURE
*feature
, MSICOMPONENT
*comp
)
1173 cl
= msi_alloc( sizeof (*cl
) );
1175 return ERROR_NOT_ENOUGH_MEMORY
;
1176 cl
->component
= comp
;
1177 list_add_tail( &feature
->Components
, &cl
->entry
);
1179 return ERROR_SUCCESS
;
1182 static UINT
iterate_load_featurecomponents(MSIRECORD
*row
, LPVOID param
)
1184 _ilfs
* ilfs
= (_ilfs
*)param
;
1188 component
= MSI_RecordGetString(row
,1);
1190 /* check to see if the component is already loaded */
1191 comp
= get_loaded_component( ilfs
->package
, component
);
1194 ERR("unknown component %s\n", debugstr_w(component
));
1195 return ERROR_FUNCTION_FAILED
;
1198 add_feature_component( ilfs
->feature
, comp
);
1199 comp
->Enabled
= TRUE
;
1201 return ERROR_SUCCESS
;
1204 static UINT
load_feature(MSIRECORD
* row
, LPVOID param
)
1206 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1207 MSIFEATURE
* feature
;
1208 static const WCHAR Query1
[] =
1209 {'S','E','L','E','C','T',' ',
1210 '`','C','o','m','p','o','n','e','n','t','_','`',
1211 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1212 'C','o','m','p','o','n','e','n','t','s','`',' ',
1213 'W','H','E','R','E',' ',
1214 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1219 /* fill in the data */
1221 feature
= msi_alloc_zero( sizeof (MSIFEATURE
) );
1223 return ERROR_NOT_ENOUGH_MEMORY
;
1225 list_init( &feature
->Components
);
1227 feature
->Feature
= msi_dup_record_field( row
, 1 );
1229 TRACE("Loading feature %s\n",debugstr_w(feature
->Feature
));
1231 feature
->Feature_Parent
= msi_dup_record_field( row
, 2 );
1232 feature
->Title
= msi_dup_record_field( row
, 3 );
1233 feature
->Description
= msi_dup_record_field( row
, 4 );
1235 if (!MSI_RecordIsNull(row
,5))
1236 feature
->Display
= MSI_RecordGetInteger(row
,5);
1238 feature
->Level
= MSI_RecordGetInteger(row
,6);
1239 feature
->Directory
= msi_dup_record_field( row
, 7 );
1240 feature
->Attributes
= MSI_RecordGetInteger(row
,8);
1242 feature
->Installed
= INSTALLSTATE_ABSENT
;
1243 feature
->Action
= INSTALLSTATE_UNKNOWN
;
1244 feature
->ActionRequest
= INSTALLSTATE_UNKNOWN
;
1246 list_add_tail( &package
->features
, &feature
->entry
);
1248 /* load feature components */
1250 rc
= MSI_OpenQuery( package
->db
, &view
, Query1
, feature
->Feature
);
1251 if (rc
!= ERROR_SUCCESS
)
1252 return ERROR_SUCCESS
;
1254 ilfs
.package
= package
;
1255 ilfs
.feature
= feature
;
1257 MSI_IterateRecords(view
, NULL
, iterate_load_featurecomponents
, &ilfs
);
1258 msiobj_release(&view
->hdr
);
1260 return ERROR_SUCCESS
;
1263 static UINT
load_all_features( MSIPACKAGE
*package
)
1265 static const WCHAR query
[] = {
1266 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1267 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1268 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1272 if (!list_empty(&package
->features
))
1273 return ERROR_SUCCESS
;
1275 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1276 if (r
!= ERROR_SUCCESS
)
1279 r
= MSI_IterateRecords( view
, NULL
, load_feature
, package
);
1280 msiobj_release( &view
->hdr
);
1284 static LPWSTR
folder_split_path(LPWSTR p
, WCHAR ch
)
1295 static UINT
load_file(MSIRECORD
*row
, LPVOID param
)
1297 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1301 /* fill in the data */
1303 file
= msi_alloc_zero( sizeof (MSIFILE
) );
1305 return ERROR_NOT_ENOUGH_MEMORY
;
1307 file
->File
= msi_dup_record_field( row
, 1 );
1309 component
= MSI_RecordGetString( row
, 2 );
1310 file
->Component
= get_loaded_component( package
, component
);
1312 if (!file
->Component
)
1313 ERR("Unfound Component %s\n",debugstr_w(component
));
1315 file
->FileName
= msi_dup_record_field( row
, 3 );
1316 reduce_to_longfilename( file
->FileName
);
1318 file
->ShortName
= msi_dup_record_field( row
, 3 );
1319 file
->LongName
= strdupW( folder_split_path(file
->ShortName
, '|'));
1321 file
->FileSize
= MSI_RecordGetInteger( row
, 4 );
1322 file
->Version
= msi_dup_record_field( row
, 5 );
1323 file
->Language
= msi_dup_record_field( row
, 6 );
1324 file
->Attributes
= MSI_RecordGetInteger( row
, 7 );
1325 file
->Sequence
= MSI_RecordGetInteger( row
, 8 );
1327 file
->state
= msifs_invalid
;
1329 /* if the compressed bits are not set in the file attributes,
1330 * then read the information from the package word count property
1332 if (file
->Attributes
& msidbFileAttributesCompressed
)
1334 file
->IsCompressed
= TRUE
;
1336 else if (file
->Attributes
& msidbFileAttributesNoncompressed
)
1338 file
->IsCompressed
= FALSE
;
1342 file
->IsCompressed
= package
->WordCount
& MSIWORDCOUNT_COMPRESSED
;
1345 if (file
->IsCompressed
)
1347 file
->Component
->ForceLocalState
= TRUE
;
1348 file
->Component
->Action
= INSTALLSTATE_LOCAL
;
1349 file
->Component
->ActionRequest
= INSTALLSTATE_LOCAL
;
1352 TRACE("File Loaded (%s)\n",debugstr_w(file
->File
));
1354 list_add_tail( &package
->files
, &file
->entry
);
1356 return ERROR_SUCCESS
;
1359 static UINT
load_all_files(MSIPACKAGE
*package
)
1363 static const WCHAR Query
[] =
1364 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1365 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1366 '`','S','e','q','u','e','n','c','e','`', 0};
1368 if (!list_empty(&package
->files
))
1369 return ERROR_SUCCESS
;
1371 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
1372 if (rc
!= ERROR_SUCCESS
)
1373 return ERROR_SUCCESS
;
1375 rc
= MSI_IterateRecords(view
, NULL
, load_file
, package
);
1376 msiobj_release(&view
->hdr
);
1378 return ERROR_SUCCESS
;
1383 * I am not doing any of the costing functionality yet.
1384 * Mostly looking at doing the Component and Feature loading
1386 * The native MSI does A LOT of modification to tables here. Mostly adding
1387 * a lot of temporary columns to the Feature and Component tables.
1389 * note: Native msi also tracks the short filename. But I am only going to
1390 * track the long ones. Also looking at this directory table
1391 * it appears that the directory table does not get the parents
1392 * resolved base on property only based on their entries in the
1395 static UINT
ACTION_CostInitialize(MSIPACKAGE
*package
)
1397 static const WCHAR szCosting
[] =
1398 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1399 static const WCHAR szZero
[] = { '0', 0 };
1401 if ( 1 == msi_get_property_int( package
, szCosting
, 0 ) )
1402 return ERROR_SUCCESS
;
1404 MSI_SetPropertyW(package
, szCosting
, szZero
);
1405 MSI_SetPropertyW(package
, cszRootDrive
, c_colon
);
1407 load_all_components( package
);
1408 load_all_features( package
);
1409 load_all_files( package
);
1411 return ERROR_SUCCESS
;
1414 static UINT
execute_script(MSIPACKAGE
*package
, UINT script
)
1417 UINT rc
= ERROR_SUCCESS
;
1419 TRACE("Executing Script %i\n",script
);
1421 if (!package
->script
)
1423 ERR("no script!\n");
1424 return ERROR_FUNCTION_FAILED
;
1427 for (i
= 0; i
< package
->script
->ActionCount
[script
]; i
++)
1430 action
= package
->script
->Actions
[script
][i
];
1431 ui_actionstart(package
, action
);
1432 TRACE("Executing Action (%s)\n",debugstr_w(action
));
1433 rc
= ACTION_PerformAction(package
, action
, TRUE
);
1434 msi_free(package
->script
->Actions
[script
][i
]);
1435 if (rc
!= ERROR_SUCCESS
)
1438 msi_free(package
->script
->Actions
[script
]);
1440 package
->script
->ActionCount
[script
] = 0;
1441 package
->script
->Actions
[script
] = NULL
;
1445 static UINT
ACTION_FileCost(MSIPACKAGE
*package
)
1447 return ERROR_SUCCESS
;
1450 static MSIFOLDER
*load_folder( MSIPACKAGE
*package
, LPCWSTR dir
)
1452 static const WCHAR Query
[] =
1453 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1454 '`','D','i','r','e','c', 't','o','r','y','`',' ',
1455 'W','H','E','R','E',' ', '`', 'D','i','r','e','c','t', 'o','r','y','`',
1456 ' ','=',' ','\'','%','s','\'',
1458 static const WCHAR szDot
[] = { '.',0 };
1459 static WCHAR szEmpty
[] = { 0 };
1460 LPWSTR p
, tgt_short
, tgt_long
, src_short
, src_long
;
1465 TRACE("Looking for dir %s\n",debugstr_w(dir
));
1467 folder
= get_loaded_folder( package
, dir
);
1471 TRACE("Working to load %s\n",debugstr_w(dir
));
1473 folder
= msi_alloc_zero( sizeof (MSIFOLDER
) );
1477 folder
->Directory
= strdupW(dir
);
1479 row
= MSI_QueryGetRecord(package
->db
, Query
, dir
);
1483 p
= msi_dup_record_field(row
, 3);
1485 /* split src and target dir */
1487 src_short
= folder_split_path( p
, ':' );
1489 /* split the long and short paths */
1490 tgt_long
= folder_split_path( tgt_short
, '|' );
1491 src_long
= folder_split_path( src_short
, '|' );
1493 /* check for no-op dirs */
1494 if (!lstrcmpW(szDot
, tgt_short
))
1495 tgt_short
= szEmpty
;
1496 if (!lstrcmpW(szDot
, src_short
))
1497 src_short
= szEmpty
;
1500 tgt_long
= tgt_short
;
1503 src_short
= tgt_short
;
1504 src_long
= tgt_long
;
1508 src_long
= src_short
;
1510 /* FIXME: use the target short path too */
1511 folder
->TargetDefault
= strdupW(tgt_long
);
1512 folder
->SourceShortPath
= strdupW(src_short
);
1513 folder
->SourceLongPath
= strdupW(src_long
);
1516 TRACE("TargetDefault = %s\n",debugstr_w( folder
->TargetDefault
));
1517 TRACE("SourceLong = %s\n", debugstr_w( folder
->SourceLongPath
));
1518 TRACE("SourceShort = %s\n", debugstr_w( folder
->SourceShortPath
));
1520 parent
= MSI_RecordGetString(row
, 2);
1523 folder
->Parent
= load_folder( package
, parent
);
1524 if ( folder
->Parent
)
1525 TRACE("loaded parent %p %s\n", folder
->Parent
,
1526 debugstr_w(folder
->Parent
->Directory
));
1528 ERR("failed to load parent folder %s\n", debugstr_w(parent
));
1531 folder
->Property
= msi_dup_property( package
, dir
);
1533 msiobj_release(&row
->hdr
);
1535 list_add_tail( &package
->folders
, &folder
->entry
);
1537 TRACE("%s returning %p\n",debugstr_w(dir
),folder
);
1542 /* scan for and update current install states */
1543 static void ACTION_UpdateInstallStates(MSIPACKAGE
*package
)
1546 MSIFEATURE
*feature
;
1548 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1551 res
= MsiGetComponentPathW( package
->ProductCode
,
1552 comp
->ComponentId
, NULL
, NULL
);
1554 res
= INSTALLSTATE_ABSENT
;
1555 comp
->Installed
= res
;
1558 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1561 INSTALLSTATE res
= -10;
1563 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1565 comp
= cl
->component
;
1568 res
= comp
->Installed
;
1571 if (res
== comp
->Installed
)
1574 if (res
!= comp
->Installed
)
1575 res
= INSTALLSTATE_INCOMPLETE
;
1578 feature
->Installed
= res
;
1582 static BOOL
process_state_property (MSIPACKAGE
* package
, LPCWSTR property
,
1585 static const WCHAR all
[]={'A','L','L',0};
1587 MSIFEATURE
*feature
;
1589 override
= msi_dup_property( package
, property
);
1593 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1595 if (strcmpiW(override
,all
)==0)
1597 feature
->ActionRequest
= state
;
1598 feature
->Action
= state
;
1602 LPWSTR ptr
= override
;
1603 LPWSTR ptr2
= strchrW(override
,',');
1607 if ((ptr2
&& strncmpW(ptr
,feature
->Feature
, ptr2
-ptr
)==0)
1608 || (!ptr2
&& strcmpW(ptr
,feature
->Feature
)==0))
1610 feature
->ActionRequest
= state
;
1611 feature
->Action
= state
;
1617 ptr2
= strchrW(ptr
,',');
1629 UINT
MSI_SetFeatureStates(MSIPACKAGE
*package
)
1632 static const WCHAR szlevel
[] =
1633 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1634 static const WCHAR szAddLocal
[] =
1635 {'A','D','D','L','O','C','A','L',0};
1636 static const WCHAR szRemove
[] =
1637 {'R','E','M','O','V','E',0};
1638 static const WCHAR szReinstall
[] =
1639 {'R','E','I','N','S','T','A','L','L',0};
1640 BOOL override
= FALSE
;
1641 MSICOMPONENT
* component
;
1642 MSIFEATURE
*feature
;
1645 /* I do not know if this is where it should happen.. but */
1647 TRACE("Checking Install Level\n");
1649 install_level
= msi_get_property_int( package
, szlevel
, 1 );
1651 /* ok hereis the _real_ rub
1652 * all these activation/deactivation things happen in order and things
1653 * later on the list override things earlier on the list.
1654 * 1) INSTALLLEVEL processing
1664 * 11) FILEADDDEFAULT
1665 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1666 * ignored for all the features. seems strange, especially since it is not
1667 * documented anywhere, but it is how it works.
1669 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1670 * REMOVE are the big ones, since we don't handle administrative installs
1673 override
|= process_state_property(package
,szAddLocal
,INSTALLSTATE_LOCAL
);
1674 override
|= process_state_property(package
,szRemove
,INSTALLSTATE_ABSENT
);
1675 override
|= process_state_property(package
,szReinstall
,INSTALLSTATE_LOCAL
);
1679 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1681 BOOL feature_state
= ((feature
->Level
> 0) &&
1682 (feature
->Level
<= install_level
));
1684 if ((feature_state
) && (feature
->Action
== INSTALLSTATE_UNKNOWN
))
1686 if (feature
->Attributes
& msidbFeatureAttributesFavorSource
)
1688 feature
->ActionRequest
= INSTALLSTATE_SOURCE
;
1689 feature
->Action
= INSTALLSTATE_SOURCE
;
1691 else if (feature
->Attributes
& msidbFeatureAttributesFavorAdvertise
)
1693 feature
->ActionRequest
= INSTALLSTATE_ADVERTISED
;
1694 feature
->Action
= INSTALLSTATE_ADVERTISED
;
1698 feature
->ActionRequest
= INSTALLSTATE_LOCAL
;
1699 feature
->Action
= INSTALLSTATE_LOCAL
;
1706 /* set the Preselected Property */
1707 static const WCHAR szPreselected
[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1708 static const WCHAR szOne
[] = { '1', 0 };
1710 MSI_SetPropertyW(package
,szPreselected
,szOne
);
1714 * now we want to enable or disable components base on feature
1717 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1721 TRACE("Examining Feature %s (Installed %i, Action %i, Request %i)\n",
1722 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
,
1723 feature
->ActionRequest
);
1725 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1727 component
= cl
->component
;
1729 if (!component
->Enabled
)
1731 component
->Action
= INSTALLSTATE_UNKNOWN
;
1732 component
->ActionRequest
= INSTALLSTATE_UNKNOWN
;
1736 if (feature
->Attributes
== msidbFeatureAttributesFavorLocal
)
1738 if (!(component
->Attributes
& msidbComponentAttributesSourceOnly
))
1740 component
->Action
= INSTALLSTATE_LOCAL
;
1741 component
->ActionRequest
= INSTALLSTATE_LOCAL
;
1744 else if (feature
->Attributes
== msidbFeatureAttributesFavorSource
)
1746 if ((component
->Action
== INSTALLSTATE_UNKNOWN
) ||
1747 (component
->Action
== INSTALLSTATE_ABSENT
) ||
1748 (component
->Action
== INSTALLSTATE_ADVERTISED
) ||
1749 (component
->Action
== INSTALLSTATE_DEFAULT
))
1752 component
->Action
= INSTALLSTATE_SOURCE
;
1753 component
->ActionRequest
= INSTALLSTATE_SOURCE
;
1756 else if (feature
->ActionRequest
== INSTALLSTATE_ADVERTISED
)
1758 if ((component
->Action
== INSTALLSTATE_UNKNOWN
) ||
1759 (component
->Action
== INSTALLSTATE_ABSENT
))
1762 component
->Action
= INSTALLSTATE_ADVERTISED
;
1763 component
->ActionRequest
= INSTALLSTATE_ADVERTISED
;
1766 else if (feature
->ActionRequest
== INSTALLSTATE_ABSENT
)
1768 if (component
->Action
== INSTALLSTATE_UNKNOWN
)
1770 component
->Action
= INSTALLSTATE_ABSENT
;
1771 component
->ActionRequest
= INSTALLSTATE_ABSENT
;
1776 if (component
->ForceLocalState
)
1778 feature
->Action
= INSTALLSTATE_LOCAL
;
1779 feature
->ActionRequest
= INSTALLSTATE_LOCAL
;
1784 LIST_FOR_EACH_ENTRY( component
, &package
->components
, MSICOMPONENT
, entry
)
1786 TRACE("Result: Component %s (Installed %i, Action %i, Request %i)\n",
1787 debugstr_w(component
->Component
), component
->Installed
,
1788 component
->Action
, component
->ActionRequest
);
1792 return ERROR_SUCCESS
;
1795 static UINT
ITERATE_CostFinalizeDirectories(MSIRECORD
*row
, LPVOID param
)
1797 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1801 name
= MSI_RecordGetString(row
,1);
1803 /* This helper function now does ALL the work */
1804 TRACE("Dir %s ...\n",debugstr_w(name
));
1805 load_folder(package
,name
);
1806 path
= resolve_folder(package
,name
,FALSE
,TRUE
,NULL
);
1807 TRACE("resolves to %s\n",debugstr_w(path
));
1810 return ERROR_SUCCESS
;
1813 static UINT
ITERATE_CostFinalizeConditions(MSIRECORD
*row
, LPVOID param
)
1815 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1817 MSIFEATURE
*feature
;
1819 name
= MSI_RecordGetString( row
, 1 );
1821 feature
= get_loaded_feature( package
, name
);
1823 ERR("FAILED to find loaded feature %s\n",debugstr_w(name
));
1827 Condition
= MSI_RecordGetString(row
,3);
1829 if (MSI_EvaluateConditionW(package
,Condition
) == MSICONDITION_TRUE
)
1831 int level
= MSI_RecordGetInteger(row
,2);
1832 TRACE("Reseting feature %s to level %i\n", debugstr_w(name
), level
);
1833 feature
->Level
= level
;
1836 return ERROR_SUCCESS
;
1841 * A lot is done in this function aside from just the costing.
1842 * The costing needs to be implemented at some point but for now I am going
1843 * to focus on the directory building
1846 static UINT
ACTION_CostFinalize(MSIPACKAGE
*package
)
1848 static const WCHAR ExecSeqQuery
[] =
1849 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1850 '`','D','i','r','e','c','t','o','r','y','`',0};
1851 static const WCHAR ConditionQuery
[] =
1852 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1853 '`','C','o','n','d','i','t','i','o','n','`',0};
1854 static const WCHAR szCosting
[] =
1855 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1856 static const WCHAR szlevel
[] =
1857 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1858 static const WCHAR szOne
[] = { '1', 0 };
1865 if ( 1 == msi_get_property_int( package
, szCosting
, 0 ) )
1866 return ERROR_SUCCESS
;
1868 TRACE("Building Directory properties\n");
1870 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
1871 if (rc
== ERROR_SUCCESS
)
1873 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CostFinalizeDirectories
,
1875 msiobj_release(&view
->hdr
);
1878 TRACE("File calculations\n");
1880 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
1882 MSICOMPONENT
* comp
= file
->Component
;
1888 /* calculate target */
1889 p
= resolve_folder(package
, comp
->Directory
, FALSE
, FALSE
, NULL
);
1891 msi_free(file
->TargetPath
);
1893 TRACE("file %s is named %s\n",
1894 debugstr_w(file
->File
),debugstr_w(file
->FileName
));
1896 file
->TargetPath
= build_directory_name(2, p
, file
->FileName
);
1900 TRACE("file %s resolves to %s\n",
1901 debugstr_w(file
->File
),debugstr_w(file
->TargetPath
));
1903 if (GetFileAttributesW(file
->TargetPath
) == INVALID_FILE_ATTRIBUTES
)
1905 file
->state
= msifs_missing
;
1906 comp
->Cost
+= file
->FileSize
;
1916 static WCHAR name
[] = {'\\',0};
1917 static const WCHAR name_fmt
[] =
1918 {'%','u','.','%','u','.','%','u','.','%','u',0};
1919 WCHAR filever
[0x100];
1920 VS_FIXEDFILEINFO
*lpVer
;
1922 TRACE("Version comparison..\n");
1923 versize
= GetFileVersionInfoSizeW(file
->TargetPath
,&handle
);
1924 version
= msi_alloc(versize
);
1925 GetFileVersionInfoW(file
->TargetPath
, 0, versize
, version
);
1927 VerQueryValueW(version
, name
, (LPVOID
*)&lpVer
, &sz
);
1929 sprintfW(filever
,name_fmt
,
1930 HIWORD(lpVer
->dwFileVersionMS
),
1931 LOWORD(lpVer
->dwFileVersionMS
),
1932 HIWORD(lpVer
->dwFileVersionLS
),
1933 LOWORD(lpVer
->dwFileVersionLS
));
1935 TRACE("new %s old %s\n", debugstr_w(file
->Version
),
1936 debugstr_w(filever
));
1937 if (strcmpiW(filever
,file
->Version
)<0)
1939 file
->state
= msifs_overwrite
;
1940 /* FIXME: cost should be diff in size */
1941 comp
->Cost
+= file
->FileSize
;
1944 file
->state
= msifs_present
;
1948 file
->state
= msifs_present
;
1951 TRACE("Evaluating Condition Table\n");
1953 rc
= MSI_DatabaseOpenViewW(package
->db
, ConditionQuery
, &view
);
1954 if (rc
== ERROR_SUCCESS
)
1956 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CostFinalizeConditions
,
1958 msiobj_release(&view
->hdr
);
1961 TRACE("Enabling or Disabling Components\n");
1962 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1964 if (comp
->Condition
)
1966 if (MSI_EvaluateConditionW(package
,
1967 comp
->Condition
) == MSICONDITION_FALSE
)
1969 TRACE("Disabling component %s\n", debugstr_w(comp
->Component
));
1970 comp
->Enabled
= FALSE
;
1975 MSI_SetPropertyW(package
,szCosting
,szOne
);
1976 /* set default run level if not set */
1977 level
= msi_dup_property( package
, szlevel
);
1979 MSI_SetPropertyW(package
,szlevel
, szOne
);
1982 ACTION_UpdateInstallStates(package
);
1984 return MSI_SetFeatureStates(package
);
1987 /* OK this value is "interpreted" and then formatted based on the
1988 first few characters */
1989 static LPSTR
parse_value(MSIPACKAGE
*package
, LPCWSTR value
, DWORD
*type
,
1993 if (value
[0]=='#' && value
[1]!='#' && value
[1]!='%')
1999 LPWSTR deformated
= NULL
;
2002 deformat_string(package
, &value
[2], &deformated
);
2004 /* binary value type */
2008 *size
= (strlenW(ptr
)/2)+1;
2010 *size
= strlenW(ptr
)/2;
2012 data
= msi_alloc(*size
);
2018 /* if uneven pad with a zero in front */
2024 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2026 TRACE("Uneven byte count\n");
2034 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2037 msi_free(deformated
);
2039 TRACE("Data %li bytes(%i)\n",*size
,count
);
2046 deformat_string(package
, &value
[1], &deformated
);
2049 *size
= sizeof(DWORD
);
2050 data
= msi_alloc(*size
);
2056 if ( (*p
< '0') || (*p
> '9') )
2062 if (deformated
[0] == '-')
2065 TRACE("DWORD %li\n",*(LPDWORD
)data
);
2067 msi_free(deformated
);
2072 static const WCHAR szMulti
[] = {'[','~',']',0};
2081 *type
=REG_EXPAND_SZ
;
2089 if (strstrW(value
,szMulti
))
2090 *type
= REG_MULTI_SZ
;
2092 *size
= deformat_string(package
, ptr
,(LPWSTR
*)&data
);
2097 static UINT
ITERATE_WriteRegistryValues(MSIRECORD
*row
, LPVOID param
)
2099 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
2100 static const WCHAR szHCR
[] =
2101 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2102 'R','O','O','T','\\',0};
2103 static const WCHAR szHCU
[] =
2104 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2105 'U','S','E','R','\\',0};
2106 static const WCHAR szHLM
[] =
2107 {'H','K','E','Y','_','L','O','C','A','L','_',
2108 'M','A','C','H','I','N','E','\\',0};
2109 static const WCHAR szHU
[] =
2110 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2112 LPSTR value_data
= NULL
;
2113 HKEY root_key
, hkey
;
2116 LPCWSTR szRoot
, component
, name
, key
, value
;
2121 BOOL check_first
= FALSE
;
2124 ui_progress(package
,2,0,0,0);
2131 component
= MSI_RecordGetString(row
, 6);
2132 comp
= get_loaded_component(package
,component
);
2134 return ERROR_SUCCESS
;
2136 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2138 TRACE("Skipping write due to disabled component %s\n",
2139 debugstr_w(component
));
2141 comp
->Action
= comp
->Installed
;
2143 return ERROR_SUCCESS
;
2146 comp
->Action
= INSTALLSTATE_LOCAL
;
2148 name
= MSI_RecordGetString(row
, 4);
2149 if( MSI_RecordIsNull(row
,5) && name
)
2151 /* null values can have special meanings */
2152 if (name
[0]=='-' && name
[1] == 0)
2153 return ERROR_SUCCESS
;
2154 else if ((name
[0]=='+' && name
[1] == 0) ||
2155 (name
[0] == '*' && name
[1] == 0))
2160 root
= MSI_RecordGetInteger(row
,2);
2161 key
= MSI_RecordGetString(row
, 3);
2163 /* get the root key */
2168 static const WCHAR szALLUSER
[] = {'A','L','L','U','S','E','R','S',0};
2169 LPWSTR all_users
= msi_dup_property( package
, szALLUSER
);
2170 if (all_users
&& all_users
[0] == '1')
2172 root_key
= HKEY_LOCAL_MACHINE
;
2177 root_key
= HKEY_CURRENT_USER
;
2180 msi_free(all_users
);
2183 case 0: root_key
= HKEY_CLASSES_ROOT
;
2186 case 1: root_key
= HKEY_CURRENT_USER
;
2189 case 2: root_key
= HKEY_LOCAL_MACHINE
;
2192 case 3: root_key
= HKEY_USERS
;
2196 ERR("Unknown root %i\n",root
);
2202 return ERROR_SUCCESS
;
2204 deformat_string(package
, key
, &deformated
);
2205 size
= strlenW(deformated
) + strlenW(szRoot
) + 1;
2206 uikey
= msi_alloc(size
*sizeof(WCHAR
));
2207 strcpyW(uikey
,szRoot
);
2208 strcatW(uikey
,deformated
);
2210 if (RegCreateKeyW( root_key
, deformated
, &hkey
))
2212 ERR("Could not create key %s\n",debugstr_w(deformated
));
2213 msi_free(deformated
);
2215 return ERROR_SUCCESS
;
2217 msi_free(deformated
);
2219 value
= MSI_RecordGetString(row
,5);
2221 value_data
= parse_value(package
, value
, &type
, &size
);
2224 static const WCHAR szEmpty
[] = {0};
2225 value_data
= (LPSTR
)strdupW(szEmpty
);
2230 deformat_string(package
, name
, &deformated
);
2232 /* get the double nulls to terminate SZ_MULTI */
2233 if (type
== REG_MULTI_SZ
)
2234 size
+=sizeof(WCHAR
);
2238 TRACE("Setting value %s of %s\n",debugstr_w(deformated
),
2240 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
)value_data
, size
);
2245 rc
= RegQueryValueExW(hkey
, deformated
, NULL
, NULL
, NULL
, &sz
);
2246 if (rc
== ERROR_SUCCESS
|| rc
== ERROR_MORE_DATA
)
2248 TRACE("value %s of %s checked already exists\n",
2249 debugstr_w(deformated
), debugstr_w(uikey
));
2253 TRACE("Checked and setting value %s of %s\n",
2254 debugstr_w(deformated
), debugstr_w(uikey
));
2255 if (deformated
|| size
)
2256 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
) value_data
, size
);
2261 uirow
= MSI_CreateRecord(3);
2262 MSI_RecordSetStringW(uirow
,2,deformated
);
2263 MSI_RecordSetStringW(uirow
,1,uikey
);
2266 MSI_RecordSetStringW(uirow
,3,(LPWSTR
)value_data
);
2268 MSI_RecordSetStringW(uirow
,3,value
);
2270 ui_actiondata(package
,szWriteRegistryValues
,uirow
);
2271 msiobj_release( &uirow
->hdr
);
2273 msi_free(value_data
);
2274 msi_free(deformated
);
2277 return ERROR_SUCCESS
;
2280 static UINT
ACTION_WriteRegistryValues(MSIPACKAGE
*package
)
2284 static const WCHAR ExecSeqQuery
[] =
2285 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2286 '`','R','e','g','i','s','t','r','y','`',0 };
2288 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2289 if (rc
!= ERROR_SUCCESS
)
2290 return ERROR_SUCCESS
;
2292 /* increment progress bar each time action data is sent */
2293 ui_progress(package
,1,REG_PROGRESS_VALUE
,1,0);
2295 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteRegistryValues
, package
);
2297 msiobj_release(&view
->hdr
);
2301 static UINT
ACTION_InstallInitialize(MSIPACKAGE
*package
)
2303 package
->script
->CurrentlyScripting
= TRUE
;
2305 return ERROR_SUCCESS
;
2309 static UINT
ACTION_InstallValidate(MSIPACKAGE
*package
)
2314 static const WCHAR q1
[]=
2315 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2316 '`','R','e','g','i','s','t','r','y','`',0};
2319 MSIFEATURE
*feature
;
2322 TRACE("InstallValidate\n");
2324 rc
= MSI_DatabaseOpenViewW(package
->db
, q1
, &view
);
2325 if (rc
== ERROR_SUCCESS
)
2327 MSI_IterateRecords( view
, &progress
, NULL
, package
);
2328 msiobj_release( &view
->hdr
);
2329 total
+= progress
* REG_PROGRESS_VALUE
;
2332 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2333 total
+= COMPONENT_PROGRESS_VALUE
;
2335 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2336 total
+= file
->FileSize
;
2338 ui_progress(package
,0,total
,0,0);
2340 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2342 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2343 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
,
2344 feature
->ActionRequest
);
2347 return ERROR_SUCCESS
;
2350 static UINT
ITERATE_LaunchConditions(MSIRECORD
*row
, LPVOID param
)
2352 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
2353 LPCWSTR cond
= NULL
;
2354 LPCWSTR message
= NULL
;
2355 static const WCHAR title
[]=
2356 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2358 cond
= MSI_RecordGetString(row
,1);
2360 if (MSI_EvaluateConditionW(package
,cond
) != MSICONDITION_TRUE
)
2363 message
= MSI_RecordGetString(row
,2);
2364 deformat_string(package
,message
,&deformated
);
2365 MessageBoxW(NULL
,deformated
,title
,MB_OK
);
2366 msi_free(deformated
);
2367 return ERROR_FUNCTION_FAILED
;
2370 return ERROR_SUCCESS
;
2373 static UINT
ACTION_LaunchConditions(MSIPACKAGE
*package
)
2376 MSIQUERY
* view
= NULL
;
2377 static const WCHAR ExecSeqQuery
[] =
2378 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2379 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2381 TRACE("Checking launch conditions\n");
2383 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2384 if (rc
!= ERROR_SUCCESS
)
2385 return ERROR_SUCCESS
;
2387 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_LaunchConditions
, package
);
2388 msiobj_release(&view
->hdr
);
2393 static LPWSTR
resolve_keypath( MSIPACKAGE
* package
, MSICOMPONENT
*cmp
)
2397 return resolve_folder(package
,cmp
->Directory
,FALSE
,FALSE
,NULL
);
2399 if (cmp
->Attributes
& msidbComponentAttributesRegistryKeyPath
)
2401 MSIRECORD
* row
= 0;
2403 LPWSTR deformated
,buffer
,deformated_name
;
2405 static const WCHAR ExecSeqQuery
[] =
2406 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2407 '`','R','e','g','i','s','t','r','y','`',' ',
2408 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2409 ' ','=',' ' ,'\'','%','s','\'',0 };
2410 static const WCHAR fmt
[]={'%','0','2','i',':','\\','%','s','\\',0};
2411 static const WCHAR fmt2
[]=
2412 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2414 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
,cmp
->KeyPath
);
2418 root
= MSI_RecordGetInteger(row
,2);
2419 key
= MSI_RecordGetString(row
, 3);
2420 name
= MSI_RecordGetString(row
, 4);
2421 deformat_string(package
, key
, &deformated
);
2422 deformat_string(package
, name
, &deformated_name
);
2424 len
= strlenW(deformated
) + 6;
2425 if (deformated_name
)
2426 len
+=strlenW(deformated_name
);
2428 buffer
= msi_alloc( len
*sizeof(WCHAR
));
2430 if (deformated_name
)
2431 sprintfW(buffer
,fmt2
,root
,deformated
,deformated_name
);
2433 sprintfW(buffer
,fmt
,root
,deformated
);
2435 msi_free(deformated
);
2436 msi_free(deformated_name
);
2437 msiobj_release(&row
->hdr
);
2441 else if (cmp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2443 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2448 MSIFILE
*file
= get_loaded_file( package
, cmp
->KeyPath
);
2451 return strdupW( file
->TargetPath
);
2456 static HKEY
openSharedDLLsKey(void)
2459 static const WCHAR path
[] =
2460 {'S','o','f','t','w','a','r','e','\\',
2461 'M','i','c','r','o','s','o','f','t','\\',
2462 'W','i','n','d','o','w','s','\\',
2463 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2464 'S','h','a','r','e','d','D','L','L','s',0};
2466 RegCreateKeyW(HKEY_LOCAL_MACHINE
,path
,&hkey
);
2470 static UINT
ACTION_GetSharedDLLsCount(LPCWSTR dll
)
2475 DWORD sz
= sizeof(count
);
2478 hkey
= openSharedDLLsKey();
2479 rc
= RegQueryValueExW(hkey
, dll
, NULL
, &type
, (LPBYTE
)&count
, &sz
);
2480 if (rc
!= ERROR_SUCCESS
)
2486 static UINT
ACTION_WriteSharedDLLsCount(LPCWSTR path
, UINT count
)
2490 hkey
= openSharedDLLsKey();
2492 msi_reg_set_val_dword( hkey
, path
, count
);
2494 RegDeleteValueW(hkey
,path
);
2500 * Return TRUE if the count should be written out and FALSE if not
2502 static void ACTION_RefCountComponent( MSIPACKAGE
* package
, MSICOMPONENT
*comp
)
2504 MSIFEATURE
*feature
;
2508 /* only refcount DLLs */
2509 if (comp
->KeyPath
== NULL
||
2510 comp
->Attributes
& msidbComponentAttributesRegistryKeyPath
||
2511 comp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2515 count
= ACTION_GetSharedDLLsCount( comp
->FullKeypath
);
2516 write
= (count
> 0);
2518 if (comp
->Attributes
& msidbComponentAttributesSharedDllRefCount
)
2522 /* increment counts */
2523 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2527 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
))
2530 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2532 if ( cl
->component
== comp
)
2537 /* decrement counts */
2538 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2542 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ABSENT
))
2545 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2547 if ( cl
->component
== comp
)
2552 /* ref count all the files in the component */
2557 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2559 if (file
->Component
== comp
)
2560 ACTION_WriteSharedDLLsCount( file
->TargetPath
, count
);
2564 /* add a count for permenent */
2565 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2568 comp
->RefCount
= count
;
2571 ACTION_WriteSharedDLLsCount( comp
->FullKeypath
, comp
->RefCount
);
2575 * Ok further analysis makes me think that this work is
2576 * actually done in the PublishComponents and PublishFeatures
2577 * step, and not here. It appears like the keypath and all that is
2578 * resolved in this step, however actually written in the Publish steps.
2579 * But we will leave it here for now because it is unclear
2581 static UINT
ACTION_ProcessComponents(MSIPACKAGE
*package
)
2583 WCHAR squished_pc
[GUID_SIZE
];
2584 WCHAR squished_cc
[GUID_SIZE
];
2587 HKEY hkey
=0,hkey2
=0;
2589 /* writes the Component and Features values to the registry */
2591 rc
= MSIREG_OpenComponents(&hkey
);
2592 if (rc
!= ERROR_SUCCESS
)
2595 squash_guid(package
->ProductCode
,squished_pc
);
2596 ui_progress(package
,1,COMPONENT_PROGRESS_VALUE
,1,0);
2598 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2602 ui_progress(package
,2,0,0,0);
2603 if (!comp
->ComponentId
)
2606 squash_guid(comp
->ComponentId
,squished_cc
);
2608 msi_free(comp
->FullKeypath
);
2609 comp
->FullKeypath
= resolve_keypath( package
, comp
);
2611 /* do the refcounting */
2612 ACTION_RefCountComponent( package
, comp
);
2614 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2615 debugstr_w(comp
->Component
),
2616 debugstr_w(squished_cc
),
2617 debugstr_w(comp
->FullKeypath
),
2620 * Write the keypath out if the component is to be registered
2621 * and delete the key if the component is to be deregistered
2623 if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2625 rc
= RegCreateKeyW(hkey
,squished_cc
,&hkey2
);
2626 if (rc
!= ERROR_SUCCESS
)
2629 if (!comp
->FullKeypath
)
2632 msi_reg_set_val_str( hkey2
, squished_pc
, comp
->FullKeypath
);
2634 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2636 static const WCHAR szPermKey
[] =
2637 { '0','0','0','0','0','0','0','0','0','0','0','0',
2638 '0','0','0','0','0','0','0','0','0','0','0','0',
2639 '0','0','0','0','0','0','0','0',0 };
2641 msi_reg_set_val_str( hkey2
, szPermKey
, comp
->FullKeypath
);
2647 uirow
= MSI_CreateRecord(3);
2648 MSI_RecordSetStringW(uirow
,1,package
->ProductCode
);
2649 MSI_RecordSetStringW(uirow
,2,comp
->ComponentId
);
2650 MSI_RecordSetStringW(uirow
,3,comp
->FullKeypath
);
2651 ui_actiondata(package
,szProcessComponents
,uirow
);
2652 msiobj_release( &uirow
->hdr
);
2654 else if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ABSENT
))
2658 rc
= RegOpenKeyW(hkey
,squished_cc
,&hkey2
);
2659 if (rc
!= ERROR_SUCCESS
)
2662 RegDeleteValueW(hkey2
,squished_pc
);
2664 /* if the key is empty delete it */
2665 res
= RegEnumKeyExW(hkey2
,0,NULL
,0,0,NULL
,0,NULL
);
2667 if (res
== ERROR_NO_MORE_ITEMS
)
2668 RegDeleteKeyW(hkey
,squished_cc
);
2671 uirow
= MSI_CreateRecord(2);
2672 MSI_RecordSetStringW(uirow
,1,package
->ProductCode
);
2673 MSI_RecordSetStringW(uirow
,2,comp
->ComponentId
);
2674 ui_actiondata(package
,szProcessComponents
,uirow
);
2675 msiobj_release( &uirow
->hdr
);
2690 static BOOL CALLBACK
Typelib_EnumResNameProc( HMODULE hModule
, LPCWSTR lpszType
,
2691 LPWSTR lpszName
, LONG_PTR lParam
)
2694 typelib_struct
*tl_struct
= (typelib_struct
*) lParam
;
2695 static const WCHAR fmt
[] = {'%','s','\\','%','i',0};
2699 if (!IS_INTRESOURCE(lpszName
))
2701 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName
));
2705 sz
= strlenW(tl_struct
->source
)+4;
2706 sz
*= sizeof(WCHAR
);
2708 if ((INT_PTR
)lpszName
== 1)
2709 tl_struct
->path
= strdupW(tl_struct
->source
);
2712 tl_struct
->path
= msi_alloc(sz
);
2713 sprintfW(tl_struct
->path
,fmt
,tl_struct
->source
, lpszName
);
2716 TRACE("trying %s\n", debugstr_w(tl_struct
->path
));
2717 res
= LoadTypeLib(tl_struct
->path
,&tl_struct
->ptLib
);
2718 if (!SUCCEEDED(res
))
2720 msi_free(tl_struct
->path
);
2721 tl_struct
->path
= NULL
;
2726 ITypeLib_GetLibAttr(tl_struct
->ptLib
, &attr
);
2727 if (IsEqualGUID(&(tl_struct
->clsid
),&(attr
->guid
)))
2729 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2733 msi_free(tl_struct
->path
);
2734 tl_struct
->path
= NULL
;
2736 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2737 ITypeLib_Release(tl_struct
->ptLib
);
2742 static UINT
ITERATE_RegisterTypeLibraries(MSIRECORD
*row
, LPVOID param
)
2744 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
2748 typelib_struct tl_struct
;
2750 static const WCHAR szTYPELIB
[] = {'T','Y','P','E','L','I','B',0};
2752 component
= MSI_RecordGetString(row
,3);
2753 comp
= get_loaded_component(package
,component
);
2755 return ERROR_SUCCESS
;
2757 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2759 TRACE("Skipping typelib reg due to disabled component\n");
2761 comp
->Action
= comp
->Installed
;
2763 return ERROR_SUCCESS
;
2766 comp
->Action
= INSTALLSTATE_LOCAL
;
2768 file
= get_loaded_file( package
, comp
->KeyPath
);
2770 return ERROR_SUCCESS
;
2772 module
= LoadLibraryExW( file
->TargetPath
, NULL
, LOAD_LIBRARY_AS_DATAFILE
);
2776 guid
= MSI_RecordGetString(row
,1);
2777 CLSIDFromString((LPWSTR
)guid
, &tl_struct
.clsid
);
2778 tl_struct
.source
= strdupW( file
->TargetPath
);
2779 tl_struct
.path
= NULL
;
2781 EnumResourceNamesW(module
, szTYPELIB
, Typelib_EnumResNameProc
,
2782 (LONG_PTR
)&tl_struct
);
2790 helpid
= MSI_RecordGetString(row
,6);
2793 help
= resolve_folder(package
,helpid
,FALSE
,FALSE
,NULL
);
2794 res
= RegisterTypeLib(tl_struct
.ptLib
,tl_struct
.path
,help
);
2797 if (!SUCCEEDED(res
))
2798 ERR("Failed to register type library %s\n",
2799 debugstr_w(tl_struct
.path
));
2802 ui_actiondata(package
,szRegisterTypeLibraries
,row
);
2804 TRACE("Registered %s\n", debugstr_w(tl_struct
.path
));
2807 ITypeLib_Release(tl_struct
.ptLib
);
2808 msi_free(tl_struct
.path
);
2811 ERR("Failed to load type library %s\n",
2812 debugstr_w(tl_struct
.source
));
2814 FreeLibrary(module
);
2815 msi_free(tl_struct
.source
);
2818 ERR("Could not load file! %s\n", debugstr_w(file
->TargetPath
));
2820 return ERROR_SUCCESS
;
2823 static UINT
ACTION_RegisterTypeLibraries(MSIPACKAGE
*package
)
2826 * OK this is a bit confusing.. I am given a _Component key and I believe
2827 * that the file that is being registered as a type library is the "key file
2828 * of that component" which I interpret to mean "The file in the KeyPath of
2833 static const WCHAR Query
[] =
2834 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2835 '`','T','y','p','e','L','i','b','`',0};
2837 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
2838 if (rc
!= ERROR_SUCCESS
)
2839 return ERROR_SUCCESS
;
2841 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_RegisterTypeLibraries
, package
);
2842 msiobj_release(&view
->hdr
);
2846 static UINT
ITERATE_CreateShortcuts(MSIRECORD
*row
, LPVOID param
)
2848 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
2849 LPWSTR target_file
, target_folder
, filename
;
2850 LPCWSTR buffer
, extension
;
2852 static const WCHAR szlnk
[]={'.','l','n','k',0};
2853 IShellLinkW
*sl
= NULL
;
2854 IPersistFile
*pf
= NULL
;
2857 buffer
= MSI_RecordGetString(row
,4);
2858 comp
= get_loaded_component(package
,buffer
);
2860 return ERROR_SUCCESS
;
2862 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2864 TRACE("Skipping shortcut creation due to disabled component\n");
2866 comp
->Action
= comp
->Installed
;
2868 return ERROR_SUCCESS
;
2871 comp
->Action
= INSTALLSTATE_LOCAL
;
2873 ui_actiondata(package
,szCreateShortcuts
,row
);
2875 res
= CoCreateInstance( &CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
,
2876 &IID_IShellLinkW
, (LPVOID
*) &sl
);
2880 ERR("CLSID_ShellLink not available\n");
2884 res
= IShellLinkW_QueryInterface( sl
, &IID_IPersistFile
,(LPVOID
*) &pf
);
2887 ERR("QueryInterface(IID_IPersistFile) failed\n");
2891 buffer
= MSI_RecordGetString(row
,2);
2892 target_folder
= resolve_folder(package
, buffer
,FALSE
,FALSE
,NULL
);
2894 /* may be needed because of a bug somehwere else */
2895 create_full_pathW(target_folder
);
2897 filename
= msi_dup_record_field( row
, 3 );
2898 reduce_to_longfilename(filename
);
2900 extension
= strchrW(filename
,'.');
2901 if (!extension
|| strcmpiW(extension
,szlnk
))
2903 int len
= strlenW(filename
);
2904 filename
= msi_realloc(filename
, len
* sizeof(WCHAR
) + sizeof(szlnk
));
2905 memcpy(filename
+ len
, szlnk
, sizeof(szlnk
));
2907 target_file
= build_directory_name(2, target_folder
, filename
);
2908 msi_free(target_folder
);
2911 buffer
= MSI_RecordGetString(row
,5);
2912 if (strchrW(buffer
,'['))
2915 deformat_string(package
,buffer
,&deformated
);
2916 IShellLinkW_SetPath(sl
,deformated
);
2917 msi_free(deformated
);
2921 FIXME("poorly handled shortcut format, advertised shortcut\n");
2922 IShellLinkW_SetPath(sl
,comp
->FullKeypath
);
2925 if (!MSI_RecordIsNull(row
,6))
2928 buffer
= MSI_RecordGetString(row
,6);
2929 deformat_string(package
,buffer
,&deformated
);
2930 IShellLinkW_SetArguments(sl
,deformated
);
2931 msi_free(deformated
);
2934 if (!MSI_RecordIsNull(row
,7))
2936 buffer
= MSI_RecordGetString(row
,7);
2937 IShellLinkW_SetDescription(sl
,buffer
);
2940 if (!MSI_RecordIsNull(row
,8))
2941 IShellLinkW_SetHotkey(sl
,MSI_RecordGetInteger(row
,8));
2943 if (!MSI_RecordIsNull(row
,9))
2948 buffer
= MSI_RecordGetString(row
,9);
2950 Path
= build_icon_path(package
,buffer
);
2951 index
= MSI_RecordGetInteger(row
,10);
2953 /* no value means 0 */
2954 if (index
== MSI_NULL_INTEGER
)
2957 IShellLinkW_SetIconLocation(sl
,Path
,index
);
2961 if (!MSI_RecordIsNull(row
,11))
2962 IShellLinkW_SetShowCmd(sl
,MSI_RecordGetInteger(row
,11));
2964 if (!MSI_RecordIsNull(row
,12))
2967 buffer
= MSI_RecordGetString(row
,12);
2968 Path
= resolve_folder(package
, buffer
, FALSE
, FALSE
, NULL
);
2970 IShellLinkW_SetWorkingDirectory(sl
,Path
);
2974 TRACE("Writing shortcut to %s\n",debugstr_w(target_file
));
2975 IPersistFile_Save(pf
,target_file
,FALSE
);
2977 msi_free(target_file
);
2981 IPersistFile_Release( pf
);
2983 IShellLinkW_Release( sl
);
2985 return ERROR_SUCCESS
;
2988 static UINT
ACTION_CreateShortcuts(MSIPACKAGE
*package
)
2993 static const WCHAR Query
[] =
2994 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2995 '`','S','h','o','r','t','c','u','t','`',0};
2997 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
2998 if (rc
!= ERROR_SUCCESS
)
2999 return ERROR_SUCCESS
;
3001 res
= CoInitialize( NULL
);
3004 ERR("CoInitialize failed\n");
3005 return ERROR_FUNCTION_FAILED
;
3008 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateShortcuts
, package
);
3009 msiobj_release(&view
->hdr
);
3016 static UINT
ITERATE_PublishProduct(MSIRECORD
*row
, LPVOID param
)
3018 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
3027 FileName
= MSI_RecordGetString(row
,1);
3030 ERR("Unable to get FileName\n");
3031 return ERROR_SUCCESS
;
3034 FilePath
= build_icon_path(package
,FileName
);
3036 TRACE("Creating icon file at %s\n",debugstr_w(FilePath
));
3038 the_file
= CreateFileW(FilePath
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
,
3039 FILE_ATTRIBUTE_NORMAL
, NULL
);
3041 if (the_file
== INVALID_HANDLE_VALUE
)
3043 ERR("Unable to create file %s\n",debugstr_w(FilePath
));
3045 return ERROR_SUCCESS
;
3052 rc
= MSI_RecordReadStream(row
,2,buffer
,&sz
);
3053 if (rc
!= ERROR_SUCCESS
)
3055 ERR("Failed to get stream\n");
3056 CloseHandle(the_file
);
3057 DeleteFileW(FilePath
);
3060 WriteFile(the_file
,buffer
,sz
,&write
,NULL
);
3061 } while (sz
== 1024);
3065 CloseHandle(the_file
);
3067 uirow
= MSI_CreateRecord(1);
3068 MSI_RecordSetStringW(uirow
,1,FileName
);
3069 ui_actiondata(package
,szPublishProduct
,uirow
);
3070 msiobj_release( &uirow
->hdr
);
3072 return ERROR_SUCCESS
;
3076 * 99% of the work done here is only done for
3077 * advertised installs. However this is where the
3078 * Icon table is processed and written out
3079 * so that is what I am going to do here.
3081 static UINT
ACTION_PublishProduct(MSIPACKAGE
*package
)
3085 static const WCHAR Query
[]=
3086 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3087 '`','I','c','o','n','`',0};
3088 /* for registry stuff */
3091 static const WCHAR szProductLanguage
[] =
3092 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3093 static const WCHAR szARPProductIcon
[] =
3094 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3095 static const WCHAR szProductVersion
[] =
3096 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3100 MSIHANDLE hDb
, hSumInfo
;
3102 /* write out icon files */
3104 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3105 if (rc
== ERROR_SUCCESS
)
3107 MSI_IterateRecords(view
, NULL
, ITERATE_PublishProduct
, package
);
3108 msiobj_release(&view
->hdr
);
3111 /* ok there is a lot more done here but i need to figure out what */
3113 rc
= MSIREG_OpenProductsKey(package
->ProductCode
,&hkey
,TRUE
);
3114 if (rc
!= ERROR_SUCCESS
)
3117 rc
= MSIREG_OpenUserProductsKey(package
->ProductCode
,&hukey
,TRUE
);
3118 if (rc
!= ERROR_SUCCESS
)
3122 buffer
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTNAMEW
);
3123 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PRODUCTNAMEW
, buffer
);
3126 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
3127 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
3129 buffer
= msi_dup_property( package
, szARPProductIcon
);
3132 LPWSTR path
= build_icon_path(package
,buffer
);
3133 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PRODUCTICONW
, path
);
3138 buffer
= msi_dup_property( package
, szProductVersion
);
3141 DWORD verdword
= msi_version_str_to_dword(buffer
);
3142 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
3146 /* FIXME: Need to write more keys to the user registry */
3148 hDb
= alloc_msihandle( &package
->db
->hdr
);
3150 rc
= ERROR_NOT_ENOUGH_MEMORY
;
3153 rc
= MsiGetSummaryInformationW(hDb
, NULL
, 0, &hSumInfo
);
3154 MsiCloseHandle(hDb
);
3155 if (rc
== ERROR_SUCCESS
)
3157 WCHAR guidbuffer
[0x200];
3159 rc
= MsiSummaryInfoGetPropertyW(hSumInfo
, 9, NULL
, NULL
, NULL
,
3161 if (rc
== ERROR_SUCCESS
)
3163 WCHAR squashed
[GUID_SIZE
];
3164 /* for now we only care about the first guid */
3165 LPWSTR ptr
= strchrW(guidbuffer
,';');
3167 squash_guid(guidbuffer
,squashed
);
3168 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PACKAGECODEW
, squashed
);
3172 ERR("Unable to query Revision_Number...\n");
3175 MsiCloseHandle(hSumInfo
);
3179 ERR("Unable to open Summary Information\n");
3191 static UINT
ITERATE_WriteIniValues(MSIRECORD
*row
, LPVOID param
)
3193 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3194 LPCWSTR component
,section
,key
,value
,identifier
,filename
,dirproperty
;
3195 LPWSTR deformated_section
, deformated_key
, deformated_value
;
3196 LPWSTR folder
, fullname
= NULL
;
3200 static const WCHAR szWindowsFolder
[] =
3201 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3203 component
= MSI_RecordGetString(row
, 8);
3204 comp
= get_loaded_component(package
,component
);
3206 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3208 TRACE("Skipping ini file due to disabled component %s\n",
3209 debugstr_w(component
));
3211 comp
->Action
= comp
->Installed
;
3213 return ERROR_SUCCESS
;
3216 comp
->Action
= INSTALLSTATE_LOCAL
;
3218 identifier
= MSI_RecordGetString(row
,1);
3219 filename
= MSI_RecordGetString(row
,2);
3220 dirproperty
= MSI_RecordGetString(row
,3);
3221 section
= MSI_RecordGetString(row
,4);
3222 key
= MSI_RecordGetString(row
,5);
3223 value
= MSI_RecordGetString(row
,6);
3224 action
= MSI_RecordGetInteger(row
,7);
3226 deformat_string(package
,section
,&deformated_section
);
3227 deformat_string(package
,key
,&deformated_key
);
3228 deformat_string(package
,value
,&deformated_value
);
3232 folder
= resolve_folder(package
, dirproperty
, FALSE
, FALSE
, NULL
);
3234 folder
= msi_dup_property( package
, dirproperty
);
3237 folder
= msi_dup_property( package
, szWindowsFolder
);
3241 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty
));
3245 fullname
= build_directory_name(2, folder
, filename
);
3249 TRACE("Adding value %s to section %s in %s\n",
3250 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3251 debugstr_w(fullname
));
3252 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3253 deformated_value
, fullname
);
3255 else if (action
== 1)
3258 GetPrivateProfileStringW(deformated_section
, deformated_key
, NULL
,
3259 returned
, 10, fullname
);
3260 if (returned
[0] == 0)
3262 TRACE("Adding value %s to section %s in %s\n",
3263 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3264 debugstr_w(fullname
));
3266 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3267 deformated_value
, fullname
);
3270 else if (action
== 3)
3271 FIXME("Append to existing section not yet implemented\n");
3273 uirow
= MSI_CreateRecord(4);
3274 MSI_RecordSetStringW(uirow
,1,identifier
);
3275 MSI_RecordSetStringW(uirow
,2,deformated_section
);
3276 MSI_RecordSetStringW(uirow
,3,deformated_key
);
3277 MSI_RecordSetStringW(uirow
,4,deformated_value
);
3278 ui_actiondata(package
,szWriteIniValues
,uirow
);
3279 msiobj_release( &uirow
->hdr
);
3283 msi_free(deformated_key
);
3284 msi_free(deformated_value
);
3285 msi_free(deformated_section
);
3286 return ERROR_SUCCESS
;
3289 static UINT
ACTION_WriteIniValues(MSIPACKAGE
*package
)
3293 static const WCHAR ExecSeqQuery
[] =
3294 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3295 '`','I','n','i','F','i','l','e','`',0};
3297 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3298 if (rc
!= ERROR_SUCCESS
)
3300 TRACE("no IniFile table\n");
3301 return ERROR_SUCCESS
;
3304 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteIniValues
, package
);
3305 msiobj_release(&view
->hdr
);
3309 static UINT
ITERATE_SelfRegModules(MSIRECORD
*row
, LPVOID param
)
3311 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3316 static const WCHAR ExeStr
[] =
3317 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3318 static const WCHAR close
[] = {'\"',0};
3320 PROCESS_INFORMATION info
;
3325 memset(&si
,0,sizeof(STARTUPINFOW
));
3327 filename
= MSI_RecordGetString(row
,1);
3328 file
= get_loaded_file( package
, filename
);
3332 ERR("Unable to find file id %s\n",debugstr_w(filename
));
3333 return ERROR_SUCCESS
;
3336 len
= strlenW(ExeStr
) + strlenW( file
->TargetPath
) + 2;
3338 FullName
= msi_alloc(len
*sizeof(WCHAR
));
3339 strcpyW(FullName
,ExeStr
);
3340 strcatW( FullName
, file
->TargetPath
);
3341 strcatW(FullName
,close
);
3343 TRACE("Registering %s\n",debugstr_w(FullName
));
3344 brc
= CreateProcessW(NULL
, FullName
, NULL
, NULL
, FALSE
, 0, NULL
, c_colon
,
3348 msi_dialog_check_messages(info
.hProcess
);
3353 uirow
= MSI_CreateRecord( 2 );
3354 uipath
= strdupW( file
->TargetPath
);
3355 p
= strrchrW(uipath
,'\\');
3358 MSI_RecordSetStringW( uirow
, 1, &p
[2] );
3359 MSI_RecordSetStringW( uirow
, 2, uipath
);
3360 ui_actiondata( package
, szSelfRegModules
, uirow
);
3361 msiobj_release( &uirow
->hdr
);
3363 /* FIXME: call ui_progress? */
3365 return ERROR_SUCCESS
;
3368 static UINT
ACTION_SelfRegModules(MSIPACKAGE
*package
)
3372 static const WCHAR ExecSeqQuery
[] =
3373 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3374 '`','S','e','l','f','R','e','g','`',0};
3376 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3377 if (rc
!= ERROR_SUCCESS
)
3379 TRACE("no SelfReg table\n");
3380 return ERROR_SUCCESS
;
3383 MSI_IterateRecords(view
, NULL
, ITERATE_SelfRegModules
, package
);
3384 msiobj_release(&view
->hdr
);
3386 return ERROR_SUCCESS
;
3389 static UINT
ACTION_PublishFeatures(MSIPACKAGE
*package
)
3391 MSIFEATURE
*feature
;
3396 rc
= MSIREG_OpenFeaturesKey(package
->ProductCode
,&hkey
,TRUE
);
3397 if (rc
!= ERROR_SUCCESS
)
3400 rc
= MSIREG_OpenUserFeaturesKey(package
->ProductCode
,&hukey
,TRUE
);
3401 if (rc
!= ERROR_SUCCESS
)
3404 /* here the guids are base 85 encoded */
3405 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
3411 BOOL absent
= FALSE
;
3414 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
) &&
3415 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_SOURCE
) &&
3416 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ADVERTISED
))
3420 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3424 if (feature
->Feature_Parent
)
3425 size
+= strlenW( feature
->Feature_Parent
)+2;
3427 data
= msi_alloc(size
* sizeof(WCHAR
));
3430 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3432 MSICOMPONENT
* component
= cl
->component
;
3436 if (component
->ComponentId
)
3438 TRACE("From %s\n",debugstr_w(component
->ComponentId
));
3439 CLSIDFromString(component
->ComponentId
, &clsid
);
3440 encode_base85_guid(&clsid
,buf
);
3441 TRACE("to %s\n",debugstr_w(buf
));
3445 if (feature
->Feature_Parent
)
3447 static const WCHAR sep
[] = {'\2',0};
3449 strcatW(data
,feature
->Feature_Parent
);
3452 msi_reg_set_val_str( hkey
, feature
->Feature
, data
);
3456 if (feature
->Feature_Parent
)
3457 size
= strlenW(feature
->Feature_Parent
)*sizeof(WCHAR
);
3460 RegSetValueExW(hukey
,feature
->Feature
,0,REG_SZ
,
3461 (LPBYTE
)feature
->Feature_Parent
,size
);
3465 size
+= 2*sizeof(WCHAR
);
3466 data
= msi_alloc(size
);
3469 if (feature
->Feature_Parent
)
3470 strcpyW( &data
[1], feature
->Feature_Parent
);
3471 RegSetValueExW(hukey
,feature
->Feature
,0,REG_SZ
,
3477 uirow
= MSI_CreateRecord( 1 );
3478 MSI_RecordSetStringW( uirow
, 1, feature
->Feature
);
3479 ui_actiondata( package
, szPublishFeatures
, uirow
);
3480 msiobj_release( &uirow
->hdr
);
3481 /* FIXME: call ui_progress? */
3490 static UINT
msi_make_package_local( MSIPACKAGE
*package
, HKEY hkey
)
3492 static const WCHAR installerPathFmt
[] = {
3493 '%','s','\\','I','n','s','t','a','l','l','e','r','\\',0};
3494 static const WCHAR fmt
[] = {
3496 'I','n','s','t','a','l','l','e','r','\\',
3497 '%','x','.','m','s','i',0};
3498 static const WCHAR szOriginalDatabase
[] =
3499 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3500 WCHAR windir
[MAX_PATH
], path
[MAX_PATH
], packagefile
[MAX_PATH
];
3505 /* copy the package locally */
3506 num
= GetTickCount() & 0xffff;
3510 GetWindowsDirectoryW( windir
, MAX_PATH
);
3511 snprintfW( packagefile
, MAX_PATH
, fmt
, windir
, num
);
3514 HANDLE handle
= CreateFileW(packagefile
,GENERIC_WRITE
, 0, NULL
,
3515 CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, 0 );
3516 if (handle
!= INVALID_HANDLE_VALUE
)
3518 CloseHandle(handle
);
3521 if (GetLastError() != ERROR_FILE_EXISTS
&&
3522 GetLastError() != ERROR_SHARING_VIOLATION
)
3524 if (!(++num
& 0xffff)) num
= 1;
3525 sprintfW(packagefile
,fmt
,num
);
3526 } while (num
!= start
);
3528 snprintfW( path
, MAX_PATH
, installerPathFmt
, windir
);
3529 create_full_pathW(path
);
3531 TRACE("Copying to local package %s\n",debugstr_w(packagefile
));
3533 msiFilePath
= msi_dup_property( package
, szOriginalDatabase
);
3534 r
= CopyFileW( msiFilePath
, packagefile
, FALSE
);
3535 msi_free( msiFilePath
);
3539 ERR("Unable to copy package (%s -> %s) (error %ld)\n",
3540 debugstr_w(msiFilePath
), debugstr_w(packagefile
), GetLastError());
3541 return ERROR_FUNCTION_FAILED
;
3544 /* FIXME: maybe set this key in ACTION_RegisterProduct instead */
3545 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_LOCALPACKAGEW
, packagefile
);
3546 return ERROR_SUCCESS
;
3549 static UINT
msi_write_uninstall_property_vals( MSIPACKAGE
*package
, HKEY hkey
)
3551 LPWSTR prop
, val
, key
;
3552 static const LPCSTR propval
[] = {
3553 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3554 "ARPCONTACT", "Contact",
3555 "ARPCOMMENTS", "Comments",
3556 "ProductName", "DisplayName",
3557 "ProductVersion", "DisplayVersion",
3558 "ARPHELPLINK", "HelpLink",
3559 "ARPHELPTELEPHONE", "HelpTelephone",
3560 "ARPINSTALLLOCATION", "InstallLocation",
3561 "SourceDir", "InstallSource",
3562 "Manufacturer", "Publisher",
3563 "ARPREADME", "Readme",
3565 "ARPURLINFOABOUT", "URLInfoAbout",
3566 "ARPURLUPDATEINFO", "URLUpdateInfo",
3569 const LPCSTR
*p
= propval
;
3573 prop
= strdupAtoW( *p
++ );
3574 key
= strdupAtoW( *p
++ );
3575 val
= msi_dup_property( package
, prop
);
3576 msi_reg_set_val_str( hkey
, key
, val
);
3581 return ERROR_SUCCESS
;
3584 static UINT
ACTION_RegisterProduct(MSIPACKAGE
*package
)
3587 LPWSTR buffer
= NULL
;
3590 static const WCHAR szWindowsInstaller
[] =
3591 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3592 static const WCHAR szUpgradeCode
[] =
3593 {'U','p','g','r','a','d','e','C','o','d','e',0};
3594 static const WCHAR modpath_fmt
[] =
3595 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3596 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3597 static const WCHAR szModifyPath
[] =
3598 {'M','o','d','i','f','y','P','a','t','h',0};
3599 static const WCHAR szUninstallString
[] =
3600 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3601 static const WCHAR szEstimatedSize
[] =
3602 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3603 static const WCHAR szProductLanguage
[] =
3604 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3605 static const WCHAR szProductVersion
[] =
3606 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3609 static const WCHAR date_fmt
[] = {'%','i','%','i','%','i',0};
3610 LPWSTR upgrade_code
;
3613 rc
= MSIREG_OpenUninstallKey(package
->ProductCode
,&hkey
,TRUE
);
3614 if (rc
!= ERROR_SUCCESS
)
3617 /* dump all the info i can grab */
3618 /* FIXME: Flesh out more information */
3620 msi_write_uninstall_property_vals( package
, hkey
);
3622 msi_reg_set_val_dword( hkey
, szWindowsInstaller
, 1 );
3624 msi_make_package_local( package
, hkey
);
3626 /* do ModifyPath and UninstallString */
3627 size
= deformat_string(package
,modpath_fmt
,&buffer
);
3628 RegSetValueExW(hkey
,szModifyPath
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
3629 RegSetValueExW(hkey
,szUninstallString
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
3632 /* FIXME: Write real Estimated Size when we have it */
3633 msi_reg_set_val_dword( hkey
, szEstimatedSize
, 0 );
3635 GetLocalTime(&systime
);
3636 sprintfW(szDate
,date_fmt
,systime
.wYear
,systime
.wMonth
,systime
.wDay
);
3637 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_INSTALLDATEW
, szDate
);
3639 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
3640 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
3642 buffer
= msi_dup_property( package
, szProductVersion
);
3645 DWORD verdword
= msi_version_str_to_dword(buffer
);
3647 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
3648 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMAJORW
, verdword
>>24 );
3649 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMINORW
, (verdword
>>16)&0x00FF );
3653 /* Handle Upgrade Codes */
3654 upgrade_code
= msi_dup_property( package
, szUpgradeCode
);
3659 MSIREG_OpenUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
3660 squash_guid(package
->ProductCode
,squashed
);
3661 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
3663 MSIREG_OpenUserUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
3664 squash_guid(package
->ProductCode
,squashed
);
3665 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
3668 msi_free(upgrade_code
);
3673 /* FIXME: call ui_actiondata */
3675 return ERROR_SUCCESS
;
3678 static UINT
ACTION_InstallExecute(MSIPACKAGE
*package
)
3680 return execute_script(package
,INSTALL_SCRIPT
);
3683 static UINT
ACTION_InstallFinalize(MSIPACKAGE
*package
)
3687 /* turn off scheduleing */
3688 package
->script
->CurrentlyScripting
= FALSE
;
3690 /* first do the same as an InstallExecute */
3691 rc
= ACTION_InstallExecute(package
);
3692 if (rc
!= ERROR_SUCCESS
)
3695 /* then handle Commit Actions */
3696 rc
= execute_script(package
,COMMIT_SCRIPT
);
3701 static UINT
ACTION_ForceReboot(MSIPACKAGE
*package
)
3703 static const WCHAR RunOnce
[] = {
3704 'S','o','f','t','w','a','r','e','\\',
3705 'M','i','c','r','o','s','o','f','t','\\',
3706 'W','i','n','d','o','w','s','\\',
3707 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3708 'R','u','n','O','n','c','e',0};
3709 static const WCHAR InstallRunOnce
[] = {
3710 'S','o','f','t','w','a','r','e','\\',
3711 'M','i','c','r','o','s','o','f','t','\\',
3712 'W','i','n','d','o','w','s','\\',
3713 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3714 'I','n','s','t','a','l','l','e','r','\\',
3715 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
3717 static const WCHAR msiexec_fmt
[] = {
3719 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
3720 '\"','%','s','\"',0};
3721 static const WCHAR install_fmt
[] = {
3722 '/','I',' ','\"','%','s','\"',' ',
3723 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
3724 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
3725 WCHAR buffer
[256], sysdir
[MAX_PATH
];
3727 WCHAR squished_pc
[100];
3729 squash_guid(package
->ProductCode
,squished_pc
);
3731 GetSystemDirectoryW(sysdir
, sizeof(sysdir
)/sizeof(sysdir
[0]));
3732 RegCreateKeyW(HKEY_LOCAL_MACHINE
,RunOnce
,&hkey
);
3733 snprintfW(buffer
,sizeof(buffer
)/sizeof(buffer
[0]),msiexec_fmt
,sysdir
,
3736 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
3739 TRACE("Reboot command %s\n",debugstr_w(buffer
));
3741 RegCreateKeyW(HKEY_LOCAL_MACHINE
,InstallRunOnce
,&hkey
);
3742 sprintfW(buffer
,install_fmt
,package
->ProductCode
,squished_pc
);
3744 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
3747 return ERROR_INSTALL_SUSPEND
;
3750 UINT
ACTION_ResolveSource(MSIPACKAGE
* package
)
3755 * we are currently doing what should be done here in the top level Install
3756 * however for Adminastrative and uninstalls this step will be needed
3758 if (!package
->PackagePath
)
3759 return ERROR_SUCCESS
;
3761 attrib
= GetFileAttributesW(package
->PackagePath
);
3762 if (attrib
== INVALID_FILE_ATTRIBUTES
)
3768 rc
= MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
3769 MSIINSTALLCONTEXT_USERMANAGED
, MSICODE_PRODUCT
,
3770 INSTALLPROPERTY_DISKPROMPTW
,NULL
,&size
);
3771 if (rc
== ERROR_MORE_DATA
)
3773 prompt
= msi_alloc(size
* sizeof(WCHAR
));
3774 MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
3775 MSIINSTALLCONTEXT_USERMANAGED
, MSICODE_PRODUCT
,
3776 INSTALLPROPERTY_DISKPROMPTW
,prompt
,&size
);
3779 prompt
= strdupW(package
->PackagePath
);
3781 msg
= generate_error_string(package
,1302,1,prompt
);
3782 while(attrib
== INVALID_FILE_ATTRIBUTES
)
3784 rc
= MessageBoxW(NULL
,msg
,NULL
,MB_OKCANCEL
);
3787 rc
= ERROR_INSTALL_USEREXIT
;
3790 attrib
= GetFileAttributesW(package
->PackagePath
);
3796 return ERROR_SUCCESS
;
3801 static UINT
ACTION_RegisterUser(MSIPACKAGE
*package
)
3808 static const WCHAR szPropKeys
[][80] =
3810 {'P','r','o','d','u','c','t','I','D',0},
3811 {'U','S','E','R','N','A','M','E',0},
3812 {'C','O','M','P','A','N','Y','N','A','M','E',0},
3816 static const WCHAR szRegKeys
[][80] =
3818 {'P','r','o','d','u','c','t','I','D',0},
3819 {'R','e','g','O','w','n','e','r',0},
3820 {'R','e','g','C','o','m','p','a','n','y',0},
3824 productid
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTIDW
);
3826 return ERROR_SUCCESS
;
3828 rc
= MSIREG_OpenUninstallKey(package
->ProductCode
,&hkey
,TRUE
);
3829 if (rc
!= ERROR_SUCCESS
)
3832 for( i
= 0; szPropKeys
[i
][0]; i
++ )
3834 buffer
= msi_dup_property( package
, szPropKeys
[i
] );
3835 msi_reg_set_val_str( hkey
, szRegKeys
[i
], buffer
);
3840 msi_free(productid
);
3843 /* FIXME: call ui_actiondata */
3845 return ERROR_SUCCESS
;
3849 static UINT
ACTION_ExecuteAction(MSIPACKAGE
*package
)
3853 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
3854 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
3860 * Code based off of code located here
3861 * http://www.codeproject.com/gdi/fontnamefromfile.asp
3863 * Using string index 4 (full font name) instead of 1 (family name)
3865 static LPWSTR
load_ttfname_from(LPCWSTR filename
)
3871 typedef struct _tagTT_OFFSET_TABLE
{
3872 USHORT uMajorVersion
;
3873 USHORT uMinorVersion
;
3874 USHORT uNumOfTables
;
3875 USHORT uSearchRange
;
3876 USHORT uEntrySelector
;
3880 typedef struct _tagTT_TABLE_DIRECTORY
{
3881 char szTag
[4]; /* table name */
3882 ULONG uCheckSum
; /* Check sum */
3883 ULONG uOffset
; /* Offset from beginning of file */
3884 ULONG uLength
; /* length of the table in bytes */
3885 }TT_TABLE_DIRECTORY
;
3887 typedef struct _tagTT_NAME_TABLE_HEADER
{
3888 USHORT uFSelector
; /* format selector. Always 0 */
3889 USHORT uNRCount
; /* Name Records count */
3890 USHORT uStorageOffset
; /* Offset for strings storage,
3891 * from start of the table */
3892 }TT_NAME_TABLE_HEADER
;
3894 typedef struct _tagTT_NAME_RECORD
{
3899 USHORT uStringLength
;
3900 USHORT uStringOffset
; /* from start of storage area */
3903 #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
3904 #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
3906 handle
= CreateFileW(filename
,GENERIC_READ
, 0, NULL
, OPEN_EXISTING
,
3907 FILE_ATTRIBUTE_NORMAL
, 0 );
3908 if (handle
!= INVALID_HANDLE_VALUE
)
3910 TT_TABLE_DIRECTORY tblDir
;
3911 BOOL bFound
= FALSE
;
3912 TT_OFFSET_TABLE ttOffsetTable
;
3915 ReadFile(handle
,&ttOffsetTable
, sizeof(TT_OFFSET_TABLE
),&dwRead
,NULL
);
3916 ttOffsetTable
.uNumOfTables
= SWAPWORD(ttOffsetTable
.uNumOfTables
);
3917 ttOffsetTable
.uMajorVersion
= SWAPWORD(ttOffsetTable
.uMajorVersion
);
3918 ttOffsetTable
.uMinorVersion
= SWAPWORD(ttOffsetTable
.uMinorVersion
);
3920 if (ttOffsetTable
.uMajorVersion
!= 1 ||
3921 ttOffsetTable
.uMinorVersion
!= 0)
3924 for (i
=0; i
< ttOffsetTable
.uNumOfTables
; i
++)
3926 ReadFile(handle
,&tblDir
, sizeof(TT_TABLE_DIRECTORY
),&dwRead
,NULL
);
3927 if (strncmp(tblDir
.szTag
,"name",4)==0)
3930 tblDir
.uLength
= SWAPLONG(tblDir
.uLength
);
3931 tblDir
.uOffset
= SWAPLONG(tblDir
.uOffset
);
3938 TT_NAME_TABLE_HEADER ttNTHeader
;
3939 TT_NAME_RECORD ttRecord
;
3941 SetFilePointer(handle
, tblDir
.uOffset
, NULL
, FILE_BEGIN
);
3942 ReadFile(handle
,&ttNTHeader
, sizeof(TT_NAME_TABLE_HEADER
),
3945 ttNTHeader
.uNRCount
= SWAPWORD(ttNTHeader
.uNRCount
);
3946 ttNTHeader
.uStorageOffset
= SWAPWORD(ttNTHeader
.uStorageOffset
);
3948 for(i
=0; i
<ttNTHeader
.uNRCount
; i
++)
3950 ReadFile(handle
,&ttRecord
, sizeof(TT_NAME_RECORD
),&dwRead
,NULL
);
3951 ttRecord
.uNameID
= SWAPWORD(ttRecord
.uNameID
);
3952 /* 4 is the Full Font Name */
3953 if(ttRecord
.uNameID
== 4)
3957 static LPCSTR tt
= " (TrueType)";
3959 ttRecord
.uStringLength
= SWAPWORD(ttRecord
.uStringLength
);
3960 ttRecord
.uStringOffset
= SWAPWORD(ttRecord
.uStringOffset
);
3961 nPos
= SetFilePointer(handle
, 0, NULL
, FILE_CURRENT
);
3962 SetFilePointer(handle
, tblDir
.uOffset
+
3963 ttRecord
.uStringOffset
+
3964 ttNTHeader
.uStorageOffset
,
3966 buf
= msi_alloc_zero( ttRecord
.uStringLength
+ 1 + strlen(tt
) );
3967 ReadFile(handle
, buf
, ttRecord
.uStringLength
, &dwRead
, NULL
);
3968 if (strlen(buf
) > 0)
3971 ret
= strdupAtoW(buf
);
3977 SetFilePointer(handle
,nPos
, NULL
, FILE_BEGIN
);
3981 CloseHandle(handle
);
3984 ERR("Unable to open font file %s\n", debugstr_w(filename
));
3986 TRACE("Returning fontname %s\n",debugstr_w(ret
));
3990 static UINT
ITERATE_RegisterFonts(MSIRECORD
*row
, LPVOID param
)
3992 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3996 static const WCHAR regfont1
[] =
3997 {'S','o','f','t','w','a','r','e','\\',
3998 'M','i','c','r','o','s','o','f','t','\\',
3999 'W','i','n','d','o','w','s',' ','N','T','\\',
4000 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4001 'F','o','n','t','s',0};
4002 static const WCHAR regfont2
[] =
4003 {'S','o','f','t','w','a','r','e','\\',
4004 'M','i','c','r','o','s','o','f','t','\\',
4005 'W','i','n','d','o','w','s','\\',
4006 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4007 'F','o','n','t','s',0};
4013 filename
= MSI_RecordGetString( row
, 1 );
4014 file
= get_loaded_file( package
, filename
);
4017 ERR("Unable to load file\n");
4018 return ERROR_SUCCESS
;
4021 /* check to make sure that component is installed */
4022 if (!ACTION_VerifyComponentForAction( file
->Component
, INSTALLSTATE_LOCAL
))
4024 TRACE("Skipping: Component not scheduled for install\n");
4025 return ERROR_SUCCESS
;
4028 RegCreateKeyW(HKEY_LOCAL_MACHINE
,regfont1
,&hkey1
);
4029 RegCreateKeyW(HKEY_LOCAL_MACHINE
,regfont2
,&hkey2
);
4031 if (MSI_RecordIsNull(row
,2))
4032 name
= load_ttfname_from( file
->TargetPath
);
4034 name
= msi_dup_record_field(row
,2);
4038 msi_reg_set_val_str( hkey1
, name
, file
->FileName
);
4039 msi_reg_set_val_str( hkey2
, name
, file
->FileName
);
4047 uirow
= MSI_CreateRecord( 1 );
4048 uipath
= strdupW( file
->TargetPath
);
4049 p
= strrchrW(uipath
,'\\');
4052 MSI_RecordSetStringW( uirow
, 1, p
);
4053 ui_actiondata( package
, szRegisterFonts
, uirow
);
4054 msiobj_release( &uirow
->hdr
);
4056 /* FIXME: call ui_progress? */
4058 return ERROR_SUCCESS
;
4061 static UINT
ACTION_RegisterFonts(MSIPACKAGE
*package
)
4065 static const WCHAR ExecSeqQuery
[] =
4066 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4067 '`','F','o','n','t','`',0};
4069 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4070 if (rc
!= ERROR_SUCCESS
)
4072 TRACE("MSI_DatabaseOpenViewW failed: %d\n", rc
);
4073 return ERROR_SUCCESS
;
4076 MSI_IterateRecords(view
, NULL
, ITERATE_RegisterFonts
, package
);
4077 msiobj_release(&view
->hdr
);
4079 return ERROR_SUCCESS
;
4082 static UINT
ITERATE_PublishComponent(MSIRECORD
*rec
, LPVOID param
)
4084 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4085 LPCWSTR compgroupid
=NULL
;
4086 LPCWSTR feature
=NULL
;
4087 LPCWSTR text
= NULL
;
4088 LPCWSTR qualifier
= NULL
;
4089 LPCWSTR component
= NULL
;
4090 LPWSTR advertise
= NULL
;
4091 LPWSTR output
= NULL
;
4093 UINT rc
= ERROR_SUCCESS
;
4098 component
= MSI_RecordGetString(rec
,3);
4099 comp
= get_loaded_component(package
,component
);
4101 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
) &&
4102 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_SOURCE
) &&
4103 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ADVERTISED
))
4105 TRACE("Skipping: Component %s not scheduled for install\n",
4106 debugstr_w(component
));
4108 return ERROR_SUCCESS
;
4111 compgroupid
= MSI_RecordGetString(rec
,1);
4112 qualifier
= MSI_RecordGetString(rec
,2);
4114 rc
= MSIREG_OpenUserComponentsKey(compgroupid
, &hkey
, TRUE
);
4115 if (rc
!= ERROR_SUCCESS
)
4118 text
= MSI_RecordGetString(rec
,4);
4119 feature
= MSI_RecordGetString(rec
,5);
4121 advertise
= create_component_advertise_string(package
, comp
, feature
);
4123 sz
= strlenW(advertise
);
4126 sz
+= lstrlenW(text
);
4129 sz
*= sizeof(WCHAR
);
4131 output
= msi_alloc_zero(sz
);
4132 strcpyW(output
,advertise
);
4133 msi_free(advertise
);
4136 strcatW(output
,text
);
4138 msi_reg_set_val_multi_str( hkey
, qualifier
, output
);
4145 uirow
= MSI_CreateRecord( 2 );
4146 MSI_RecordSetStringW( uirow
, 1, compgroupid
);
4147 MSI_RecordSetStringW( uirow
, 2, qualifier
);
4148 ui_actiondata( package
, szPublishComponents
, uirow
);
4149 msiobj_release( &uirow
->hdr
);
4150 /* FIXME: call ui_progress? */
4156 * At present I am ignorning the advertised components part of this and only
4157 * focusing on the qualified component sets
4159 static UINT
ACTION_PublishComponents(MSIPACKAGE
*package
)
4163 static const WCHAR ExecSeqQuery
[] =
4164 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4165 '`','P','u','b','l','i','s','h',
4166 'C','o','m','p','o','n','e','n','t','`',0};
4168 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4169 if (rc
!= ERROR_SUCCESS
)
4170 return ERROR_SUCCESS
;
4172 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_PublishComponent
, package
);
4173 msiobj_release(&view
->hdr
);
4178 static UINT
msi_unimplemented_action_stub( MSIPACKAGE
*package
,
4179 LPCSTR action
, LPCWSTR table
)
4181 static const WCHAR query
[] = {
4182 'S','E','L','E','C','T',' ','*',' ',
4183 'F','R','O','M',' ','`','%','s','`',0 };
4184 MSIQUERY
*view
= NULL
;
4188 r
= MSI_OpenQuery( package
->db
, &view
, query
, table
);
4189 if (r
== ERROR_SUCCESS
)
4191 r
= MSI_IterateRecords(view
, &count
, NULL
, package
);
4192 msiobj_release(&view
->hdr
);
4196 FIXME("%s -> %lu ignored %s table values\n",
4197 action
, count
, debugstr_w(table
));
4199 return ERROR_SUCCESS
;
4202 static UINT
ACTION_AllocateRegistrySpace( MSIPACKAGE
*package
)
4204 TRACE("%p\n", package
);
4205 return ERROR_SUCCESS
;
4208 static UINT
ACTION_RemoveIniValues( MSIPACKAGE
*package
)
4210 static const WCHAR table
[] =
4211 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
4212 return msi_unimplemented_action_stub( package
, "RemoveIniValues", table
);
4215 static UINT
ACTION_MoveFiles( MSIPACKAGE
*package
)
4217 static const WCHAR table
[] = { 'M','o','v','e','F','i','l','e',0 };
4218 return msi_unimplemented_action_stub( package
, "MoveFiles", table
);
4221 static UINT
ACTION_PatchFiles( MSIPACKAGE
*package
)
4223 static const WCHAR table
[] = { 'P','a','t','c','h',0 };
4224 return msi_unimplemented_action_stub( package
, "PatchFiles", table
);
4227 static UINT
ACTION_BindImage( MSIPACKAGE
*package
)
4229 static const WCHAR table
[] = { 'B','i','n','d','I','m','a','g','e',0 };
4230 return msi_unimplemented_action_stub( package
, "BindImage", table
);
4233 static UINT
ACTION_IsolateComponents( MSIPACKAGE
*package
)
4235 static const WCHAR table
[] = {
4236 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
4237 return msi_unimplemented_action_stub( package
, "IsolateComponents", table
);
4240 static UINT
ACTION_MigrateFeatureStates( MSIPACKAGE
*package
)
4242 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
4243 return msi_unimplemented_action_stub( package
, "MigrateFeatureStates", table
);
4246 static UINT
ACTION_SelfUnregModules( MSIPACKAGE
*package
)
4248 static const WCHAR table
[] = { 'S','e','l','f','R','e','g',0 };
4249 return msi_unimplemented_action_stub( package
, "SelfUnregModules", table
);
4252 static UINT
ACTION_InstallServices( MSIPACKAGE
*package
)
4254 static const WCHAR table
[] = {
4255 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0 };
4256 return msi_unimplemented_action_stub( package
, "InstallServices", table
);
4259 static UINT
ACTION_StartServices( MSIPACKAGE
*package
)
4261 static const WCHAR table
[] = {
4262 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4263 return msi_unimplemented_action_stub( package
, "StartServices", table
);
4266 static UINT
ACTION_StopServices( MSIPACKAGE
*package
)
4268 static const WCHAR table
[] = {
4269 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4270 return msi_unimplemented_action_stub( package
, "StopServices", table
);
4273 static UINT
ACTION_DeleteServices( MSIPACKAGE
*package
)
4275 static const WCHAR table
[] = {
4276 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4277 return msi_unimplemented_action_stub( package
, "DeleteServices", table
);
4280 static UINT
ACTION_WriteEnvironmentStrings( MSIPACKAGE
*package
)
4282 static const WCHAR table
[] = {
4283 'E','n','v','i','r','o','n','m','e','n','t',0 };
4284 return msi_unimplemented_action_stub( package
, "WriteEnvironmentStrings", table
);
4287 static UINT
ACTION_RemoveEnvironmentStrings( MSIPACKAGE
*package
)
4289 static const WCHAR table
[] = {
4290 'E','n','v','i','r','o','n','m','e','n','t',0 };
4291 return msi_unimplemented_action_stub( package
, "RemoveEnvironmentStrings", table
);
4294 static UINT
ACTION_MsiPublishAssemblies( MSIPACKAGE
*package
)
4296 static const WCHAR table
[] = {
4297 'M','s','i','A','s','s','e','m','b','l','y',0 };
4298 return msi_unimplemented_action_stub( package
, "MsiPublishAssemblies", table
);
4301 static UINT
ACTION_MsiUnpublishAssemblies( MSIPACKAGE
*package
)
4303 static const WCHAR table
[] = {
4304 'M','s','i','A','s','s','e','m','b','l','y',0 };
4305 return msi_unimplemented_action_stub( package
, "MsiUnpublishAssemblies", table
);
4308 static UINT
ACTION_UnregisterFonts( MSIPACKAGE
*package
)
4310 static const WCHAR table
[] = { 'F','o','n','t',0 };
4311 return msi_unimplemented_action_stub( package
, "UnregisterFonts", table
);
4314 static UINT
ACTION_CCPSearch( MSIPACKAGE
*package
)
4316 static const WCHAR table
[] = { 'C','C','P','S','e','a','r','c','h',0 };
4317 return msi_unimplemented_action_stub( package
, "CCPSearch", table
);
4320 static UINT
ACTION_RMCCPSearch( MSIPACKAGE
*package
)
4322 static const WCHAR table
[] = { 'C','C','P','S','e','a','r','c','h',0 };
4323 return msi_unimplemented_action_stub( package
, "RMCCPSearch", table
);
4326 static UINT
ACTION_RegisterComPlus( MSIPACKAGE
*package
)
4328 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
4329 return msi_unimplemented_action_stub( package
, "RegisterComPlus", table
);
4332 static UINT
ACTION_UnregisterComPlus( MSIPACKAGE
*package
)
4334 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
4335 return msi_unimplemented_action_stub( package
, "UnregisterComPlus", table
);
4338 static struct _actions StandardActions
[] = {
4339 { szAllocateRegistrySpace
, ACTION_AllocateRegistrySpace
},
4340 { szAppSearch
, ACTION_AppSearch
},
4341 { szBindImage
, ACTION_BindImage
},
4342 { szCCPSearch
, ACTION_CCPSearch
},
4343 { szCostFinalize
, ACTION_CostFinalize
},
4344 { szCostInitialize
, ACTION_CostInitialize
},
4345 { szCreateFolders
, ACTION_CreateFolders
},
4346 { szCreateShortcuts
, ACTION_CreateShortcuts
},
4347 { szDeleteServices
, ACTION_DeleteServices
},
4348 { szDisableRollback
, NULL
},
4349 { szDuplicateFiles
, ACTION_DuplicateFiles
},
4350 { szExecuteAction
, ACTION_ExecuteAction
},
4351 { szFileCost
, ACTION_FileCost
},
4352 { szFindRelatedProducts
, ACTION_FindRelatedProducts
},
4353 { szForceReboot
, ACTION_ForceReboot
},
4354 { szInstallAdminPackage
, NULL
},
4355 { szInstallExecute
, ACTION_InstallExecute
},
4356 { szInstallExecuteAgain
, ACTION_InstallExecute
},
4357 { szInstallFiles
, ACTION_InstallFiles
},
4358 { szInstallFinalize
, ACTION_InstallFinalize
},
4359 { szInstallInitialize
, ACTION_InstallInitialize
},
4360 { szInstallSFPCatalogFile
, NULL
},
4361 { szInstallValidate
, ACTION_InstallValidate
},
4362 { szIsolateComponents
, ACTION_IsolateComponents
},
4363 { szLaunchConditions
, ACTION_LaunchConditions
},
4364 { szMigrateFeatureStates
, ACTION_MigrateFeatureStates
},
4365 { szMoveFiles
, ACTION_MoveFiles
},
4366 { szMsiPublishAssemblies
, ACTION_MsiPublishAssemblies
},
4367 { szMsiUnpublishAssemblies
, ACTION_MsiUnpublishAssemblies
},
4368 { szInstallODBC
, NULL
},
4369 { szInstallServices
, ACTION_InstallServices
},
4370 { szPatchFiles
, ACTION_PatchFiles
},
4371 { szProcessComponents
, ACTION_ProcessComponents
},
4372 { szPublishComponents
, ACTION_PublishComponents
},
4373 { szPublishFeatures
, ACTION_PublishFeatures
},
4374 { szPublishProduct
, ACTION_PublishProduct
},
4375 { szRegisterClassInfo
, ACTION_RegisterClassInfo
},
4376 { szRegisterComPlus
, ACTION_RegisterComPlus
},
4377 { szRegisterExtensionInfo
, ACTION_RegisterExtensionInfo
},
4378 { szRegisterFonts
, ACTION_RegisterFonts
},
4379 { szRegisterMIMEInfo
, ACTION_RegisterMIMEInfo
},
4380 { szRegisterProduct
, ACTION_RegisterProduct
},
4381 { szRegisterProgIdInfo
, ACTION_RegisterProgIdInfo
},
4382 { szRegisterTypeLibraries
, ACTION_RegisterTypeLibraries
},
4383 { szRegisterUser
, ACTION_RegisterUser
},
4384 { szRemoveDuplicateFiles
, NULL
},
4385 { szRemoveEnvironmentStrings
, ACTION_RemoveEnvironmentStrings
},
4386 { szRemoveExistingProducts
, NULL
},
4387 { szRemoveFiles
, ACTION_RemoveFiles
},
4388 { szRemoveFolders
, NULL
},
4389 { szRemoveIniValues
, ACTION_RemoveIniValues
},
4390 { szRemoveODBC
, NULL
},
4391 { szRemoveRegistryValues
, NULL
},
4392 { szRemoveShortcuts
, NULL
},
4393 { szResolveSource
, ACTION_ResolveSource
},
4394 { szRMCCPSearch
, ACTION_RMCCPSearch
},
4395 { szScheduleReboot
, NULL
},
4396 { szSelfRegModules
, ACTION_SelfRegModules
},
4397 { szSelfUnregModules
, ACTION_SelfUnregModules
},
4398 { szSetODBCFolders
, NULL
},
4399 { szStartServices
, ACTION_StartServices
},
4400 { szStopServices
, ACTION_StopServices
},
4401 { szUnpublishComponents
, NULL
},
4402 { szUnpublishFeatures
, NULL
},
4403 { szUnregisterClassInfo
, NULL
},
4404 { szUnregisterComPlus
, ACTION_UnregisterComPlus
},
4405 { szUnregisterExtensionInfo
, NULL
},
4406 { szUnregisterFonts
, ACTION_UnregisterFonts
},
4407 { szUnregisterMIMEInfo
, NULL
},
4408 { szUnregisterProgIdInfo
, NULL
},
4409 { szUnregisterTypeLibraries
, NULL
},
4410 { szValidateProductID
, NULL
},
4411 { szWriteEnvironmentStrings
, ACTION_WriteEnvironmentStrings
},
4412 { szWriteIniValues
, ACTION_WriteIniValues
},
4413 { szWriteRegistryValues
, ACTION_WriteRegistryValues
},