2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
26 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
38 #include "wine/debug.h"
43 #include "wine/unicode.h"
46 #define REG_PROGRESS_VALUE 13200
47 #define COMPONENT_PROGRESS_VALUE 24000
49 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
54 static UINT
ACTION_ProcessExecSequence(MSIPACKAGE
*package
, BOOL UIran
);
55 static UINT
ACTION_ProcessUISequence(MSIPACKAGE
*package
);
56 static UINT
ACTION_PerformActionSequence(MSIPACKAGE
*package
, UINT seq
, BOOL UI
);
59 * consts and values used
61 static const WCHAR c_colon
[] = {'C',':','\\',0};
63 static const WCHAR szCreateFolders
[] =
64 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
65 static const WCHAR szCostFinalize
[] =
66 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
67 const WCHAR szInstallFiles
[] =
68 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
69 const WCHAR szDuplicateFiles
[] =
70 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
71 static const WCHAR szWriteRegistryValues
[] =
72 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
73 'V','a','l','u','e','s',0};
74 static const WCHAR szCostInitialize
[] =
75 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
76 static const WCHAR szFileCost
[] =
77 {'F','i','l','e','C','o','s','t',0};
78 static const WCHAR szInstallInitialize
[] =
79 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
80 static const WCHAR szInstallValidate
[] =
81 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
82 static const WCHAR szLaunchConditions
[] =
83 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
84 static const WCHAR szProcessComponents
[] =
85 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
86 static const WCHAR szRegisterTypeLibraries
[] =
87 {'R','e','g','i','s','t','e','r','T','y','p','e',
88 'L','i','b','r','a','r','i','e','s',0};
89 const WCHAR szRegisterClassInfo
[] =
90 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
91 const WCHAR szRegisterProgIdInfo
[] =
92 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
93 static const WCHAR szCreateShortcuts
[] =
94 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
95 static const WCHAR szPublishProduct
[] =
96 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
97 static const WCHAR szWriteIniValues
[] =
98 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
99 static const WCHAR szSelfRegModules
[] =
100 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
101 static const WCHAR szPublishFeatures
[] =
102 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
103 static const WCHAR szRegisterProduct
[] =
104 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
105 static const WCHAR szInstallExecute
[] =
106 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
107 static const WCHAR szInstallExecuteAgain
[] =
108 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
109 'A','g','a','i','n',0};
110 static const WCHAR szInstallFinalize
[] =
111 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
112 static const WCHAR szForceReboot
[] =
113 {'F','o','r','c','e','R','e','b','o','o','t',0};
114 static const WCHAR szResolveSource
[] =
115 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
116 static const WCHAR szAppSearch
[] =
117 {'A','p','p','S','e','a','r','c','h',0};
118 static const WCHAR szAllocateRegistrySpace
[] =
119 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
120 'S','p','a','c','e',0};
121 static const WCHAR szBindImage
[] =
122 {'B','i','n','d','I','m','a','g','e',0};
123 static const WCHAR szCCPSearch
[] =
124 {'C','C','P','S','e','a','r','c','h',0};
125 static const WCHAR szDeleteServices
[] =
126 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
127 static const WCHAR szDisableRollback
[] =
128 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
129 static const WCHAR szExecuteAction
[] =
130 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
131 const WCHAR szFindRelatedProducts
[] =
132 {'F','i','n','d','R','e','l','a','t','e','d',
133 'P','r','o','d','u','c','t','s',0};
134 static const WCHAR szInstallAdminPackage
[] =
135 {'I','n','s','t','a','l','l','A','d','m','i','n',
136 'P','a','c','k','a','g','e',0};
137 static const WCHAR szInstallSFPCatalogFile
[] =
138 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
140 static const WCHAR szIsolateComponents
[] =
141 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
142 const WCHAR szMigrateFeatureStates
[] =
143 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
144 'S','t','a','t','e','s',0};
145 const WCHAR szMoveFiles
[] =
146 {'M','o','v','e','F','i','l','e','s',0};
147 static const WCHAR szMsiPublishAssemblies
[] =
148 {'M','s','i','P','u','b','l','i','s','h',
149 'A','s','s','e','m','b','l','i','e','s',0};
150 static const WCHAR szMsiUnpublishAssemblies
[] =
151 {'M','s','i','U','n','p','u','b','l','i','s','h',
152 'A','s','s','e','m','b','l','i','e','s',0};
153 static const WCHAR szInstallODBC
[] =
154 {'I','n','s','t','a','l','l','O','D','B','C',0};
155 static const WCHAR szInstallServices
[] =
156 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
157 const WCHAR szPatchFiles
[] =
158 {'P','a','t','c','h','F','i','l','e','s',0};
159 static const WCHAR szPublishComponents
[] =
160 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
161 static const WCHAR szRegisterComPlus
[] =
162 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
163 const WCHAR szRegisterExtensionInfo
[] =
164 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
166 static const WCHAR szRegisterFonts
[] =
167 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
168 const WCHAR szRegisterMIMEInfo
[] =
169 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
170 static const WCHAR szRegisterUser
[] =
171 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
172 const WCHAR szRemoveDuplicateFiles
[] =
173 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
174 'F','i','l','e','s',0};
175 static const WCHAR szRemoveEnvironmentStrings
[] =
176 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
177 'S','t','r','i','n','g','s',0};
178 const WCHAR szRemoveExistingProducts
[] =
179 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
180 'P','r','o','d','u','c','t','s',0};
181 const WCHAR szRemoveFiles
[] =
182 {'R','e','m','o','v','e','F','i','l','e','s',0};
183 static const WCHAR szRemoveFolders
[] =
184 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
185 static const WCHAR szRemoveIniValues
[] =
186 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
187 static const WCHAR szRemoveODBC
[] =
188 {'R','e','m','o','v','e','O','D','B','C',0};
189 static const WCHAR szRemoveRegistryValues
[] =
190 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
191 'V','a','l','u','e','s',0};
192 static const WCHAR szRemoveShortcuts
[] =
193 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
194 static const WCHAR szRMCCPSearch
[] =
195 {'R','M','C','C','P','S','e','a','r','c','h',0};
196 static const WCHAR szScheduleReboot
[] =
197 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
198 static const WCHAR szSelfUnregModules
[] =
199 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
200 static const WCHAR szSetODBCFolders
[] =
201 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
202 static const WCHAR szStartServices
[] =
203 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
204 static const WCHAR szStopServices
[] =
205 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
206 static const WCHAR szUnpublishComponents
[] =
207 {'U','n','p','u','b','l','i','s','h',
208 'C','o','m','p','o','n','e','n','t','s',0};
209 static const WCHAR szUnpublishFeatures
[] =
210 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
211 const WCHAR szUnregisterClassInfo
[] =
212 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
214 static const WCHAR szUnregisterComPlus
[] =
215 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
216 const WCHAR szUnregisterExtensionInfo
[] =
217 {'U','n','r','e','g','i','s','t','e','r',
218 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
219 static const WCHAR szUnregisterFonts
[] =
220 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
221 const WCHAR szUnregisterMIMEInfo
[] =
222 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
223 const WCHAR szUnregisterProgIdInfo
[] =
224 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
226 static const WCHAR szUnregisterTypeLibraries
[] =
227 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
228 'L','i','b','r','a','r','i','e','s',0};
229 static const WCHAR szValidateProductID
[] =
230 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
231 static const WCHAR szWriteEnvironmentStrings
[] =
232 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
233 'S','t','r','i','n','g','s',0};
235 /* action handlers */
236 typedef UINT (*STANDARDACTIONHANDLER
)(MSIPACKAGE
*);
240 STANDARDACTIONHANDLER handler
;
243 static struct _actions StandardActions
[];
246 /********************************************************
248 ********************************************************/
250 static void ui_actionstart(MSIPACKAGE
*package
, LPCWSTR action
)
252 static const WCHAR Query_t
[] =
253 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
254 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
255 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
256 ' ','\'','%','s','\'',0};
259 row
= MSI_QueryGetRecord( package
->db
, Query_t
, action
);
262 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONSTART
, row
);
263 msiobj_release(&row
->hdr
);
266 static void ui_actioninfo(MSIPACKAGE
*package
, LPCWSTR action
, BOOL start
,
270 static const WCHAR template_s
[]=
271 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
273 static const WCHAR template_e
[]=
274 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
275 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
277 static const WCHAR format
[] =
278 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
282 GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, NULL
, format
, timet
, 0x100);
284 sprintfW(message
,template_s
,timet
,action
);
286 sprintfW(message
,template_e
,timet
,action
,rc
);
288 row
= MSI_CreateRecord(1);
289 MSI_RecordSetStringW(row
,1,message
);
291 MSI_ProcessMessage(package
, INSTALLMESSAGE_INFO
, row
);
292 msiobj_release(&row
->hdr
);
295 static UINT
msi_parse_command_line( MSIPACKAGE
*package
, LPCWSTR szCommandLine
)
300 LPWSTR prop
= NULL
, val
= NULL
;
303 return ERROR_SUCCESS
;
315 TRACE("Looking at %s\n",debugstr_w(ptr
));
317 ptr2
= strchrW(ptr
,'=');
320 ERR("command line contains unknown string : %s\n", debugstr_w(ptr
));
327 prop
= msi_alloc((len
+1)*sizeof(WCHAR
));
328 memcpy(prop
,ptr
,len
*sizeof(WCHAR
));
334 while (*ptr
&& (quote
|| (!quote
&& *ptr
!=' ')))
347 val
= msi_alloc((len
+1)*sizeof(WCHAR
));
348 memcpy(val
,ptr2
,len
*sizeof(WCHAR
));
351 if (lstrlenW(prop
) > 0)
353 TRACE("Found commandline property (%s) = (%s)\n",
354 debugstr_w(prop
), debugstr_w(val
));
355 MSI_SetPropertyW(package
,prop
,val
);
361 return ERROR_SUCCESS
;
365 static LPWSTR
* msi_split_string( LPCWSTR str
, WCHAR sep
)
368 LPWSTR p
, *ret
= NULL
;
374 /* count the number of substrings */
375 for ( pc
= str
, count
= 0; pc
; count
++ )
377 pc
= strchrW( pc
, sep
);
382 /* allocate space for an array of substring pointers and the substrings */
383 ret
= msi_alloc( (count
+1) * sizeof (LPWSTR
) +
384 (lstrlenW(str
)+1) * sizeof(WCHAR
) );
388 /* copy the string and set the pointers */
389 p
= (LPWSTR
) &ret
[count
+1];
391 for( count
= 0; (ret
[count
] = p
); count
++ )
393 p
= strchrW( p
, sep
);
401 static UINT
msi_check_transform_applicable( MSIPACKAGE
*package
, IStorage
*patch
)
403 WCHAR szProductCode
[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
404 LPWSTR prod_code
, patch_product
;
407 prod_code
= msi_dup_property( package
, szProductCode
);
408 patch_product
= msi_get_suminfo_product( patch
);
410 TRACE("db = %s patch = %s\n", debugstr_w(prod_code
), debugstr_w(patch_product
));
412 if ( strstrW( patch_product
, prod_code
) )
415 ret
= ERROR_FUNCTION_FAILED
;
417 msi_free( patch_product
);
418 msi_free( prod_code
);
423 static UINT
msi_apply_substorage_transform( MSIPACKAGE
*package
,
424 MSIDATABASE
*patch_db
, LPCWSTR name
)
426 UINT ret
= ERROR_FUNCTION_FAILED
;
427 IStorage
*stg
= NULL
;
430 TRACE("%p %s\n", package
, debugstr_w(name
) );
434 ERR("expected a colon in %s\n", debugstr_w(name
));
435 return ERROR_FUNCTION_FAILED
;
438 r
= IStorage_OpenStorage( patch_db
->storage
, name
, NULL
, STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
441 ret
= msi_check_transform_applicable( package
, stg
);
442 if (ret
== ERROR_SUCCESS
)
443 msi_table_apply_transform( package
->db
, stg
);
445 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name
));
446 IStorage_Release( stg
);
449 ERR("failed to open substorage %s\n", debugstr_w(name
));
451 return ERROR_SUCCESS
;
454 static UINT
msi_check_patch_applicable( MSIPACKAGE
*package
, MSISUMMARYINFO
*si
)
456 static const WCHAR szProdID
[] = { 'P','r','o','d','u','c','t','I','D',0 };
457 LPWSTR guid_list
, *guids
, product_id
;
458 UINT i
, ret
= ERROR_FUNCTION_FAILED
;
460 product_id
= msi_dup_property( package
, szProdID
);
463 /* FIXME: the property ProductID should be written into the DB somewhere */
464 ERR("no product ID to check\n");
465 return ERROR_SUCCESS
;
468 guid_list
= msi_suminfo_dup_string( si
, PID_TEMPLATE
);
469 guids
= msi_split_string( guid_list
, ';' );
470 for ( i
= 0; guids
[i
] && ret
!= ERROR_SUCCESS
; i
++ )
472 if (!lstrcmpW( guids
[i
], product_id
))
476 msi_free( guid_list
);
477 msi_free( product_id
);
482 static UINT
msi_parse_patch_summary( MSIPACKAGE
*package
, MSIDATABASE
*patch_db
)
485 LPWSTR str
, *substorage
;
486 UINT i
, r
= ERROR_SUCCESS
;
488 si
= MSI_GetSummaryInformationW( patch_db
->storage
, 0 );
490 return ERROR_FUNCTION_FAILED
;
492 msi_check_patch_applicable( package
, si
);
494 /* enumerate the substorage */
495 str
= msi_suminfo_dup_string( si
, PID_LASTAUTHOR
);
496 substorage
= msi_split_string( str
, ';' );
497 for ( i
= 0; substorage
&& substorage
[i
] && r
== ERROR_SUCCESS
; i
++ )
498 r
= msi_apply_substorage_transform( package
, patch_db
, substorage
[i
] );
499 msi_free( substorage
);
502 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
504 msiobj_release( &si
->hdr
);
509 static UINT
msi_apply_patch_package( MSIPACKAGE
*package
, LPCWSTR file
)
511 MSIDATABASE
*patch_db
= NULL
;
514 TRACE("%p %s\n", package
, debugstr_w( file
) );
517 * We probably want to make sure we only open a patch collection here.
518 * Patch collections (.msp) and databases (.msi) have different GUIDs
519 * but currently MSI_OpenDatabaseW will accept both.
521 r
= MSI_OpenDatabaseW( file
, MSIDBOPEN_READONLY
, &patch_db
);
522 if ( r
!= ERROR_SUCCESS
)
524 ERR("failed to open patch collection %s\n", debugstr_w( file
) );
528 msi_parse_patch_summary( package
, patch_db
);
531 * There might be a CAB file in the patch package,
532 * so append it to the list of storage to search for streams.
534 append_storage_to_db( package
->db
, patch_db
->storage
);
536 msiobj_release( &patch_db
->hdr
);
538 return ERROR_SUCCESS
;
541 /* get the PATCH property, and apply all the patches it specifies */
542 static UINT
msi_apply_patches( MSIPACKAGE
*package
)
544 static const WCHAR szPatch
[] = { 'P','A','T','C','H',0 };
545 LPWSTR patch_list
, *patches
;
546 UINT i
, r
= ERROR_SUCCESS
;
548 patch_list
= msi_dup_property( package
, szPatch
);
550 TRACE("patches to be applied: %s\n", debugstr_w( patch_list
) );
552 patches
= msi_split_string( patch_list
, ';' );
553 for( i
=0; patches
&& patches
[i
] && r
== ERROR_SUCCESS
; i
++ )
554 r
= msi_apply_patch_package( package
, patches
[i
] );
557 msi_free( patch_list
);
562 static UINT
msi_apply_transforms( MSIPACKAGE
*package
)
564 static const WCHAR szTransforms
[] = {
565 'T','R','A','N','S','F','O','R','M','S',0 };
566 LPWSTR xform_list
, *xforms
;
567 UINT i
, r
= ERROR_SUCCESS
;
569 xform_list
= msi_dup_property( package
, szTransforms
);
570 xforms
= msi_split_string( xform_list
, ';' );
572 for( i
=0; xforms
&& xforms
[i
] && r
== ERROR_SUCCESS
; i
++ )
574 if (xforms
[i
][0] == ':')
575 r
= msi_apply_substorage_transform( package
, package
->db
, &xforms
[i
][1] );
577 r
= MSI_DatabaseApplyTransformW( package
->db
, xforms
[i
], 0 );
581 msi_free( xform_list
);
586 /****************************************************
587 * TOP level entry points
588 *****************************************************/
590 UINT
MSI_InstallPackage( MSIPACKAGE
*package
, LPCWSTR szPackagePath
,
591 LPCWSTR szCommandLine
)
595 static const WCHAR szUILevel
[] = {'U','I','L','e','v','e','l',0};
596 static const WCHAR szAction
[] = {'A','C','T','I','O','N',0};
597 static const WCHAR szInstall
[] = {'I','N','S','T','A','L','L',0};
599 MSI_SetPropertyW(package
, szAction
, szInstall
);
601 package
->script
= msi_alloc_zero(sizeof(MSISCRIPT
));
603 package
->script
->InWhatSequence
= SEQUENCE_INSTALL
;
607 LPWSTR p
, check
, path
;
609 path
= strdupW(szPackagePath
);
610 p
= strrchrW(path
,'\\');
619 path
= msi_alloc(MAX_PATH
*sizeof(WCHAR
));
620 GetCurrentDirectoryW(MAX_PATH
,path
);
624 check
= msi_dup_property( package
, cszSourceDir
);
626 MSI_SetPropertyW(package
, cszSourceDir
, path
);
629 check
= msi_dup_property( package
, cszSOURCEDIR
);
631 MSI_SetPropertyW(package
, cszSOURCEDIR
, path
);
633 msi_free( package
->PackagePath
);
634 package
->PackagePath
= path
;
639 msi_parse_command_line( package
, szCommandLine
);
641 msi_apply_transforms( package
);
642 msi_apply_patches( package
);
644 if ( msi_get_property_int(package
, szUILevel
, 0) >= INSTALLUILEVEL_REDUCED
)
646 package
->script
->InWhatSequence
|= SEQUENCE_UI
;
647 rc
= ACTION_ProcessUISequence(package
);
649 if (rc
== ERROR_SUCCESS
)
651 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
652 rc
= ACTION_ProcessExecSequence(package
,TRUE
);
656 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
660 /* install was halted but should be considered a success */
664 package
->script
->CurrentlyScripting
= FALSE
;
666 /* process the ending type action */
667 if (rc
== ERROR_SUCCESS
)
668 ACTION_PerformActionSequence(package
,-1,ui
);
669 else if (rc
== ERROR_INSTALL_USEREXIT
)
670 ACTION_PerformActionSequence(package
,-2,ui
);
671 else if (rc
== ERROR_INSTALL_SUSPEND
)
672 ACTION_PerformActionSequence(package
,-4,ui
);
674 ACTION_PerformActionSequence(package
,-3,ui
);
676 /* finish up running custom actions */
677 ACTION_FinishCustomActions(package
);
682 static UINT
ACTION_PerformActionSequence(MSIPACKAGE
*package
, UINT seq
, BOOL UI
)
684 UINT rc
= ERROR_SUCCESS
;
686 static const WCHAR ExecSeqQuery
[] =
687 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
688 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
689 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
690 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
692 static const WCHAR UISeqQuery
[] =
693 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
694 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
695 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
696 ' ', '=',' ','%','i',0};
699 row
= MSI_QueryGetRecord(package
->db
, UISeqQuery
, seq
);
701 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
, seq
);
705 LPCWSTR action
, cond
;
707 TRACE("Running the actions\n");
709 /* check conditions */
710 cond
= MSI_RecordGetString(row
,2);
712 /* this is a hack to skip errors in the condition code */
713 if (MSI_EvaluateConditionW(package
, cond
) == MSICONDITION_FALSE
)
716 action
= MSI_RecordGetString(row
,1);
719 ERR("failed to fetch action\n");
720 rc
= ERROR_FUNCTION_FAILED
;
725 rc
= ACTION_PerformUIAction(package
,action
);
727 rc
= ACTION_PerformAction(package
,action
,FALSE
);
729 msiobj_release(&row
->hdr
);
740 } iterate_action_param
;
742 static UINT
ITERATE_Actions(MSIRECORD
*row
, LPVOID param
)
744 iterate_action_param
*iap
= (iterate_action_param
*)param
;
746 LPCWSTR cond
, action
;
748 action
= MSI_RecordGetString(row
,1);
751 ERR("Error is retrieving action name\n");
752 return ERROR_FUNCTION_FAILED
;
755 /* check conditions */
756 cond
= MSI_RecordGetString(row
,2);
758 /* this is a hack to skip errors in the condition code */
759 if (MSI_EvaluateConditionW(iap
->package
, cond
) == MSICONDITION_FALSE
)
761 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action
));
762 return ERROR_SUCCESS
;
766 rc
= ACTION_PerformUIAction(iap
->package
,action
);
768 rc
= ACTION_PerformAction(iap
->package
,action
,FALSE
);
770 msi_dialog_check_messages( NULL
);
772 if (iap
->package
->CurrentInstallState
!= ERROR_SUCCESS
)
773 rc
= iap
->package
->CurrentInstallState
;
775 if (rc
== ERROR_FUNCTION_NOT_CALLED
)
778 if (rc
!= ERROR_SUCCESS
)
779 ERR("Execution halted, action %s returned %i\n", debugstr_w(action
), rc
);
784 UINT
MSI_Sequence( MSIPACKAGE
*package
, LPCWSTR szTable
, INT iSequenceMode
)
788 static const WCHAR query
[] =
789 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
791 ' ','W','H','E','R','E',' ',
792 '`','S','e','q','u','e','n','c','e','`',' ',
793 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
794 '`','S','e','q','u','e','n','c','e','`',0};
795 iterate_action_param iap
;
798 * FIXME: probably should be checking UILevel in the
799 * ACTION_PerformUIAction/ACTION_PerformAction
800 * rather than saving the UI level here. Those
801 * two functions can be merged too.
803 iap
.package
= package
;
806 TRACE("%p %s %i\n", package
, debugstr_w(szTable
), iSequenceMode
);
808 r
= MSI_OpenQuery( package
->db
, &view
, query
, szTable
);
809 if (r
== ERROR_SUCCESS
)
811 r
= MSI_IterateRecords( view
, NULL
, ITERATE_Actions
, &iap
);
812 msiobj_release(&view
->hdr
);
818 static UINT
ACTION_ProcessExecSequence(MSIPACKAGE
*package
, BOOL UIran
)
822 static const WCHAR ExecSeqQuery
[] =
823 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
824 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
825 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
826 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
827 'O','R','D','E','R',' ', 'B','Y',' ',
828 '`','S','e','q','u','e','n','c','e','`',0 };
830 static const WCHAR IVQuery
[] =
831 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
832 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
833 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
834 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
835 ' ','\'', 'I','n','s','t','a','l','l',
836 'V','a','l','i','d','a','t','e','\'', 0};
838 iterate_action_param iap
;
840 iap
.package
= package
;
843 if (package
->script
->ExecuteSequenceRun
)
845 TRACE("Execute Sequence already Run\n");
846 return ERROR_SUCCESS
;
849 package
->script
->ExecuteSequenceRun
= TRUE
;
851 /* get the sequence number */
854 row
= MSI_QueryGetRecord(package
->db
, IVQuery
);
856 return ERROR_FUNCTION_FAILED
;
857 seq
= MSI_RecordGetInteger(row
,1);
858 msiobj_release(&row
->hdr
);
861 rc
= MSI_OpenQuery(package
->db
, &view
, ExecSeqQuery
, seq
);
862 if (rc
== ERROR_SUCCESS
)
864 TRACE("Running the actions\n");
866 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, &iap
);
867 msiobj_release(&view
->hdr
);
873 static UINT
ACTION_ProcessUISequence(MSIPACKAGE
*package
)
877 static const WCHAR ExecSeqQuery
[] =
878 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
879 '`','I','n','s','t','a','l','l',
880 'U','I','S','e','q','u','e','n','c','e','`',
881 ' ','W','H','E','R','E',' ',
882 '`','S','e','q','u','e','n','c','e','`',' ',
883 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
884 '`','S','e','q','u','e','n','c','e','`',0};
885 iterate_action_param iap
;
887 iap
.package
= package
;
890 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
892 if (rc
== ERROR_SUCCESS
)
894 TRACE("Running the actions\n");
896 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, &iap
);
897 msiobj_release(&view
->hdr
);
903 /********************************************************
904 * ACTION helper functions and functions that perform the actions
905 *******************************************************/
906 static BOOL
ACTION_HandleStandardAction(MSIPACKAGE
*package
, LPCWSTR action
,
907 UINT
* rc
, BOOL force
)
913 if (!run
&& !package
->script
->CurrentlyScripting
)
918 if (strcmpW(action
,szInstallFinalize
) == 0 ||
919 strcmpW(action
,szInstallExecute
) == 0 ||
920 strcmpW(action
,szInstallExecuteAgain
) == 0)
925 while (StandardActions
[i
].action
!= NULL
)
927 if (strcmpW(StandardActions
[i
].action
, action
)==0)
931 ui_actioninfo(package
, action
, TRUE
, 0);
932 *rc
= schedule_action(package
,INSTALL_SCRIPT
,action
);
933 ui_actioninfo(package
, action
, FALSE
, *rc
);
937 ui_actionstart(package
, action
);
938 if (StandardActions
[i
].handler
)
940 *rc
= StandardActions
[i
].handler(package
);
944 FIXME("unhandled standard action %s\n",debugstr_w(action
));
956 static BOOL
ACTION_HandleCustomAction( MSIPACKAGE
* package
, LPCWSTR action
,
957 UINT
* rc
, BOOL force
)
962 arc
= ACTION_CustomAction(package
,action
, force
);
964 if (arc
!= ERROR_CALL_NOT_IMPLEMENTED
)
973 * A lot of actions are really important even if they don't do anything
974 * explicit... Lots of properties are set at the beginning of the installation
975 * CostFinalize does a bunch of work to translate the directories and such
977 * But until I get write access to the database that is hard, so I am going to
978 * hack it to see if I can get something to run.
980 UINT
ACTION_PerformAction(MSIPACKAGE
*package
, const WCHAR
*action
, BOOL force
)
982 UINT rc
= ERROR_SUCCESS
;
985 TRACE("Performing action (%s)\n",debugstr_w(action
));
987 handled
= ACTION_HandleStandardAction(package
, action
, &rc
, force
);
990 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, force
);
994 FIXME("unhandled msi action %s\n",debugstr_w(action
));
995 rc
= ERROR_FUNCTION_NOT_CALLED
;
1001 UINT
ACTION_PerformUIAction(MSIPACKAGE
*package
, const WCHAR
*action
)
1003 UINT rc
= ERROR_SUCCESS
;
1004 BOOL handled
= FALSE
;
1006 TRACE("Performing action (%s)\n",debugstr_w(action
));
1008 handled
= ACTION_HandleStandardAction(package
, action
, &rc
,TRUE
);
1011 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, FALSE
);
1013 if( !handled
&& ACTION_DialogBox(package
,action
) == ERROR_SUCCESS
)
1018 FIXME("unhandled msi action %s\n",debugstr_w(action
));
1019 rc
= ERROR_FUNCTION_NOT_CALLED
;
1027 * Actual Action Handlers
1030 static UINT
ITERATE_CreateFolders(MSIRECORD
*row
, LPVOID param
)
1032 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1038 dir
= MSI_RecordGetString(row
,1);
1041 ERR("Unable to get folder id\n");
1042 return ERROR_SUCCESS
;
1045 full_path
= resolve_folder(package
,dir
,FALSE
,FALSE
,&folder
);
1048 ERR("Unable to resolve folder id %s\n",debugstr_w(dir
));
1049 return ERROR_SUCCESS
;
1052 TRACE("Folder is %s\n",debugstr_w(full_path
));
1055 uirow
= MSI_CreateRecord(1);
1056 MSI_RecordSetStringW(uirow
,1,full_path
);
1057 ui_actiondata(package
,szCreateFolders
,uirow
);
1058 msiobj_release( &uirow
->hdr
);
1060 if (folder
->State
== 0)
1061 create_full_pathW(full_path
);
1065 msi_free(full_path
);
1066 return ERROR_SUCCESS
;
1069 /* FIXME: probably should merge this with the above function */
1070 static UINT
msi_create_directory( MSIPACKAGE
* package
, LPCWSTR dir
)
1072 UINT rc
= ERROR_SUCCESS
;
1074 LPWSTR install_path
;
1076 install_path
= resolve_folder(package
, dir
, FALSE
, FALSE
, &folder
);
1078 return ERROR_FUNCTION_FAILED
;
1080 /* create the path */
1081 if (folder
->State
== 0)
1083 create_full_pathW(install_path
);
1086 msi_free(install_path
);
1091 UINT
msi_create_component_directories( MSIPACKAGE
*package
)
1095 /* create all the folders required by the components are going to install */
1096 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1098 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
1100 msi_create_directory( package
, comp
->Directory
);
1103 return ERROR_SUCCESS
;
1107 * Also we cannot enable/disable components either, so for now I am just going
1108 * to do all the directories for all the components.
1110 static UINT
ACTION_CreateFolders(MSIPACKAGE
*package
)
1112 static const WCHAR ExecSeqQuery
[] =
1113 {'S','E','L','E','C','T',' ',
1114 '`','D','i','r','e','c','t','o','r','y','_','`',
1115 ' ','F','R','O','M',' ',
1116 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1120 /* create all the empty folders specified in the CreateFolder table */
1121 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
1122 if (rc
!= ERROR_SUCCESS
)
1123 return ERROR_SUCCESS
;
1125 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateFolders
, package
);
1126 msiobj_release(&view
->hdr
);
1128 msi_create_component_directories( package
);
1133 static UINT
load_component( MSIRECORD
*row
, LPVOID param
)
1135 MSIPACKAGE
*package
= param
;
1138 comp
= msi_alloc_zero( sizeof(MSICOMPONENT
) );
1140 return ERROR_FUNCTION_FAILED
;
1142 list_add_tail( &package
->components
, &comp
->entry
);
1144 /* fill in the data */
1145 comp
->Component
= msi_dup_record_field( row
, 1 );
1147 TRACE("Loading Component %s\n", debugstr_w(comp
->Component
));
1149 comp
->ComponentId
= msi_dup_record_field( row
, 2 );
1150 comp
->Directory
= msi_dup_record_field( row
, 3 );
1151 comp
->Attributes
= MSI_RecordGetInteger(row
,4);
1152 comp
->Condition
= msi_dup_record_field( row
, 5 );
1153 comp
->KeyPath
= msi_dup_record_field( row
, 6 );
1155 comp
->Installed
= INSTALLSTATE_UNKNOWN
;
1156 msi_component_set_state( comp
, INSTALLSTATE_UNKNOWN
);
1158 return ERROR_SUCCESS
;
1161 static UINT
load_all_components( MSIPACKAGE
*package
)
1163 static const WCHAR query
[] = {
1164 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1165 '`','C','o','m','p','o','n','e','n','t','`',0 };
1169 if (!list_empty(&package
->components
))
1170 return ERROR_SUCCESS
;
1172 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1173 if (r
!= ERROR_SUCCESS
)
1176 r
= MSI_IterateRecords(view
, NULL
, load_component
, package
);
1177 msiobj_release(&view
->hdr
);
1182 MSIPACKAGE
*package
;
1183 MSIFEATURE
*feature
;
1186 static UINT
add_feature_component( MSIFEATURE
*feature
, MSICOMPONENT
*comp
)
1190 cl
= msi_alloc( sizeof (*cl
) );
1192 return ERROR_NOT_ENOUGH_MEMORY
;
1193 cl
->component
= comp
;
1194 list_add_tail( &feature
->Components
, &cl
->entry
);
1196 return ERROR_SUCCESS
;
1199 static UINT
add_feature_child( MSIFEATURE
*parent
, MSIFEATURE
*child
)
1203 fl
= msi_alloc( sizeof(*fl
) );
1205 return ERROR_NOT_ENOUGH_MEMORY
;
1206 fl
->feature
= child
;
1207 list_add_tail( &parent
->Children
, &fl
->entry
);
1209 return ERROR_SUCCESS
;
1212 static UINT
iterate_load_featurecomponents(MSIRECORD
*row
, LPVOID param
)
1214 _ilfs
* ilfs
= (_ilfs
*)param
;
1218 component
= MSI_RecordGetString(row
,1);
1220 /* check to see if the component is already loaded */
1221 comp
= get_loaded_component( ilfs
->package
, component
);
1224 ERR("unknown component %s\n", debugstr_w(component
));
1225 return ERROR_FUNCTION_FAILED
;
1228 add_feature_component( ilfs
->feature
, comp
);
1229 comp
->Enabled
= TRUE
;
1231 return ERROR_SUCCESS
;
1234 static MSIFEATURE
*find_feature_by_name( MSIPACKAGE
*package
, LPCWSTR name
)
1236 MSIFEATURE
*feature
;
1238 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1240 if ( !lstrcmpW( feature
->Feature
, name
) )
1247 static UINT
load_feature(MSIRECORD
* row
, LPVOID param
)
1249 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1250 MSIFEATURE
* feature
;
1251 static const WCHAR Query1
[] =
1252 {'S','E','L','E','C','T',' ',
1253 '`','C','o','m','p','o','n','e','n','t','_','`',
1254 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1255 'C','o','m','p','o','n','e','n','t','s','`',' ',
1256 'W','H','E','R','E',' ',
1257 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1262 /* fill in the data */
1264 feature
= msi_alloc_zero( sizeof (MSIFEATURE
) );
1266 return ERROR_NOT_ENOUGH_MEMORY
;
1268 list_init( &feature
->Children
);
1269 list_init( &feature
->Components
);
1271 feature
->Feature
= msi_dup_record_field( row
, 1 );
1273 TRACE("Loading feature %s\n",debugstr_w(feature
->Feature
));
1275 feature
->Feature_Parent
= msi_dup_record_field( row
, 2 );
1276 feature
->Title
= msi_dup_record_field( row
, 3 );
1277 feature
->Description
= msi_dup_record_field( row
, 4 );
1279 if (!MSI_RecordIsNull(row
,5))
1280 feature
->Display
= MSI_RecordGetInteger(row
,5);
1282 feature
->Level
= MSI_RecordGetInteger(row
,6);
1283 feature
->Directory
= msi_dup_record_field( row
, 7 );
1284 feature
->Attributes
= MSI_RecordGetInteger(row
,8);
1286 feature
->Installed
= INSTALLSTATE_UNKNOWN
;
1287 msi_feature_set_state( feature
, INSTALLSTATE_UNKNOWN
);
1289 list_add_tail( &package
->features
, &feature
->entry
);
1291 /* load feature components */
1293 rc
= MSI_OpenQuery( package
->db
, &view
, Query1
, feature
->Feature
);
1294 if (rc
!= ERROR_SUCCESS
)
1295 return ERROR_SUCCESS
;
1297 ilfs
.package
= package
;
1298 ilfs
.feature
= feature
;
1300 MSI_IterateRecords(view
, NULL
, iterate_load_featurecomponents
, &ilfs
);
1301 msiobj_release(&view
->hdr
);
1303 return ERROR_SUCCESS
;
1306 static UINT
find_feature_children(MSIRECORD
* row
, LPVOID param
)
1308 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1309 MSIFEATURE
*parent
, *child
;
1311 child
= find_feature_by_name( package
, MSI_RecordGetString( row
, 1 ) );
1313 return ERROR_FUNCTION_FAILED
;
1315 if (!child
->Feature_Parent
)
1316 return ERROR_SUCCESS
;
1318 parent
= find_feature_by_name( package
, child
->Feature_Parent
);
1320 return ERROR_FUNCTION_FAILED
;
1322 add_feature_child( parent
, child
);
1323 return ERROR_SUCCESS
;
1326 static UINT
load_all_features( MSIPACKAGE
*package
)
1328 static const WCHAR query
[] = {
1329 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1330 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1331 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1335 if (!list_empty(&package
->features
))
1336 return ERROR_SUCCESS
;
1338 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1339 if (r
!= ERROR_SUCCESS
)
1342 r
= MSI_IterateRecords( view
, NULL
, load_feature
, package
);
1343 if (r
!= ERROR_SUCCESS
)
1346 r
= MSI_IterateRecords( view
, NULL
, find_feature_children
, package
);
1347 msiobj_release( &view
->hdr
);
1352 static LPWSTR
folder_split_path(LPWSTR p
, WCHAR ch
)
1363 static UINT
load_file(MSIRECORD
*row
, LPVOID param
)
1365 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1369 /* fill in the data */
1371 file
= msi_alloc_zero( sizeof (MSIFILE
) );
1373 return ERROR_NOT_ENOUGH_MEMORY
;
1375 file
->File
= msi_dup_record_field( row
, 1 );
1377 component
= MSI_RecordGetString( row
, 2 );
1378 file
->Component
= get_loaded_component( package
, component
);
1380 if (!file
->Component
)
1381 ERR("Unfound Component %s\n",debugstr_w(component
));
1383 file
->FileName
= msi_dup_record_field( row
, 3 );
1384 reduce_to_longfilename( file
->FileName
);
1386 file
->ShortName
= msi_dup_record_field( row
, 3 );
1387 file
->LongName
= strdupW( folder_split_path(file
->ShortName
, '|'));
1389 file
->FileSize
= MSI_RecordGetInteger( row
, 4 );
1390 file
->Version
= msi_dup_record_field( row
, 5 );
1391 file
->Language
= msi_dup_record_field( row
, 6 );
1392 file
->Attributes
= MSI_RecordGetInteger( row
, 7 );
1393 file
->Sequence
= MSI_RecordGetInteger( row
, 8 );
1395 file
->state
= msifs_invalid
;
1397 /* if the compressed bits are not set in the file attributes,
1398 * then read the information from the package word count property
1400 if (file
->Attributes
& msidbFileAttributesCompressed
)
1402 file
->IsCompressed
= TRUE
;
1404 else if (file
->Attributes
& msidbFileAttributesNoncompressed
)
1406 file
->IsCompressed
= FALSE
;
1410 file
->IsCompressed
= package
->WordCount
& MSIWORDCOUNT_COMPRESSED
;
1413 TRACE("File Loaded (%s)\n",debugstr_w(file
->File
));
1415 list_add_tail( &package
->files
, &file
->entry
);
1417 return ERROR_SUCCESS
;
1420 static UINT
load_all_files(MSIPACKAGE
*package
)
1424 static const WCHAR Query
[] =
1425 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1426 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1427 '`','S','e','q','u','e','n','c','e','`', 0};
1429 if (!list_empty(&package
->files
))
1430 return ERROR_SUCCESS
;
1432 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
1433 if (rc
!= ERROR_SUCCESS
)
1434 return ERROR_SUCCESS
;
1436 rc
= MSI_IterateRecords(view
, NULL
, load_file
, package
);
1437 msiobj_release(&view
->hdr
);
1439 return ERROR_SUCCESS
;
1442 static UINT
load_folder( MSIRECORD
*row
, LPVOID param
)
1444 MSIPACKAGE
*package
= param
;
1445 static const WCHAR szDot
[] = { '.',0 };
1446 static WCHAR szEmpty
[] = { 0 };
1447 LPWSTR p
, tgt_short
, tgt_long
, src_short
, src_long
;
1450 folder
= msi_alloc_zero( sizeof (MSIFOLDER
) );
1452 return ERROR_NOT_ENOUGH_MEMORY
;
1454 folder
->Directory
= msi_dup_record_field( row
, 1 );
1456 TRACE("%s\n", debugstr_w(folder
->Directory
));
1458 p
= msi_dup_record_field(row
, 3);
1460 /* split src and target dir */
1462 src_short
= folder_split_path( p
, ':' );
1464 /* split the long and short paths */
1465 tgt_long
= folder_split_path( tgt_short
, '|' );
1466 src_long
= folder_split_path( src_short
, '|' );
1468 /* check for no-op dirs */
1469 if (!lstrcmpW(szDot
, tgt_short
))
1470 tgt_short
= szEmpty
;
1471 if (!lstrcmpW(szDot
, src_short
))
1472 src_short
= szEmpty
;
1475 tgt_long
= tgt_short
;
1478 src_short
= tgt_short
;
1479 src_long
= tgt_long
;
1483 src_long
= src_short
;
1485 /* FIXME: use the target short path too */
1486 folder
->TargetDefault
= strdupW(tgt_long
);
1487 folder
->SourceShortPath
= strdupW(src_short
);
1488 folder
->SourceLongPath
= strdupW(src_long
);
1491 TRACE("TargetDefault = %s\n",debugstr_w( folder
->TargetDefault
));
1492 TRACE("SourceLong = %s\n", debugstr_w( folder
->SourceLongPath
));
1493 TRACE("SourceShort = %s\n", debugstr_w( folder
->SourceShortPath
));
1495 folder
->Parent
= msi_dup_record_field( row
, 2 );
1497 folder
->Property
= msi_dup_property( package
, folder
->Directory
);
1499 list_add_tail( &package
->folders
, &folder
->entry
);
1501 TRACE("returning %p\n", folder
);
1503 return ERROR_SUCCESS
;
1506 static UINT
load_all_folders( MSIPACKAGE
*package
)
1508 static const WCHAR query
[] = {
1509 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1510 '`','D','i','r','e','c','t','o','r','y','`',0 };
1514 if (!list_empty(&package
->folders
))
1515 return ERROR_SUCCESS
;
1517 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1518 if (r
!= ERROR_SUCCESS
)
1521 r
= MSI_IterateRecords(view
, NULL
, load_folder
, package
);
1522 msiobj_release(&view
->hdr
);
1527 * I am not doing any of the costing functionality yet.
1528 * Mostly looking at doing the Component and Feature loading
1530 * The native MSI does A LOT of modification to tables here. Mostly adding
1531 * a lot of temporary columns to the Feature and Component tables.
1533 * note: Native msi also tracks the short filename. But I am only going to
1534 * track the long ones. Also looking at this directory table
1535 * it appears that the directory table does not get the parents
1536 * resolved base on property only based on their entries in the
1539 static UINT
ACTION_CostInitialize(MSIPACKAGE
*package
)
1541 static const WCHAR szCosting
[] =
1542 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1543 static const WCHAR szZero
[] = { '0', 0 };
1545 if ( 1 == msi_get_property_int( package
, szCosting
, 0 ) )
1546 return ERROR_SUCCESS
;
1548 MSI_SetPropertyW(package
, szCosting
, szZero
);
1549 MSI_SetPropertyW(package
, cszRootDrive
, c_colon
);
1551 load_all_components( package
);
1552 load_all_features( package
);
1553 load_all_files( package
);
1554 load_all_folders( package
);
1556 return ERROR_SUCCESS
;
1559 static UINT
execute_script(MSIPACKAGE
*package
, UINT script
)
1562 UINT rc
= ERROR_SUCCESS
;
1564 TRACE("Executing Script %i\n",script
);
1566 if (!package
->script
)
1568 ERR("no script!\n");
1569 return ERROR_FUNCTION_FAILED
;
1572 for (i
= 0; i
< package
->script
->ActionCount
[script
]; i
++)
1575 action
= package
->script
->Actions
[script
][i
];
1576 ui_actionstart(package
, action
);
1577 TRACE("Executing Action (%s)\n",debugstr_w(action
));
1578 rc
= ACTION_PerformAction(package
, action
, TRUE
);
1579 if (rc
!= ERROR_SUCCESS
)
1582 msi_free_action_script(package
, script
);
1586 static UINT
ACTION_FileCost(MSIPACKAGE
*package
)
1588 return ERROR_SUCCESS
;
1591 static void ACTION_GetComponentInstallStates(MSIPACKAGE
*package
)
1595 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1599 if (!comp
->ComponentId
)
1602 res
= MsiGetComponentPathW( package
->ProductCode
,
1603 comp
->ComponentId
, NULL
, NULL
);
1605 res
= INSTALLSTATE_ABSENT
;
1606 comp
->Installed
= res
;
1610 /* scan for and update current install states */
1611 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE
*package
)
1614 MSIFEATURE
*feature
;
1616 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1619 INSTALLSTATE res
= INSTALLSTATE_ABSENT
;
1621 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1623 comp
= cl
->component
;
1625 if (!comp
->ComponentId
)
1627 res
= INSTALLSTATE_ABSENT
;
1631 if (res
== INSTALLSTATE_ABSENT
)
1632 res
= comp
->Installed
;
1635 if (res
== comp
->Installed
)
1638 if (res
!= INSTALLSTATE_DEFAULT
&& res
!= INSTALLSTATE_LOCAL
&&
1639 res
!= INSTALLSTATE_SOURCE
)
1641 res
= INSTALLSTATE_INCOMPLETE
;
1645 feature
->Installed
= res
;
1649 static BOOL
process_state_property (MSIPACKAGE
* package
, LPCWSTR property
,
1652 static const WCHAR all
[]={'A','L','L',0};
1654 MSIFEATURE
*feature
;
1656 override
= msi_dup_property( package
, property
);
1660 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1662 if (strcmpiW(override
,all
)==0)
1663 msi_feature_set_state( feature
, state
);
1666 LPWSTR ptr
= override
;
1667 LPWSTR ptr2
= strchrW(override
,',');
1671 if ((ptr2
&& strncmpW(ptr
,feature
->Feature
, ptr2
-ptr
)==0)
1672 || (!ptr2
&& strcmpW(ptr
,feature
->Feature
)==0))
1674 msi_feature_set_state( feature
, state
);
1680 ptr2
= strchrW(ptr
,',');
1692 UINT
MSI_SetFeatureStates(MSIPACKAGE
*package
)
1695 static const WCHAR szlevel
[] =
1696 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1697 static const WCHAR szAddLocal
[] =
1698 {'A','D','D','L','O','C','A','L',0};
1699 static const WCHAR szRemove
[] =
1700 {'R','E','M','O','V','E',0};
1701 static const WCHAR szReinstall
[] =
1702 {'R','E','I','N','S','T','A','L','L',0};
1703 BOOL override
= FALSE
;
1704 MSICOMPONENT
* component
;
1705 MSIFEATURE
*feature
;
1708 /* I do not know if this is where it should happen.. but */
1710 TRACE("Checking Install Level\n");
1712 install_level
= msi_get_property_int( package
, szlevel
, 1 );
1714 /* ok here is the _real_ rub
1715 * all these activation/deactivation things happen in order and things
1716 * later on the list override things earlier on the list.
1717 * 1) INSTALLLEVEL processing
1727 * 11) FILEADDDEFAULT
1728 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1729 * ignored for all the features. seems strange, especially since it is not
1730 * documented anywhere, but it is how it works.
1732 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1733 * REMOVE are the big ones, since we don't handle administrative installs
1736 override
|= process_state_property(package
,szAddLocal
,INSTALLSTATE_LOCAL
);
1737 override
|= process_state_property(package
,szRemove
,INSTALLSTATE_ABSENT
);
1738 override
|= process_state_property(package
,szReinstall
,INSTALLSTATE_LOCAL
);
1742 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1744 BOOL feature_state
= ((feature
->Level
> 0) &&
1745 (feature
->Level
<= install_level
));
1747 if ((feature_state
) && (feature
->Action
== INSTALLSTATE_UNKNOWN
))
1749 if (feature
->Attributes
& msidbFeatureAttributesFavorSource
)
1750 msi_feature_set_state( feature
, INSTALLSTATE_SOURCE
);
1751 else if (feature
->Attributes
& msidbFeatureAttributesFavorAdvertise
)
1752 msi_feature_set_state( feature
, INSTALLSTATE_ADVERTISED
);
1754 msi_feature_set_state( feature
, INSTALLSTATE_LOCAL
);
1758 /* disable child features of unselected parent features */
1759 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1763 if (feature
->Level
> 0 && feature
->Level
<= install_level
)
1766 LIST_FOR_EACH_ENTRY( fl
, &feature
->Children
, FeatureList
, entry
)
1767 msi_feature_set_state( fl
->feature
, INSTALLSTATE_UNKNOWN
);
1772 /* set the Preselected Property */
1773 static const WCHAR szPreselected
[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1774 static const WCHAR szOne
[] = { '1', 0 };
1776 MSI_SetPropertyW(package
,szPreselected
,szOne
);
1780 * now we want to enable or disable components base on feature
1783 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1787 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1788 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
);
1790 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1792 component
= cl
->component
;
1794 if (!component
->Enabled
)
1797 if (component
->Attributes
& msidbComponentAttributesOptional
)
1798 msi_component_set_state( component
, INSTALLSTATE_DEFAULT
);
1801 if (component
->Attributes
& msidbComponentAttributesSourceOnly
)
1802 msi_component_set_state( component
, INSTALLSTATE_SOURCE
);
1804 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1807 if (component
->ForceLocalState
)
1808 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1810 if (feature
->Attributes
== msidbFeatureAttributesFavorLocal
)
1812 if (!(component
->Attributes
& msidbComponentAttributesSourceOnly
))
1813 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1815 else if (feature
->Attributes
== msidbFeatureAttributesFavorSource
)
1817 if ((component
->Action
== INSTALLSTATE_UNKNOWN
) ||
1818 (component
->Action
== INSTALLSTATE_ABSENT
) ||
1819 (component
->Action
== INSTALLSTATE_ADVERTISED
) ||
1820 (component
->Action
== INSTALLSTATE_DEFAULT
))
1821 msi_component_set_state( component
, INSTALLSTATE_SOURCE
);
1823 else if (feature
->ActionRequest
== INSTALLSTATE_ADVERTISED
)
1825 if ((component
->Action
== INSTALLSTATE_UNKNOWN
) ||
1826 (component
->Action
== INSTALLSTATE_ABSENT
))
1827 msi_component_set_state( component
, INSTALLSTATE_ADVERTISED
);
1829 else if (feature
->ActionRequest
== INSTALLSTATE_ABSENT
)
1831 if (component
->Action
== INSTALLSTATE_UNKNOWN
)
1832 msi_component_set_state( component
, INSTALLSTATE_ABSENT
);
1834 else if (feature
->ActionRequest
== INSTALLSTATE_UNKNOWN
)
1835 msi_component_set_state( component
, INSTALLSTATE_UNKNOWN
);
1837 if (component
->ForceLocalState
&& feature
->Action
== INSTALLSTATE_SOURCE
)
1838 msi_feature_set_state( feature
, INSTALLSTATE_LOCAL
);
1842 LIST_FOR_EACH_ENTRY( component
, &package
->components
, MSICOMPONENT
, entry
)
1844 if (component
->Action
== INSTALLSTATE_DEFAULT
)
1846 TRACE("%s was default, setting to local\n", debugstr_w(component
->Component
));
1847 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1850 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1851 debugstr_w(component
->Component
), component
->Installed
, component
->Action
);
1855 return ERROR_SUCCESS
;
1858 static UINT
ITERATE_CostFinalizeDirectories(MSIRECORD
*row
, LPVOID param
)
1860 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1864 name
= MSI_RecordGetString(row
,1);
1866 /* This helper function now does ALL the work */
1867 TRACE("Dir %s ...\n",debugstr_w(name
));
1868 path
= resolve_folder(package
,name
,FALSE
,TRUE
,NULL
);
1869 TRACE("resolves to %s\n",debugstr_w(path
));
1872 return ERROR_SUCCESS
;
1875 static UINT
ITERATE_CostFinalizeConditions(MSIRECORD
*row
, LPVOID param
)
1877 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1879 MSIFEATURE
*feature
;
1881 name
= MSI_RecordGetString( row
, 1 );
1883 feature
= get_loaded_feature( package
, name
);
1885 ERR("FAILED to find loaded feature %s\n",debugstr_w(name
));
1889 Condition
= MSI_RecordGetString(row
,3);
1891 if (MSI_EvaluateConditionW(package
,Condition
) == MSICONDITION_TRUE
)
1893 int level
= MSI_RecordGetInteger(row
,2);
1894 TRACE("Reseting feature %s to level %i\n", debugstr_w(name
), level
);
1895 feature
->Level
= level
;
1898 return ERROR_SUCCESS
;
1901 LPWSTR
msi_get_disk_file_version( LPCWSTR filename
)
1903 static const WCHAR name_fmt
[] =
1904 {'%','u','.','%','u','.','%','u','.','%','u',0};
1905 static WCHAR name
[] = {'\\',0};
1906 VS_FIXEDFILEINFO
*lpVer
;
1907 WCHAR filever
[0x100];
1913 TRACE("%s\n", debugstr_w(filename
));
1915 versize
= GetFileVersionInfoSizeW( filename
, &handle
);
1919 version
= msi_alloc( versize
);
1920 GetFileVersionInfoW( filename
, 0, versize
, version
);
1922 VerQueryValueW( version
, name
, (LPVOID
*)&lpVer
, &sz
);
1923 msi_free( version
);
1925 sprintfW( filever
, name_fmt
,
1926 HIWORD(lpVer
->dwFileVersionMS
),
1927 LOWORD(lpVer
->dwFileVersionMS
),
1928 HIWORD(lpVer
->dwFileVersionLS
),
1929 LOWORD(lpVer
->dwFileVersionLS
));
1931 return strdupW( filever
);
1934 static UINT
msi_check_file_install_states( MSIPACKAGE
*package
)
1936 LPWSTR file_version
;
1939 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
1941 MSICOMPONENT
* comp
= file
->Component
;
1947 if (file
->IsCompressed
)
1948 comp
->ForceLocalState
= TRUE
;
1950 /* calculate target */
1951 p
= resolve_folder(package
, comp
->Directory
, FALSE
, FALSE
, NULL
);
1953 msi_free(file
->TargetPath
);
1955 TRACE("file %s is named %s\n",
1956 debugstr_w(file
->File
), debugstr_w(file
->FileName
));
1958 file
->TargetPath
= build_directory_name(2, p
, file
->FileName
);
1962 TRACE("file %s resolves to %s\n",
1963 debugstr_w(file
->File
), debugstr_w(file
->TargetPath
));
1965 /* don't check files of components that aren't installed */
1966 if (comp
->Installed
== INSTALLSTATE_UNKNOWN
||
1967 comp
->Installed
== INSTALLSTATE_ABSENT
)
1969 file
->state
= msifs_missing
; /* assume files are missing */
1973 if (GetFileAttributesW(file
->TargetPath
) == INVALID_FILE_ATTRIBUTES
)
1975 file
->state
= msifs_missing
;
1976 comp
->Cost
+= file
->FileSize
;
1977 comp
->Installed
= INSTALLSTATE_INCOMPLETE
;
1981 if (file
->Version
&&
1982 (file_version
= msi_get_disk_file_version( file
->TargetPath
)))
1984 TRACE("new %s old %s\n", debugstr_w(file
->Version
),
1985 debugstr_w(file_version
));
1986 /* FIXME: seems like a bad way to compare version numbers */
1987 if (lstrcmpiW(file_version
, file
->Version
)<0)
1989 file
->state
= msifs_overwrite
;
1990 comp
->Cost
+= file
->FileSize
;
1991 comp
->Installed
= INSTALLSTATE_INCOMPLETE
;
1994 file
->state
= msifs_present
;
1995 msi_free( file_version
);
1998 file
->state
= msifs_present
;
2001 return ERROR_SUCCESS
;
2005 * A lot is done in this function aside from just the costing.
2006 * The costing needs to be implemented at some point but for now I am going
2007 * to focus on the directory building
2010 static UINT
ACTION_CostFinalize(MSIPACKAGE
*package
)
2012 static const WCHAR ExecSeqQuery
[] =
2013 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2014 '`','D','i','r','e','c','t','o','r','y','`',0};
2015 static const WCHAR ConditionQuery
[] =
2016 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2017 '`','C','o','n','d','i','t','i','o','n','`',0};
2018 static const WCHAR szCosting
[] =
2019 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2020 static const WCHAR szlevel
[] =
2021 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2022 static const WCHAR szOne
[] = { '1', 0 };
2028 if ( 1 == msi_get_property_int( package
, szCosting
, 0 ) )
2029 return ERROR_SUCCESS
;
2031 TRACE("Building Directory properties\n");
2033 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2034 if (rc
== ERROR_SUCCESS
)
2036 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CostFinalizeDirectories
,
2038 msiobj_release(&view
->hdr
);
2041 /* read components states from the registry */
2042 ACTION_GetComponentInstallStates(package
);
2044 TRACE("File calculations\n");
2045 msi_check_file_install_states( package
);
2047 TRACE("Evaluating Condition Table\n");
2049 rc
= MSI_DatabaseOpenViewW(package
->db
, ConditionQuery
, &view
);
2050 if (rc
== ERROR_SUCCESS
)
2052 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CostFinalizeConditions
,
2054 msiobj_release(&view
->hdr
);
2057 TRACE("Enabling or Disabling Components\n");
2058 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2060 if (MSI_EvaluateConditionW(package
, comp
->Condition
) == MSICONDITION_FALSE
)
2062 TRACE("Disabling component %s\n", debugstr_w(comp
->Component
));
2063 comp
->Enabled
= FALSE
;
2067 MSI_SetPropertyW(package
,szCosting
,szOne
);
2068 /* set default run level if not set */
2069 level
= msi_dup_property( package
, szlevel
);
2071 MSI_SetPropertyW(package
,szlevel
, szOne
);
2074 ACTION_UpdateFeatureInstallStates(package
);
2076 return MSI_SetFeatureStates(package
);
2079 /* OK this value is "interpreted" and then formatted based on the
2080 first few characters */
2081 static LPSTR
parse_value(MSIPACKAGE
*package
, LPCWSTR value
, DWORD
*type
,
2085 if (value
[0]=='#' && value
[1]!='#' && value
[1]!='%')
2091 LPWSTR deformated
= NULL
;
2094 deformat_string(package
, &value
[2], &deformated
);
2096 /* binary value type */
2100 *size
= (strlenW(ptr
)/2)+1;
2102 *size
= strlenW(ptr
)/2;
2104 data
= msi_alloc(*size
);
2110 /* if uneven pad with a zero in front */
2116 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2118 TRACE("Uneven byte count\n");
2126 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2129 msi_free(deformated
);
2131 TRACE("Data %i bytes(%i)\n",*size
,count
);
2138 deformat_string(package
, &value
[1], &deformated
);
2141 *size
= sizeof(DWORD
);
2142 data
= msi_alloc(*size
);
2148 if ( (*p
< '0') || (*p
> '9') )
2154 if (deformated
[0] == '-')
2157 TRACE("DWORD %i\n",*(LPDWORD
)data
);
2159 msi_free(deformated
);
2164 static const WCHAR szMulti
[] = {'[','~',']',0};
2173 *type
=REG_EXPAND_SZ
;
2181 if (strstrW(value
,szMulti
))
2182 *type
= REG_MULTI_SZ
;
2184 *size
= deformat_string(package
, ptr
,(LPWSTR
*)&data
);
2189 static UINT
ITERATE_WriteRegistryValues(MSIRECORD
*row
, LPVOID param
)
2191 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
2192 static const WCHAR szHCR
[] =
2193 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2194 'R','O','O','T','\\',0};
2195 static const WCHAR szHCU
[] =
2196 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2197 'U','S','E','R','\\',0};
2198 static const WCHAR szHLM
[] =
2199 {'H','K','E','Y','_','L','O','C','A','L','_',
2200 'M','A','C','H','I','N','E','\\',0};
2201 static const WCHAR szHU
[] =
2202 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2204 LPSTR value_data
= NULL
;
2205 HKEY root_key
, hkey
;
2208 LPCWSTR szRoot
, component
, name
, key
, value
;
2213 BOOL check_first
= FALSE
;
2216 ui_progress(package
,2,0,0,0);
2223 component
= MSI_RecordGetString(row
, 6);
2224 comp
= get_loaded_component(package
,component
);
2226 return ERROR_SUCCESS
;
2228 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2230 TRACE("Skipping write due to disabled component %s\n",
2231 debugstr_w(component
));
2233 comp
->Action
= comp
->Installed
;
2235 return ERROR_SUCCESS
;
2238 comp
->Action
= INSTALLSTATE_LOCAL
;
2240 name
= MSI_RecordGetString(row
, 4);
2241 if( MSI_RecordIsNull(row
,5) && name
)
2243 /* null values can have special meanings */
2244 if (name
[0]=='-' && name
[1] == 0)
2245 return ERROR_SUCCESS
;
2246 else if ((name
[0]=='+' && name
[1] == 0) ||
2247 (name
[0] == '*' && name
[1] == 0))
2252 root
= MSI_RecordGetInteger(row
,2);
2253 key
= MSI_RecordGetString(row
, 3);
2255 /* get the root key */
2260 static const WCHAR szALLUSER
[] = {'A','L','L','U','S','E','R','S',0};
2261 LPWSTR all_users
= msi_dup_property( package
, szALLUSER
);
2262 if (all_users
&& all_users
[0] == '1')
2264 root_key
= HKEY_LOCAL_MACHINE
;
2269 root_key
= HKEY_CURRENT_USER
;
2272 msi_free(all_users
);
2275 case 0: root_key
= HKEY_CLASSES_ROOT
;
2278 case 1: root_key
= HKEY_CURRENT_USER
;
2281 case 2: root_key
= HKEY_LOCAL_MACHINE
;
2284 case 3: root_key
= HKEY_USERS
;
2288 ERR("Unknown root %i\n",root
);
2294 return ERROR_SUCCESS
;
2296 deformat_string(package
, key
, &deformated
);
2297 size
= strlenW(deformated
) + strlenW(szRoot
) + 1;
2298 uikey
= msi_alloc(size
*sizeof(WCHAR
));
2299 strcpyW(uikey
,szRoot
);
2300 strcatW(uikey
,deformated
);
2302 if (RegCreateKeyW( root_key
, deformated
, &hkey
))
2304 ERR("Could not create key %s\n",debugstr_w(deformated
));
2305 msi_free(deformated
);
2307 return ERROR_SUCCESS
;
2309 msi_free(deformated
);
2311 value
= MSI_RecordGetString(row
,5);
2313 value_data
= parse_value(package
, value
, &type
, &size
);
2316 static const WCHAR szEmpty
[] = {0};
2317 value_data
= (LPSTR
)strdupW(szEmpty
);
2322 deformat_string(package
, name
, &deformated
);
2324 /* get the double nulls to terminate SZ_MULTI */
2325 if (type
== REG_MULTI_SZ
)
2326 size
+=sizeof(WCHAR
);
2330 TRACE("Setting value %s of %s\n",debugstr_w(deformated
),
2332 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
)value_data
, size
);
2337 rc
= RegQueryValueExW(hkey
, deformated
, NULL
, NULL
, NULL
, &sz
);
2338 if (rc
== ERROR_SUCCESS
|| rc
== ERROR_MORE_DATA
)
2340 TRACE("value %s of %s checked already exists\n",
2341 debugstr_w(deformated
), debugstr_w(uikey
));
2345 TRACE("Checked and setting value %s of %s\n",
2346 debugstr_w(deformated
), debugstr_w(uikey
));
2347 if (deformated
|| size
)
2348 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
) value_data
, size
);
2353 uirow
= MSI_CreateRecord(3);
2354 MSI_RecordSetStringW(uirow
,2,deformated
);
2355 MSI_RecordSetStringW(uirow
,1,uikey
);
2358 MSI_RecordSetStringW(uirow
,3,(LPWSTR
)value_data
);
2360 MSI_RecordSetStringW(uirow
,3,value
);
2362 ui_actiondata(package
,szWriteRegistryValues
,uirow
);
2363 msiobj_release( &uirow
->hdr
);
2365 msi_free(value_data
);
2366 msi_free(deformated
);
2369 return ERROR_SUCCESS
;
2372 static UINT
ACTION_WriteRegistryValues(MSIPACKAGE
*package
)
2376 static const WCHAR ExecSeqQuery
[] =
2377 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2378 '`','R','e','g','i','s','t','r','y','`',0 };
2380 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2381 if (rc
!= ERROR_SUCCESS
)
2382 return ERROR_SUCCESS
;
2384 /* increment progress bar each time action data is sent */
2385 ui_progress(package
,1,REG_PROGRESS_VALUE
,1,0);
2387 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteRegistryValues
, package
);
2389 msiobj_release(&view
->hdr
);
2393 static UINT
ACTION_InstallInitialize(MSIPACKAGE
*package
)
2395 package
->script
->CurrentlyScripting
= TRUE
;
2397 return ERROR_SUCCESS
;
2401 static UINT
ACTION_InstallValidate(MSIPACKAGE
*package
)
2406 static const WCHAR q1
[]=
2407 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2408 '`','R','e','g','i','s','t','r','y','`',0};
2411 MSIFEATURE
*feature
;
2414 TRACE("InstallValidate\n");
2416 rc
= MSI_DatabaseOpenViewW(package
->db
, q1
, &view
);
2417 if (rc
== ERROR_SUCCESS
)
2419 MSI_IterateRecords( view
, &progress
, NULL
, package
);
2420 msiobj_release( &view
->hdr
);
2421 total
+= progress
* REG_PROGRESS_VALUE
;
2424 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2425 total
+= COMPONENT_PROGRESS_VALUE
;
2427 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2428 total
+= file
->FileSize
;
2430 ui_progress(package
,0,total
,0,0);
2432 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2434 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2435 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
,
2436 feature
->ActionRequest
);
2439 return ERROR_SUCCESS
;
2442 static UINT
ITERATE_LaunchConditions(MSIRECORD
*row
, LPVOID param
)
2444 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
2445 LPCWSTR cond
= NULL
;
2446 LPCWSTR message
= NULL
;
2447 static const WCHAR title
[]=
2448 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2450 cond
= MSI_RecordGetString(row
,1);
2452 if (MSI_EvaluateConditionW(package
,cond
) != MSICONDITION_TRUE
)
2455 message
= MSI_RecordGetString(row
,2);
2456 deformat_string(package
,message
,&deformated
);
2457 MessageBoxW(NULL
,deformated
,title
,MB_OK
);
2458 msi_free(deformated
);
2459 return ERROR_FUNCTION_FAILED
;
2462 return ERROR_SUCCESS
;
2465 static UINT
ACTION_LaunchConditions(MSIPACKAGE
*package
)
2468 MSIQUERY
* view
= NULL
;
2469 static const WCHAR ExecSeqQuery
[] =
2470 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2471 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2473 TRACE("Checking launch conditions\n");
2475 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2476 if (rc
!= ERROR_SUCCESS
)
2477 return ERROR_SUCCESS
;
2479 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_LaunchConditions
, package
);
2480 msiobj_release(&view
->hdr
);
2485 static LPWSTR
resolve_keypath( MSIPACKAGE
* package
, MSICOMPONENT
*cmp
)
2489 return resolve_folder(package
,cmp
->Directory
,FALSE
,FALSE
,NULL
);
2491 if (cmp
->Attributes
& msidbComponentAttributesRegistryKeyPath
)
2493 MSIRECORD
* row
= 0;
2495 LPWSTR deformated
,buffer
,deformated_name
;
2497 static const WCHAR ExecSeqQuery
[] =
2498 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2499 '`','R','e','g','i','s','t','r','y','`',' ',
2500 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2501 ' ','=',' ' ,'\'','%','s','\'',0 };
2502 static const WCHAR fmt
[]={'%','0','2','i',':','\\','%','s','\\',0};
2503 static const WCHAR fmt2
[]=
2504 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2506 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
,cmp
->KeyPath
);
2510 root
= MSI_RecordGetInteger(row
,2);
2511 key
= MSI_RecordGetString(row
, 3);
2512 name
= MSI_RecordGetString(row
, 4);
2513 deformat_string(package
, key
, &deformated
);
2514 deformat_string(package
, name
, &deformated_name
);
2516 len
= strlenW(deformated
) + 6;
2517 if (deformated_name
)
2518 len
+=strlenW(deformated_name
);
2520 buffer
= msi_alloc( len
*sizeof(WCHAR
));
2522 if (deformated_name
)
2523 sprintfW(buffer
,fmt2
,root
,deformated
,deformated_name
);
2525 sprintfW(buffer
,fmt
,root
,deformated
);
2527 msi_free(deformated
);
2528 msi_free(deformated_name
);
2529 msiobj_release(&row
->hdr
);
2533 else if (cmp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2535 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2540 MSIFILE
*file
= get_loaded_file( package
, cmp
->KeyPath
);
2543 return strdupW( file
->TargetPath
);
2548 static HKEY
openSharedDLLsKey(void)
2551 static const WCHAR path
[] =
2552 {'S','o','f','t','w','a','r','e','\\',
2553 'M','i','c','r','o','s','o','f','t','\\',
2554 'W','i','n','d','o','w','s','\\',
2555 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2556 'S','h','a','r','e','d','D','L','L','s',0};
2558 RegCreateKeyW(HKEY_LOCAL_MACHINE
,path
,&hkey
);
2562 static UINT
ACTION_GetSharedDLLsCount(LPCWSTR dll
)
2567 DWORD sz
= sizeof(count
);
2570 hkey
= openSharedDLLsKey();
2571 rc
= RegQueryValueExW(hkey
, dll
, NULL
, &type
, (LPBYTE
)&count
, &sz
);
2572 if (rc
!= ERROR_SUCCESS
)
2578 static UINT
ACTION_WriteSharedDLLsCount(LPCWSTR path
, UINT count
)
2582 hkey
= openSharedDLLsKey();
2584 msi_reg_set_val_dword( hkey
, path
, count
);
2586 RegDeleteValueW(hkey
,path
);
2592 * Return TRUE if the count should be written out and FALSE if not
2594 static void ACTION_RefCountComponent( MSIPACKAGE
* package
, MSICOMPONENT
*comp
)
2596 MSIFEATURE
*feature
;
2600 /* only refcount DLLs */
2601 if (comp
->KeyPath
== NULL
||
2602 comp
->Attributes
& msidbComponentAttributesRegistryKeyPath
||
2603 comp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2607 count
= ACTION_GetSharedDLLsCount( comp
->FullKeypath
);
2608 write
= (count
> 0);
2610 if (comp
->Attributes
& msidbComponentAttributesSharedDllRefCount
)
2614 /* increment counts */
2615 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2619 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
))
2622 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2624 if ( cl
->component
== comp
)
2629 /* decrement counts */
2630 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2634 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ABSENT
))
2637 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2639 if ( cl
->component
== comp
)
2644 /* ref count all the files in the component */
2649 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2651 if (file
->Component
== comp
)
2652 ACTION_WriteSharedDLLsCount( file
->TargetPath
, count
);
2656 /* add a count for permenent */
2657 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2660 comp
->RefCount
= count
;
2663 ACTION_WriteSharedDLLsCount( comp
->FullKeypath
, comp
->RefCount
);
2667 * Ok further analysis makes me think that this work is
2668 * actually done in the PublishComponents and PublishFeatures
2669 * step, and not here. It appears like the keypath and all that is
2670 * resolved in this step, however actually written in the Publish steps.
2671 * But we will leave it here for now because it is unclear
2673 static UINT
ACTION_ProcessComponents(MSIPACKAGE
*package
)
2675 WCHAR squished_pc
[GUID_SIZE
];
2676 WCHAR squished_cc
[GUID_SIZE
];
2679 HKEY hkey
=0,hkey2
=0;
2681 /* writes the Component and Features values to the registry */
2683 rc
= MSIREG_OpenComponents(&hkey
);
2684 if (rc
!= ERROR_SUCCESS
)
2687 squash_guid(package
->ProductCode
,squished_pc
);
2688 ui_progress(package
,1,COMPONENT_PROGRESS_VALUE
,1,0);
2690 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2694 ui_progress(package
,2,0,0,0);
2695 if (!comp
->ComponentId
)
2698 squash_guid(comp
->ComponentId
,squished_cc
);
2700 msi_free(comp
->FullKeypath
);
2701 comp
->FullKeypath
= resolve_keypath( package
, comp
);
2703 /* do the refcounting */
2704 ACTION_RefCountComponent( package
, comp
);
2706 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2707 debugstr_w(comp
->Component
),
2708 debugstr_w(squished_cc
),
2709 debugstr_w(comp
->FullKeypath
),
2712 * Write the keypath out if the component is to be registered
2713 * and delete the key if the component is to be deregistered
2715 if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2717 rc
= RegCreateKeyW(hkey
,squished_cc
,&hkey2
);
2718 if (rc
!= ERROR_SUCCESS
)
2721 if (!comp
->FullKeypath
)
2724 msi_reg_set_val_str( hkey2
, squished_pc
, comp
->FullKeypath
);
2726 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2728 static const WCHAR szPermKey
[] =
2729 { '0','0','0','0','0','0','0','0','0','0','0','0',
2730 '0','0','0','0','0','0','0','0','0','0','0','0',
2731 '0','0','0','0','0','0','0','0',0 };
2733 msi_reg_set_val_str( hkey2
, szPermKey
, comp
->FullKeypath
);
2739 uirow
= MSI_CreateRecord(3);
2740 MSI_RecordSetStringW(uirow
,1,package
->ProductCode
);
2741 MSI_RecordSetStringW(uirow
,2,comp
->ComponentId
);
2742 MSI_RecordSetStringW(uirow
,3,comp
->FullKeypath
);
2743 ui_actiondata(package
,szProcessComponents
,uirow
);
2744 msiobj_release( &uirow
->hdr
);
2746 else if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ABSENT
))
2750 rc
= RegOpenKeyW(hkey
,squished_cc
,&hkey2
);
2751 if (rc
!= ERROR_SUCCESS
)
2754 RegDeleteValueW(hkey2
,squished_pc
);
2756 /* if the key is empty delete it */
2757 res
= RegEnumKeyExW(hkey2
,0,NULL
,0,0,NULL
,0,NULL
);
2759 if (res
== ERROR_NO_MORE_ITEMS
)
2760 RegDeleteKeyW(hkey
,squished_cc
);
2763 uirow
= MSI_CreateRecord(2);
2764 MSI_RecordSetStringW(uirow
,1,package
->ProductCode
);
2765 MSI_RecordSetStringW(uirow
,2,comp
->ComponentId
);
2766 ui_actiondata(package
,szProcessComponents
,uirow
);
2767 msiobj_release( &uirow
->hdr
);
2782 static BOOL CALLBACK
Typelib_EnumResNameProc( HMODULE hModule
, LPCWSTR lpszType
,
2783 LPWSTR lpszName
, LONG_PTR lParam
)
2786 typelib_struct
*tl_struct
= (typelib_struct
*) lParam
;
2787 static const WCHAR fmt
[] = {'%','s','\\','%','i',0};
2791 if (!IS_INTRESOURCE(lpszName
))
2793 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName
));
2797 sz
= strlenW(tl_struct
->source
)+4;
2798 sz
*= sizeof(WCHAR
);
2800 if ((INT_PTR
)lpszName
== 1)
2801 tl_struct
->path
= strdupW(tl_struct
->source
);
2804 tl_struct
->path
= msi_alloc(sz
);
2805 sprintfW(tl_struct
->path
,fmt
,tl_struct
->source
, lpszName
);
2808 TRACE("trying %s\n", debugstr_w(tl_struct
->path
));
2809 res
= LoadTypeLib(tl_struct
->path
,&tl_struct
->ptLib
);
2810 if (!SUCCEEDED(res
))
2812 msi_free(tl_struct
->path
);
2813 tl_struct
->path
= NULL
;
2818 ITypeLib_GetLibAttr(tl_struct
->ptLib
, &attr
);
2819 if (IsEqualGUID(&(tl_struct
->clsid
),&(attr
->guid
)))
2821 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2825 msi_free(tl_struct
->path
);
2826 tl_struct
->path
= NULL
;
2828 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2829 ITypeLib_Release(tl_struct
->ptLib
);
2834 static UINT
ITERATE_RegisterTypeLibraries(MSIRECORD
*row
, LPVOID param
)
2836 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
2840 typelib_struct tl_struct
;
2842 static const WCHAR szTYPELIB
[] = {'T','Y','P','E','L','I','B',0};
2844 component
= MSI_RecordGetString(row
,3);
2845 comp
= get_loaded_component(package
,component
);
2847 return ERROR_SUCCESS
;
2849 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2851 TRACE("Skipping typelib reg due to disabled component\n");
2853 comp
->Action
= comp
->Installed
;
2855 return ERROR_SUCCESS
;
2858 comp
->Action
= INSTALLSTATE_LOCAL
;
2860 file
= get_loaded_file( package
, comp
->KeyPath
);
2862 return ERROR_SUCCESS
;
2864 module
= LoadLibraryExW( file
->TargetPath
, NULL
, LOAD_LIBRARY_AS_DATAFILE
);
2868 guid
= MSI_RecordGetString(row
,1);
2869 CLSIDFromString((LPWSTR
)guid
, &tl_struct
.clsid
);
2870 tl_struct
.source
= strdupW( file
->TargetPath
);
2871 tl_struct
.path
= NULL
;
2873 EnumResourceNamesW(module
, szTYPELIB
, Typelib_EnumResNameProc
,
2874 (LONG_PTR
)&tl_struct
);
2882 helpid
= MSI_RecordGetString(row
,6);
2885 help
= resolve_folder(package
,helpid
,FALSE
,FALSE
,NULL
);
2886 res
= RegisterTypeLib(tl_struct
.ptLib
,tl_struct
.path
,help
);
2889 if (!SUCCEEDED(res
))
2890 ERR("Failed to register type library %s\n",
2891 debugstr_w(tl_struct
.path
));
2894 ui_actiondata(package
,szRegisterTypeLibraries
,row
);
2896 TRACE("Registered %s\n", debugstr_w(tl_struct
.path
));
2899 ITypeLib_Release(tl_struct
.ptLib
);
2900 msi_free(tl_struct
.path
);
2903 ERR("Failed to load type library %s\n",
2904 debugstr_w(tl_struct
.source
));
2906 FreeLibrary(module
);
2907 msi_free(tl_struct
.source
);
2910 ERR("Could not load file! %s\n", debugstr_w(file
->TargetPath
));
2912 return ERROR_SUCCESS
;
2915 static UINT
ACTION_RegisterTypeLibraries(MSIPACKAGE
*package
)
2918 * OK this is a bit confusing.. I am given a _Component key and I believe
2919 * that the file that is being registered as a type library is the "key file
2920 * of that component" which I interpret to mean "The file in the KeyPath of
2925 static const WCHAR Query
[] =
2926 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2927 '`','T','y','p','e','L','i','b','`',0};
2929 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
2930 if (rc
!= ERROR_SUCCESS
)
2931 return ERROR_SUCCESS
;
2933 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_RegisterTypeLibraries
, package
);
2934 msiobj_release(&view
->hdr
);
2938 static UINT
ITERATE_CreateShortcuts(MSIRECORD
*row
, LPVOID param
)
2940 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
2941 LPWSTR target_file
, target_folder
, filename
;
2942 LPCWSTR buffer
, extension
;
2944 static const WCHAR szlnk
[]={'.','l','n','k',0};
2945 IShellLinkW
*sl
= NULL
;
2946 IPersistFile
*pf
= NULL
;
2949 buffer
= MSI_RecordGetString(row
,4);
2950 comp
= get_loaded_component(package
,buffer
);
2952 return ERROR_SUCCESS
;
2954 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2956 TRACE("Skipping shortcut creation due to disabled component\n");
2958 comp
->Action
= comp
->Installed
;
2960 return ERROR_SUCCESS
;
2963 comp
->Action
= INSTALLSTATE_LOCAL
;
2965 ui_actiondata(package
,szCreateShortcuts
,row
);
2967 res
= CoCreateInstance( &CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
,
2968 &IID_IShellLinkW
, (LPVOID
*) &sl
);
2972 ERR("CLSID_ShellLink not available\n");
2976 res
= IShellLinkW_QueryInterface( sl
, &IID_IPersistFile
,(LPVOID
*) &pf
);
2979 ERR("QueryInterface(IID_IPersistFile) failed\n");
2983 buffer
= MSI_RecordGetString(row
,2);
2984 target_folder
= resolve_folder(package
, buffer
,FALSE
,FALSE
,NULL
);
2986 /* may be needed because of a bug somehwere else */
2987 create_full_pathW(target_folder
);
2989 filename
= msi_dup_record_field( row
, 3 );
2990 reduce_to_longfilename(filename
);
2992 extension
= strchrW(filename
,'.');
2993 if (!extension
|| strcmpiW(extension
,szlnk
))
2995 int len
= strlenW(filename
);
2996 filename
= msi_realloc(filename
, len
* sizeof(WCHAR
) + sizeof(szlnk
));
2997 memcpy(filename
+ len
, szlnk
, sizeof(szlnk
));
2999 target_file
= build_directory_name(2, target_folder
, filename
);
3000 msi_free(target_folder
);
3003 buffer
= MSI_RecordGetString(row
,5);
3004 if (strchrW(buffer
,'['))
3007 deformat_string(package
,buffer
,&deformated
);
3008 IShellLinkW_SetPath(sl
,deformated
);
3009 msi_free(deformated
);
3013 FIXME("poorly handled shortcut format, advertised shortcut\n");
3014 IShellLinkW_SetPath(sl
,comp
->FullKeypath
);
3017 if (!MSI_RecordIsNull(row
,6))
3020 buffer
= MSI_RecordGetString(row
,6);
3021 deformat_string(package
,buffer
,&deformated
);
3022 IShellLinkW_SetArguments(sl
,deformated
);
3023 msi_free(deformated
);
3026 if (!MSI_RecordIsNull(row
,7))
3028 buffer
= MSI_RecordGetString(row
,7);
3029 IShellLinkW_SetDescription(sl
,buffer
);
3032 if (!MSI_RecordIsNull(row
,8))
3033 IShellLinkW_SetHotkey(sl
,MSI_RecordGetInteger(row
,8));
3035 if (!MSI_RecordIsNull(row
,9))
3040 buffer
= MSI_RecordGetString(row
,9);
3042 Path
= build_icon_path(package
,buffer
);
3043 index
= MSI_RecordGetInteger(row
,10);
3045 /* no value means 0 */
3046 if (index
== MSI_NULL_INTEGER
)
3049 IShellLinkW_SetIconLocation(sl
,Path
,index
);
3053 if (!MSI_RecordIsNull(row
,11))
3054 IShellLinkW_SetShowCmd(sl
,MSI_RecordGetInteger(row
,11));
3056 if (!MSI_RecordIsNull(row
,12))
3059 buffer
= MSI_RecordGetString(row
,12);
3060 Path
= resolve_folder(package
, buffer
, FALSE
, FALSE
, NULL
);
3062 IShellLinkW_SetWorkingDirectory(sl
,Path
);
3066 TRACE("Writing shortcut to %s\n",debugstr_w(target_file
));
3067 IPersistFile_Save(pf
,target_file
,FALSE
);
3069 msi_free(target_file
);
3073 IPersistFile_Release( pf
);
3075 IShellLinkW_Release( sl
);
3077 return ERROR_SUCCESS
;
3080 static UINT
ACTION_CreateShortcuts(MSIPACKAGE
*package
)
3085 static const WCHAR Query
[] =
3086 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3087 '`','S','h','o','r','t','c','u','t','`',0};
3089 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3090 if (rc
!= ERROR_SUCCESS
)
3091 return ERROR_SUCCESS
;
3093 res
= CoInitialize( NULL
);
3096 ERR("CoInitialize failed\n");
3097 return ERROR_FUNCTION_FAILED
;
3100 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateShortcuts
, package
);
3101 msiobj_release(&view
->hdr
);
3108 static UINT
ITERATE_PublishProduct(MSIRECORD
*row
, LPVOID param
)
3110 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
3119 FileName
= MSI_RecordGetString(row
,1);
3122 ERR("Unable to get FileName\n");
3123 return ERROR_SUCCESS
;
3126 FilePath
= build_icon_path(package
,FileName
);
3128 TRACE("Creating icon file at %s\n",debugstr_w(FilePath
));
3130 the_file
= CreateFileW(FilePath
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
,
3131 FILE_ATTRIBUTE_NORMAL
, NULL
);
3133 if (the_file
== INVALID_HANDLE_VALUE
)
3135 ERR("Unable to create file %s\n",debugstr_w(FilePath
));
3137 return ERROR_SUCCESS
;
3144 rc
= MSI_RecordReadStream(row
,2,buffer
,&sz
);
3145 if (rc
!= ERROR_SUCCESS
)
3147 ERR("Failed to get stream\n");
3148 CloseHandle(the_file
);
3149 DeleteFileW(FilePath
);
3152 WriteFile(the_file
,buffer
,sz
,&write
,NULL
);
3153 } while (sz
== 1024);
3157 CloseHandle(the_file
);
3159 uirow
= MSI_CreateRecord(1);
3160 MSI_RecordSetStringW(uirow
,1,FileName
);
3161 ui_actiondata(package
,szPublishProduct
,uirow
);
3162 msiobj_release( &uirow
->hdr
);
3164 return ERROR_SUCCESS
;
3168 * 99% of the work done here is only done for
3169 * advertised installs. However this is where the
3170 * Icon table is processed and written out
3171 * so that is what I am going to do here.
3173 static UINT
ACTION_PublishProduct(MSIPACKAGE
*package
)
3177 static const WCHAR Query
[]=
3178 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3179 '`','I','c','o','n','`',0};
3180 /* for registry stuff */
3183 static const WCHAR szProductLanguage
[] =
3184 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3185 static const WCHAR szARPProductIcon
[] =
3186 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3187 static const WCHAR szProductVersion
[] =
3188 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3192 MSIHANDLE hDb
, hSumInfo
;
3194 /* write out icon files */
3196 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3197 if (rc
== ERROR_SUCCESS
)
3199 MSI_IterateRecords(view
, NULL
, ITERATE_PublishProduct
, package
);
3200 msiobj_release(&view
->hdr
);
3203 /* ok there is a lot more done here but i need to figure out what */
3205 rc
= MSIREG_OpenProductsKey(package
->ProductCode
,&hkey
,TRUE
);
3206 if (rc
!= ERROR_SUCCESS
)
3209 rc
= MSIREG_OpenUserProductsKey(package
->ProductCode
,&hukey
,TRUE
);
3210 if (rc
!= ERROR_SUCCESS
)
3214 buffer
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTNAMEW
);
3215 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PRODUCTNAMEW
, buffer
);
3218 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
3219 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
3221 buffer
= msi_dup_property( package
, szARPProductIcon
);
3224 LPWSTR path
= build_icon_path(package
,buffer
);
3225 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PRODUCTICONW
, path
);
3230 buffer
= msi_dup_property( package
, szProductVersion
);
3233 DWORD verdword
= msi_version_str_to_dword(buffer
);
3234 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
3238 /* FIXME: Need to write more keys to the user registry */
3240 hDb
= alloc_msihandle( &package
->db
->hdr
);
3242 rc
= ERROR_NOT_ENOUGH_MEMORY
;
3245 rc
= MsiGetSummaryInformationW(hDb
, NULL
, 0, &hSumInfo
);
3246 MsiCloseHandle(hDb
);
3247 if (rc
== ERROR_SUCCESS
)
3249 WCHAR guidbuffer
[0x200];
3251 rc
= MsiSummaryInfoGetPropertyW(hSumInfo
, 9, NULL
, NULL
, NULL
,
3253 if (rc
== ERROR_SUCCESS
)
3255 WCHAR squashed
[GUID_SIZE
];
3256 /* for now we only care about the first guid */
3257 LPWSTR ptr
= strchrW(guidbuffer
,';');
3259 squash_guid(guidbuffer
,squashed
);
3260 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PACKAGECODEW
, squashed
);
3264 ERR("Unable to query Revision_Number...\n");
3267 MsiCloseHandle(hSumInfo
);
3271 ERR("Unable to open Summary Information\n");
3283 static UINT
ITERATE_WriteIniValues(MSIRECORD
*row
, LPVOID param
)
3285 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3286 LPCWSTR component
,section
,key
,value
,identifier
,filename
,dirproperty
;
3287 LPWSTR deformated_section
, deformated_key
, deformated_value
;
3288 LPWSTR folder
, fullname
= NULL
;
3292 static const WCHAR szWindowsFolder
[] =
3293 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3295 component
= MSI_RecordGetString(row
, 8);
3296 comp
= get_loaded_component(package
,component
);
3298 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3300 TRACE("Skipping ini file due to disabled component %s\n",
3301 debugstr_w(component
));
3303 comp
->Action
= comp
->Installed
;
3305 return ERROR_SUCCESS
;
3308 comp
->Action
= INSTALLSTATE_LOCAL
;
3310 identifier
= MSI_RecordGetString(row
,1);
3311 filename
= MSI_RecordGetString(row
,2);
3312 dirproperty
= MSI_RecordGetString(row
,3);
3313 section
= MSI_RecordGetString(row
,4);
3314 key
= MSI_RecordGetString(row
,5);
3315 value
= MSI_RecordGetString(row
,6);
3316 action
= MSI_RecordGetInteger(row
,7);
3318 deformat_string(package
,section
,&deformated_section
);
3319 deformat_string(package
,key
,&deformated_key
);
3320 deformat_string(package
,value
,&deformated_value
);
3324 folder
= resolve_folder(package
, dirproperty
, FALSE
, FALSE
, NULL
);
3326 folder
= msi_dup_property( package
, dirproperty
);
3329 folder
= msi_dup_property( package
, szWindowsFolder
);
3333 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty
));
3337 fullname
= build_directory_name(2, folder
, filename
);
3341 TRACE("Adding value %s to section %s in %s\n",
3342 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3343 debugstr_w(fullname
));
3344 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3345 deformated_value
, fullname
);
3347 else if (action
== 1)
3350 GetPrivateProfileStringW(deformated_section
, deformated_key
, NULL
,
3351 returned
, 10, fullname
);
3352 if (returned
[0] == 0)
3354 TRACE("Adding value %s to section %s in %s\n",
3355 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3356 debugstr_w(fullname
));
3358 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3359 deformated_value
, fullname
);
3362 else if (action
== 3)
3363 FIXME("Append to existing section not yet implemented\n");
3365 uirow
= MSI_CreateRecord(4);
3366 MSI_RecordSetStringW(uirow
,1,identifier
);
3367 MSI_RecordSetStringW(uirow
,2,deformated_section
);
3368 MSI_RecordSetStringW(uirow
,3,deformated_key
);
3369 MSI_RecordSetStringW(uirow
,4,deformated_value
);
3370 ui_actiondata(package
,szWriteIniValues
,uirow
);
3371 msiobj_release( &uirow
->hdr
);
3375 msi_free(deformated_key
);
3376 msi_free(deformated_value
);
3377 msi_free(deformated_section
);
3378 return ERROR_SUCCESS
;
3381 static UINT
ACTION_WriteIniValues(MSIPACKAGE
*package
)
3385 static const WCHAR ExecSeqQuery
[] =
3386 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3387 '`','I','n','i','F','i','l','e','`',0};
3389 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3390 if (rc
!= ERROR_SUCCESS
)
3392 TRACE("no IniFile table\n");
3393 return ERROR_SUCCESS
;
3396 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteIniValues
, package
);
3397 msiobj_release(&view
->hdr
);
3401 static UINT
ITERATE_SelfRegModules(MSIRECORD
*row
, LPVOID param
)
3403 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3408 static const WCHAR ExeStr
[] =
3409 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3410 static const WCHAR close
[] = {'\"',0};
3412 PROCESS_INFORMATION info
;
3417 memset(&si
,0,sizeof(STARTUPINFOW
));
3419 filename
= MSI_RecordGetString(row
,1);
3420 file
= get_loaded_file( package
, filename
);
3424 ERR("Unable to find file id %s\n",debugstr_w(filename
));
3425 return ERROR_SUCCESS
;
3428 len
= strlenW(ExeStr
) + strlenW( file
->TargetPath
) + 2;
3430 FullName
= msi_alloc(len
*sizeof(WCHAR
));
3431 strcpyW(FullName
,ExeStr
);
3432 strcatW( FullName
, file
->TargetPath
);
3433 strcatW(FullName
,close
);
3435 TRACE("Registering %s\n",debugstr_w(FullName
));
3436 brc
= CreateProcessW(NULL
, FullName
, NULL
, NULL
, FALSE
, 0, NULL
, c_colon
,
3440 msi_dialog_check_messages(info
.hProcess
);
3445 uirow
= MSI_CreateRecord( 2 );
3446 uipath
= strdupW( file
->TargetPath
);
3447 p
= strrchrW(uipath
,'\\');
3450 MSI_RecordSetStringW( uirow
, 1, &p
[2] );
3451 MSI_RecordSetStringW( uirow
, 2, uipath
);
3452 ui_actiondata( package
, szSelfRegModules
, uirow
);
3453 msiobj_release( &uirow
->hdr
);
3455 /* FIXME: call ui_progress? */
3457 return ERROR_SUCCESS
;
3460 static UINT
ACTION_SelfRegModules(MSIPACKAGE
*package
)
3464 static const WCHAR ExecSeqQuery
[] =
3465 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3466 '`','S','e','l','f','R','e','g','`',0};
3468 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3469 if (rc
!= ERROR_SUCCESS
)
3471 TRACE("no SelfReg table\n");
3472 return ERROR_SUCCESS
;
3475 MSI_IterateRecords(view
, NULL
, ITERATE_SelfRegModules
, package
);
3476 msiobj_release(&view
->hdr
);
3478 return ERROR_SUCCESS
;
3481 static UINT
ACTION_PublishFeatures(MSIPACKAGE
*package
)
3483 MSIFEATURE
*feature
;
3488 rc
= MSIREG_OpenFeaturesKey(package
->ProductCode
,&hkey
,TRUE
);
3489 if (rc
!= ERROR_SUCCESS
)
3492 rc
= MSIREG_OpenUserFeaturesKey(package
->ProductCode
,&hukey
,TRUE
);
3493 if (rc
!= ERROR_SUCCESS
)
3496 /* here the guids are base 85 encoded */
3497 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
3503 BOOL absent
= FALSE
;
3506 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
) &&
3507 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_SOURCE
) &&
3508 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ADVERTISED
))
3512 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3516 if (feature
->Feature_Parent
)
3517 size
+= strlenW( feature
->Feature_Parent
)+2;
3519 data
= msi_alloc(size
* sizeof(WCHAR
));
3522 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3524 MSICOMPONENT
* component
= cl
->component
;
3528 if (component
->ComponentId
)
3530 TRACE("From %s\n",debugstr_w(component
->ComponentId
));
3531 CLSIDFromString(component
->ComponentId
, &clsid
);
3532 encode_base85_guid(&clsid
,buf
);
3533 TRACE("to %s\n",debugstr_w(buf
));
3537 if (feature
->Feature_Parent
)
3539 static const WCHAR sep
[] = {'\2',0};
3541 strcatW(data
,feature
->Feature_Parent
);
3544 msi_reg_set_val_str( hkey
, feature
->Feature
, data
);
3548 if (feature
->Feature_Parent
)
3549 size
= strlenW(feature
->Feature_Parent
)*sizeof(WCHAR
);
3552 RegSetValueExW(hukey
,feature
->Feature
,0,REG_SZ
,
3553 (LPBYTE
)feature
->Feature_Parent
,size
);
3557 size
+= 2*sizeof(WCHAR
);
3558 data
= msi_alloc(size
);
3561 if (feature
->Feature_Parent
)
3562 strcpyW( &data
[1], feature
->Feature_Parent
);
3563 RegSetValueExW(hukey
,feature
->Feature
,0,REG_SZ
,
3569 uirow
= MSI_CreateRecord( 1 );
3570 MSI_RecordSetStringW( uirow
, 1, feature
->Feature
);
3571 ui_actiondata( package
, szPublishFeatures
, uirow
);
3572 msiobj_release( &uirow
->hdr
);
3573 /* FIXME: call ui_progress? */
3582 static UINT
msi_get_local_package_name( LPWSTR path
)
3584 static const WCHAR szInstaller
[] = {
3585 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3586 static const WCHAR fmt
[] = { '%','x','.','m','s','i',0};
3590 time
= GetTickCount();
3591 GetWindowsDirectoryW( path
, MAX_PATH
);
3592 lstrcatW( path
, szInstaller
);
3593 CreateDirectoryW( path
, NULL
);
3595 len
= lstrlenW(path
);
3596 for (i
=0; i
<0x10000; i
++)
3598 snprintfW( &path
[len
], MAX_PATH
- len
, fmt
, (time
+i
)&0xffff );
3599 handle
= CreateFileW( path
, GENERIC_WRITE
, 0, NULL
,
3600 CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, 0 );
3601 if (handle
!= INVALID_HANDLE_VALUE
)
3603 CloseHandle(handle
);
3606 if (GetLastError() != ERROR_FILE_EXISTS
&&
3607 GetLastError() != ERROR_SHARING_VIOLATION
)
3608 return ERROR_FUNCTION_FAILED
;
3611 return ERROR_SUCCESS
;
3614 static UINT
msi_make_package_local( MSIPACKAGE
*package
, HKEY hkey
)
3616 static const WCHAR szOriginalDatabase
[] =
3617 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3618 WCHAR packagefile
[MAX_PATH
];
3622 r
= msi_get_local_package_name( packagefile
);
3623 if (r
!= ERROR_SUCCESS
)
3626 TRACE("Copying to local package %s\n",debugstr_w(packagefile
));
3628 msiFilePath
= msi_dup_property( package
, szOriginalDatabase
);
3629 r
= CopyFileW( msiFilePath
, packagefile
, FALSE
);
3630 msi_free( msiFilePath
);
3634 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3635 debugstr_w(msiFilePath
), debugstr_w(packagefile
), GetLastError());
3636 return ERROR_FUNCTION_FAILED
;
3639 /* FIXME: maybe set this key in ACTION_RegisterProduct instead */
3640 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_LOCALPACKAGEW
, packagefile
);
3641 return ERROR_SUCCESS
;
3644 static UINT
msi_write_uninstall_property_vals( MSIPACKAGE
*package
, HKEY hkey
)
3646 LPWSTR prop
, val
, key
;
3647 static const LPCSTR propval
[] = {
3648 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3649 "ARPCONTACT", "Contact",
3650 "ARPCOMMENTS", "Comments",
3651 "ProductName", "DisplayName",
3652 "ProductVersion", "DisplayVersion",
3653 "ARPHELPLINK", "HelpLink",
3654 "ARPHELPTELEPHONE", "HelpTelephone",
3655 "ARPINSTALLLOCATION", "InstallLocation",
3656 "SourceDir", "InstallSource",
3657 "Manufacturer", "Publisher",
3658 "ARPREADME", "Readme",
3660 "ARPURLINFOABOUT", "URLInfoAbout",
3661 "ARPURLUPDATEINFO", "URLUpdateInfo",
3664 const LPCSTR
*p
= propval
;
3668 prop
= strdupAtoW( *p
++ );
3669 key
= strdupAtoW( *p
++ );
3670 val
= msi_dup_property( package
, prop
);
3671 msi_reg_set_val_str( hkey
, key
, val
);
3676 return ERROR_SUCCESS
;
3679 static UINT
ACTION_RegisterProduct(MSIPACKAGE
*package
)
3682 LPWSTR buffer
= NULL
;
3685 static const WCHAR szWindowsInstaller
[] =
3686 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3687 static const WCHAR szUpgradeCode
[] =
3688 {'U','p','g','r','a','d','e','C','o','d','e',0};
3689 static const WCHAR modpath_fmt
[] =
3690 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3691 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3692 static const WCHAR szModifyPath
[] =
3693 {'M','o','d','i','f','y','P','a','t','h',0};
3694 static const WCHAR szUninstallString
[] =
3695 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3696 static const WCHAR szEstimatedSize
[] =
3697 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3698 static const WCHAR szProductLanguage
[] =
3699 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3700 static const WCHAR szProductVersion
[] =
3701 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3704 static const WCHAR date_fmt
[] = {'%','i','%','i','%','i',0};
3705 LPWSTR upgrade_code
;
3708 rc
= MSIREG_OpenUninstallKey(package
->ProductCode
,&hkey
,TRUE
);
3709 if (rc
!= ERROR_SUCCESS
)
3712 /* dump all the info i can grab */
3713 /* FIXME: Flesh out more information */
3715 msi_write_uninstall_property_vals( package
, hkey
);
3717 msi_reg_set_val_dword( hkey
, szWindowsInstaller
, 1 );
3719 msi_make_package_local( package
, hkey
);
3721 /* do ModifyPath and UninstallString */
3722 size
= deformat_string(package
,modpath_fmt
,&buffer
);
3723 RegSetValueExW(hkey
,szModifyPath
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
3724 RegSetValueExW(hkey
,szUninstallString
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
3727 /* FIXME: Write real Estimated Size when we have it */
3728 msi_reg_set_val_dword( hkey
, szEstimatedSize
, 0 );
3730 GetLocalTime(&systime
);
3731 sprintfW(szDate
,date_fmt
,systime
.wYear
,systime
.wMonth
,systime
.wDay
);
3732 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_INSTALLDATEW
, szDate
);
3734 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
3735 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
3737 buffer
= msi_dup_property( package
, szProductVersion
);
3740 DWORD verdword
= msi_version_str_to_dword(buffer
);
3742 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
3743 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMAJORW
, verdword
>>24 );
3744 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMINORW
, (verdword
>>16)&0x00FF );
3748 /* Handle Upgrade Codes */
3749 upgrade_code
= msi_dup_property( package
, szUpgradeCode
);
3754 MSIREG_OpenUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
3755 squash_guid(package
->ProductCode
,squashed
);
3756 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
3758 MSIREG_OpenUserUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
3759 squash_guid(package
->ProductCode
,squashed
);
3760 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
3763 msi_free(upgrade_code
);
3768 /* FIXME: call ui_actiondata */
3770 return ERROR_SUCCESS
;
3773 static UINT
ACTION_InstallExecute(MSIPACKAGE
*package
)
3775 return execute_script(package
,INSTALL_SCRIPT
);
3778 static UINT
ACTION_InstallFinalize(MSIPACKAGE
*package
)
3782 /* turn off scheduling */
3783 package
->script
->CurrentlyScripting
= FALSE
;
3785 /* first do the same as an InstallExecute */
3786 rc
= ACTION_InstallExecute(package
);
3787 if (rc
!= ERROR_SUCCESS
)
3790 /* then handle Commit Actions */
3791 rc
= execute_script(package
,COMMIT_SCRIPT
);
3796 static UINT
ACTION_ForceReboot(MSIPACKAGE
*package
)
3798 static const WCHAR RunOnce
[] = {
3799 'S','o','f','t','w','a','r','e','\\',
3800 'M','i','c','r','o','s','o','f','t','\\',
3801 'W','i','n','d','o','w','s','\\',
3802 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3803 'R','u','n','O','n','c','e',0};
3804 static const WCHAR InstallRunOnce
[] = {
3805 'S','o','f','t','w','a','r','e','\\',
3806 'M','i','c','r','o','s','o','f','t','\\',
3807 'W','i','n','d','o','w','s','\\',
3808 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3809 'I','n','s','t','a','l','l','e','r','\\',
3810 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
3812 static const WCHAR msiexec_fmt
[] = {
3814 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
3815 '\"','%','s','\"',0};
3816 static const WCHAR install_fmt
[] = {
3817 '/','I',' ','\"','%','s','\"',' ',
3818 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
3819 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
3820 WCHAR buffer
[256], sysdir
[MAX_PATH
];
3822 WCHAR squished_pc
[100];
3824 squash_guid(package
->ProductCode
,squished_pc
);
3826 GetSystemDirectoryW(sysdir
, sizeof(sysdir
)/sizeof(sysdir
[0]));
3827 RegCreateKeyW(HKEY_LOCAL_MACHINE
,RunOnce
,&hkey
);
3828 snprintfW(buffer
,sizeof(buffer
)/sizeof(buffer
[0]),msiexec_fmt
,sysdir
,
3831 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
3834 TRACE("Reboot command %s\n",debugstr_w(buffer
));
3836 RegCreateKeyW(HKEY_LOCAL_MACHINE
,InstallRunOnce
,&hkey
);
3837 sprintfW(buffer
,install_fmt
,package
->ProductCode
,squished_pc
);
3839 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
3842 return ERROR_INSTALL_SUSPEND
;
3845 UINT
msi_set_sourcedir_props(MSIPACKAGE
*package
)
3850 p
= strrchrW( package
->PackagePath
, '\\' );
3852 return ERROR_SUCCESS
;
3854 len
= p
- package
->PackagePath
+ 2;
3855 source
= msi_alloc( len
* sizeof(WCHAR
) );
3856 lstrcpynW( source
, package
->PackagePath
, len
);
3858 MSI_SetPropertyW( package
, cszSourceDir
, source
);
3859 MSI_SetPropertyW( package
, cszSOURCEDIR
, source
);
3863 return ERROR_SUCCESS
;
3866 static UINT
ACTION_ResolveSource(MSIPACKAGE
* package
)
3872 * We are currently doing what should be done here in the top level Install
3873 * however for Administrative and uninstalls this step will be needed
3875 if (!package
->PackagePath
)
3876 return ERROR_SUCCESS
;
3878 msi_set_sourcedir_props(package
);
3880 attrib
= GetFileAttributesW(package
->PackagePath
);
3881 if (attrib
== INVALID_FILE_ATTRIBUTES
)
3887 rc
= MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
3888 MSIINSTALLCONTEXT_USERMANAGED
, MSICODE_PRODUCT
,
3889 INSTALLPROPERTY_DISKPROMPTW
,NULL
,&size
);
3890 if (rc
== ERROR_MORE_DATA
)
3892 prompt
= msi_alloc(size
* sizeof(WCHAR
));
3893 MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
3894 MSIINSTALLCONTEXT_USERMANAGED
, MSICODE_PRODUCT
,
3895 INSTALLPROPERTY_DISKPROMPTW
,prompt
,&size
);
3898 prompt
= strdupW(package
->PackagePath
);
3900 msg
= generate_error_string(package
,1302,1,prompt
);
3901 while(attrib
== INVALID_FILE_ATTRIBUTES
)
3903 rc
= MessageBoxW(NULL
,msg
,NULL
,MB_OKCANCEL
);
3906 rc
= ERROR_INSTALL_USEREXIT
;
3909 attrib
= GetFileAttributesW(package
->PackagePath
);
3915 return ERROR_SUCCESS
;
3920 static UINT
ACTION_RegisterUser(MSIPACKAGE
*package
)
3927 static const WCHAR szPropKeys
[][80] =
3929 {'P','r','o','d','u','c','t','I','D',0},
3930 {'U','S','E','R','N','A','M','E',0},
3931 {'C','O','M','P','A','N','Y','N','A','M','E',0},
3935 static const WCHAR szRegKeys
[][80] =
3937 {'P','r','o','d','u','c','t','I','D',0},
3938 {'R','e','g','O','w','n','e','r',0},
3939 {'R','e','g','C','o','m','p','a','n','y',0},
3943 productid
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTIDW
);
3945 return ERROR_SUCCESS
;
3947 rc
= MSIREG_OpenUninstallKey(package
->ProductCode
,&hkey
,TRUE
);
3948 if (rc
!= ERROR_SUCCESS
)
3951 for( i
= 0; szPropKeys
[i
][0]; i
++ )
3953 buffer
= msi_dup_property( package
, szPropKeys
[i
] );
3954 msi_reg_set_val_str( hkey
, szRegKeys
[i
], buffer
);
3959 msi_free(productid
);
3962 /* FIXME: call ui_actiondata */
3964 return ERROR_SUCCESS
;
3968 static UINT
ACTION_ExecuteAction(MSIPACKAGE
*package
)
3972 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
3973 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
3978 static UINT
ITERATE_PublishComponent(MSIRECORD
*rec
, LPVOID param
)
3980 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3981 LPCWSTR compgroupid
=NULL
;
3982 LPCWSTR feature
=NULL
;
3983 LPCWSTR text
= NULL
;
3984 LPCWSTR qualifier
= NULL
;
3985 LPCWSTR component
= NULL
;
3986 LPWSTR advertise
= NULL
;
3987 LPWSTR output
= NULL
;
3989 UINT rc
= ERROR_SUCCESS
;
3994 component
= MSI_RecordGetString(rec
,3);
3995 comp
= get_loaded_component(package
,component
);
3997 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
) &&
3998 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_SOURCE
) &&
3999 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ADVERTISED
))
4001 TRACE("Skipping: Component %s not scheduled for install\n",
4002 debugstr_w(component
));
4004 return ERROR_SUCCESS
;
4007 compgroupid
= MSI_RecordGetString(rec
,1);
4008 qualifier
= MSI_RecordGetString(rec
,2);
4010 rc
= MSIREG_OpenUserComponentsKey(compgroupid
, &hkey
, TRUE
);
4011 if (rc
!= ERROR_SUCCESS
)
4014 text
= MSI_RecordGetString(rec
,4);
4015 feature
= MSI_RecordGetString(rec
,5);
4017 advertise
= create_component_advertise_string(package
, comp
, feature
);
4019 sz
= strlenW(advertise
);
4022 sz
+= lstrlenW(text
);
4025 sz
*= sizeof(WCHAR
);
4027 output
= msi_alloc_zero(sz
);
4028 strcpyW(output
,advertise
);
4029 msi_free(advertise
);
4032 strcatW(output
,text
);
4034 msi_reg_set_val_multi_str( hkey
, qualifier
, output
);
4041 uirow
= MSI_CreateRecord( 2 );
4042 MSI_RecordSetStringW( uirow
, 1, compgroupid
);
4043 MSI_RecordSetStringW( uirow
, 2, qualifier
);
4044 ui_actiondata( package
, szPublishComponents
, uirow
);
4045 msiobj_release( &uirow
->hdr
);
4046 /* FIXME: call ui_progress? */
4052 * At present I am ignorning the advertised components part of this and only
4053 * focusing on the qualified component sets
4055 static UINT
ACTION_PublishComponents(MSIPACKAGE
*package
)
4059 static const WCHAR ExecSeqQuery
[] =
4060 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4061 '`','P','u','b','l','i','s','h',
4062 'C','o','m','p','o','n','e','n','t','`',0};
4064 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4065 if (rc
!= ERROR_SUCCESS
)
4066 return ERROR_SUCCESS
;
4068 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_PublishComponent
, package
);
4069 msiobj_release(&view
->hdr
);
4074 static UINT
ITERATE_InstallService(MSIRECORD
*rec
, LPVOID param
)
4076 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4079 SC_HANDLE hscm
, service
= NULL
;
4080 LPCWSTR name
, disp
, comp
, depends
, pass
;
4081 LPCWSTR load_order
, serv_name
, key
;
4082 DWORD serv_type
, start_type
;
4085 static const WCHAR query
[] =
4086 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4087 '`','C','o','m','p','o','n','e','n','t','`',' ',
4088 'W','H','E','R','E',' ',
4089 '`','C','o','m','p','o','n','e','n','t','`',' ',
4090 '=','\'','%','s','\'',0};
4092 hscm
= OpenSCManagerW(NULL
, SERVICES_ACTIVE_DATABASEW
, GENERIC_WRITE
);
4095 ERR("Failed to open the SC Manager!\n");
4099 start_type
= MSI_RecordGetInteger(rec
, 5);
4100 if (start_type
== SERVICE_BOOT_START
|| start_type
== SERVICE_SYSTEM_START
)
4103 depends
= MSI_RecordGetString(rec
, 8);
4104 if (depends
&& *depends
)
4105 FIXME("Dependency list unhandled!\n");
4107 name
= MSI_RecordGetString(rec
, 2);
4108 disp
= MSI_RecordGetString(rec
, 3);
4109 serv_type
= MSI_RecordGetInteger(rec
, 4);
4110 err_control
= MSI_RecordGetInteger(rec
, 6);
4111 load_order
= MSI_RecordGetString(rec
, 7);
4112 serv_name
= MSI_RecordGetString(rec
, 9);
4113 pass
= MSI_RecordGetString(rec
, 10);
4114 comp
= MSI_RecordGetString(rec
, 12);
4116 /* fetch the service path */
4117 row
= MSI_QueryGetRecord(package
->db
, query
, comp
);
4120 ERR("Control query failed!\n");
4124 key
= MSI_RecordGetString(row
, 6);
4125 msiobj_release(&row
->hdr
);
4127 file
= get_loaded_file(package
, key
);
4130 ERR("Failed to load the service file\n");
4134 service
= CreateServiceW(hscm
, name
, disp
, GENERIC_ALL
, serv_type
,
4135 start_type
, err_control
, file
->TargetPath
,
4136 load_order
, NULL
, NULL
, serv_name
, pass
);
4139 if (GetLastError() != ERROR_SERVICE_EXISTS
)
4140 ERR("Failed to create service %s: %d\n", debugstr_w(name
), GetLastError());
4144 CloseServiceHandle(service
);
4145 CloseServiceHandle(hscm
);
4147 return ERROR_SUCCESS
;
4150 static UINT
ACTION_InstallServices( MSIPACKAGE
*package
)
4154 static const WCHAR ExecSeqQuery
[] =
4155 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4156 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4158 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4159 if (rc
!= ERROR_SUCCESS
)
4160 return ERROR_SUCCESS
;
4162 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallService
, package
);
4163 msiobj_release(&view
->hdr
);
4168 static UINT
msi_unimplemented_action_stub( MSIPACKAGE
*package
,
4169 LPCSTR action
, LPCWSTR table
)
4171 static const WCHAR query
[] = {
4172 'S','E','L','E','C','T',' ','*',' ',
4173 'F','R','O','M',' ','`','%','s','`',0 };
4174 MSIQUERY
*view
= NULL
;
4178 r
= MSI_OpenQuery( package
->db
, &view
, query
, table
);
4179 if (r
== ERROR_SUCCESS
)
4181 r
= MSI_IterateRecords(view
, &count
, NULL
, package
);
4182 msiobj_release(&view
->hdr
);
4186 FIXME("%s -> %u ignored %s table values\n",
4187 action
, count
, debugstr_w(table
));
4189 return ERROR_SUCCESS
;
4192 static UINT
ACTION_AllocateRegistrySpace( MSIPACKAGE
*package
)
4194 TRACE("%p\n", package
);
4195 return ERROR_SUCCESS
;
4198 static UINT
ACTION_RemoveIniValues( MSIPACKAGE
*package
)
4200 static const WCHAR table
[] =
4201 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
4202 return msi_unimplemented_action_stub( package
, "RemoveIniValues", table
);
4205 static UINT
ACTION_MoveFiles( MSIPACKAGE
*package
)
4207 static const WCHAR table
[] = { 'M','o','v','e','F','i','l','e',0 };
4208 return msi_unimplemented_action_stub( package
, "MoveFiles", table
);
4211 static UINT
ACTION_PatchFiles( MSIPACKAGE
*package
)
4213 static const WCHAR table
[] = { 'P','a','t','c','h',0 };
4214 return msi_unimplemented_action_stub( package
, "PatchFiles", table
);
4217 static UINT
ACTION_BindImage( MSIPACKAGE
*package
)
4219 static const WCHAR table
[] = { 'B','i','n','d','I','m','a','g','e',0 };
4220 return msi_unimplemented_action_stub( package
, "BindImage", table
);
4223 static UINT
ACTION_IsolateComponents( MSIPACKAGE
*package
)
4225 static const WCHAR table
[] = {
4226 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
4227 return msi_unimplemented_action_stub( package
, "IsolateComponents", table
);
4230 static UINT
ACTION_MigrateFeatureStates( MSIPACKAGE
*package
)
4232 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
4233 return msi_unimplemented_action_stub( package
, "MigrateFeatureStates", table
);
4236 static UINT
ACTION_SelfUnregModules( MSIPACKAGE
*package
)
4238 static const WCHAR table
[] = { 'S','e','l','f','R','e','g',0 };
4239 return msi_unimplemented_action_stub( package
, "SelfUnregModules", table
);
4242 static UINT
ACTION_StartServices( MSIPACKAGE
*package
)
4244 static const WCHAR table
[] = {
4245 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4246 return msi_unimplemented_action_stub( package
, "StartServices", table
);
4249 static UINT
ACTION_StopServices( MSIPACKAGE
*package
)
4251 static const WCHAR table
[] = {
4252 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4253 return msi_unimplemented_action_stub( package
, "StopServices", table
);
4256 static UINT
ACTION_DeleteServices( MSIPACKAGE
*package
)
4258 static const WCHAR table
[] = {
4259 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4260 return msi_unimplemented_action_stub( package
, "DeleteServices", table
);
4263 static UINT
ACTION_WriteEnvironmentStrings( MSIPACKAGE
*package
)
4265 static const WCHAR table
[] = {
4266 'E','n','v','i','r','o','n','m','e','n','t',0 };
4267 return msi_unimplemented_action_stub( package
, "WriteEnvironmentStrings", table
);
4270 static UINT
ACTION_RemoveEnvironmentStrings( MSIPACKAGE
*package
)
4272 static const WCHAR table
[] = {
4273 'E','n','v','i','r','o','n','m','e','n','t',0 };
4274 return msi_unimplemented_action_stub( package
, "RemoveEnvironmentStrings", table
);
4277 static UINT
ACTION_MsiPublishAssemblies( MSIPACKAGE
*package
)
4279 static const WCHAR table
[] = {
4280 'M','s','i','A','s','s','e','m','b','l','y',0 };
4281 return msi_unimplemented_action_stub( package
, "MsiPublishAssemblies", table
);
4284 static UINT
ACTION_MsiUnpublishAssemblies( MSIPACKAGE
*package
)
4286 static const WCHAR table
[] = {
4287 'M','s','i','A','s','s','e','m','b','l','y',0 };
4288 return msi_unimplemented_action_stub( package
, "MsiUnpublishAssemblies", table
);
4291 static UINT
ACTION_UnregisterFonts( MSIPACKAGE
*package
)
4293 static const WCHAR table
[] = { 'F','o','n','t',0 };
4294 return msi_unimplemented_action_stub( package
, "UnregisterFonts", table
);
4297 static UINT
ACTION_CCPSearch( MSIPACKAGE
*package
)
4299 static const WCHAR table
[] = { 'C','C','P','S','e','a','r','c','h',0 };
4300 return msi_unimplemented_action_stub( package
, "CCPSearch", table
);
4303 static UINT
ACTION_RMCCPSearch( MSIPACKAGE
*package
)
4305 static const WCHAR table
[] = { 'C','C','P','S','e','a','r','c','h',0 };
4306 return msi_unimplemented_action_stub( package
, "RMCCPSearch", table
);
4309 static UINT
ACTION_RegisterComPlus( MSIPACKAGE
*package
)
4311 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
4312 return msi_unimplemented_action_stub( package
, "RegisterComPlus", table
);
4315 static UINT
ACTION_UnregisterComPlus( MSIPACKAGE
*package
)
4317 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
4318 return msi_unimplemented_action_stub( package
, "UnregisterComPlus", table
);
4321 static struct _actions StandardActions
[] = {
4322 { szAllocateRegistrySpace
, ACTION_AllocateRegistrySpace
},
4323 { szAppSearch
, ACTION_AppSearch
},
4324 { szBindImage
, ACTION_BindImage
},
4325 { szCCPSearch
, ACTION_CCPSearch
},
4326 { szCostFinalize
, ACTION_CostFinalize
},
4327 { szCostInitialize
, ACTION_CostInitialize
},
4328 { szCreateFolders
, ACTION_CreateFolders
},
4329 { szCreateShortcuts
, ACTION_CreateShortcuts
},
4330 { szDeleteServices
, ACTION_DeleteServices
},
4331 { szDisableRollback
, NULL
},
4332 { szDuplicateFiles
, ACTION_DuplicateFiles
},
4333 { szExecuteAction
, ACTION_ExecuteAction
},
4334 { szFileCost
, ACTION_FileCost
},
4335 { szFindRelatedProducts
, ACTION_FindRelatedProducts
},
4336 { szForceReboot
, ACTION_ForceReboot
},
4337 { szInstallAdminPackage
, NULL
},
4338 { szInstallExecute
, ACTION_InstallExecute
},
4339 { szInstallExecuteAgain
, ACTION_InstallExecute
},
4340 { szInstallFiles
, ACTION_InstallFiles
},
4341 { szInstallFinalize
, ACTION_InstallFinalize
},
4342 { szInstallInitialize
, ACTION_InstallInitialize
},
4343 { szInstallSFPCatalogFile
, NULL
},
4344 { szInstallValidate
, ACTION_InstallValidate
},
4345 { szIsolateComponents
, ACTION_IsolateComponents
},
4346 { szLaunchConditions
, ACTION_LaunchConditions
},
4347 { szMigrateFeatureStates
, ACTION_MigrateFeatureStates
},
4348 { szMoveFiles
, ACTION_MoveFiles
},
4349 { szMsiPublishAssemblies
, ACTION_MsiPublishAssemblies
},
4350 { szMsiUnpublishAssemblies
, ACTION_MsiUnpublishAssemblies
},
4351 { szInstallODBC
, NULL
},
4352 { szInstallServices
, ACTION_InstallServices
},
4353 { szPatchFiles
, ACTION_PatchFiles
},
4354 { szProcessComponents
, ACTION_ProcessComponents
},
4355 { szPublishComponents
, ACTION_PublishComponents
},
4356 { szPublishFeatures
, ACTION_PublishFeatures
},
4357 { szPublishProduct
, ACTION_PublishProduct
},
4358 { szRegisterClassInfo
, ACTION_RegisterClassInfo
},
4359 { szRegisterComPlus
, ACTION_RegisterComPlus
},
4360 { szRegisterExtensionInfo
, ACTION_RegisterExtensionInfo
},
4361 { szRegisterFonts
, ACTION_RegisterFonts
},
4362 { szRegisterMIMEInfo
, ACTION_RegisterMIMEInfo
},
4363 { szRegisterProduct
, ACTION_RegisterProduct
},
4364 { szRegisterProgIdInfo
, ACTION_RegisterProgIdInfo
},
4365 { szRegisterTypeLibraries
, ACTION_RegisterTypeLibraries
},
4366 { szRegisterUser
, ACTION_RegisterUser
},
4367 { szRemoveDuplicateFiles
, NULL
},
4368 { szRemoveEnvironmentStrings
, ACTION_RemoveEnvironmentStrings
},
4369 { szRemoveExistingProducts
, NULL
},
4370 { szRemoveFiles
, ACTION_RemoveFiles
},
4371 { szRemoveFolders
, NULL
},
4372 { szRemoveIniValues
, ACTION_RemoveIniValues
},
4373 { szRemoveODBC
, NULL
},
4374 { szRemoveRegistryValues
, NULL
},
4375 { szRemoveShortcuts
, NULL
},
4376 { szResolveSource
, ACTION_ResolveSource
},
4377 { szRMCCPSearch
, ACTION_RMCCPSearch
},
4378 { szScheduleReboot
, NULL
},
4379 { szSelfRegModules
, ACTION_SelfRegModules
},
4380 { szSelfUnregModules
, ACTION_SelfUnregModules
},
4381 { szSetODBCFolders
, NULL
},
4382 { szStartServices
, ACTION_StartServices
},
4383 { szStopServices
, ACTION_StopServices
},
4384 { szUnpublishComponents
, NULL
},
4385 { szUnpublishFeatures
, NULL
},
4386 { szUnregisterClassInfo
, NULL
},
4387 { szUnregisterComPlus
, ACTION_UnregisterComPlus
},
4388 { szUnregisterExtensionInfo
, NULL
},
4389 { szUnregisterFonts
, ACTION_UnregisterFonts
},
4390 { szUnregisterMIMEInfo
, NULL
},
4391 { szUnregisterProgIdInfo
, NULL
},
4392 { szUnregisterTypeLibraries
, NULL
},
4393 { szValidateProductID
, NULL
},
4394 { szWriteEnvironmentStrings
, ACTION_WriteEnvironmentStrings
},
4395 { szWriteIniValues
, ACTION_WriteIniValues
},
4396 { szWriteRegistryValues
, ACTION_WriteRegistryValues
},