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
39 #include "wine/debug.h"
44 #include "wine/unicode.h"
47 #define REG_PROGRESS_VALUE 13200
48 #define COMPONENT_PROGRESS_VALUE 24000
50 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
55 static UINT
ACTION_ProcessExecSequence(MSIPACKAGE
*package
, BOOL UIran
);
56 static UINT
ACTION_ProcessUISequence(MSIPACKAGE
*package
);
57 static UINT
ACTION_PerformActionSequence(MSIPACKAGE
*package
, UINT seq
, BOOL UI
);
60 * consts and values used
62 static const WCHAR c_colon
[] = {'C',':','\\',0};
64 static const WCHAR szCreateFolders
[] =
65 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
66 static const WCHAR szCostFinalize
[] =
67 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
68 const WCHAR szInstallFiles
[] =
69 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
70 const WCHAR szDuplicateFiles
[] =
71 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
72 static const WCHAR szWriteRegistryValues
[] =
73 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
74 'V','a','l','u','e','s',0};
75 static const WCHAR szCostInitialize
[] =
76 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
77 static const WCHAR szFileCost
[] =
78 {'F','i','l','e','C','o','s','t',0};
79 static const WCHAR szInstallInitialize
[] =
80 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
81 static const WCHAR szInstallValidate
[] =
82 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
83 static const WCHAR szLaunchConditions
[] =
84 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
85 static const WCHAR szProcessComponents
[] =
86 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
87 static const WCHAR szRegisterTypeLibraries
[] =
88 {'R','e','g','i','s','t','e','r','T','y','p','e',
89 'L','i','b','r','a','r','i','e','s',0};
90 const WCHAR szRegisterClassInfo
[] =
91 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
92 const WCHAR szRegisterProgIdInfo
[] =
93 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
94 static const WCHAR szCreateShortcuts
[] =
95 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
96 static const WCHAR szPublishProduct
[] =
97 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
98 static const WCHAR szWriteIniValues
[] =
99 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
100 static const WCHAR szSelfRegModules
[] =
101 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
102 static const WCHAR szPublishFeatures
[] =
103 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
104 static const WCHAR szRegisterProduct
[] =
105 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
106 static const WCHAR szInstallExecute
[] =
107 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
108 static const WCHAR szInstallExecuteAgain
[] =
109 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
110 'A','g','a','i','n',0};
111 static const WCHAR szInstallFinalize
[] =
112 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
113 static const WCHAR szForceReboot
[] =
114 {'F','o','r','c','e','R','e','b','o','o','t',0};
115 static const WCHAR szResolveSource
[] =
116 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
117 static const WCHAR szAppSearch
[] =
118 {'A','p','p','S','e','a','r','c','h',0};
119 static const WCHAR szAllocateRegistrySpace
[] =
120 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
121 'S','p','a','c','e',0};
122 static const WCHAR szBindImage
[] =
123 {'B','i','n','d','I','m','a','g','e',0};
124 static const WCHAR szCCPSearch
[] =
125 {'C','C','P','S','e','a','r','c','h',0};
126 static const WCHAR szDeleteServices
[] =
127 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
128 static const WCHAR szDisableRollback
[] =
129 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
130 static const WCHAR szExecuteAction
[] =
131 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
132 const WCHAR szFindRelatedProducts
[] =
133 {'F','i','n','d','R','e','l','a','t','e','d',
134 'P','r','o','d','u','c','t','s',0};
135 static const WCHAR szInstallAdminPackage
[] =
136 {'I','n','s','t','a','l','l','A','d','m','i','n',
137 'P','a','c','k','a','g','e',0};
138 static const WCHAR szInstallSFPCatalogFile
[] =
139 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
141 static const WCHAR szIsolateComponents
[] =
142 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
143 const WCHAR szMigrateFeatureStates
[] =
144 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
145 'S','t','a','t','e','s',0};
146 const WCHAR szMoveFiles
[] =
147 {'M','o','v','e','F','i','l','e','s',0};
148 static const WCHAR szMsiPublishAssemblies
[] =
149 {'M','s','i','P','u','b','l','i','s','h',
150 'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szMsiUnpublishAssemblies
[] =
152 {'M','s','i','U','n','p','u','b','l','i','s','h',
153 'A','s','s','e','m','b','l','i','e','s',0};
154 static const WCHAR szInstallODBC
[] =
155 {'I','n','s','t','a','l','l','O','D','B','C',0};
156 static const WCHAR szInstallServices
[] =
157 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
158 const WCHAR szPatchFiles
[] =
159 {'P','a','t','c','h','F','i','l','e','s',0};
160 static const WCHAR szPublishComponents
[] =
161 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
162 static const WCHAR szRegisterComPlus
[] =
163 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
164 const WCHAR szRegisterExtensionInfo
[] =
165 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
167 static const WCHAR szRegisterFonts
[] =
168 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
169 const WCHAR szRegisterMIMEInfo
[] =
170 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
171 static const WCHAR szRegisterUser
[] =
172 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
173 const WCHAR szRemoveDuplicateFiles
[] =
174 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
175 'F','i','l','e','s',0};
176 static const WCHAR szRemoveEnvironmentStrings
[] =
177 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
178 'S','t','r','i','n','g','s',0};
179 const WCHAR szRemoveExistingProducts
[] =
180 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
181 'P','r','o','d','u','c','t','s',0};
182 const WCHAR szRemoveFiles
[] =
183 {'R','e','m','o','v','e','F','i','l','e','s',0};
184 static const WCHAR szRemoveFolders
[] =
185 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
186 static const WCHAR szRemoveIniValues
[] =
187 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
188 static const WCHAR szRemoveODBC
[] =
189 {'R','e','m','o','v','e','O','D','B','C',0};
190 static const WCHAR szRemoveRegistryValues
[] =
191 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
192 'V','a','l','u','e','s',0};
193 static const WCHAR szRemoveShortcuts
[] =
194 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
195 static const WCHAR szRMCCPSearch
[] =
196 {'R','M','C','C','P','S','e','a','r','c','h',0};
197 static const WCHAR szScheduleReboot
[] =
198 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
199 static const WCHAR szSelfUnregModules
[] =
200 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
201 static const WCHAR szSetODBCFolders
[] =
202 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
203 static const WCHAR szStartServices
[] =
204 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
205 static const WCHAR szStopServices
[] =
206 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
207 static const WCHAR szUnpublishComponents
[] =
208 {'U','n','p','u','b','l','i','s','h',
209 'C','o','m','p','o','n','e','n','t','s',0};
210 static const WCHAR szUnpublishFeatures
[] =
211 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
212 const WCHAR szUnregisterClassInfo
[] =
213 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
215 static const WCHAR szUnregisterComPlus
[] =
216 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
217 const WCHAR szUnregisterExtensionInfo
[] =
218 {'U','n','r','e','g','i','s','t','e','r',
219 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
220 static const WCHAR szUnregisterFonts
[] =
221 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
222 const WCHAR szUnregisterMIMEInfo
[] =
223 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
224 const WCHAR szUnregisterProgIdInfo
[] =
225 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
227 static const WCHAR szUnregisterTypeLibraries
[] =
228 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
229 'L','i','b','r','a','r','i','e','s',0};
230 static const WCHAR szValidateProductID
[] =
231 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
232 static const WCHAR szWriteEnvironmentStrings
[] =
233 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
234 'S','t','r','i','n','g','s',0};
236 /* action handlers */
237 typedef UINT (*STANDARDACTIONHANDLER
)(MSIPACKAGE
*);
241 STANDARDACTIONHANDLER handler
;
244 static struct _actions StandardActions
[];
247 /********************************************************
249 ********************************************************/
251 static void ui_actionstart(MSIPACKAGE
*package
, LPCWSTR action
)
253 static const WCHAR Query_t
[] =
254 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
255 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
256 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
257 ' ','\'','%','s','\'',0};
260 row
= MSI_QueryGetRecord( package
->db
, Query_t
, action
);
263 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONSTART
, row
);
264 msiobj_release(&row
->hdr
);
267 static void ui_actioninfo(MSIPACKAGE
*package
, LPCWSTR action
, BOOL start
,
271 static const WCHAR template_s
[]=
272 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
274 static const WCHAR template_e
[]=
275 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
276 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
278 static const WCHAR format
[] =
279 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
283 GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, NULL
, format
, timet
, 0x100);
285 sprintfW(message
,template_s
,timet
,action
);
287 sprintfW(message
,template_e
,timet
,action
,rc
);
289 row
= MSI_CreateRecord(1);
290 MSI_RecordSetStringW(row
,1,message
);
292 MSI_ProcessMessage(package
, INSTALLMESSAGE_INFO
, row
);
293 msiobj_release(&row
->hdr
);
296 static UINT
msi_parse_command_line( MSIPACKAGE
*package
, LPCWSTR szCommandLine
)
301 LPWSTR prop
= NULL
, val
= NULL
;
304 return ERROR_SUCCESS
;
316 TRACE("Looking at %s\n",debugstr_w(ptr
));
318 ptr2
= strchrW(ptr
,'=');
321 ERR("command line contains unknown string : %s\n", debugstr_w(ptr
));
328 prop
= msi_alloc((len
+1)*sizeof(WCHAR
));
329 memcpy(prop
,ptr
,len
*sizeof(WCHAR
));
335 while (*ptr
&& (quote
|| (!quote
&& *ptr
!=' ')))
348 val
= msi_alloc((len
+1)*sizeof(WCHAR
));
349 memcpy(val
,ptr2
,len
*sizeof(WCHAR
));
352 if (lstrlenW(prop
) > 0)
354 TRACE("Found commandline property (%s) = (%s)\n",
355 debugstr_w(prop
), debugstr_w(val
));
356 MSI_SetPropertyW(package
,prop
,val
);
362 return ERROR_SUCCESS
;
366 static LPWSTR
* msi_split_string( LPCWSTR str
, WCHAR sep
)
369 LPWSTR p
, *ret
= NULL
;
375 /* count the number of substrings */
376 for ( pc
= str
, count
= 0; pc
; count
++ )
378 pc
= strchrW( pc
, sep
);
383 /* allocate space for an array of substring pointers and the substrings */
384 ret
= msi_alloc( (count
+1) * sizeof (LPWSTR
) +
385 (lstrlenW(str
)+1) * sizeof(WCHAR
) );
389 /* copy the string and set the pointers */
390 p
= (LPWSTR
) &ret
[count
+1];
392 for( count
= 0; (ret
[count
] = p
); count
++ )
394 p
= strchrW( p
, sep
);
402 static UINT
msi_check_transform_applicable( MSIPACKAGE
*package
, IStorage
*patch
)
404 WCHAR szProductCode
[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
405 LPWSTR prod_code
, patch_product
;
408 prod_code
= msi_dup_property( package
, szProductCode
);
409 patch_product
= msi_get_suminfo_product( patch
);
411 TRACE("db = %s patch = %s\n", debugstr_w(prod_code
), debugstr_w(patch_product
));
413 if ( strstrW( patch_product
, prod_code
) )
416 ret
= ERROR_FUNCTION_FAILED
;
418 msi_free( patch_product
);
419 msi_free( prod_code
);
424 static UINT
msi_apply_substorage_transform( MSIPACKAGE
*package
,
425 MSIDATABASE
*patch_db
, LPCWSTR name
)
427 UINT ret
= ERROR_FUNCTION_FAILED
;
428 IStorage
*stg
= NULL
;
431 TRACE("%p %s\n", package
, debugstr_w(name
) );
435 ERR("expected a colon in %s\n", debugstr_w(name
));
436 return ERROR_FUNCTION_FAILED
;
439 r
= IStorage_OpenStorage( patch_db
->storage
, name
, NULL
, STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
442 ret
= msi_check_transform_applicable( package
, stg
);
443 if (ret
== ERROR_SUCCESS
)
444 msi_table_apply_transform( package
->db
, stg
);
446 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name
));
447 IStorage_Release( stg
);
450 ERR("failed to open substorage %s\n", debugstr_w(name
));
452 return ERROR_SUCCESS
;
455 static UINT
msi_check_patch_applicable( MSIPACKAGE
*package
, MSISUMMARYINFO
*si
)
457 static const WCHAR szProdID
[] = { 'P','r','o','d','u','c','t','I','D',0 };
458 LPWSTR guid_list
, *guids
, product_id
;
459 UINT i
, ret
= ERROR_FUNCTION_FAILED
;
461 product_id
= msi_dup_property( package
, szProdID
);
464 /* FIXME: the property ProductID should be written into the DB somewhere */
465 ERR("no product ID to check\n");
466 return ERROR_SUCCESS
;
469 guid_list
= msi_suminfo_dup_string( si
, PID_TEMPLATE
);
470 guids
= msi_split_string( guid_list
, ';' );
471 for ( i
= 0; guids
[i
] && ret
!= ERROR_SUCCESS
; i
++ )
473 if (!lstrcmpW( guids
[i
], product_id
))
477 msi_free( guid_list
);
478 msi_free( product_id
);
483 static UINT
msi_parse_patch_summary( MSIPACKAGE
*package
, MSIDATABASE
*patch_db
)
486 LPWSTR str
, *substorage
;
487 UINT i
, r
= ERROR_SUCCESS
;
489 si
= MSI_GetSummaryInformationW( patch_db
->storage
, 0 );
491 return ERROR_FUNCTION_FAILED
;
493 msi_check_patch_applicable( package
, si
);
495 /* enumerate the substorage */
496 str
= msi_suminfo_dup_string( si
, PID_LASTAUTHOR
);
497 substorage
= msi_split_string( str
, ';' );
498 for ( i
= 0; substorage
&& substorage
[i
] && r
== ERROR_SUCCESS
; i
++ )
499 r
= msi_apply_substorage_transform( package
, patch_db
, substorage
[i
] );
500 msi_free( substorage
);
503 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
505 msiobj_release( &si
->hdr
);
510 static UINT
msi_apply_patch_package( MSIPACKAGE
*package
, LPCWSTR file
)
512 MSIDATABASE
*patch_db
= NULL
;
515 TRACE("%p %s\n", package
, debugstr_w( file
) );
518 * We probably want to make sure we only open a patch collection here.
519 * Patch collections (.msp) and databases (.msi) have different GUIDs
520 * but currently MSI_OpenDatabaseW will accept both.
522 r
= MSI_OpenDatabaseW( file
, MSIDBOPEN_READONLY
, &patch_db
);
523 if ( r
!= ERROR_SUCCESS
)
525 ERR("failed to open patch collection %s\n", debugstr_w( file
) );
529 msi_parse_patch_summary( package
, patch_db
);
532 * There might be a CAB file in the patch package,
533 * so append it to the list of storage to search for streams.
535 append_storage_to_db( package
->db
, patch_db
->storage
);
537 msiobj_release( &patch_db
->hdr
);
539 return ERROR_SUCCESS
;
542 /* get the PATCH property, and apply all the patches it specifies */
543 static UINT
msi_apply_patches( MSIPACKAGE
*package
)
545 static const WCHAR szPatch
[] = { 'P','A','T','C','H',0 };
546 LPWSTR patch_list
, *patches
;
547 UINT i
, r
= ERROR_SUCCESS
;
549 patch_list
= msi_dup_property( package
, szPatch
);
551 TRACE("patches to be applied: %s\n", debugstr_w( patch_list
) );
553 patches
= msi_split_string( patch_list
, ';' );
554 for( i
=0; patches
&& patches
[i
] && r
== ERROR_SUCCESS
; i
++ )
555 r
= msi_apply_patch_package( package
, patches
[i
] );
558 msi_free( patch_list
);
563 static UINT
msi_apply_transforms( MSIPACKAGE
*package
)
565 static const WCHAR szTransforms
[] = {
566 'T','R','A','N','S','F','O','R','M','S',0 };
567 LPWSTR xform_list
, *xforms
;
568 UINT i
, r
= ERROR_SUCCESS
;
570 xform_list
= msi_dup_property( package
, szTransforms
);
571 xforms
= msi_split_string( xform_list
, ';' );
573 for( i
=0; xforms
&& xforms
[i
] && r
== ERROR_SUCCESS
; i
++ )
575 if (xforms
[i
][0] == ':')
576 r
= msi_apply_substorage_transform( package
, package
->db
, &xforms
[i
][1] );
578 r
= MSI_DatabaseApplyTransformW( package
->db
, xforms
[i
], 0 );
582 msi_free( xform_list
);
587 /****************************************************
588 * TOP level entry points
589 *****************************************************/
591 UINT
MSI_InstallPackage( MSIPACKAGE
*package
, LPCWSTR szPackagePath
,
592 LPCWSTR szCommandLine
)
596 static const WCHAR szUILevel
[] = {'U','I','L','e','v','e','l',0};
597 static const WCHAR szAction
[] = {'A','C','T','I','O','N',0};
598 static const WCHAR szInstall
[] = {'I','N','S','T','A','L','L',0};
600 MSI_SetPropertyW(package
, szAction
, szInstall
);
602 package
->script
= msi_alloc_zero(sizeof(MSISCRIPT
));
604 package
->script
->InWhatSequence
= SEQUENCE_INSTALL
;
608 LPWSTR p
, check
, path
;
610 path
= strdupW(szPackagePath
);
611 p
= strrchrW(path
,'\\');
620 path
= msi_alloc(MAX_PATH
*sizeof(WCHAR
));
621 GetCurrentDirectoryW(MAX_PATH
,path
);
625 check
= msi_dup_property( package
, cszSourceDir
);
627 MSI_SetPropertyW(package
, cszSourceDir
, path
);
630 check
= msi_dup_property( package
, cszSOURCEDIR
);
632 MSI_SetPropertyW(package
, cszSOURCEDIR
, path
);
634 msi_free( package
->PackagePath
);
635 package
->PackagePath
= path
;
640 msi_parse_command_line( package
, szCommandLine
);
642 msi_apply_transforms( package
);
643 msi_apply_patches( package
);
645 if ( msi_get_property_int(package
, szUILevel
, 0) >= INSTALLUILEVEL_REDUCED
)
647 package
->script
->InWhatSequence
|= SEQUENCE_UI
;
648 rc
= ACTION_ProcessUISequence(package
);
650 if (rc
== ERROR_SUCCESS
)
652 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
653 rc
= ACTION_ProcessExecSequence(package
,TRUE
);
657 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
661 /* install was halted but should be considered a success */
665 package
->script
->CurrentlyScripting
= FALSE
;
667 /* process the ending type action */
668 if (rc
== ERROR_SUCCESS
)
669 ACTION_PerformActionSequence(package
,-1,ui
);
670 else if (rc
== ERROR_INSTALL_USEREXIT
)
671 ACTION_PerformActionSequence(package
,-2,ui
);
672 else if (rc
== ERROR_INSTALL_SUSPEND
)
673 ACTION_PerformActionSequence(package
,-4,ui
);
675 ACTION_PerformActionSequence(package
,-3,ui
);
677 /* finish up running custom actions */
678 ACTION_FinishCustomActions(package
);
683 static UINT
ACTION_PerformActionSequence(MSIPACKAGE
*package
, UINT seq
, BOOL UI
)
685 UINT rc
= ERROR_SUCCESS
;
687 static const WCHAR ExecSeqQuery
[] =
688 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
689 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
690 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
691 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
693 static const WCHAR UISeqQuery
[] =
694 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
695 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
696 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
697 ' ', '=',' ','%','i',0};
700 row
= MSI_QueryGetRecord(package
->db
, UISeqQuery
, seq
);
702 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
, seq
);
706 LPCWSTR action
, cond
;
708 TRACE("Running the actions\n");
710 /* check conditions */
711 cond
= MSI_RecordGetString(row
,2);
713 /* this is a hack to skip errors in the condition code */
714 if (MSI_EvaluateConditionW(package
, cond
) == MSICONDITION_FALSE
)
717 action
= MSI_RecordGetString(row
,1);
720 ERR("failed to fetch action\n");
721 rc
= ERROR_FUNCTION_FAILED
;
726 rc
= ACTION_PerformUIAction(package
,action
);
728 rc
= ACTION_PerformAction(package
,action
,FALSE
);
730 msiobj_release(&row
->hdr
);
741 } iterate_action_param
;
743 static UINT
ITERATE_Actions(MSIRECORD
*row
, LPVOID param
)
745 iterate_action_param
*iap
= (iterate_action_param
*)param
;
747 LPCWSTR cond
, action
;
749 action
= MSI_RecordGetString(row
,1);
752 ERR("Error is retrieving action name\n");
753 return ERROR_FUNCTION_FAILED
;
756 /* check conditions */
757 cond
= MSI_RecordGetString(row
,2);
759 /* this is a hack to skip errors in the condition code */
760 if (MSI_EvaluateConditionW(iap
->package
, cond
) == MSICONDITION_FALSE
)
762 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action
));
763 return ERROR_SUCCESS
;
767 rc
= ACTION_PerformUIAction(iap
->package
,action
);
769 rc
= ACTION_PerformAction(iap
->package
,action
,FALSE
);
771 msi_dialog_check_messages( NULL
);
773 if (iap
->package
->CurrentInstallState
!= ERROR_SUCCESS
)
774 rc
= iap
->package
->CurrentInstallState
;
776 if (rc
== ERROR_FUNCTION_NOT_CALLED
)
779 if (rc
!= ERROR_SUCCESS
)
780 ERR("Execution halted, action %s returned %i\n", debugstr_w(action
), rc
);
785 UINT
MSI_Sequence( MSIPACKAGE
*package
, LPCWSTR szTable
, INT iSequenceMode
)
789 static const WCHAR query
[] =
790 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
792 ' ','W','H','E','R','E',' ',
793 '`','S','e','q','u','e','n','c','e','`',' ',
794 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
795 '`','S','e','q','u','e','n','c','e','`',0};
796 iterate_action_param iap
;
799 * FIXME: probably should be checking UILevel in the
800 * ACTION_PerformUIAction/ACTION_PerformAction
801 * rather than saving the UI level here. Those
802 * two functions can be merged too.
804 iap
.package
= package
;
807 TRACE("%p %s %i\n", package
, debugstr_w(szTable
), iSequenceMode
);
809 r
= MSI_OpenQuery( package
->db
, &view
, query
, szTable
);
810 if (r
== ERROR_SUCCESS
)
812 r
= MSI_IterateRecords( view
, NULL
, ITERATE_Actions
, &iap
);
813 msiobj_release(&view
->hdr
);
819 static UINT
ACTION_ProcessExecSequence(MSIPACKAGE
*package
, BOOL UIran
)
823 static const WCHAR ExecSeqQuery
[] =
824 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
825 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
826 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
827 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
828 'O','R','D','E','R',' ', 'B','Y',' ',
829 '`','S','e','q','u','e','n','c','e','`',0 };
831 static const WCHAR IVQuery
[] =
832 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
833 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
834 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
835 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
836 ' ','\'', 'I','n','s','t','a','l','l',
837 'V','a','l','i','d','a','t','e','\'', 0};
839 iterate_action_param iap
;
841 iap
.package
= package
;
844 if (package
->script
->ExecuteSequenceRun
)
846 TRACE("Execute Sequence already Run\n");
847 return ERROR_SUCCESS
;
850 package
->script
->ExecuteSequenceRun
= TRUE
;
852 /* get the sequence number */
855 row
= MSI_QueryGetRecord(package
->db
, IVQuery
);
857 return ERROR_FUNCTION_FAILED
;
858 seq
= MSI_RecordGetInteger(row
,1);
859 msiobj_release(&row
->hdr
);
862 rc
= MSI_OpenQuery(package
->db
, &view
, ExecSeqQuery
, seq
);
863 if (rc
== ERROR_SUCCESS
)
865 TRACE("Running the actions\n");
867 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, &iap
);
868 msiobj_release(&view
->hdr
);
874 static UINT
ACTION_ProcessUISequence(MSIPACKAGE
*package
)
878 static const WCHAR ExecSeqQuery
[] =
879 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
880 '`','I','n','s','t','a','l','l',
881 'U','I','S','e','q','u','e','n','c','e','`',
882 ' ','W','H','E','R','E',' ',
883 '`','S','e','q','u','e','n','c','e','`',' ',
884 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
885 '`','S','e','q','u','e','n','c','e','`',0};
886 iterate_action_param iap
;
888 iap
.package
= package
;
891 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
893 if (rc
== ERROR_SUCCESS
)
895 TRACE("Running the actions\n");
897 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, &iap
);
898 msiobj_release(&view
->hdr
);
904 /********************************************************
905 * ACTION helper functions and functions that perform the actions
906 *******************************************************/
907 static BOOL
ACTION_HandleStandardAction(MSIPACKAGE
*package
, LPCWSTR action
,
908 UINT
* rc
, BOOL force
)
914 if (!run
&& !package
->script
->CurrentlyScripting
)
919 if (strcmpW(action
,szInstallFinalize
) == 0 ||
920 strcmpW(action
,szInstallExecute
) == 0 ||
921 strcmpW(action
,szInstallExecuteAgain
) == 0)
926 while (StandardActions
[i
].action
!= NULL
)
928 if (strcmpW(StandardActions
[i
].action
, action
)==0)
932 ui_actioninfo(package
, action
, TRUE
, 0);
933 *rc
= schedule_action(package
,INSTALL_SCRIPT
,action
);
934 ui_actioninfo(package
, action
, FALSE
, *rc
);
938 ui_actionstart(package
, action
);
939 if (StandardActions
[i
].handler
)
941 *rc
= StandardActions
[i
].handler(package
);
945 FIXME("unhandled standard action %s\n",debugstr_w(action
));
957 static BOOL
ACTION_HandleCustomAction( MSIPACKAGE
* package
, LPCWSTR action
,
958 UINT
* rc
, BOOL force
)
963 arc
= ACTION_CustomAction(package
,action
, force
);
965 if (arc
!= ERROR_CALL_NOT_IMPLEMENTED
)
974 * A lot of actions are really important even if they don't do anything
975 * explicit... Lots of properties are set at the beginning of the installation
976 * CostFinalize does a bunch of work to translate the directories and such
978 * But until I get write access to the database that is hard, so I am going to
979 * hack it to see if I can get something to run.
981 UINT
ACTION_PerformAction(MSIPACKAGE
*package
, const WCHAR
*action
, BOOL force
)
983 UINT rc
= ERROR_SUCCESS
;
986 TRACE("Performing action (%s)\n",debugstr_w(action
));
988 handled
= ACTION_HandleStandardAction(package
, action
, &rc
, force
);
991 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, force
);
995 FIXME("unhandled msi action %s\n",debugstr_w(action
));
996 rc
= ERROR_FUNCTION_NOT_CALLED
;
1002 UINT
ACTION_PerformUIAction(MSIPACKAGE
*package
, const WCHAR
*action
)
1004 UINT rc
= ERROR_SUCCESS
;
1005 BOOL handled
= FALSE
;
1007 TRACE("Performing action (%s)\n",debugstr_w(action
));
1009 handled
= ACTION_HandleStandardAction(package
, action
, &rc
,TRUE
);
1012 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, FALSE
);
1014 if( !handled
&& ACTION_DialogBox(package
,action
) == ERROR_SUCCESS
)
1019 FIXME("unhandled msi action %s\n",debugstr_w(action
));
1020 rc
= ERROR_FUNCTION_NOT_CALLED
;
1028 * Actual Action Handlers
1031 static UINT
ITERATE_CreateFolders(MSIRECORD
*row
, LPVOID param
)
1033 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1039 dir
= MSI_RecordGetString(row
,1);
1042 ERR("Unable to get folder id\n");
1043 return ERROR_SUCCESS
;
1046 full_path
= resolve_folder(package
,dir
,FALSE
,FALSE
,&folder
);
1049 ERR("Unable to resolve folder id %s\n",debugstr_w(dir
));
1050 return ERROR_SUCCESS
;
1053 TRACE("Folder is %s\n",debugstr_w(full_path
));
1056 uirow
= MSI_CreateRecord(1);
1057 MSI_RecordSetStringW(uirow
,1,full_path
);
1058 ui_actiondata(package
,szCreateFolders
,uirow
);
1059 msiobj_release( &uirow
->hdr
);
1061 if (folder
->State
== 0)
1062 create_full_pathW(full_path
);
1066 msi_free(full_path
);
1067 return ERROR_SUCCESS
;
1070 /* FIXME: probably should merge this with the above function */
1071 static UINT
msi_create_directory( MSIPACKAGE
* package
, LPCWSTR dir
)
1073 UINT rc
= ERROR_SUCCESS
;
1075 LPWSTR install_path
;
1077 install_path
= resolve_folder(package
, dir
, FALSE
, FALSE
, &folder
);
1079 return ERROR_FUNCTION_FAILED
;
1081 /* create the path */
1082 if (folder
->State
== 0)
1084 create_full_pathW(install_path
);
1087 msi_free(install_path
);
1092 UINT
msi_create_component_directories( MSIPACKAGE
*package
)
1096 /* create all the folders required by the components are going to install */
1097 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1099 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
1101 msi_create_directory( package
, comp
->Directory
);
1104 return ERROR_SUCCESS
;
1108 * Also we cannot enable/disable components either, so for now I am just going
1109 * to do all the directories for all the components.
1111 static UINT
ACTION_CreateFolders(MSIPACKAGE
*package
)
1113 static const WCHAR ExecSeqQuery
[] =
1114 {'S','E','L','E','C','T',' ',
1115 '`','D','i','r','e','c','t','o','r','y','_','`',
1116 ' ','F','R','O','M',' ',
1117 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1121 /* create all the empty folders specified in the CreateFolder table */
1122 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
1123 if (rc
!= ERROR_SUCCESS
)
1124 return ERROR_SUCCESS
;
1126 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateFolders
, package
);
1127 msiobj_release(&view
->hdr
);
1129 msi_create_component_directories( package
);
1134 static UINT
load_component( MSIRECORD
*row
, LPVOID param
)
1136 MSIPACKAGE
*package
= param
;
1139 comp
= msi_alloc_zero( sizeof(MSICOMPONENT
) );
1141 return ERROR_FUNCTION_FAILED
;
1143 list_add_tail( &package
->components
, &comp
->entry
);
1145 /* fill in the data */
1146 comp
->Component
= msi_dup_record_field( row
, 1 );
1148 TRACE("Loading Component %s\n", debugstr_w(comp
->Component
));
1150 comp
->ComponentId
= msi_dup_record_field( row
, 2 );
1151 comp
->Directory
= msi_dup_record_field( row
, 3 );
1152 comp
->Attributes
= MSI_RecordGetInteger(row
,4);
1153 comp
->Condition
= msi_dup_record_field( row
, 5 );
1154 comp
->KeyPath
= msi_dup_record_field( row
, 6 );
1156 comp
->Installed
= INSTALLSTATE_UNKNOWN
;
1157 msi_component_set_state( comp
, INSTALLSTATE_UNKNOWN
);
1159 return ERROR_SUCCESS
;
1162 static UINT
load_all_components( MSIPACKAGE
*package
)
1164 static const WCHAR query
[] = {
1165 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1166 '`','C','o','m','p','o','n','e','n','t','`',0 };
1170 if (!list_empty(&package
->components
))
1171 return ERROR_SUCCESS
;
1173 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1174 if (r
!= ERROR_SUCCESS
)
1177 r
= MSI_IterateRecords(view
, NULL
, load_component
, package
);
1178 msiobj_release(&view
->hdr
);
1183 MSIPACKAGE
*package
;
1184 MSIFEATURE
*feature
;
1187 static UINT
add_feature_component( MSIFEATURE
*feature
, MSICOMPONENT
*comp
)
1191 cl
= msi_alloc( sizeof (*cl
) );
1193 return ERROR_NOT_ENOUGH_MEMORY
;
1194 cl
->component
= comp
;
1195 list_add_tail( &feature
->Components
, &cl
->entry
);
1197 return ERROR_SUCCESS
;
1200 static UINT
add_feature_child( MSIFEATURE
*parent
, MSIFEATURE
*child
)
1204 fl
= msi_alloc( sizeof(*fl
) );
1206 return ERROR_NOT_ENOUGH_MEMORY
;
1207 fl
->feature
= child
;
1208 list_add_tail( &parent
->Children
, &fl
->entry
);
1210 return ERROR_SUCCESS
;
1213 static UINT
iterate_load_featurecomponents(MSIRECORD
*row
, LPVOID param
)
1215 _ilfs
* ilfs
= (_ilfs
*)param
;
1219 component
= MSI_RecordGetString(row
,1);
1221 /* check to see if the component is already loaded */
1222 comp
= get_loaded_component( ilfs
->package
, component
);
1225 ERR("unknown component %s\n", debugstr_w(component
));
1226 return ERROR_FUNCTION_FAILED
;
1229 add_feature_component( ilfs
->feature
, comp
);
1230 comp
->Enabled
= TRUE
;
1232 return ERROR_SUCCESS
;
1235 static MSIFEATURE
*find_feature_by_name( MSIPACKAGE
*package
, LPCWSTR name
)
1237 MSIFEATURE
*feature
;
1239 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1241 if ( !lstrcmpW( feature
->Feature
, name
) )
1248 static UINT
load_feature(MSIRECORD
* row
, LPVOID param
)
1250 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1251 MSIFEATURE
* feature
;
1252 static const WCHAR Query1
[] =
1253 {'S','E','L','E','C','T',' ',
1254 '`','C','o','m','p','o','n','e','n','t','_','`',
1255 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1256 'C','o','m','p','o','n','e','n','t','s','`',' ',
1257 'W','H','E','R','E',' ',
1258 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1263 /* fill in the data */
1265 feature
= msi_alloc_zero( sizeof (MSIFEATURE
) );
1267 return ERROR_NOT_ENOUGH_MEMORY
;
1269 list_init( &feature
->Children
);
1270 list_init( &feature
->Components
);
1272 feature
->Feature
= msi_dup_record_field( row
, 1 );
1274 TRACE("Loading feature %s\n",debugstr_w(feature
->Feature
));
1276 feature
->Feature_Parent
= msi_dup_record_field( row
, 2 );
1277 feature
->Title
= msi_dup_record_field( row
, 3 );
1278 feature
->Description
= msi_dup_record_field( row
, 4 );
1280 if (!MSI_RecordIsNull(row
,5))
1281 feature
->Display
= MSI_RecordGetInteger(row
,5);
1283 feature
->Level
= MSI_RecordGetInteger(row
,6);
1284 feature
->Directory
= msi_dup_record_field( row
, 7 );
1285 feature
->Attributes
= MSI_RecordGetInteger(row
,8);
1287 feature
->Installed
= INSTALLSTATE_UNKNOWN
;
1288 msi_feature_set_state( feature
, INSTALLSTATE_UNKNOWN
);
1290 list_add_tail( &package
->features
, &feature
->entry
);
1292 /* load feature components */
1294 rc
= MSI_OpenQuery( package
->db
, &view
, Query1
, feature
->Feature
);
1295 if (rc
!= ERROR_SUCCESS
)
1296 return ERROR_SUCCESS
;
1298 ilfs
.package
= package
;
1299 ilfs
.feature
= feature
;
1301 MSI_IterateRecords(view
, NULL
, iterate_load_featurecomponents
, &ilfs
);
1302 msiobj_release(&view
->hdr
);
1304 return ERROR_SUCCESS
;
1307 static UINT
find_feature_children(MSIRECORD
* row
, LPVOID param
)
1309 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1310 MSIFEATURE
*parent
, *child
;
1312 child
= find_feature_by_name( package
, MSI_RecordGetString( row
, 1 ) );
1314 return ERROR_FUNCTION_FAILED
;
1316 if (!child
->Feature_Parent
)
1317 return ERROR_SUCCESS
;
1319 parent
= find_feature_by_name( package
, child
->Feature_Parent
);
1321 return ERROR_FUNCTION_FAILED
;
1323 add_feature_child( parent
, child
);
1324 return ERROR_SUCCESS
;
1327 static UINT
load_all_features( MSIPACKAGE
*package
)
1329 static const WCHAR query
[] = {
1330 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1331 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1332 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1336 if (!list_empty(&package
->features
))
1337 return ERROR_SUCCESS
;
1339 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1340 if (r
!= ERROR_SUCCESS
)
1343 r
= MSI_IterateRecords( view
, NULL
, load_feature
, package
);
1344 if (r
!= ERROR_SUCCESS
)
1347 r
= MSI_IterateRecords( view
, NULL
, find_feature_children
, package
);
1348 msiobj_release( &view
->hdr
);
1353 static LPWSTR
folder_split_path(LPWSTR p
, WCHAR ch
)
1364 static UINT
load_file(MSIRECORD
*row
, LPVOID param
)
1366 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1370 /* fill in the data */
1372 file
= msi_alloc_zero( sizeof (MSIFILE
) );
1374 return ERROR_NOT_ENOUGH_MEMORY
;
1376 file
->File
= msi_dup_record_field( row
, 1 );
1378 component
= MSI_RecordGetString( row
, 2 );
1379 file
->Component
= get_loaded_component( package
, component
);
1381 if (!file
->Component
)
1382 ERR("Unfound Component %s\n",debugstr_w(component
));
1384 file
->FileName
= msi_dup_record_field( row
, 3 );
1385 reduce_to_longfilename( file
->FileName
);
1387 file
->ShortName
= msi_dup_record_field( row
, 3 );
1388 file
->LongName
= strdupW( folder_split_path(file
->ShortName
, '|'));
1390 file
->FileSize
= MSI_RecordGetInteger( row
, 4 );
1391 file
->Version
= msi_dup_record_field( row
, 5 );
1392 file
->Language
= msi_dup_record_field( row
, 6 );
1393 file
->Attributes
= MSI_RecordGetInteger( row
, 7 );
1394 file
->Sequence
= MSI_RecordGetInteger( row
, 8 );
1396 file
->state
= msifs_invalid
;
1398 /* if the compressed bits are not set in the file attributes,
1399 * then read the information from the package word count property
1401 if (file
->Attributes
& msidbFileAttributesCompressed
)
1403 file
->IsCompressed
= TRUE
;
1405 else if (file
->Attributes
& msidbFileAttributesNoncompressed
)
1407 file
->IsCompressed
= FALSE
;
1411 file
->IsCompressed
= package
->WordCount
& MSIWORDCOUNT_COMPRESSED
;
1414 TRACE("File Loaded (%s)\n",debugstr_w(file
->File
));
1416 list_add_tail( &package
->files
, &file
->entry
);
1418 return ERROR_SUCCESS
;
1421 static UINT
load_all_files(MSIPACKAGE
*package
)
1425 static const WCHAR Query
[] =
1426 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1427 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1428 '`','S','e','q','u','e','n','c','e','`', 0};
1430 if (!list_empty(&package
->files
))
1431 return ERROR_SUCCESS
;
1433 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
1434 if (rc
!= ERROR_SUCCESS
)
1435 return ERROR_SUCCESS
;
1437 rc
= MSI_IterateRecords(view
, NULL
, load_file
, package
);
1438 msiobj_release(&view
->hdr
);
1440 return ERROR_SUCCESS
;
1445 * I am not doing any of the costing functionality yet.
1446 * Mostly looking at doing the Component and Feature loading
1448 * The native MSI does A LOT of modification to tables here. Mostly adding
1449 * a lot of temporary columns to the Feature and Component tables.
1451 * note: Native msi also tracks the short filename. But I am only going to
1452 * track the long ones. Also looking at this directory table
1453 * it appears that the directory table does not get the parents
1454 * resolved base on property only based on their entries in the
1457 static UINT
ACTION_CostInitialize(MSIPACKAGE
*package
)
1459 static const WCHAR szCosting
[] =
1460 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1461 static const WCHAR szZero
[] = { '0', 0 };
1463 if ( 1 == msi_get_property_int( package
, szCosting
, 0 ) )
1464 return ERROR_SUCCESS
;
1466 MSI_SetPropertyW(package
, szCosting
, szZero
);
1467 MSI_SetPropertyW(package
, cszRootDrive
, c_colon
);
1469 load_all_components( package
);
1470 load_all_features( package
);
1471 load_all_files( package
);
1473 return ERROR_SUCCESS
;
1476 static UINT
execute_script(MSIPACKAGE
*package
, UINT script
)
1479 UINT rc
= ERROR_SUCCESS
;
1481 TRACE("Executing Script %i\n",script
);
1483 if (!package
->script
)
1485 ERR("no script!\n");
1486 return ERROR_FUNCTION_FAILED
;
1489 for (i
= 0; i
< package
->script
->ActionCount
[script
]; i
++)
1492 action
= package
->script
->Actions
[script
][i
];
1493 ui_actionstart(package
, action
);
1494 TRACE("Executing Action (%s)\n",debugstr_w(action
));
1495 rc
= ACTION_PerformAction(package
, action
, TRUE
);
1496 if (rc
!= ERROR_SUCCESS
)
1499 msi_free_action_script(package
, script
);
1503 static UINT
ACTION_FileCost(MSIPACKAGE
*package
)
1505 return ERROR_SUCCESS
;
1508 static MSIFOLDER
*load_folder( MSIPACKAGE
*package
, LPCWSTR dir
)
1510 static const WCHAR Query
[] =
1511 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1512 '`','D','i','r','e','c', 't','o','r','y','`',' ',
1513 'W','H','E','R','E',' ', '`', 'D','i','r','e','c','t', 'o','r','y','`',
1514 ' ','=',' ','\'','%','s','\'',
1516 static const WCHAR szDot
[] = { '.',0 };
1517 static WCHAR szEmpty
[] = { 0 };
1518 LPWSTR p
, tgt_short
, tgt_long
, src_short
, src_long
;
1523 TRACE("Looking for dir %s\n",debugstr_w(dir
));
1525 folder
= get_loaded_folder( package
, dir
);
1529 TRACE("Working to load %s\n",debugstr_w(dir
));
1531 folder
= msi_alloc_zero( sizeof (MSIFOLDER
) );
1535 folder
->Directory
= strdupW(dir
);
1537 row
= MSI_QueryGetRecord(package
->db
, Query
, dir
);
1541 p
= msi_dup_record_field(row
, 3);
1543 /* split src and target dir */
1545 src_short
= folder_split_path( p
, ':' );
1547 /* split the long and short paths */
1548 tgt_long
= folder_split_path( tgt_short
, '|' );
1549 src_long
= folder_split_path( src_short
, '|' );
1551 /* check for no-op dirs */
1552 if (!lstrcmpW(szDot
, tgt_short
))
1553 tgt_short
= szEmpty
;
1554 if (!lstrcmpW(szDot
, src_short
))
1555 src_short
= szEmpty
;
1558 tgt_long
= tgt_short
;
1561 src_short
= tgt_short
;
1562 src_long
= tgt_long
;
1566 src_long
= src_short
;
1568 /* FIXME: use the target short path too */
1569 folder
->TargetDefault
= strdupW(tgt_long
);
1570 folder
->SourceShortPath
= strdupW(src_short
);
1571 folder
->SourceLongPath
= strdupW(src_long
);
1574 TRACE("TargetDefault = %s\n",debugstr_w( folder
->TargetDefault
));
1575 TRACE("SourceLong = %s\n", debugstr_w( folder
->SourceLongPath
));
1576 TRACE("SourceShort = %s\n", debugstr_w( folder
->SourceShortPath
));
1578 parent
= MSI_RecordGetString(row
, 2);
1581 folder
->Parent
= load_folder( package
, parent
);
1582 if ( folder
->Parent
)
1583 TRACE("loaded parent %p %s\n", folder
->Parent
,
1584 debugstr_w(folder
->Parent
->Directory
));
1586 ERR("failed to load parent folder %s\n", debugstr_w(parent
));
1589 folder
->Property
= msi_dup_property( package
, dir
);
1591 msiobj_release(&row
->hdr
);
1593 list_add_tail( &package
->folders
, &folder
->entry
);
1595 TRACE("%s returning %p\n",debugstr_w(dir
),folder
);
1600 static void ACTION_GetComponentInstallStates(MSIPACKAGE
*package
)
1604 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1608 if (!comp
->ComponentId
)
1611 res
= MsiGetComponentPathW( package
->ProductCode
,
1612 comp
->ComponentId
, NULL
, NULL
);
1614 res
= INSTALLSTATE_ABSENT
;
1615 comp
->Installed
= res
;
1619 /* scan for and update current install states */
1620 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE
*package
)
1623 MSIFEATURE
*feature
;
1625 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1628 INSTALLSTATE res
= INSTALLSTATE_ABSENT
;
1630 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1632 comp
= cl
->component
;
1634 if (!comp
->ComponentId
)
1636 res
= INSTALLSTATE_ABSENT
;
1640 if (res
== INSTALLSTATE_ABSENT
)
1641 res
= comp
->Installed
;
1644 if (res
== comp
->Installed
)
1647 if (res
!= INSTALLSTATE_DEFAULT
|| res
!= INSTALLSTATE_LOCAL
||
1648 res
!= INSTALLSTATE_SOURCE
)
1650 res
= INSTALLSTATE_INCOMPLETE
;
1654 feature
->Installed
= res
;
1658 static BOOL
process_state_property (MSIPACKAGE
* package
, LPCWSTR property
,
1661 static const WCHAR all
[]={'A','L','L',0};
1663 MSIFEATURE
*feature
;
1665 override
= msi_dup_property( package
, property
);
1669 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1671 if (strcmpiW(override
,all
)==0)
1672 msi_feature_set_state( feature
, state
);
1675 LPWSTR ptr
= override
;
1676 LPWSTR ptr2
= strchrW(override
,',');
1680 if ((ptr2
&& strncmpW(ptr
,feature
->Feature
, ptr2
-ptr
)==0)
1681 || (!ptr2
&& strcmpW(ptr
,feature
->Feature
)==0))
1683 msi_feature_set_state( feature
, state
);
1689 ptr2
= strchrW(ptr
,',');
1701 UINT
MSI_SetFeatureStates(MSIPACKAGE
*package
)
1704 static const WCHAR szlevel
[] =
1705 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1706 static const WCHAR szAddLocal
[] =
1707 {'A','D','D','L','O','C','A','L',0};
1708 static const WCHAR szRemove
[] =
1709 {'R','E','M','O','V','E',0};
1710 static const WCHAR szReinstall
[] =
1711 {'R','E','I','N','S','T','A','L','L',0};
1712 BOOL override
= FALSE
;
1713 MSICOMPONENT
* component
;
1714 MSIFEATURE
*feature
;
1717 /* I do not know if this is where it should happen.. but */
1719 TRACE("Checking Install Level\n");
1721 install_level
= msi_get_property_int( package
, szlevel
, 1 );
1723 /* ok here is the _real_ rub
1724 * all these activation/deactivation things happen in order and things
1725 * later on the list override things earlier on the list.
1726 * 1) INSTALLLEVEL processing
1736 * 11) FILEADDDEFAULT
1737 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1738 * ignored for all the features. seems strange, especially since it is not
1739 * documented anywhere, but it is how it works.
1741 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1742 * REMOVE are the big ones, since we don't handle administrative installs
1745 override
|= process_state_property(package
,szAddLocal
,INSTALLSTATE_LOCAL
);
1746 override
|= process_state_property(package
,szRemove
,INSTALLSTATE_ABSENT
);
1747 override
|= process_state_property(package
,szReinstall
,INSTALLSTATE_LOCAL
);
1751 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1753 BOOL feature_state
= ((feature
->Level
> 0) &&
1754 (feature
->Level
<= install_level
));
1756 if ((feature_state
) && (feature
->Action
== INSTALLSTATE_UNKNOWN
))
1758 if (feature
->Attributes
& msidbFeatureAttributesFavorSource
)
1759 msi_feature_set_state( feature
, INSTALLSTATE_SOURCE
);
1760 else if (feature
->Attributes
& msidbFeatureAttributesFavorAdvertise
)
1761 msi_feature_set_state( feature
, INSTALLSTATE_ADVERTISED
);
1763 msi_feature_set_state( feature
, INSTALLSTATE_LOCAL
);
1767 /* disable child features of unselected parent features */
1768 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1772 if (feature
->Level
> 0 && feature
->Level
<= install_level
)
1775 LIST_FOR_EACH_ENTRY( fl
, &feature
->Children
, FeatureList
, entry
)
1776 msi_feature_set_state( fl
->feature
, INSTALLSTATE_UNKNOWN
);
1781 /* set the Preselected Property */
1782 static const WCHAR szPreselected
[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1783 static const WCHAR szOne
[] = { '1', 0 };
1785 MSI_SetPropertyW(package
,szPreselected
,szOne
);
1789 * now we want to enable or disable components base on feature
1792 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1796 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1797 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
);
1799 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1801 component
= cl
->component
;
1803 if (!component
->Enabled
)
1806 if (component
->Attributes
& msidbComponentAttributesOptional
)
1807 msi_component_set_state( component
, INSTALLSTATE_DEFAULT
);
1810 if (component
->Attributes
& msidbComponentAttributesSourceOnly
)
1811 msi_component_set_state( component
, INSTALLSTATE_SOURCE
);
1813 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1816 if (component
->ForceLocalState
)
1817 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1819 if (feature
->Attributes
== msidbFeatureAttributesFavorLocal
)
1821 if (!(component
->Attributes
& msidbComponentAttributesSourceOnly
))
1822 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1824 else if (feature
->Attributes
== msidbFeatureAttributesFavorSource
)
1826 if ((component
->Action
== INSTALLSTATE_UNKNOWN
) ||
1827 (component
->Action
== INSTALLSTATE_ABSENT
) ||
1828 (component
->Action
== INSTALLSTATE_ADVERTISED
) ||
1829 (component
->Action
== INSTALLSTATE_DEFAULT
))
1830 msi_component_set_state( component
, INSTALLSTATE_SOURCE
);
1832 else if (feature
->ActionRequest
== INSTALLSTATE_ADVERTISED
)
1834 if ((component
->Action
== INSTALLSTATE_UNKNOWN
) ||
1835 (component
->Action
== INSTALLSTATE_ABSENT
))
1836 msi_component_set_state( component
, INSTALLSTATE_ADVERTISED
);
1838 else if (feature
->ActionRequest
== INSTALLSTATE_ABSENT
)
1840 if (component
->Action
== INSTALLSTATE_UNKNOWN
)
1841 msi_component_set_state( component
, INSTALLSTATE_ABSENT
);
1843 else if (feature
->ActionRequest
== INSTALLSTATE_UNKNOWN
)
1844 msi_component_set_state( component
, INSTALLSTATE_UNKNOWN
);
1846 if (component
->ForceLocalState
&& feature
->Action
== INSTALLSTATE_SOURCE
)
1847 msi_feature_set_state( feature
, INSTALLSTATE_LOCAL
);
1851 LIST_FOR_EACH_ENTRY( component
, &package
->components
, MSICOMPONENT
, entry
)
1853 if (component
->Action
== INSTALLSTATE_DEFAULT
)
1855 TRACE("%s was default, setting to local\n", debugstr_w(component
->Component
));
1856 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1859 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1860 debugstr_w(component
->Component
), component
->Installed
, component
->Action
);
1864 return ERROR_SUCCESS
;
1867 static UINT
ITERATE_CostFinalizeDirectories(MSIRECORD
*row
, LPVOID param
)
1869 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1873 name
= MSI_RecordGetString(row
,1);
1875 /* This helper function now does ALL the work */
1876 TRACE("Dir %s ...\n",debugstr_w(name
));
1877 load_folder(package
,name
);
1878 path
= resolve_folder(package
,name
,FALSE
,TRUE
,NULL
);
1879 TRACE("resolves to %s\n",debugstr_w(path
));
1882 return ERROR_SUCCESS
;
1885 static UINT
ITERATE_CostFinalizeConditions(MSIRECORD
*row
, LPVOID param
)
1887 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1889 MSIFEATURE
*feature
;
1891 name
= MSI_RecordGetString( row
, 1 );
1893 feature
= get_loaded_feature( package
, name
);
1895 ERR("FAILED to find loaded feature %s\n",debugstr_w(name
));
1899 Condition
= MSI_RecordGetString(row
,3);
1901 if (MSI_EvaluateConditionW(package
,Condition
) == MSICONDITION_TRUE
)
1903 int level
= MSI_RecordGetInteger(row
,2);
1904 TRACE("Reseting feature %s to level %i\n", debugstr_w(name
), level
);
1905 feature
->Level
= level
;
1908 return ERROR_SUCCESS
;
1911 LPWSTR
msi_get_disk_file_version( LPCWSTR filename
)
1913 static const WCHAR name_fmt
[] =
1914 {'%','u','.','%','u','.','%','u','.','%','u',0};
1915 static WCHAR name
[] = {'\\',0};
1916 VS_FIXEDFILEINFO
*lpVer
;
1917 WCHAR filever
[0x100];
1923 TRACE("%s\n", debugstr_w(filename
));
1925 versize
= GetFileVersionInfoSizeW( filename
, &handle
);
1929 version
= msi_alloc( versize
);
1930 GetFileVersionInfoW( filename
, 0, versize
, version
);
1932 VerQueryValueW( version
, name
, (LPVOID
*)&lpVer
, &sz
);
1933 msi_free( version
);
1935 sprintfW( filever
, name_fmt
,
1936 HIWORD(lpVer
->dwFileVersionMS
),
1937 LOWORD(lpVer
->dwFileVersionMS
),
1938 HIWORD(lpVer
->dwFileVersionLS
),
1939 LOWORD(lpVer
->dwFileVersionLS
));
1941 return strdupW( filever
);
1944 static UINT
msi_check_file_install_states( MSIPACKAGE
*package
)
1946 LPWSTR file_version
;
1949 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
1951 MSICOMPONENT
* comp
= file
->Component
;
1957 if (file
->IsCompressed
)
1958 comp
->ForceLocalState
= TRUE
;
1960 /* calculate target */
1961 p
= resolve_folder(package
, comp
->Directory
, FALSE
, FALSE
, NULL
);
1963 msi_free(file
->TargetPath
);
1965 TRACE("file %s is named %s\n",
1966 debugstr_w(file
->File
), debugstr_w(file
->FileName
));
1968 file
->TargetPath
= build_directory_name(2, p
, file
->FileName
);
1972 TRACE("file %s resolves to %s\n",
1973 debugstr_w(file
->File
), debugstr_w(file
->TargetPath
));
1975 /* don't check files of components that aren't installed */
1976 if (comp
->Installed
== INSTALLSTATE_UNKNOWN
||
1977 comp
->Installed
== INSTALLSTATE_ABSENT
)
1979 file
->state
= msifs_missing
; /* assume files are missing */
1983 if (GetFileAttributesW(file
->TargetPath
) == INVALID_FILE_ATTRIBUTES
)
1985 file
->state
= msifs_missing
;
1986 comp
->Cost
+= file
->FileSize
;
1987 comp
->Installed
= INSTALLSTATE_INCOMPLETE
;
1991 if (file
->Version
&&
1992 (file_version
= msi_get_disk_file_version( file
->TargetPath
)))
1994 TRACE("new %s old %s\n", debugstr_w(file
->Version
),
1995 debugstr_w(file_version
));
1996 /* FIXME: seems like a bad way to compare version numbers */
1997 if (lstrcmpiW(file_version
, file
->Version
)<0)
1999 file
->state
= msifs_overwrite
;
2000 comp
->Cost
+= file
->FileSize
;
2001 comp
->Installed
= INSTALLSTATE_INCOMPLETE
;
2004 file
->state
= msifs_present
;
2005 msi_free( file_version
);
2008 file
->state
= msifs_present
;
2011 return ERROR_SUCCESS
;
2015 * A lot is done in this function aside from just the costing.
2016 * The costing needs to be implemented at some point but for now I am going
2017 * to focus on the directory building
2020 static UINT
ACTION_CostFinalize(MSIPACKAGE
*package
)
2022 static const WCHAR ExecSeqQuery
[] =
2023 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2024 '`','D','i','r','e','c','t','o','r','y','`',0};
2025 static const WCHAR ConditionQuery
[] =
2026 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2027 '`','C','o','n','d','i','t','i','o','n','`',0};
2028 static const WCHAR szCosting
[] =
2029 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2030 static const WCHAR szlevel
[] =
2031 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2032 static const WCHAR szOne
[] = { '1', 0 };
2038 if ( 1 == msi_get_property_int( package
, szCosting
, 0 ) )
2039 return ERROR_SUCCESS
;
2041 TRACE("Building Directory properties\n");
2043 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2044 if (rc
== ERROR_SUCCESS
)
2046 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CostFinalizeDirectories
,
2048 msiobj_release(&view
->hdr
);
2051 /* read components states from the registry */
2052 ACTION_GetComponentInstallStates(package
);
2054 TRACE("File calculations\n");
2055 msi_check_file_install_states( package
);
2057 TRACE("Evaluating Condition Table\n");
2059 rc
= MSI_DatabaseOpenViewW(package
->db
, ConditionQuery
, &view
);
2060 if (rc
== ERROR_SUCCESS
)
2062 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CostFinalizeConditions
,
2064 msiobj_release(&view
->hdr
);
2067 TRACE("Enabling or Disabling Components\n");
2068 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2070 if (MSI_EvaluateConditionW(package
, comp
->Condition
) == MSICONDITION_FALSE
)
2072 TRACE("Disabling component %s\n", debugstr_w(comp
->Component
));
2073 comp
->Enabled
= FALSE
;
2077 MSI_SetPropertyW(package
,szCosting
,szOne
);
2078 /* set default run level if not set */
2079 level
= msi_dup_property( package
, szlevel
);
2081 MSI_SetPropertyW(package
,szlevel
, szOne
);
2084 ACTION_UpdateFeatureInstallStates(package
);
2086 return MSI_SetFeatureStates(package
);
2089 /* OK this value is "interpreted" and then formatted based on the
2090 first few characters */
2091 static LPSTR
parse_value(MSIPACKAGE
*package
, LPCWSTR value
, DWORD
*type
,
2095 if (value
[0]=='#' && value
[1]!='#' && value
[1]!='%')
2101 LPWSTR deformated
= NULL
;
2104 deformat_string(package
, &value
[2], &deformated
);
2106 /* binary value type */
2110 *size
= (strlenW(ptr
)/2)+1;
2112 *size
= strlenW(ptr
)/2;
2114 data
= msi_alloc(*size
);
2120 /* if uneven pad with a zero in front */
2126 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2128 TRACE("Uneven byte count\n");
2136 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2139 msi_free(deformated
);
2141 TRACE("Data %i bytes(%i)\n",*size
,count
);
2148 deformat_string(package
, &value
[1], &deformated
);
2151 *size
= sizeof(DWORD
);
2152 data
= msi_alloc(*size
);
2158 if ( (*p
< '0') || (*p
> '9') )
2164 if (deformated
[0] == '-')
2167 TRACE("DWORD %i\n",*(LPDWORD
)data
);
2169 msi_free(deformated
);
2174 static const WCHAR szMulti
[] = {'[','~',']',0};
2183 *type
=REG_EXPAND_SZ
;
2191 if (strstrW(value
,szMulti
))
2192 *type
= REG_MULTI_SZ
;
2194 *size
= deformat_string(package
, ptr
,(LPWSTR
*)&data
);
2199 static UINT
ITERATE_WriteRegistryValues(MSIRECORD
*row
, LPVOID param
)
2201 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
2202 static const WCHAR szHCR
[] =
2203 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2204 'R','O','O','T','\\',0};
2205 static const WCHAR szHCU
[] =
2206 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2207 'U','S','E','R','\\',0};
2208 static const WCHAR szHLM
[] =
2209 {'H','K','E','Y','_','L','O','C','A','L','_',
2210 'M','A','C','H','I','N','E','\\',0};
2211 static const WCHAR szHU
[] =
2212 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2214 LPSTR value_data
= NULL
;
2215 HKEY root_key
, hkey
;
2218 LPCWSTR szRoot
, component
, name
, key
, value
;
2223 BOOL check_first
= FALSE
;
2226 ui_progress(package
,2,0,0,0);
2233 component
= MSI_RecordGetString(row
, 6);
2234 comp
= get_loaded_component(package
,component
);
2236 return ERROR_SUCCESS
;
2238 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2240 TRACE("Skipping write due to disabled component %s\n",
2241 debugstr_w(component
));
2243 comp
->Action
= comp
->Installed
;
2245 return ERROR_SUCCESS
;
2248 comp
->Action
= INSTALLSTATE_LOCAL
;
2250 name
= MSI_RecordGetString(row
, 4);
2251 if( MSI_RecordIsNull(row
,5) && name
)
2253 /* null values can have special meanings */
2254 if (name
[0]=='-' && name
[1] == 0)
2255 return ERROR_SUCCESS
;
2256 else if ((name
[0]=='+' && name
[1] == 0) ||
2257 (name
[0] == '*' && name
[1] == 0))
2262 root
= MSI_RecordGetInteger(row
,2);
2263 key
= MSI_RecordGetString(row
, 3);
2265 /* get the root key */
2270 static const WCHAR szALLUSER
[] = {'A','L','L','U','S','E','R','S',0};
2271 LPWSTR all_users
= msi_dup_property( package
, szALLUSER
);
2272 if (all_users
&& all_users
[0] == '1')
2274 root_key
= HKEY_LOCAL_MACHINE
;
2279 root_key
= HKEY_CURRENT_USER
;
2282 msi_free(all_users
);
2285 case 0: root_key
= HKEY_CLASSES_ROOT
;
2288 case 1: root_key
= HKEY_CURRENT_USER
;
2291 case 2: root_key
= HKEY_LOCAL_MACHINE
;
2294 case 3: root_key
= HKEY_USERS
;
2298 ERR("Unknown root %i\n",root
);
2304 return ERROR_SUCCESS
;
2306 deformat_string(package
, key
, &deformated
);
2307 size
= strlenW(deformated
) + strlenW(szRoot
) + 1;
2308 uikey
= msi_alloc(size
*sizeof(WCHAR
));
2309 strcpyW(uikey
,szRoot
);
2310 strcatW(uikey
,deformated
);
2312 if (RegCreateKeyW( root_key
, deformated
, &hkey
))
2314 ERR("Could not create key %s\n",debugstr_w(deformated
));
2315 msi_free(deformated
);
2317 return ERROR_SUCCESS
;
2319 msi_free(deformated
);
2321 value
= MSI_RecordGetString(row
,5);
2323 value_data
= parse_value(package
, value
, &type
, &size
);
2326 static const WCHAR szEmpty
[] = {0};
2327 value_data
= (LPSTR
)strdupW(szEmpty
);
2332 deformat_string(package
, name
, &deformated
);
2334 /* get the double nulls to terminate SZ_MULTI */
2335 if (type
== REG_MULTI_SZ
)
2336 size
+=sizeof(WCHAR
);
2340 TRACE("Setting value %s of %s\n",debugstr_w(deformated
),
2342 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
)value_data
, size
);
2347 rc
= RegQueryValueExW(hkey
, deformated
, NULL
, NULL
, NULL
, &sz
);
2348 if (rc
== ERROR_SUCCESS
|| rc
== ERROR_MORE_DATA
)
2350 TRACE("value %s of %s checked already exists\n",
2351 debugstr_w(deformated
), debugstr_w(uikey
));
2355 TRACE("Checked and setting value %s of %s\n",
2356 debugstr_w(deformated
), debugstr_w(uikey
));
2357 if (deformated
|| size
)
2358 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
) value_data
, size
);
2363 uirow
= MSI_CreateRecord(3);
2364 MSI_RecordSetStringW(uirow
,2,deformated
);
2365 MSI_RecordSetStringW(uirow
,1,uikey
);
2368 MSI_RecordSetStringW(uirow
,3,(LPWSTR
)value_data
);
2370 MSI_RecordSetStringW(uirow
,3,value
);
2372 ui_actiondata(package
,szWriteRegistryValues
,uirow
);
2373 msiobj_release( &uirow
->hdr
);
2375 msi_free(value_data
);
2376 msi_free(deformated
);
2379 return ERROR_SUCCESS
;
2382 static UINT
ACTION_WriteRegistryValues(MSIPACKAGE
*package
)
2386 static const WCHAR ExecSeqQuery
[] =
2387 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2388 '`','R','e','g','i','s','t','r','y','`',0 };
2390 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2391 if (rc
!= ERROR_SUCCESS
)
2392 return ERROR_SUCCESS
;
2394 /* increment progress bar each time action data is sent */
2395 ui_progress(package
,1,REG_PROGRESS_VALUE
,1,0);
2397 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteRegistryValues
, package
);
2399 msiobj_release(&view
->hdr
);
2403 static UINT
ACTION_InstallInitialize(MSIPACKAGE
*package
)
2405 package
->script
->CurrentlyScripting
= TRUE
;
2407 return ERROR_SUCCESS
;
2411 static UINT
ACTION_InstallValidate(MSIPACKAGE
*package
)
2416 static const WCHAR q1
[]=
2417 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2418 '`','R','e','g','i','s','t','r','y','`',0};
2421 MSIFEATURE
*feature
;
2424 TRACE("InstallValidate\n");
2426 rc
= MSI_DatabaseOpenViewW(package
->db
, q1
, &view
);
2427 if (rc
== ERROR_SUCCESS
)
2429 MSI_IterateRecords( view
, &progress
, NULL
, package
);
2430 msiobj_release( &view
->hdr
);
2431 total
+= progress
* REG_PROGRESS_VALUE
;
2434 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2435 total
+= COMPONENT_PROGRESS_VALUE
;
2437 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2438 total
+= file
->FileSize
;
2440 ui_progress(package
,0,total
,0,0);
2442 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2444 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2445 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
,
2446 feature
->ActionRequest
);
2449 return ERROR_SUCCESS
;
2452 static UINT
ITERATE_LaunchConditions(MSIRECORD
*row
, LPVOID param
)
2454 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
2455 LPCWSTR cond
= NULL
;
2456 LPCWSTR message
= NULL
;
2457 static const WCHAR title
[]=
2458 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2460 cond
= MSI_RecordGetString(row
,1);
2462 if (MSI_EvaluateConditionW(package
,cond
) != MSICONDITION_TRUE
)
2465 message
= MSI_RecordGetString(row
,2);
2466 deformat_string(package
,message
,&deformated
);
2467 MessageBoxW(NULL
,deformated
,title
,MB_OK
);
2468 msi_free(deformated
);
2469 return ERROR_FUNCTION_FAILED
;
2472 return ERROR_SUCCESS
;
2475 static UINT
ACTION_LaunchConditions(MSIPACKAGE
*package
)
2478 MSIQUERY
* view
= NULL
;
2479 static const WCHAR ExecSeqQuery
[] =
2480 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2481 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2483 TRACE("Checking launch conditions\n");
2485 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2486 if (rc
!= ERROR_SUCCESS
)
2487 return ERROR_SUCCESS
;
2489 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_LaunchConditions
, package
);
2490 msiobj_release(&view
->hdr
);
2495 static LPWSTR
resolve_keypath( MSIPACKAGE
* package
, MSICOMPONENT
*cmp
)
2499 return resolve_folder(package
,cmp
->Directory
,FALSE
,FALSE
,NULL
);
2501 if (cmp
->Attributes
& msidbComponentAttributesRegistryKeyPath
)
2503 MSIRECORD
* row
= 0;
2505 LPWSTR deformated
,buffer
,deformated_name
;
2507 static const WCHAR ExecSeqQuery
[] =
2508 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2509 '`','R','e','g','i','s','t','r','y','`',' ',
2510 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2511 ' ','=',' ' ,'\'','%','s','\'',0 };
2512 static const WCHAR fmt
[]={'%','0','2','i',':','\\','%','s','\\',0};
2513 static const WCHAR fmt2
[]=
2514 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2516 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
,cmp
->KeyPath
);
2520 root
= MSI_RecordGetInteger(row
,2);
2521 key
= MSI_RecordGetString(row
, 3);
2522 name
= MSI_RecordGetString(row
, 4);
2523 deformat_string(package
, key
, &deformated
);
2524 deformat_string(package
, name
, &deformated_name
);
2526 len
= strlenW(deformated
) + 6;
2527 if (deformated_name
)
2528 len
+=strlenW(deformated_name
);
2530 buffer
= msi_alloc( len
*sizeof(WCHAR
));
2532 if (deformated_name
)
2533 sprintfW(buffer
,fmt2
,root
,deformated
,deformated_name
);
2535 sprintfW(buffer
,fmt
,root
,deformated
);
2537 msi_free(deformated
);
2538 msi_free(deformated_name
);
2539 msiobj_release(&row
->hdr
);
2543 else if (cmp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2545 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2550 MSIFILE
*file
= get_loaded_file( package
, cmp
->KeyPath
);
2553 return strdupW( file
->TargetPath
);
2558 static HKEY
openSharedDLLsKey(void)
2561 static const WCHAR path
[] =
2562 {'S','o','f','t','w','a','r','e','\\',
2563 'M','i','c','r','o','s','o','f','t','\\',
2564 'W','i','n','d','o','w','s','\\',
2565 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2566 'S','h','a','r','e','d','D','L','L','s',0};
2568 RegCreateKeyW(HKEY_LOCAL_MACHINE
,path
,&hkey
);
2572 static UINT
ACTION_GetSharedDLLsCount(LPCWSTR dll
)
2577 DWORD sz
= sizeof(count
);
2580 hkey
= openSharedDLLsKey();
2581 rc
= RegQueryValueExW(hkey
, dll
, NULL
, &type
, (LPBYTE
)&count
, &sz
);
2582 if (rc
!= ERROR_SUCCESS
)
2588 static UINT
ACTION_WriteSharedDLLsCount(LPCWSTR path
, UINT count
)
2592 hkey
= openSharedDLLsKey();
2594 msi_reg_set_val_dword( hkey
, path
, count
);
2596 RegDeleteValueW(hkey
,path
);
2602 * Return TRUE if the count should be written out and FALSE if not
2604 static void ACTION_RefCountComponent( MSIPACKAGE
* package
, MSICOMPONENT
*comp
)
2606 MSIFEATURE
*feature
;
2610 /* only refcount DLLs */
2611 if (comp
->KeyPath
== NULL
||
2612 comp
->Attributes
& msidbComponentAttributesRegistryKeyPath
||
2613 comp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2617 count
= ACTION_GetSharedDLLsCount( comp
->FullKeypath
);
2618 write
= (count
> 0);
2620 if (comp
->Attributes
& msidbComponentAttributesSharedDllRefCount
)
2624 /* increment counts */
2625 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2629 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
))
2632 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2634 if ( cl
->component
== comp
)
2639 /* decrement counts */
2640 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2644 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ABSENT
))
2647 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2649 if ( cl
->component
== comp
)
2654 /* ref count all the files in the component */
2659 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2661 if (file
->Component
== comp
)
2662 ACTION_WriteSharedDLLsCount( file
->TargetPath
, count
);
2666 /* add a count for permenent */
2667 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2670 comp
->RefCount
= count
;
2673 ACTION_WriteSharedDLLsCount( comp
->FullKeypath
, comp
->RefCount
);
2677 * Ok further analysis makes me think that this work is
2678 * actually done in the PublishComponents and PublishFeatures
2679 * step, and not here. It appears like the keypath and all that is
2680 * resolved in this step, however actually written in the Publish steps.
2681 * But we will leave it here for now because it is unclear
2683 static UINT
ACTION_ProcessComponents(MSIPACKAGE
*package
)
2685 WCHAR squished_pc
[GUID_SIZE
];
2686 WCHAR squished_cc
[GUID_SIZE
];
2689 HKEY hkey
=0,hkey2
=0;
2691 /* writes the Component and Features values to the registry */
2693 rc
= MSIREG_OpenComponents(&hkey
);
2694 if (rc
!= ERROR_SUCCESS
)
2697 squash_guid(package
->ProductCode
,squished_pc
);
2698 ui_progress(package
,1,COMPONENT_PROGRESS_VALUE
,1,0);
2700 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2704 ui_progress(package
,2,0,0,0);
2705 if (!comp
->ComponentId
)
2708 squash_guid(comp
->ComponentId
,squished_cc
);
2710 msi_free(comp
->FullKeypath
);
2711 comp
->FullKeypath
= resolve_keypath( package
, comp
);
2713 /* do the refcounting */
2714 ACTION_RefCountComponent( package
, comp
);
2716 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2717 debugstr_w(comp
->Component
),
2718 debugstr_w(squished_cc
),
2719 debugstr_w(comp
->FullKeypath
),
2722 * Write the keypath out if the component is to be registered
2723 * and delete the key if the component is to be deregistered
2725 if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2727 rc
= RegCreateKeyW(hkey
,squished_cc
,&hkey2
);
2728 if (rc
!= ERROR_SUCCESS
)
2731 if (!comp
->FullKeypath
)
2734 msi_reg_set_val_str( hkey2
, squished_pc
, comp
->FullKeypath
);
2736 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2738 static const WCHAR szPermKey
[] =
2739 { '0','0','0','0','0','0','0','0','0','0','0','0',
2740 '0','0','0','0','0','0','0','0','0','0','0','0',
2741 '0','0','0','0','0','0','0','0',0 };
2743 msi_reg_set_val_str( hkey2
, szPermKey
, comp
->FullKeypath
);
2749 uirow
= MSI_CreateRecord(3);
2750 MSI_RecordSetStringW(uirow
,1,package
->ProductCode
);
2751 MSI_RecordSetStringW(uirow
,2,comp
->ComponentId
);
2752 MSI_RecordSetStringW(uirow
,3,comp
->FullKeypath
);
2753 ui_actiondata(package
,szProcessComponents
,uirow
);
2754 msiobj_release( &uirow
->hdr
);
2756 else if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ABSENT
))
2760 rc
= RegOpenKeyW(hkey
,squished_cc
,&hkey2
);
2761 if (rc
!= ERROR_SUCCESS
)
2764 RegDeleteValueW(hkey2
,squished_pc
);
2766 /* if the key is empty delete it */
2767 res
= RegEnumKeyExW(hkey2
,0,NULL
,0,0,NULL
,0,NULL
);
2769 if (res
== ERROR_NO_MORE_ITEMS
)
2770 RegDeleteKeyW(hkey
,squished_cc
);
2773 uirow
= MSI_CreateRecord(2);
2774 MSI_RecordSetStringW(uirow
,1,package
->ProductCode
);
2775 MSI_RecordSetStringW(uirow
,2,comp
->ComponentId
);
2776 ui_actiondata(package
,szProcessComponents
,uirow
);
2777 msiobj_release( &uirow
->hdr
);
2792 static BOOL CALLBACK
Typelib_EnumResNameProc( HMODULE hModule
, LPCWSTR lpszType
,
2793 LPWSTR lpszName
, LONG_PTR lParam
)
2796 typelib_struct
*tl_struct
= (typelib_struct
*) lParam
;
2797 static const WCHAR fmt
[] = {'%','s','\\','%','i',0};
2801 if (!IS_INTRESOURCE(lpszName
))
2803 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName
));
2807 sz
= strlenW(tl_struct
->source
)+4;
2808 sz
*= sizeof(WCHAR
);
2810 if ((INT_PTR
)lpszName
== 1)
2811 tl_struct
->path
= strdupW(tl_struct
->source
);
2814 tl_struct
->path
= msi_alloc(sz
);
2815 sprintfW(tl_struct
->path
,fmt
,tl_struct
->source
, lpszName
);
2818 TRACE("trying %s\n", debugstr_w(tl_struct
->path
));
2819 res
= LoadTypeLib(tl_struct
->path
,&tl_struct
->ptLib
);
2820 if (!SUCCEEDED(res
))
2822 msi_free(tl_struct
->path
);
2823 tl_struct
->path
= NULL
;
2828 ITypeLib_GetLibAttr(tl_struct
->ptLib
, &attr
);
2829 if (IsEqualGUID(&(tl_struct
->clsid
),&(attr
->guid
)))
2831 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2835 msi_free(tl_struct
->path
);
2836 tl_struct
->path
= NULL
;
2838 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2839 ITypeLib_Release(tl_struct
->ptLib
);
2844 static UINT
ITERATE_RegisterTypeLibraries(MSIRECORD
*row
, LPVOID param
)
2846 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
2850 typelib_struct tl_struct
;
2852 static const WCHAR szTYPELIB
[] = {'T','Y','P','E','L','I','B',0};
2854 component
= MSI_RecordGetString(row
,3);
2855 comp
= get_loaded_component(package
,component
);
2857 return ERROR_SUCCESS
;
2859 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2861 TRACE("Skipping typelib reg due to disabled component\n");
2863 comp
->Action
= comp
->Installed
;
2865 return ERROR_SUCCESS
;
2868 comp
->Action
= INSTALLSTATE_LOCAL
;
2870 file
= get_loaded_file( package
, comp
->KeyPath
);
2872 return ERROR_SUCCESS
;
2874 module
= LoadLibraryExW( file
->TargetPath
, NULL
, LOAD_LIBRARY_AS_DATAFILE
);
2878 guid
= MSI_RecordGetString(row
,1);
2879 CLSIDFromString((LPWSTR
)guid
, &tl_struct
.clsid
);
2880 tl_struct
.source
= strdupW( file
->TargetPath
);
2881 tl_struct
.path
= NULL
;
2883 EnumResourceNamesW(module
, szTYPELIB
, Typelib_EnumResNameProc
,
2884 (LONG_PTR
)&tl_struct
);
2892 helpid
= MSI_RecordGetString(row
,6);
2895 help
= resolve_folder(package
,helpid
,FALSE
,FALSE
,NULL
);
2896 res
= RegisterTypeLib(tl_struct
.ptLib
,tl_struct
.path
,help
);
2899 if (!SUCCEEDED(res
))
2900 ERR("Failed to register type library %s\n",
2901 debugstr_w(tl_struct
.path
));
2904 ui_actiondata(package
,szRegisterTypeLibraries
,row
);
2906 TRACE("Registered %s\n", debugstr_w(tl_struct
.path
));
2909 ITypeLib_Release(tl_struct
.ptLib
);
2910 msi_free(tl_struct
.path
);
2913 ERR("Failed to load type library %s\n",
2914 debugstr_w(tl_struct
.source
));
2916 FreeLibrary(module
);
2917 msi_free(tl_struct
.source
);
2920 ERR("Could not load file! %s\n", debugstr_w(file
->TargetPath
));
2922 return ERROR_SUCCESS
;
2925 static UINT
ACTION_RegisterTypeLibraries(MSIPACKAGE
*package
)
2928 * OK this is a bit confusing.. I am given a _Component key and I believe
2929 * that the file that is being registered as a type library is the "key file
2930 * of that component" which I interpret to mean "The file in the KeyPath of
2935 static const WCHAR Query
[] =
2936 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2937 '`','T','y','p','e','L','i','b','`',0};
2939 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
2940 if (rc
!= ERROR_SUCCESS
)
2941 return ERROR_SUCCESS
;
2943 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_RegisterTypeLibraries
, package
);
2944 msiobj_release(&view
->hdr
);
2948 static UINT
ITERATE_CreateShortcuts(MSIRECORD
*row
, LPVOID param
)
2950 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
2951 LPWSTR target_file
, target_folder
, filename
;
2952 LPCWSTR buffer
, extension
;
2954 static const WCHAR szlnk
[]={'.','l','n','k',0};
2955 IShellLinkW
*sl
= NULL
;
2956 IPersistFile
*pf
= NULL
;
2959 buffer
= MSI_RecordGetString(row
,4);
2960 comp
= get_loaded_component(package
,buffer
);
2962 return ERROR_SUCCESS
;
2964 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2966 TRACE("Skipping shortcut creation due to disabled component\n");
2968 comp
->Action
= comp
->Installed
;
2970 return ERROR_SUCCESS
;
2973 comp
->Action
= INSTALLSTATE_LOCAL
;
2975 ui_actiondata(package
,szCreateShortcuts
,row
);
2977 res
= CoCreateInstance( &CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
,
2978 &IID_IShellLinkW
, (LPVOID
*) &sl
);
2982 ERR("CLSID_ShellLink not available\n");
2986 res
= IShellLinkW_QueryInterface( sl
, &IID_IPersistFile
,(LPVOID
*) &pf
);
2989 ERR("QueryInterface(IID_IPersistFile) failed\n");
2993 buffer
= MSI_RecordGetString(row
,2);
2994 target_folder
= resolve_folder(package
, buffer
,FALSE
,FALSE
,NULL
);
2996 /* may be needed because of a bug somehwere else */
2997 create_full_pathW(target_folder
);
2999 filename
= msi_dup_record_field( row
, 3 );
3000 reduce_to_longfilename(filename
);
3002 extension
= strchrW(filename
,'.');
3003 if (!extension
|| strcmpiW(extension
,szlnk
))
3005 int len
= strlenW(filename
);
3006 filename
= msi_realloc(filename
, len
* sizeof(WCHAR
) + sizeof(szlnk
));
3007 memcpy(filename
+ len
, szlnk
, sizeof(szlnk
));
3009 target_file
= build_directory_name(2, target_folder
, filename
);
3010 msi_free(target_folder
);
3013 buffer
= MSI_RecordGetString(row
,5);
3014 if (strchrW(buffer
,'['))
3017 deformat_string(package
,buffer
,&deformated
);
3018 IShellLinkW_SetPath(sl
,deformated
);
3019 msi_free(deformated
);
3023 FIXME("poorly handled shortcut format, advertised shortcut\n");
3024 IShellLinkW_SetPath(sl
,comp
->FullKeypath
);
3027 if (!MSI_RecordIsNull(row
,6))
3030 buffer
= MSI_RecordGetString(row
,6);
3031 deformat_string(package
,buffer
,&deformated
);
3032 IShellLinkW_SetArguments(sl
,deformated
);
3033 msi_free(deformated
);
3036 if (!MSI_RecordIsNull(row
,7))
3038 buffer
= MSI_RecordGetString(row
,7);
3039 IShellLinkW_SetDescription(sl
,buffer
);
3042 if (!MSI_RecordIsNull(row
,8))
3043 IShellLinkW_SetHotkey(sl
,MSI_RecordGetInteger(row
,8));
3045 if (!MSI_RecordIsNull(row
,9))
3050 buffer
= MSI_RecordGetString(row
,9);
3052 Path
= build_icon_path(package
,buffer
);
3053 index
= MSI_RecordGetInteger(row
,10);
3055 /* no value means 0 */
3056 if (index
== MSI_NULL_INTEGER
)
3059 IShellLinkW_SetIconLocation(sl
,Path
,index
);
3063 if (!MSI_RecordIsNull(row
,11))
3064 IShellLinkW_SetShowCmd(sl
,MSI_RecordGetInteger(row
,11));
3066 if (!MSI_RecordIsNull(row
,12))
3069 buffer
= MSI_RecordGetString(row
,12);
3070 Path
= resolve_folder(package
, buffer
, FALSE
, FALSE
, NULL
);
3072 IShellLinkW_SetWorkingDirectory(sl
,Path
);
3076 TRACE("Writing shortcut to %s\n",debugstr_w(target_file
));
3077 IPersistFile_Save(pf
,target_file
,FALSE
);
3079 msi_free(target_file
);
3083 IPersistFile_Release( pf
);
3085 IShellLinkW_Release( sl
);
3087 return ERROR_SUCCESS
;
3090 static UINT
ACTION_CreateShortcuts(MSIPACKAGE
*package
)
3095 static const WCHAR Query
[] =
3096 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3097 '`','S','h','o','r','t','c','u','t','`',0};
3099 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3100 if (rc
!= ERROR_SUCCESS
)
3101 return ERROR_SUCCESS
;
3103 res
= CoInitialize( NULL
);
3106 ERR("CoInitialize failed\n");
3107 return ERROR_FUNCTION_FAILED
;
3110 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateShortcuts
, package
);
3111 msiobj_release(&view
->hdr
);
3118 static UINT
ITERATE_PublishProduct(MSIRECORD
*row
, LPVOID param
)
3120 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
3129 FileName
= MSI_RecordGetString(row
,1);
3132 ERR("Unable to get FileName\n");
3133 return ERROR_SUCCESS
;
3136 FilePath
= build_icon_path(package
,FileName
);
3138 TRACE("Creating icon file at %s\n",debugstr_w(FilePath
));
3140 the_file
= CreateFileW(FilePath
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
,
3141 FILE_ATTRIBUTE_NORMAL
, NULL
);
3143 if (the_file
== INVALID_HANDLE_VALUE
)
3145 ERR("Unable to create file %s\n",debugstr_w(FilePath
));
3147 return ERROR_SUCCESS
;
3154 rc
= MSI_RecordReadStream(row
,2,buffer
,&sz
);
3155 if (rc
!= ERROR_SUCCESS
)
3157 ERR("Failed to get stream\n");
3158 CloseHandle(the_file
);
3159 DeleteFileW(FilePath
);
3162 WriteFile(the_file
,buffer
,sz
,&write
,NULL
);
3163 } while (sz
== 1024);
3167 CloseHandle(the_file
);
3169 uirow
= MSI_CreateRecord(1);
3170 MSI_RecordSetStringW(uirow
,1,FileName
);
3171 ui_actiondata(package
,szPublishProduct
,uirow
);
3172 msiobj_release( &uirow
->hdr
);
3174 return ERROR_SUCCESS
;
3178 * 99% of the work done here is only done for
3179 * advertised installs. However this is where the
3180 * Icon table is processed and written out
3181 * so that is what I am going to do here.
3183 static UINT
ACTION_PublishProduct(MSIPACKAGE
*package
)
3187 static const WCHAR Query
[]=
3188 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3189 '`','I','c','o','n','`',0};
3190 /* for registry stuff */
3193 static const WCHAR szProductLanguage
[] =
3194 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3195 static const WCHAR szARPProductIcon
[] =
3196 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3197 static const WCHAR szProductVersion
[] =
3198 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3202 MSIHANDLE hDb
, hSumInfo
;
3204 /* write out icon files */
3206 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3207 if (rc
== ERROR_SUCCESS
)
3209 MSI_IterateRecords(view
, NULL
, ITERATE_PublishProduct
, package
);
3210 msiobj_release(&view
->hdr
);
3213 /* ok there is a lot more done here but i need to figure out what */
3215 rc
= MSIREG_OpenProductsKey(package
->ProductCode
,&hkey
,TRUE
);
3216 if (rc
!= ERROR_SUCCESS
)
3219 rc
= MSIREG_OpenUserProductsKey(package
->ProductCode
,&hukey
,TRUE
);
3220 if (rc
!= ERROR_SUCCESS
)
3224 buffer
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTNAMEW
);
3225 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PRODUCTNAMEW
, buffer
);
3228 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
3229 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
3231 buffer
= msi_dup_property( package
, szARPProductIcon
);
3234 LPWSTR path
= build_icon_path(package
,buffer
);
3235 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PRODUCTICONW
, path
);
3240 buffer
= msi_dup_property( package
, szProductVersion
);
3243 DWORD verdword
= msi_version_str_to_dword(buffer
);
3244 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
3248 /* FIXME: Need to write more keys to the user registry */
3250 hDb
= alloc_msihandle( &package
->db
->hdr
);
3252 rc
= ERROR_NOT_ENOUGH_MEMORY
;
3255 rc
= MsiGetSummaryInformationW(hDb
, NULL
, 0, &hSumInfo
);
3256 MsiCloseHandle(hDb
);
3257 if (rc
== ERROR_SUCCESS
)
3259 WCHAR guidbuffer
[0x200];
3261 rc
= MsiSummaryInfoGetPropertyW(hSumInfo
, 9, NULL
, NULL
, NULL
,
3263 if (rc
== ERROR_SUCCESS
)
3265 WCHAR squashed
[GUID_SIZE
];
3266 /* for now we only care about the first guid */
3267 LPWSTR ptr
= strchrW(guidbuffer
,';');
3269 squash_guid(guidbuffer
,squashed
);
3270 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PACKAGECODEW
, squashed
);
3274 ERR("Unable to query Revision_Number...\n");
3277 MsiCloseHandle(hSumInfo
);
3281 ERR("Unable to open Summary Information\n");
3293 static UINT
ITERATE_WriteIniValues(MSIRECORD
*row
, LPVOID param
)
3295 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3296 LPCWSTR component
,section
,key
,value
,identifier
,filename
,dirproperty
;
3297 LPWSTR deformated_section
, deformated_key
, deformated_value
;
3298 LPWSTR folder
, fullname
= NULL
;
3302 static const WCHAR szWindowsFolder
[] =
3303 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3305 component
= MSI_RecordGetString(row
, 8);
3306 comp
= get_loaded_component(package
,component
);
3308 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3310 TRACE("Skipping ini file due to disabled component %s\n",
3311 debugstr_w(component
));
3313 comp
->Action
= comp
->Installed
;
3315 return ERROR_SUCCESS
;
3318 comp
->Action
= INSTALLSTATE_LOCAL
;
3320 identifier
= MSI_RecordGetString(row
,1);
3321 filename
= MSI_RecordGetString(row
,2);
3322 dirproperty
= MSI_RecordGetString(row
,3);
3323 section
= MSI_RecordGetString(row
,4);
3324 key
= MSI_RecordGetString(row
,5);
3325 value
= MSI_RecordGetString(row
,6);
3326 action
= MSI_RecordGetInteger(row
,7);
3328 deformat_string(package
,section
,&deformated_section
);
3329 deformat_string(package
,key
,&deformated_key
);
3330 deformat_string(package
,value
,&deformated_value
);
3334 folder
= resolve_folder(package
, dirproperty
, FALSE
, FALSE
, NULL
);
3336 folder
= msi_dup_property( package
, dirproperty
);
3339 folder
= msi_dup_property( package
, szWindowsFolder
);
3343 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty
));
3347 fullname
= build_directory_name(2, folder
, filename
);
3351 TRACE("Adding value %s to section %s in %s\n",
3352 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3353 debugstr_w(fullname
));
3354 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3355 deformated_value
, fullname
);
3357 else if (action
== 1)
3360 GetPrivateProfileStringW(deformated_section
, deformated_key
, NULL
,
3361 returned
, 10, fullname
);
3362 if (returned
[0] == 0)
3364 TRACE("Adding value %s to section %s in %s\n",
3365 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3366 debugstr_w(fullname
));
3368 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3369 deformated_value
, fullname
);
3372 else if (action
== 3)
3373 FIXME("Append to existing section not yet implemented\n");
3375 uirow
= MSI_CreateRecord(4);
3376 MSI_RecordSetStringW(uirow
,1,identifier
);
3377 MSI_RecordSetStringW(uirow
,2,deformated_section
);
3378 MSI_RecordSetStringW(uirow
,3,deformated_key
);
3379 MSI_RecordSetStringW(uirow
,4,deformated_value
);
3380 ui_actiondata(package
,szWriteIniValues
,uirow
);
3381 msiobj_release( &uirow
->hdr
);
3385 msi_free(deformated_key
);
3386 msi_free(deformated_value
);
3387 msi_free(deformated_section
);
3388 return ERROR_SUCCESS
;
3391 static UINT
ACTION_WriteIniValues(MSIPACKAGE
*package
)
3395 static const WCHAR ExecSeqQuery
[] =
3396 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3397 '`','I','n','i','F','i','l','e','`',0};
3399 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3400 if (rc
!= ERROR_SUCCESS
)
3402 TRACE("no IniFile table\n");
3403 return ERROR_SUCCESS
;
3406 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteIniValues
, package
);
3407 msiobj_release(&view
->hdr
);
3411 static UINT
ITERATE_SelfRegModules(MSIRECORD
*row
, LPVOID param
)
3413 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3418 static const WCHAR ExeStr
[] =
3419 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3420 static const WCHAR close
[] = {'\"',0};
3422 PROCESS_INFORMATION info
;
3427 memset(&si
,0,sizeof(STARTUPINFOW
));
3429 filename
= MSI_RecordGetString(row
,1);
3430 file
= get_loaded_file( package
, filename
);
3434 ERR("Unable to find file id %s\n",debugstr_w(filename
));
3435 return ERROR_SUCCESS
;
3438 len
= strlenW(ExeStr
) + strlenW( file
->TargetPath
) + 2;
3440 FullName
= msi_alloc(len
*sizeof(WCHAR
));
3441 strcpyW(FullName
,ExeStr
);
3442 strcatW( FullName
, file
->TargetPath
);
3443 strcatW(FullName
,close
);
3445 TRACE("Registering %s\n",debugstr_w(FullName
));
3446 brc
= CreateProcessW(NULL
, FullName
, NULL
, NULL
, FALSE
, 0, NULL
, c_colon
,
3450 msi_dialog_check_messages(info
.hProcess
);
3455 uirow
= MSI_CreateRecord( 2 );
3456 uipath
= strdupW( file
->TargetPath
);
3457 p
= strrchrW(uipath
,'\\');
3460 MSI_RecordSetStringW( uirow
, 1, &p
[2] );
3461 MSI_RecordSetStringW( uirow
, 2, uipath
);
3462 ui_actiondata( package
, szSelfRegModules
, uirow
);
3463 msiobj_release( &uirow
->hdr
);
3465 /* FIXME: call ui_progress? */
3467 return ERROR_SUCCESS
;
3470 static UINT
ACTION_SelfRegModules(MSIPACKAGE
*package
)
3474 static const WCHAR ExecSeqQuery
[] =
3475 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3476 '`','S','e','l','f','R','e','g','`',0};
3478 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3479 if (rc
!= ERROR_SUCCESS
)
3481 TRACE("no SelfReg table\n");
3482 return ERROR_SUCCESS
;
3485 MSI_IterateRecords(view
, NULL
, ITERATE_SelfRegModules
, package
);
3486 msiobj_release(&view
->hdr
);
3488 return ERROR_SUCCESS
;
3491 static UINT
ACTION_PublishFeatures(MSIPACKAGE
*package
)
3493 MSIFEATURE
*feature
;
3498 rc
= MSIREG_OpenFeaturesKey(package
->ProductCode
,&hkey
,TRUE
);
3499 if (rc
!= ERROR_SUCCESS
)
3502 rc
= MSIREG_OpenUserFeaturesKey(package
->ProductCode
,&hukey
,TRUE
);
3503 if (rc
!= ERROR_SUCCESS
)
3506 /* here the guids are base 85 encoded */
3507 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
3513 BOOL absent
= FALSE
;
3516 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
) &&
3517 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_SOURCE
) &&
3518 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ADVERTISED
))
3522 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3526 if (feature
->Feature_Parent
)
3527 size
+= strlenW( feature
->Feature_Parent
)+2;
3529 data
= msi_alloc(size
* sizeof(WCHAR
));
3532 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3534 MSICOMPONENT
* component
= cl
->component
;
3538 if (component
->ComponentId
)
3540 TRACE("From %s\n",debugstr_w(component
->ComponentId
));
3541 CLSIDFromString(component
->ComponentId
, &clsid
);
3542 encode_base85_guid(&clsid
,buf
);
3543 TRACE("to %s\n",debugstr_w(buf
));
3547 if (feature
->Feature_Parent
)
3549 static const WCHAR sep
[] = {'\2',0};
3551 strcatW(data
,feature
->Feature_Parent
);
3554 msi_reg_set_val_str( hkey
, feature
->Feature
, data
);
3558 if (feature
->Feature_Parent
)
3559 size
= strlenW(feature
->Feature_Parent
)*sizeof(WCHAR
);
3562 RegSetValueExW(hukey
,feature
->Feature
,0,REG_SZ
,
3563 (LPBYTE
)feature
->Feature_Parent
,size
);
3567 size
+= 2*sizeof(WCHAR
);
3568 data
= msi_alloc(size
);
3571 if (feature
->Feature_Parent
)
3572 strcpyW( &data
[1], feature
->Feature_Parent
);
3573 RegSetValueExW(hukey
,feature
->Feature
,0,REG_SZ
,
3579 uirow
= MSI_CreateRecord( 1 );
3580 MSI_RecordSetStringW( uirow
, 1, feature
->Feature
);
3581 ui_actiondata( package
, szPublishFeatures
, uirow
);
3582 msiobj_release( &uirow
->hdr
);
3583 /* FIXME: call ui_progress? */
3592 static UINT
msi_get_local_package_name( LPWSTR path
)
3594 static const WCHAR szInstaller
[] = {
3595 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3596 static const WCHAR fmt
[] = { '%','x','.','m','s','i',0};
3600 time
= GetTickCount();
3601 GetWindowsDirectoryW( path
, MAX_PATH
);
3602 lstrcatW( path
, szInstaller
);
3603 CreateDirectoryW( path
, NULL
);
3605 len
= lstrlenW(path
);
3606 for (i
=0; i
<0x10000; i
++)
3608 snprintfW( &path
[len
], MAX_PATH
- len
, fmt
, (time
+i
)&0xffff );
3609 handle
= CreateFileW( path
, GENERIC_WRITE
, 0, NULL
,
3610 CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, 0 );
3611 if (handle
!= INVALID_HANDLE_VALUE
)
3613 CloseHandle(handle
);
3616 if (GetLastError() != ERROR_FILE_EXISTS
&&
3617 GetLastError() != ERROR_SHARING_VIOLATION
)
3618 return ERROR_FUNCTION_FAILED
;
3621 return ERROR_SUCCESS
;
3624 static UINT
msi_make_package_local( MSIPACKAGE
*package
, HKEY hkey
)
3626 static const WCHAR szOriginalDatabase
[] =
3627 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3628 WCHAR packagefile
[MAX_PATH
];
3632 r
= msi_get_local_package_name( packagefile
);
3633 if (r
!= ERROR_SUCCESS
)
3636 TRACE("Copying to local package %s\n",debugstr_w(packagefile
));
3638 msiFilePath
= msi_dup_property( package
, szOriginalDatabase
);
3639 r
= CopyFileW( msiFilePath
, packagefile
, FALSE
);
3640 msi_free( msiFilePath
);
3644 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3645 debugstr_w(msiFilePath
), debugstr_w(packagefile
), GetLastError());
3646 return ERROR_FUNCTION_FAILED
;
3649 /* FIXME: maybe set this key in ACTION_RegisterProduct instead */
3650 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_LOCALPACKAGEW
, packagefile
);
3651 return ERROR_SUCCESS
;
3654 static UINT
msi_write_uninstall_property_vals( MSIPACKAGE
*package
, HKEY hkey
)
3656 LPWSTR prop
, val
, key
;
3657 static const LPCSTR propval
[] = {
3658 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3659 "ARPCONTACT", "Contact",
3660 "ARPCOMMENTS", "Comments",
3661 "ProductName", "DisplayName",
3662 "ProductVersion", "DisplayVersion",
3663 "ARPHELPLINK", "HelpLink",
3664 "ARPHELPTELEPHONE", "HelpTelephone",
3665 "ARPINSTALLLOCATION", "InstallLocation",
3666 "SourceDir", "InstallSource",
3667 "Manufacturer", "Publisher",
3668 "ARPREADME", "Readme",
3670 "ARPURLINFOABOUT", "URLInfoAbout",
3671 "ARPURLUPDATEINFO", "URLUpdateInfo",
3674 const LPCSTR
*p
= propval
;
3678 prop
= strdupAtoW( *p
++ );
3679 key
= strdupAtoW( *p
++ );
3680 val
= msi_dup_property( package
, prop
);
3681 msi_reg_set_val_str( hkey
, key
, val
);
3686 return ERROR_SUCCESS
;
3689 static UINT
ACTION_RegisterProduct(MSIPACKAGE
*package
)
3692 LPWSTR buffer
= NULL
;
3695 static const WCHAR szWindowsInstaller
[] =
3696 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3697 static const WCHAR szUpgradeCode
[] =
3698 {'U','p','g','r','a','d','e','C','o','d','e',0};
3699 static const WCHAR modpath_fmt
[] =
3700 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3701 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3702 static const WCHAR szModifyPath
[] =
3703 {'M','o','d','i','f','y','P','a','t','h',0};
3704 static const WCHAR szUninstallString
[] =
3705 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3706 static const WCHAR szEstimatedSize
[] =
3707 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3708 static const WCHAR szProductLanguage
[] =
3709 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3710 static const WCHAR szProductVersion
[] =
3711 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3714 static const WCHAR date_fmt
[] = {'%','i','%','i','%','i',0};
3715 LPWSTR upgrade_code
;
3718 rc
= MSIREG_OpenUninstallKey(package
->ProductCode
,&hkey
,TRUE
);
3719 if (rc
!= ERROR_SUCCESS
)
3722 /* dump all the info i can grab */
3723 /* FIXME: Flesh out more information */
3725 msi_write_uninstall_property_vals( package
, hkey
);
3727 msi_reg_set_val_dword( hkey
, szWindowsInstaller
, 1 );
3729 msi_make_package_local( package
, hkey
);
3731 /* do ModifyPath and UninstallString */
3732 size
= deformat_string(package
,modpath_fmt
,&buffer
);
3733 RegSetValueExW(hkey
,szModifyPath
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
3734 RegSetValueExW(hkey
,szUninstallString
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
3737 /* FIXME: Write real Estimated Size when we have it */
3738 msi_reg_set_val_dword( hkey
, szEstimatedSize
, 0 );
3740 GetLocalTime(&systime
);
3741 sprintfW(szDate
,date_fmt
,systime
.wYear
,systime
.wMonth
,systime
.wDay
);
3742 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_INSTALLDATEW
, szDate
);
3744 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
3745 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
3747 buffer
= msi_dup_property( package
, szProductVersion
);
3750 DWORD verdword
= msi_version_str_to_dword(buffer
);
3752 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
3753 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMAJORW
, verdword
>>24 );
3754 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMINORW
, (verdword
>>16)&0x00FF );
3758 /* Handle Upgrade Codes */
3759 upgrade_code
= msi_dup_property( package
, szUpgradeCode
);
3764 MSIREG_OpenUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
3765 squash_guid(package
->ProductCode
,squashed
);
3766 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
3768 MSIREG_OpenUserUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
3769 squash_guid(package
->ProductCode
,squashed
);
3770 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
3773 msi_free(upgrade_code
);
3778 /* FIXME: call ui_actiondata */
3780 return ERROR_SUCCESS
;
3783 static UINT
ACTION_InstallExecute(MSIPACKAGE
*package
)
3785 return execute_script(package
,INSTALL_SCRIPT
);
3788 static UINT
ACTION_InstallFinalize(MSIPACKAGE
*package
)
3792 /* turn off scheduleing */
3793 package
->script
->CurrentlyScripting
= FALSE
;
3795 /* first do the same as an InstallExecute */
3796 rc
= ACTION_InstallExecute(package
);
3797 if (rc
!= ERROR_SUCCESS
)
3800 /* then handle Commit Actions */
3801 rc
= execute_script(package
,COMMIT_SCRIPT
);
3806 static UINT
ACTION_ForceReboot(MSIPACKAGE
*package
)
3808 static const WCHAR RunOnce
[] = {
3809 'S','o','f','t','w','a','r','e','\\',
3810 'M','i','c','r','o','s','o','f','t','\\',
3811 'W','i','n','d','o','w','s','\\',
3812 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3813 'R','u','n','O','n','c','e',0};
3814 static const WCHAR InstallRunOnce
[] = {
3815 'S','o','f','t','w','a','r','e','\\',
3816 'M','i','c','r','o','s','o','f','t','\\',
3817 'W','i','n','d','o','w','s','\\',
3818 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3819 'I','n','s','t','a','l','l','e','r','\\',
3820 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
3822 static const WCHAR msiexec_fmt
[] = {
3824 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
3825 '\"','%','s','\"',0};
3826 static const WCHAR install_fmt
[] = {
3827 '/','I',' ','\"','%','s','\"',' ',
3828 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
3829 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
3830 WCHAR buffer
[256], sysdir
[MAX_PATH
];
3832 WCHAR squished_pc
[100];
3834 squash_guid(package
->ProductCode
,squished_pc
);
3836 GetSystemDirectoryW(sysdir
, sizeof(sysdir
)/sizeof(sysdir
[0]));
3837 RegCreateKeyW(HKEY_LOCAL_MACHINE
,RunOnce
,&hkey
);
3838 snprintfW(buffer
,sizeof(buffer
)/sizeof(buffer
[0]),msiexec_fmt
,sysdir
,
3841 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
3844 TRACE("Reboot command %s\n",debugstr_w(buffer
));
3846 RegCreateKeyW(HKEY_LOCAL_MACHINE
,InstallRunOnce
,&hkey
);
3847 sprintfW(buffer
,install_fmt
,package
->ProductCode
,squished_pc
);
3849 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
3852 return ERROR_INSTALL_SUSPEND
;
3855 static UINT
ACTION_ResolveSource(MSIPACKAGE
* package
)
3862 * we are currently doing what should be done here in the top level Install
3863 * however for Adminastrative and uninstalls this step will be needed
3865 if (!package
->PackagePath
)
3866 return ERROR_SUCCESS
;
3868 ptr
= strrchrW(package
->PackagePath
, '\\');
3870 return ERROR_SUCCESS
;
3872 len
= ptr
- package
->PackagePath
+ 2;
3873 source
= msi_alloc(len
* sizeof(WCHAR
));
3874 lstrcpynW(source
, package
->PackagePath
, len
);
3876 MSI_SetPropertyW(package
, cszSourceDir
, source
);
3877 MSI_SetPropertyW(package
, cszSOURCEDIR
, source
);
3881 attrib
= GetFileAttributesW(package
->PackagePath
);
3882 if (attrib
== INVALID_FILE_ATTRIBUTES
)
3888 rc
= MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
3889 MSIINSTALLCONTEXT_USERMANAGED
, MSICODE_PRODUCT
,
3890 INSTALLPROPERTY_DISKPROMPTW
,NULL
,&size
);
3891 if (rc
== ERROR_MORE_DATA
)
3893 prompt
= msi_alloc(size
* sizeof(WCHAR
));
3894 MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
3895 MSIINSTALLCONTEXT_USERMANAGED
, MSICODE_PRODUCT
,
3896 INSTALLPROPERTY_DISKPROMPTW
,prompt
,&size
);
3899 prompt
= strdupW(package
->PackagePath
);
3901 msg
= generate_error_string(package
,1302,1,prompt
);
3902 while(attrib
== INVALID_FILE_ATTRIBUTES
)
3904 rc
= MessageBoxW(NULL
,msg
,NULL
,MB_OKCANCEL
);
3907 rc
= ERROR_INSTALL_USEREXIT
;
3910 attrib
= GetFileAttributesW(package
->PackagePath
);
3916 return ERROR_SUCCESS
;
3921 static UINT
ACTION_RegisterUser(MSIPACKAGE
*package
)
3928 static const WCHAR szPropKeys
[][80] =
3930 {'P','r','o','d','u','c','t','I','D',0},
3931 {'U','S','E','R','N','A','M','E',0},
3932 {'C','O','M','P','A','N','Y','N','A','M','E',0},
3936 static const WCHAR szRegKeys
[][80] =
3938 {'P','r','o','d','u','c','t','I','D',0},
3939 {'R','e','g','O','w','n','e','r',0},
3940 {'R','e','g','C','o','m','p','a','n','y',0},
3944 productid
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTIDW
);
3946 return ERROR_SUCCESS
;
3948 rc
= MSIREG_OpenUninstallKey(package
->ProductCode
,&hkey
,TRUE
);
3949 if (rc
!= ERROR_SUCCESS
)
3952 for( i
= 0; szPropKeys
[i
][0]; i
++ )
3954 buffer
= msi_dup_property( package
, szPropKeys
[i
] );
3955 msi_reg_set_val_str( hkey
, szRegKeys
[i
], buffer
);
3960 msi_free(productid
);
3963 /* FIXME: call ui_actiondata */
3965 return ERROR_SUCCESS
;
3969 static UINT
ACTION_ExecuteAction(MSIPACKAGE
*package
)
3973 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
3974 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
3979 static UINT
ITERATE_PublishComponent(MSIRECORD
*rec
, LPVOID param
)
3981 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3982 LPCWSTR compgroupid
=NULL
;
3983 LPCWSTR feature
=NULL
;
3984 LPCWSTR text
= NULL
;
3985 LPCWSTR qualifier
= NULL
;
3986 LPCWSTR component
= NULL
;
3987 LPWSTR advertise
= NULL
;
3988 LPWSTR output
= NULL
;
3990 UINT rc
= ERROR_SUCCESS
;
3995 component
= MSI_RecordGetString(rec
,3);
3996 comp
= get_loaded_component(package
,component
);
3998 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
) &&
3999 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_SOURCE
) &&
4000 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ADVERTISED
))
4002 TRACE("Skipping: Component %s not scheduled for install\n",
4003 debugstr_w(component
));
4005 return ERROR_SUCCESS
;
4008 compgroupid
= MSI_RecordGetString(rec
,1);
4009 qualifier
= MSI_RecordGetString(rec
,2);
4011 rc
= MSIREG_OpenUserComponentsKey(compgroupid
, &hkey
, TRUE
);
4012 if (rc
!= ERROR_SUCCESS
)
4015 text
= MSI_RecordGetString(rec
,4);
4016 feature
= MSI_RecordGetString(rec
,5);
4018 advertise
= create_component_advertise_string(package
, comp
, feature
);
4020 sz
= strlenW(advertise
);
4023 sz
+= lstrlenW(text
);
4026 sz
*= sizeof(WCHAR
);
4028 output
= msi_alloc_zero(sz
);
4029 strcpyW(output
,advertise
);
4030 msi_free(advertise
);
4033 strcatW(output
,text
);
4035 msi_reg_set_val_multi_str( hkey
, qualifier
, output
);
4042 uirow
= MSI_CreateRecord( 2 );
4043 MSI_RecordSetStringW( uirow
, 1, compgroupid
);
4044 MSI_RecordSetStringW( uirow
, 2, qualifier
);
4045 ui_actiondata( package
, szPublishComponents
, uirow
);
4046 msiobj_release( &uirow
->hdr
);
4047 /* FIXME: call ui_progress? */
4053 * At present I am ignorning the advertised components part of this and only
4054 * focusing on the qualified component sets
4056 static UINT
ACTION_PublishComponents(MSIPACKAGE
*package
)
4060 static const WCHAR ExecSeqQuery
[] =
4061 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4062 '`','P','u','b','l','i','s','h',
4063 'C','o','m','p','o','n','e','n','t','`',0};
4065 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4066 if (rc
!= ERROR_SUCCESS
)
4067 return ERROR_SUCCESS
;
4069 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_PublishComponent
, package
);
4070 msiobj_release(&view
->hdr
);
4075 static UINT
ITERATE_InstallService(MSIRECORD
*rec
, LPVOID param
)
4077 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4080 SC_HANDLE hscm
, service
= NULL
;
4081 LPCWSTR name
, disp
, comp
, depends
, pass
;
4082 LPCWSTR load_order
, serv_name
, key
;
4083 DWORD serv_type
, start_type
;
4086 static const WCHAR query
[] =
4087 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4088 '`','C','o','m','p','o','n','e','n','t','`',' ',
4089 'W','H','E','R','E',' ',
4090 '`','C','o','m','p','o','n','e','n','t','`',' ',
4091 '=','\'','%','s','\'',0};
4093 hscm
= OpenSCManagerW(NULL
, SERVICES_ACTIVE_DATABASEW
, GENERIC_WRITE
);
4096 ERR("Failed to open the SC Manager!\n");
4100 start_type
= MSI_RecordGetInteger(rec
, 5);
4101 if (start_type
== SERVICE_BOOT_START
|| start_type
== SERVICE_SYSTEM_START
)
4104 depends
= MSI_RecordGetString(rec
, 8);
4105 if (depends
&& *depends
)
4106 FIXME("Dependency list unhandled!\n");
4108 name
= MSI_RecordGetString(rec
, 2);
4109 disp
= MSI_RecordGetString(rec
, 3);
4110 serv_type
= MSI_RecordGetInteger(rec
, 4);
4111 err_control
= MSI_RecordGetInteger(rec
, 6);
4112 load_order
= MSI_RecordGetString(rec
, 7);
4113 serv_name
= MSI_RecordGetString(rec
, 9);
4114 pass
= MSI_RecordGetString(rec
, 10);
4115 comp
= MSI_RecordGetString(rec
, 12);
4117 /* fetch the service path */
4118 row
= MSI_QueryGetRecord(package
->db
, query
, comp
);
4121 ERR("Control query failed!\n");
4125 key
= MSI_RecordGetString(row
, 6);
4126 msiobj_release(&row
->hdr
);
4128 file
= get_loaded_file(package
, key
);
4131 ERR("Failed to load the service file\n");
4135 service
= CreateServiceW(hscm
, name
, disp
, GENERIC_ALL
, serv_type
,
4136 start_type
, err_control
, file
->TargetPath
,
4137 load_order
, NULL
, NULL
, serv_name
, pass
);
4140 if (GetLastError() != ERROR_SERVICE_EXISTS
)
4141 ERR("Failed to create service %s: %d\n", debugstr_w(name
), GetLastError());
4145 CloseServiceHandle(service
);
4146 CloseServiceHandle(hscm
);
4148 return ERROR_SUCCESS
;
4151 static UINT
ACTION_InstallServices( MSIPACKAGE
*package
)
4155 static const WCHAR ExecSeqQuery
[] =
4156 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4157 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4159 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4160 if (rc
!= ERROR_SUCCESS
)
4161 return ERROR_SUCCESS
;
4163 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallService
, package
);
4164 msiobj_release(&view
->hdr
);
4169 static UINT
msi_unimplemented_action_stub( MSIPACKAGE
*package
,
4170 LPCSTR action
, LPCWSTR table
)
4172 static const WCHAR query
[] = {
4173 'S','E','L','E','C','T',' ','*',' ',
4174 'F','R','O','M',' ','`','%','s','`',0 };
4175 MSIQUERY
*view
= NULL
;
4179 r
= MSI_OpenQuery( package
->db
, &view
, query
, table
);
4180 if (r
== ERROR_SUCCESS
)
4182 r
= MSI_IterateRecords(view
, &count
, NULL
, package
);
4183 msiobj_release(&view
->hdr
);
4187 FIXME("%s -> %u ignored %s table values\n",
4188 action
, count
, debugstr_w(table
));
4190 return ERROR_SUCCESS
;
4193 static UINT
ACTION_AllocateRegistrySpace( MSIPACKAGE
*package
)
4195 TRACE("%p\n", package
);
4196 return ERROR_SUCCESS
;
4199 static UINT
ACTION_RemoveIniValues( MSIPACKAGE
*package
)
4201 static const WCHAR table
[] =
4202 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
4203 return msi_unimplemented_action_stub( package
, "RemoveIniValues", table
);
4206 static UINT
ACTION_MoveFiles( MSIPACKAGE
*package
)
4208 static const WCHAR table
[] = { 'M','o','v','e','F','i','l','e',0 };
4209 return msi_unimplemented_action_stub( package
, "MoveFiles", table
);
4212 static UINT
ACTION_PatchFiles( MSIPACKAGE
*package
)
4214 static const WCHAR table
[] = { 'P','a','t','c','h',0 };
4215 return msi_unimplemented_action_stub( package
, "PatchFiles", table
);
4218 static UINT
ACTION_BindImage( MSIPACKAGE
*package
)
4220 static const WCHAR table
[] = { 'B','i','n','d','I','m','a','g','e',0 };
4221 return msi_unimplemented_action_stub( package
, "BindImage", table
);
4224 static UINT
ACTION_IsolateComponents( MSIPACKAGE
*package
)
4226 static const WCHAR table
[] = {
4227 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
4228 return msi_unimplemented_action_stub( package
, "IsolateComponents", table
);
4231 static UINT
ACTION_MigrateFeatureStates( MSIPACKAGE
*package
)
4233 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
4234 return msi_unimplemented_action_stub( package
, "MigrateFeatureStates", table
);
4237 static UINT
ACTION_SelfUnregModules( MSIPACKAGE
*package
)
4239 static const WCHAR table
[] = { 'S','e','l','f','R','e','g',0 };
4240 return msi_unimplemented_action_stub( package
, "SelfUnregModules", table
);
4243 static UINT
ACTION_StartServices( MSIPACKAGE
*package
)
4245 static const WCHAR table
[] = {
4246 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4247 return msi_unimplemented_action_stub( package
, "StartServices", table
);
4250 static UINT
ACTION_StopServices( MSIPACKAGE
*package
)
4252 static const WCHAR table
[] = {
4253 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4254 return msi_unimplemented_action_stub( package
, "StopServices", table
);
4257 static UINT
ACTION_DeleteServices( MSIPACKAGE
*package
)
4259 static const WCHAR table
[] = {
4260 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4261 return msi_unimplemented_action_stub( package
, "DeleteServices", table
);
4264 static UINT
ACTION_WriteEnvironmentStrings( MSIPACKAGE
*package
)
4266 static const WCHAR table
[] = {
4267 'E','n','v','i','r','o','n','m','e','n','t',0 };
4268 return msi_unimplemented_action_stub( package
, "WriteEnvironmentStrings", table
);
4271 static UINT
ACTION_RemoveEnvironmentStrings( MSIPACKAGE
*package
)
4273 static const WCHAR table
[] = {
4274 'E','n','v','i','r','o','n','m','e','n','t',0 };
4275 return msi_unimplemented_action_stub( package
, "RemoveEnvironmentStrings", table
);
4278 static UINT
ACTION_MsiPublishAssemblies( MSIPACKAGE
*package
)
4280 static const WCHAR table
[] = {
4281 'M','s','i','A','s','s','e','m','b','l','y',0 };
4282 return msi_unimplemented_action_stub( package
, "MsiPublishAssemblies", table
);
4285 static UINT
ACTION_MsiUnpublishAssemblies( MSIPACKAGE
*package
)
4287 static const WCHAR table
[] = {
4288 'M','s','i','A','s','s','e','m','b','l','y',0 };
4289 return msi_unimplemented_action_stub( package
, "MsiUnpublishAssemblies", table
);
4292 static UINT
ACTION_UnregisterFonts( MSIPACKAGE
*package
)
4294 static const WCHAR table
[] = { 'F','o','n','t',0 };
4295 return msi_unimplemented_action_stub( package
, "UnregisterFonts", table
);
4298 static UINT
ACTION_CCPSearch( MSIPACKAGE
*package
)
4300 static const WCHAR table
[] = { 'C','C','P','S','e','a','r','c','h',0 };
4301 return msi_unimplemented_action_stub( package
, "CCPSearch", table
);
4304 static UINT
ACTION_RMCCPSearch( MSIPACKAGE
*package
)
4306 static const WCHAR table
[] = { 'C','C','P','S','e','a','r','c','h',0 };
4307 return msi_unimplemented_action_stub( package
, "RMCCPSearch", table
);
4310 static UINT
ACTION_RegisterComPlus( MSIPACKAGE
*package
)
4312 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
4313 return msi_unimplemented_action_stub( package
, "RegisterComPlus", table
);
4316 static UINT
ACTION_UnregisterComPlus( MSIPACKAGE
*package
)
4318 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
4319 return msi_unimplemented_action_stub( package
, "UnregisterComPlus", table
);
4322 static struct _actions StandardActions
[] = {
4323 { szAllocateRegistrySpace
, ACTION_AllocateRegistrySpace
},
4324 { szAppSearch
, ACTION_AppSearch
},
4325 { szBindImage
, ACTION_BindImage
},
4326 { szCCPSearch
, ACTION_CCPSearch
},
4327 { szCostFinalize
, ACTION_CostFinalize
},
4328 { szCostInitialize
, ACTION_CostInitialize
},
4329 { szCreateFolders
, ACTION_CreateFolders
},
4330 { szCreateShortcuts
, ACTION_CreateShortcuts
},
4331 { szDeleteServices
, ACTION_DeleteServices
},
4332 { szDisableRollback
, NULL
},
4333 { szDuplicateFiles
, ACTION_DuplicateFiles
},
4334 { szExecuteAction
, ACTION_ExecuteAction
},
4335 { szFileCost
, ACTION_FileCost
},
4336 { szFindRelatedProducts
, ACTION_FindRelatedProducts
},
4337 { szForceReboot
, ACTION_ForceReboot
},
4338 { szInstallAdminPackage
, NULL
},
4339 { szInstallExecute
, ACTION_InstallExecute
},
4340 { szInstallExecuteAgain
, ACTION_InstallExecute
},
4341 { szInstallFiles
, ACTION_InstallFiles
},
4342 { szInstallFinalize
, ACTION_InstallFinalize
},
4343 { szInstallInitialize
, ACTION_InstallInitialize
},
4344 { szInstallSFPCatalogFile
, NULL
},
4345 { szInstallValidate
, ACTION_InstallValidate
},
4346 { szIsolateComponents
, ACTION_IsolateComponents
},
4347 { szLaunchConditions
, ACTION_LaunchConditions
},
4348 { szMigrateFeatureStates
, ACTION_MigrateFeatureStates
},
4349 { szMoveFiles
, ACTION_MoveFiles
},
4350 { szMsiPublishAssemblies
, ACTION_MsiPublishAssemblies
},
4351 { szMsiUnpublishAssemblies
, ACTION_MsiUnpublishAssemblies
},
4352 { szInstallODBC
, NULL
},
4353 { szInstallServices
, ACTION_InstallServices
},
4354 { szPatchFiles
, ACTION_PatchFiles
},
4355 { szProcessComponents
, ACTION_ProcessComponents
},
4356 { szPublishComponents
, ACTION_PublishComponents
},
4357 { szPublishFeatures
, ACTION_PublishFeatures
},
4358 { szPublishProduct
, ACTION_PublishProduct
},
4359 { szRegisterClassInfo
, ACTION_RegisterClassInfo
},
4360 { szRegisterComPlus
, ACTION_RegisterComPlus
},
4361 { szRegisterExtensionInfo
, ACTION_RegisterExtensionInfo
},
4362 { szRegisterFonts
, ACTION_RegisterFonts
},
4363 { szRegisterMIMEInfo
, ACTION_RegisterMIMEInfo
},
4364 { szRegisterProduct
, ACTION_RegisterProduct
},
4365 { szRegisterProgIdInfo
, ACTION_RegisterProgIdInfo
},
4366 { szRegisterTypeLibraries
, ACTION_RegisterTypeLibraries
},
4367 { szRegisterUser
, ACTION_RegisterUser
},
4368 { szRemoveDuplicateFiles
, NULL
},
4369 { szRemoveEnvironmentStrings
, ACTION_RemoveEnvironmentStrings
},
4370 { szRemoveExistingProducts
, NULL
},
4371 { szRemoveFiles
, ACTION_RemoveFiles
},
4372 { szRemoveFolders
, NULL
},
4373 { szRemoveIniValues
, ACTION_RemoveIniValues
},
4374 { szRemoveODBC
, NULL
},
4375 { szRemoveRegistryValues
, NULL
},
4376 { szRemoveShortcuts
, NULL
},
4377 { szResolveSource
, ACTION_ResolveSource
},
4378 { szRMCCPSearch
, ACTION_RMCCPSearch
},
4379 { szScheduleReboot
, NULL
},
4380 { szSelfRegModules
, ACTION_SelfRegModules
},
4381 { szSelfUnregModules
, ACTION_SelfUnregModules
},
4382 { szSetODBCFolders
, NULL
},
4383 { szStartServices
, ACTION_StartServices
},
4384 { szStopServices
, ACTION_StopServices
},
4385 { szUnpublishComponents
, NULL
},
4386 { szUnpublishFeatures
, NULL
},
4387 { szUnregisterClassInfo
, NULL
},
4388 { szUnregisterComPlus
, ACTION_UnregisterComPlus
},
4389 { szUnregisterExtensionInfo
, NULL
},
4390 { szUnregisterFonts
, ACTION_UnregisterFonts
},
4391 { szUnregisterMIMEInfo
, NULL
},
4392 { szUnregisterProgIdInfo
, NULL
},
4393 { szUnregisterTypeLibraries
, NULL
},
4394 { szValidateProductID
, NULL
},
4395 { szWriteEnvironmentStrings
, ACTION_WriteEnvironmentStrings
},
4396 { szWriteIniValues
, ACTION_WriteIniValues
},
4397 { szWriteRegistryValues
, ACTION_WriteRegistryValues
},