2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
26 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
39 #include "wine/debug.h"
44 #include "wine/unicode.h"
47 #define REG_PROGRESS_VALUE 13200
48 #define COMPONENT_PROGRESS_VALUE 24000
50 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
55 static UINT
ACTION_ProcessExecSequence(MSIPACKAGE
*package
, BOOL UIran
);
56 static UINT
ACTION_ProcessUISequence(MSIPACKAGE
*package
);
57 static UINT
ACTION_PerformActionSequence(MSIPACKAGE
*package
, UINT seq
, BOOL UI
);
60 * consts and values used
62 static const WCHAR c_colon
[] = {'C',':','\\',0};
64 static const WCHAR szCreateFolders
[] =
65 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
66 static const WCHAR szCostFinalize
[] =
67 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
68 const WCHAR szInstallFiles
[] =
69 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
70 const WCHAR szDuplicateFiles
[] =
71 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
72 static const WCHAR szWriteRegistryValues
[] =
73 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
74 'V','a','l','u','e','s',0};
75 static const WCHAR szCostInitialize
[] =
76 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
77 static const WCHAR szFileCost
[] =
78 {'F','i','l','e','C','o','s','t',0};
79 static const WCHAR szInstallInitialize
[] =
80 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
81 static const WCHAR szInstallValidate
[] =
82 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
83 static const WCHAR szLaunchConditions
[] =
84 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
85 static const WCHAR szProcessComponents
[] =
86 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
87 static const WCHAR szRegisterTypeLibraries
[] =
88 {'R','e','g','i','s','t','e','r','T','y','p','e',
89 'L','i','b','r','a','r','i','e','s',0};
90 const WCHAR szRegisterClassInfo
[] =
91 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
92 const WCHAR szRegisterProgIdInfo
[] =
93 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
94 static const WCHAR szCreateShortcuts
[] =
95 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
96 static const WCHAR szPublishProduct
[] =
97 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
98 static const WCHAR szWriteIniValues
[] =
99 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
100 static const WCHAR szSelfRegModules
[] =
101 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
102 static const WCHAR szPublishFeatures
[] =
103 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
104 static const WCHAR szRegisterProduct
[] =
105 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
106 static const WCHAR szInstallExecute
[] =
107 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
108 static const WCHAR szInstallExecuteAgain
[] =
109 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
110 'A','g','a','i','n',0};
111 static const WCHAR szInstallFinalize
[] =
112 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
113 static const WCHAR szForceReboot
[] =
114 {'F','o','r','c','e','R','e','b','o','o','t',0};
115 static const WCHAR szResolveSource
[] =
116 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
117 static const WCHAR szAppSearch
[] =
118 {'A','p','p','S','e','a','r','c','h',0};
119 static const WCHAR szAllocateRegistrySpace
[] =
120 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
121 'S','p','a','c','e',0};
122 static const WCHAR szBindImage
[] =
123 {'B','i','n','d','I','m','a','g','e',0};
124 static const WCHAR szCCPSearch
[] =
125 {'C','C','P','S','e','a','r','c','h',0};
126 static const WCHAR szDeleteServices
[] =
127 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
128 static const WCHAR szDisableRollback
[] =
129 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
130 static const WCHAR szExecuteAction
[] =
131 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
132 const WCHAR szFindRelatedProducts
[] =
133 {'F','i','n','d','R','e','l','a','t','e','d',
134 'P','r','o','d','u','c','t','s',0};
135 static const WCHAR szInstallAdminPackage
[] =
136 {'I','n','s','t','a','l','l','A','d','m','i','n',
137 'P','a','c','k','a','g','e',0};
138 static const WCHAR szInstallSFPCatalogFile
[] =
139 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
141 static const WCHAR szIsolateComponents
[] =
142 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
143 const WCHAR szMigrateFeatureStates
[] =
144 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
145 'S','t','a','t','e','s',0};
146 const WCHAR szMoveFiles
[] =
147 {'M','o','v','e','F','i','l','e','s',0};
148 static const WCHAR szMsiPublishAssemblies
[] =
149 {'M','s','i','P','u','b','l','i','s','h',
150 'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szMsiUnpublishAssemblies
[] =
152 {'M','s','i','U','n','p','u','b','l','i','s','h',
153 'A','s','s','e','m','b','l','i','e','s',0};
154 static const WCHAR szInstallODBC
[] =
155 {'I','n','s','t','a','l','l','O','D','B','C',0};
156 static const WCHAR szInstallServices
[] =
157 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
158 const WCHAR szPatchFiles
[] =
159 {'P','a','t','c','h','F','i','l','e','s',0};
160 static const WCHAR szPublishComponents
[] =
161 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
162 static const WCHAR szRegisterComPlus
[] =
163 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
164 const WCHAR szRegisterExtensionInfo
[] =
165 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
167 static const WCHAR szRegisterFonts
[] =
168 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
169 const WCHAR szRegisterMIMEInfo
[] =
170 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
171 static const WCHAR szRegisterUser
[] =
172 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
173 const WCHAR szRemoveDuplicateFiles
[] =
174 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
175 'F','i','l','e','s',0};
176 static const WCHAR szRemoveEnvironmentStrings
[] =
177 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
178 'S','t','r','i','n','g','s',0};
179 const WCHAR szRemoveExistingProducts
[] =
180 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
181 'P','r','o','d','u','c','t','s',0};
182 const WCHAR szRemoveFiles
[] =
183 {'R','e','m','o','v','e','F','i','l','e','s',0};
184 static const WCHAR szRemoveFolders
[] =
185 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
186 static const WCHAR szRemoveIniValues
[] =
187 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
188 static const WCHAR szRemoveODBC
[] =
189 {'R','e','m','o','v','e','O','D','B','C',0};
190 static const WCHAR szRemoveRegistryValues
[] =
191 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
192 'V','a','l','u','e','s',0};
193 static const WCHAR szRemoveShortcuts
[] =
194 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
195 static const WCHAR szRMCCPSearch
[] =
196 {'R','M','C','C','P','S','e','a','r','c','h',0};
197 static const WCHAR szScheduleReboot
[] =
198 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
199 static const WCHAR szSelfUnregModules
[] =
200 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
201 static const WCHAR szSetODBCFolders
[] =
202 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
203 static const WCHAR szStartServices
[] =
204 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
205 static const WCHAR szStopServices
[] =
206 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
207 static const WCHAR szUnpublishComponents
[] =
208 {'U','n','p','u','b','l','i','s','h',
209 'C','o','m','p','o','n','e','n','t','s',0};
210 static const WCHAR szUnpublishFeatures
[] =
211 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
212 const WCHAR szUnregisterClassInfo
[] =
213 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
215 static const WCHAR szUnregisterComPlus
[] =
216 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
217 const WCHAR szUnregisterExtensionInfo
[] =
218 {'U','n','r','e','g','i','s','t','e','r',
219 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
220 static const WCHAR szUnregisterFonts
[] =
221 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
222 const WCHAR szUnregisterMIMEInfo
[] =
223 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
224 const WCHAR szUnregisterProgIdInfo
[] =
225 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
227 static const WCHAR szUnregisterTypeLibraries
[] =
228 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
229 'L','i','b','r','a','r','i','e','s',0};
230 static const WCHAR szValidateProductID
[] =
231 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
232 static const WCHAR szWriteEnvironmentStrings
[] =
233 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
234 'S','t','r','i','n','g','s',0};
236 /* action handlers */
237 typedef UINT (*STANDARDACTIONHANDLER
)(MSIPACKAGE
*);
241 STANDARDACTIONHANDLER handler
;
244 static const struct _actions StandardActions
[];
247 /********************************************************
249 ********************************************************/
251 static void ui_actionstart(MSIPACKAGE
*package
, LPCWSTR action
)
253 static const WCHAR Query_t
[] =
254 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
255 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
256 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
257 ' ','\'','%','s','\'',0};
260 row
= MSI_QueryGetRecord( package
->db
, Query_t
, action
);
263 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONSTART
, row
);
264 msiobj_release(&row
->hdr
);
267 static void ui_actioninfo(MSIPACKAGE
*package
, LPCWSTR action
, BOOL start
,
271 static const WCHAR template_s
[]=
272 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
274 static const WCHAR template_e
[]=
275 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
276 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
278 static const WCHAR format
[] =
279 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
283 GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, NULL
, format
, timet
, 0x100);
285 sprintfW(message
,template_s
,timet
,action
);
287 sprintfW(message
,template_e
,timet
,action
,rc
);
289 row
= MSI_CreateRecord(1);
290 MSI_RecordSetStringW(row
,1,message
);
292 MSI_ProcessMessage(package
, INSTALLMESSAGE_INFO
, row
);
293 msiobj_release(&row
->hdr
);
296 UINT
msi_parse_command_line( MSIPACKAGE
*package
, LPCWSTR szCommandLine
)
301 LPWSTR prop
= NULL
, val
= NULL
;
304 return ERROR_SUCCESS
;
316 TRACE("Looking at %s\n",debugstr_w(ptr
));
318 ptr2
= strchrW(ptr
,'=');
321 ERR("command line contains unknown string : %s\n", debugstr_w(ptr
));
328 prop
= msi_alloc((len
+1)*sizeof(WCHAR
));
329 memcpy(prop
,ptr
,len
*sizeof(WCHAR
));
335 while (*ptr
&& (quote
|| (!quote
&& *ptr
!=' ')))
348 val
= msi_alloc((len
+1)*sizeof(WCHAR
));
349 memcpy(val
,ptr2
,len
*sizeof(WCHAR
));
352 if (lstrlenW(prop
) > 0)
354 TRACE("Found commandline property (%s) = (%s)\n",
355 debugstr_w(prop
), debugstr_w(val
));
356 MSI_SetPropertyW(package
,prop
,val
);
362 return ERROR_SUCCESS
;
366 static LPWSTR
* msi_split_string( LPCWSTR str
, WCHAR sep
)
369 LPWSTR p
, *ret
= NULL
;
375 /* count the number of substrings */
376 for ( pc
= str
, count
= 0; pc
; count
++ )
378 pc
= strchrW( pc
, sep
);
383 /* allocate space for an array of substring pointers and the substrings */
384 ret
= msi_alloc( (count
+1) * sizeof (LPWSTR
) +
385 (lstrlenW(str
)+1) * sizeof(WCHAR
) );
389 /* copy the string and set the pointers */
390 p
= (LPWSTR
) &ret
[count
+1];
392 for( count
= 0; (ret
[count
] = p
); count
++ )
394 p
= strchrW( p
, sep
);
402 static UINT
msi_check_transform_applicable( MSIPACKAGE
*package
, IStorage
*patch
)
404 WCHAR szProductCode
[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
405 LPWSTR prod_code
, patch_product
;
408 prod_code
= msi_dup_property( package
, szProductCode
);
409 patch_product
= msi_get_suminfo_product( patch
);
411 TRACE("db = %s patch = %s\n", debugstr_w(prod_code
), debugstr_w(patch_product
));
413 if ( strstrW( patch_product
, prod_code
) )
416 ret
= ERROR_FUNCTION_FAILED
;
418 msi_free( patch_product
);
419 msi_free( prod_code
);
424 static UINT
msi_apply_substorage_transform( MSIPACKAGE
*package
,
425 MSIDATABASE
*patch_db
, LPCWSTR name
)
427 UINT ret
= ERROR_FUNCTION_FAILED
;
428 IStorage
*stg
= NULL
;
431 TRACE("%p %s\n", package
, debugstr_w(name
) );
435 ERR("expected a colon in %s\n", debugstr_w(name
));
436 return ERROR_FUNCTION_FAILED
;
439 r
= IStorage_OpenStorage( patch_db
->storage
, name
, NULL
, STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
442 ret
= msi_check_transform_applicable( package
, stg
);
443 if (ret
== ERROR_SUCCESS
)
444 msi_table_apply_transform( package
->db
, stg
);
446 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name
));
447 IStorage_Release( stg
);
450 ERR("failed to open substorage %s\n", debugstr_w(name
));
452 return ERROR_SUCCESS
;
455 static UINT
msi_check_patch_applicable( MSIPACKAGE
*package
, MSISUMMARYINFO
*si
)
457 static const WCHAR szProdCode
[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
458 LPWSTR guid_list
, *guids
, product_code
;
459 UINT i
, ret
= ERROR_FUNCTION_FAILED
;
461 product_code
= msi_dup_property( package
, szProdCode
);
464 /* FIXME: the property ProductCode should be written into the DB somewhere */
465 ERR("no product code to check\n");
466 return ERROR_SUCCESS
;
469 guid_list
= msi_suminfo_dup_string( si
, PID_TEMPLATE
);
470 guids
= msi_split_string( guid_list
, ';' );
471 for ( i
= 0; guids
[i
] && ret
!= ERROR_SUCCESS
; i
++ )
473 if (!lstrcmpW( guids
[i
], product_code
))
477 msi_free( guid_list
);
478 msi_free( product_code
);
483 static UINT
msi_parse_patch_summary( MSIPACKAGE
*package
, MSIDATABASE
*patch_db
)
486 LPWSTR str
, *substorage
;
487 UINT i
, r
= ERROR_SUCCESS
;
489 si
= MSI_GetSummaryInformationW( patch_db
->storage
, 0 );
491 return ERROR_FUNCTION_FAILED
;
493 msi_check_patch_applicable( package
, si
);
495 /* enumerate the substorage */
496 str
= msi_suminfo_dup_string( si
, PID_LASTAUTHOR
);
497 substorage
= msi_split_string( str
, ';' );
498 for ( i
= 0; substorage
&& substorage
[i
] && r
== ERROR_SUCCESS
; i
++ )
499 r
= msi_apply_substorage_transform( package
, patch_db
, substorage
[i
] );
500 msi_free( substorage
);
503 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
505 msiobj_release( &si
->hdr
);
510 static UINT
msi_apply_patch_package( MSIPACKAGE
*package
, LPCWSTR file
)
512 MSIDATABASE
*patch_db
= NULL
;
515 TRACE("%p %s\n", package
, debugstr_w( file
) );
518 * We probably want to make sure we only open a patch collection here.
519 * Patch collections (.msp) and databases (.msi) have different GUIDs
520 * but currently MSI_OpenDatabaseW will accept both.
522 r
= MSI_OpenDatabaseW( file
, MSIDBOPEN_READONLY
, &patch_db
);
523 if ( r
!= ERROR_SUCCESS
)
525 ERR("failed to open patch collection %s\n", debugstr_w( file
) );
529 msi_parse_patch_summary( package
, patch_db
);
532 * There might be a CAB file in the patch package,
533 * so append it to the list of storage to search for streams.
535 append_storage_to_db( package
->db
, patch_db
->storage
);
537 msiobj_release( &patch_db
->hdr
);
539 return ERROR_SUCCESS
;
542 /* get the PATCH property, and apply all the patches it specifies */
543 static UINT
msi_apply_patches( MSIPACKAGE
*package
)
545 static const WCHAR szPatch
[] = { 'P','A','T','C','H',0 };
546 LPWSTR patch_list
, *patches
;
547 UINT i
, r
= ERROR_SUCCESS
;
549 patch_list
= msi_dup_property( package
, szPatch
);
551 TRACE("patches to be applied: %s\n", debugstr_w( patch_list
) );
553 patches
= msi_split_string( patch_list
, ';' );
554 for( i
=0; patches
&& patches
[i
] && r
== ERROR_SUCCESS
; i
++ )
555 r
= msi_apply_patch_package( package
, patches
[i
] );
558 msi_free( patch_list
);
563 static UINT
msi_apply_transforms( MSIPACKAGE
*package
)
565 static const WCHAR szTransforms
[] = {
566 'T','R','A','N','S','F','O','R','M','S',0 };
567 LPWSTR xform_list
, *xforms
;
568 UINT i
, r
= ERROR_SUCCESS
;
570 xform_list
= msi_dup_property( package
, szTransforms
);
571 xforms
= msi_split_string( xform_list
, ';' );
573 for( i
=0; xforms
&& xforms
[i
] && r
== ERROR_SUCCESS
; i
++ )
575 if (xforms
[i
][0] == ':')
576 r
= msi_apply_substorage_transform( package
, package
->db
, &xforms
[i
][1] );
578 r
= MSI_DatabaseApplyTransformW( package
->db
, xforms
[i
], 0 );
582 msi_free( xform_list
);
587 BOOL
ui_sequence_exists( MSIPACKAGE
*package
)
592 static const WCHAR ExecSeqQuery
[] =
593 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
594 '`','I','n','s','t','a','l','l',
595 'U','I','S','e','q','u','e','n','c','e','`',
596 ' ','W','H','E','R','E',' ',
597 '`','S','e','q','u','e','n','c','e','`',' ',
598 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
599 '`','S','e','q','u','e','n','c','e','`',0};
601 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
602 if (rc
== ERROR_SUCCESS
)
604 msiobj_release(&view
->hdr
);
611 /****************************************************
612 * TOP level entry points
613 *****************************************************/
615 UINT
MSI_InstallPackage( MSIPACKAGE
*package
, LPCWSTR szPackagePath
,
616 LPCWSTR szCommandLine
)
619 BOOL ui
= FALSE
, ui_exists
;
620 static const WCHAR szUILevel
[] = {'U','I','L','e','v','e','l',0};
621 static const WCHAR szAction
[] = {'A','C','T','I','O','N',0};
622 static const WCHAR szInstall
[] = {'I','N','S','T','A','L','L',0};
624 MSI_SetPropertyW(package
, szAction
, szInstall
);
626 package
->script
= msi_alloc_zero(sizeof(MSISCRIPT
));
628 package
->script
->InWhatSequence
= SEQUENCE_INSTALL
;
632 LPWSTR p
, check
, dir
;
635 dir
= strdupW(szPackagePath
);
636 p
= strrchrW(dir
, '\\');
640 file
= szPackagePath
+ (p
- dir
);
645 dir
= msi_alloc(MAX_PATH
*sizeof(WCHAR
));
646 GetCurrentDirectoryW(MAX_PATH
, dir
);
647 lstrcatW(dir
, cszbs
);
648 file
= szPackagePath
;
651 msi_free( package
->PackagePath
);
652 package
->PackagePath
= msi_alloc((lstrlenW(dir
) + lstrlenW(file
) + 1) * sizeof(WCHAR
));
653 if (!package
->PackagePath
)
656 return ERROR_OUTOFMEMORY
;
659 lstrcpyW(package
->PackagePath
, dir
);
660 lstrcatW(package
->PackagePath
, file
);
662 check
= msi_dup_property( package
, cszSourceDir
);
664 MSI_SetPropertyW(package
, cszSourceDir
, dir
);
667 check
= msi_dup_property( package
, cszSOURCEDIR
);
669 MSI_SetPropertyW(package
, cszSOURCEDIR
, dir
);
675 msi_parse_command_line( package
, szCommandLine
);
677 msi_apply_transforms( package
);
678 msi_apply_patches( package
);
680 /* properties may have been added by a transform */
681 msi_clone_properties( package
);
683 if ( (msi_get_property_int(package
, szUILevel
, 0) & INSTALLUILEVEL_MASK
) >= INSTALLUILEVEL_REDUCED
)
685 package
->script
->InWhatSequence
|= SEQUENCE_UI
;
686 rc
= ACTION_ProcessUISequence(package
);
688 ui_exists
= ui_sequence_exists(package
);
689 if (rc
== ERROR_SUCCESS
|| !ui_exists
)
691 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
692 rc
= ACTION_ProcessExecSequence(package
,ui_exists
);
696 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
700 /* install was halted but should be considered a success */
704 package
->script
->CurrentlyScripting
= FALSE
;
706 /* process the ending type action */
707 if (rc
== ERROR_SUCCESS
)
708 ACTION_PerformActionSequence(package
,-1,ui
);
709 else if (rc
== ERROR_INSTALL_USEREXIT
)
710 ACTION_PerformActionSequence(package
,-2,ui
);
711 else if (rc
== ERROR_INSTALL_SUSPEND
)
712 ACTION_PerformActionSequence(package
,-4,ui
);
714 ACTION_PerformActionSequence(package
,-3,ui
);
716 /* finish up running custom actions */
717 ACTION_FinishCustomActions(package
);
722 static UINT
ACTION_PerformActionSequence(MSIPACKAGE
*package
, UINT seq
, BOOL UI
)
724 UINT rc
= ERROR_SUCCESS
;
726 static const WCHAR ExecSeqQuery
[] =
727 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
728 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
729 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
730 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
732 static const WCHAR UISeqQuery
[] =
733 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
734 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
735 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
736 ' ', '=',' ','%','i',0};
739 row
= MSI_QueryGetRecord(package
->db
, UISeqQuery
, seq
);
741 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
, seq
);
745 LPCWSTR action
, cond
;
747 TRACE("Running the actions\n");
749 /* check conditions */
750 cond
= MSI_RecordGetString(row
,2);
752 /* this is a hack to skip errors in the condition code */
753 if (MSI_EvaluateConditionW(package
, cond
) == MSICONDITION_FALSE
)
756 action
= MSI_RecordGetString(row
,1);
759 ERR("failed to fetch action\n");
760 rc
= ERROR_FUNCTION_FAILED
;
765 rc
= ACTION_PerformUIAction(package
,action
,-1);
767 rc
= ACTION_PerformAction(package
,action
,-1,FALSE
);
769 msiobj_release(&row
->hdr
);
780 } iterate_action_param
;
782 static UINT
ITERATE_Actions(MSIRECORD
*row
, LPVOID param
)
784 iterate_action_param
*iap
= (iterate_action_param
*)param
;
786 LPCWSTR cond
, action
;
788 action
= MSI_RecordGetString(row
,1);
791 ERR("Error is retrieving action name\n");
792 return ERROR_FUNCTION_FAILED
;
795 /* check conditions */
796 cond
= MSI_RecordGetString(row
,2);
798 /* this is a hack to skip errors in the condition code */
799 if (MSI_EvaluateConditionW(iap
->package
, cond
) == MSICONDITION_FALSE
)
801 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action
));
802 return ERROR_SUCCESS
;
806 rc
= ACTION_PerformUIAction(iap
->package
,action
,-1);
808 rc
= ACTION_PerformAction(iap
->package
,action
,-1,FALSE
);
810 msi_dialog_check_messages( NULL
);
812 if (iap
->package
->CurrentInstallState
!= ERROR_SUCCESS
)
813 rc
= iap
->package
->CurrentInstallState
;
815 if (rc
== ERROR_FUNCTION_NOT_CALLED
)
818 if (rc
!= ERROR_SUCCESS
)
819 ERR("Execution halted, action %s returned %i\n", debugstr_w(action
), rc
);
824 UINT
MSI_Sequence( MSIPACKAGE
*package
, LPCWSTR szTable
, INT iSequenceMode
)
828 static const WCHAR query
[] =
829 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
831 ' ','W','H','E','R','E',' ',
832 '`','S','e','q','u','e','n','c','e','`',' ',
833 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
834 '`','S','e','q','u','e','n','c','e','`',0};
835 iterate_action_param iap
;
838 * FIXME: probably should be checking UILevel in the
839 * ACTION_PerformUIAction/ACTION_PerformAction
840 * rather than saving the UI level here. Those
841 * two functions can be merged too.
843 iap
.package
= package
;
846 TRACE("%p %s %i\n", package
, debugstr_w(szTable
), iSequenceMode
);
848 r
= MSI_OpenQuery( package
->db
, &view
, query
, szTable
);
849 if (r
== ERROR_SUCCESS
)
851 r
= MSI_IterateRecords( view
, NULL
, ITERATE_Actions
, &iap
);
852 msiobj_release(&view
->hdr
);
858 static UINT
ACTION_ProcessExecSequence(MSIPACKAGE
*package
, BOOL UIran
)
862 static const WCHAR ExecSeqQuery
[] =
863 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
864 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
865 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
866 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
867 'O','R','D','E','R',' ', 'B','Y',' ',
868 '`','S','e','q','u','e','n','c','e','`',0 };
870 static const WCHAR IVQuery
[] =
871 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
872 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
873 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
874 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
875 ' ','\'', 'I','n','s','t','a','l','l',
876 'V','a','l','i','d','a','t','e','\'', 0};
878 iterate_action_param iap
;
880 iap
.package
= package
;
883 if (package
->script
->ExecuteSequenceRun
)
885 TRACE("Execute Sequence already Run\n");
886 return ERROR_SUCCESS
;
889 package
->script
->ExecuteSequenceRun
= TRUE
;
891 /* get the sequence number */
894 row
= MSI_QueryGetRecord(package
->db
, IVQuery
);
896 return ERROR_FUNCTION_FAILED
;
897 seq
= MSI_RecordGetInteger(row
,1);
898 msiobj_release(&row
->hdr
);
901 rc
= MSI_OpenQuery(package
->db
, &view
, ExecSeqQuery
, seq
);
902 if (rc
== ERROR_SUCCESS
)
904 TRACE("Running the actions\n");
906 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, &iap
);
907 msiobj_release(&view
->hdr
);
913 static UINT
ACTION_ProcessUISequence(MSIPACKAGE
*package
)
917 static const WCHAR ExecSeqQuery
[] =
918 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
919 '`','I','n','s','t','a','l','l',
920 'U','I','S','e','q','u','e','n','c','e','`',
921 ' ','W','H','E','R','E',' ',
922 '`','S','e','q','u','e','n','c','e','`',' ',
923 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
924 '`','S','e','q','u','e','n','c','e','`',0};
925 iterate_action_param iap
;
927 iap
.package
= package
;
930 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
932 if (rc
== ERROR_SUCCESS
)
934 TRACE("Running the actions\n");
936 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, &iap
);
937 msiobj_release(&view
->hdr
);
943 /********************************************************
944 * ACTION helper functions and functions that perform the actions
945 *******************************************************/
946 static BOOL
ACTION_HandleStandardAction(MSIPACKAGE
*package
, LPCWSTR action
,
947 UINT
* rc
, BOOL force
)
953 if (!run
&& !package
->script
->CurrentlyScripting
)
958 if (strcmpW(action
,szInstallFinalize
) == 0 ||
959 strcmpW(action
,szInstallExecute
) == 0 ||
960 strcmpW(action
,szInstallExecuteAgain
) == 0)
965 while (StandardActions
[i
].action
!= NULL
)
967 if (strcmpW(StandardActions
[i
].action
, action
)==0)
971 ui_actioninfo(package
, action
, TRUE
, 0);
972 *rc
= schedule_action(package
,INSTALL_SCRIPT
,action
);
973 ui_actioninfo(package
, action
, FALSE
, *rc
);
977 ui_actionstart(package
, action
);
978 if (StandardActions
[i
].handler
)
980 *rc
= StandardActions
[i
].handler(package
);
984 FIXME("unhandled standard action %s\n",debugstr_w(action
));
996 static BOOL
ACTION_HandleCustomAction( MSIPACKAGE
* package
, LPCWSTR action
,
997 UINT
* rc
, UINT script
, BOOL force
)
1002 arc
= ACTION_CustomAction(package
, action
, script
, force
);
1004 if (arc
!= ERROR_CALL_NOT_IMPLEMENTED
)
1013 * A lot of actions are really important even if they don't do anything
1014 * explicit... Lots of properties are set at the beginning of the installation
1015 * CostFinalize does a bunch of work to translate the directories and such
1017 * But until I get write access to the database that is hard, so I am going to
1018 * hack it to see if I can get something to run.
1020 UINT
ACTION_PerformAction(MSIPACKAGE
*package
, const WCHAR
*action
, UINT script
, BOOL force
)
1022 UINT rc
= ERROR_SUCCESS
;
1025 TRACE("Performing action (%s)\n",debugstr_w(action
));
1027 handled
= ACTION_HandleStandardAction(package
, action
, &rc
, force
);
1030 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, script
, force
);
1034 FIXME("unhandled msi action %s\n",debugstr_w(action
));
1035 rc
= ERROR_FUNCTION_NOT_CALLED
;
1041 UINT
ACTION_PerformUIAction(MSIPACKAGE
*package
, const WCHAR
*action
, UINT script
)
1043 UINT rc
= ERROR_SUCCESS
;
1044 BOOL handled
= FALSE
;
1046 TRACE("Performing action (%s)\n",debugstr_w(action
));
1048 handled
= ACTION_HandleStandardAction(package
, action
, &rc
,TRUE
);
1051 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, script
, FALSE
);
1053 if( !handled
&& ACTION_DialogBox(package
,action
) == ERROR_SUCCESS
)
1058 FIXME("unhandled msi action %s\n",debugstr_w(action
));
1059 rc
= ERROR_FUNCTION_NOT_CALLED
;
1067 * Actual Action Handlers
1070 static UINT
ITERATE_CreateFolders(MSIRECORD
*row
, LPVOID param
)
1072 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1078 dir
= MSI_RecordGetString(row
,1);
1081 ERR("Unable to get folder id\n");
1082 return ERROR_SUCCESS
;
1085 full_path
= resolve_folder(package
,dir
,FALSE
,FALSE
,TRUE
,&folder
);
1088 ERR("Unable to resolve folder id %s\n",debugstr_w(dir
));
1089 return ERROR_SUCCESS
;
1092 TRACE("Folder is %s\n",debugstr_w(full_path
));
1095 uirow
= MSI_CreateRecord(1);
1096 MSI_RecordSetStringW(uirow
,1,full_path
);
1097 ui_actiondata(package
,szCreateFolders
,uirow
);
1098 msiobj_release( &uirow
->hdr
);
1100 if (folder
->State
== 0)
1101 create_full_pathW(full_path
);
1105 msi_free(full_path
);
1106 return ERROR_SUCCESS
;
1109 /* FIXME: probably should merge this with the above function */
1110 static UINT
msi_create_directory( MSIPACKAGE
* package
, LPCWSTR dir
)
1112 UINT rc
= ERROR_SUCCESS
;
1114 LPWSTR install_path
;
1116 install_path
= resolve_folder(package
, dir
, FALSE
, FALSE
, TRUE
, &folder
);
1118 return ERROR_FUNCTION_FAILED
;
1120 /* create the path */
1121 if (folder
->State
== 0)
1123 create_full_pathW(install_path
);
1126 msi_free(install_path
);
1131 UINT
msi_create_component_directories( MSIPACKAGE
*package
)
1135 /* create all the folders required by the components are going to install */
1136 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1138 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
1140 msi_create_directory( package
, comp
->Directory
);
1143 return ERROR_SUCCESS
;
1147 * Also we cannot enable/disable components either, so for now I am just going
1148 * to do all the directories for all the components.
1150 static UINT
ACTION_CreateFolders(MSIPACKAGE
*package
)
1152 static const WCHAR ExecSeqQuery
[] =
1153 {'S','E','L','E','C','T',' ',
1154 '`','D','i','r','e','c','t','o','r','y','_','`',
1155 ' ','F','R','O','M',' ',
1156 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1160 /* create all the empty folders specified in the CreateFolder table */
1161 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
1162 if (rc
!= ERROR_SUCCESS
)
1163 return ERROR_SUCCESS
;
1165 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateFolders
, package
);
1166 msiobj_release(&view
->hdr
);
1168 msi_create_component_directories( package
);
1173 static UINT
load_component( MSIRECORD
*row
, LPVOID param
)
1175 MSIPACKAGE
*package
= param
;
1178 comp
= msi_alloc_zero( sizeof(MSICOMPONENT
) );
1180 return ERROR_FUNCTION_FAILED
;
1182 list_add_tail( &package
->components
, &comp
->entry
);
1184 /* fill in the data */
1185 comp
->Component
= msi_dup_record_field( row
, 1 );
1187 TRACE("Loading Component %s\n", debugstr_w(comp
->Component
));
1189 comp
->ComponentId
= msi_dup_record_field( row
, 2 );
1190 comp
->Directory
= msi_dup_record_field( row
, 3 );
1191 comp
->Attributes
= MSI_RecordGetInteger(row
,4);
1192 comp
->Condition
= msi_dup_record_field( row
, 5 );
1193 comp
->KeyPath
= msi_dup_record_field( row
, 6 );
1195 comp
->Installed
= INSTALLSTATE_UNKNOWN
;
1196 msi_component_set_state( comp
, INSTALLSTATE_UNKNOWN
);
1198 return ERROR_SUCCESS
;
1201 static UINT
load_all_components( MSIPACKAGE
*package
)
1203 static const WCHAR query
[] = {
1204 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1205 '`','C','o','m','p','o','n','e','n','t','`',0 };
1209 if (!list_empty(&package
->components
))
1210 return ERROR_SUCCESS
;
1212 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1213 if (r
!= ERROR_SUCCESS
)
1216 r
= MSI_IterateRecords(view
, NULL
, load_component
, package
);
1217 msiobj_release(&view
->hdr
);
1222 MSIPACKAGE
*package
;
1223 MSIFEATURE
*feature
;
1226 static UINT
add_feature_component( MSIFEATURE
*feature
, MSICOMPONENT
*comp
)
1230 cl
= msi_alloc( sizeof (*cl
) );
1232 return ERROR_NOT_ENOUGH_MEMORY
;
1233 cl
->component
= comp
;
1234 list_add_tail( &feature
->Components
, &cl
->entry
);
1236 return ERROR_SUCCESS
;
1239 static UINT
add_feature_child( MSIFEATURE
*parent
, MSIFEATURE
*child
)
1243 fl
= msi_alloc( sizeof(*fl
) );
1245 return ERROR_NOT_ENOUGH_MEMORY
;
1246 fl
->feature
= child
;
1247 list_add_tail( &parent
->Children
, &fl
->entry
);
1249 return ERROR_SUCCESS
;
1252 static UINT
iterate_load_featurecomponents(MSIRECORD
*row
, LPVOID param
)
1254 _ilfs
* ilfs
= (_ilfs
*)param
;
1258 component
= MSI_RecordGetString(row
,1);
1260 /* check to see if the component is already loaded */
1261 comp
= get_loaded_component( ilfs
->package
, component
);
1264 ERR("unknown component %s\n", debugstr_w(component
));
1265 return ERROR_FUNCTION_FAILED
;
1268 add_feature_component( ilfs
->feature
, comp
);
1269 comp
->Enabled
= TRUE
;
1271 return ERROR_SUCCESS
;
1274 static MSIFEATURE
*find_feature_by_name( MSIPACKAGE
*package
, LPCWSTR name
)
1276 MSIFEATURE
*feature
;
1278 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1280 if ( !lstrcmpW( feature
->Feature
, name
) )
1287 static UINT
load_feature(MSIRECORD
* row
, LPVOID param
)
1289 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1290 MSIFEATURE
* feature
;
1291 static const WCHAR Query1
[] =
1292 {'S','E','L','E','C','T',' ',
1293 '`','C','o','m','p','o','n','e','n','t','_','`',
1294 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1295 'C','o','m','p','o','n','e','n','t','s','`',' ',
1296 'W','H','E','R','E',' ',
1297 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1302 /* fill in the data */
1304 feature
= msi_alloc_zero( sizeof (MSIFEATURE
) );
1306 return ERROR_NOT_ENOUGH_MEMORY
;
1308 list_init( &feature
->Children
);
1309 list_init( &feature
->Components
);
1311 feature
->Feature
= msi_dup_record_field( row
, 1 );
1313 TRACE("Loading feature %s\n",debugstr_w(feature
->Feature
));
1315 feature
->Feature_Parent
= msi_dup_record_field( row
, 2 );
1316 feature
->Title
= msi_dup_record_field( row
, 3 );
1317 feature
->Description
= msi_dup_record_field( row
, 4 );
1319 if (!MSI_RecordIsNull(row
,5))
1320 feature
->Display
= MSI_RecordGetInteger(row
,5);
1322 feature
->Level
= MSI_RecordGetInteger(row
,6);
1323 feature
->Directory
= msi_dup_record_field( row
, 7 );
1324 feature
->Attributes
= MSI_RecordGetInteger(row
,8);
1326 feature
->Installed
= INSTALLSTATE_UNKNOWN
;
1327 msi_feature_set_state( feature
, INSTALLSTATE_UNKNOWN
);
1329 list_add_tail( &package
->features
, &feature
->entry
);
1331 /* load feature components */
1333 rc
= MSI_OpenQuery( package
->db
, &view
, Query1
, feature
->Feature
);
1334 if (rc
!= ERROR_SUCCESS
)
1335 return ERROR_SUCCESS
;
1337 ilfs
.package
= package
;
1338 ilfs
.feature
= feature
;
1340 MSI_IterateRecords(view
, NULL
, iterate_load_featurecomponents
, &ilfs
);
1341 msiobj_release(&view
->hdr
);
1343 return ERROR_SUCCESS
;
1346 static UINT
find_feature_children(MSIRECORD
* row
, LPVOID param
)
1348 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1349 MSIFEATURE
*parent
, *child
;
1351 child
= find_feature_by_name( package
, MSI_RecordGetString( row
, 1 ) );
1353 return ERROR_FUNCTION_FAILED
;
1355 if (!child
->Feature_Parent
)
1356 return ERROR_SUCCESS
;
1358 parent
= find_feature_by_name( package
, child
->Feature_Parent
);
1360 return ERROR_FUNCTION_FAILED
;
1362 add_feature_child( parent
, child
);
1363 return ERROR_SUCCESS
;
1366 static UINT
load_all_features( MSIPACKAGE
*package
)
1368 static const WCHAR query
[] = {
1369 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1370 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1371 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1375 if (!list_empty(&package
->features
))
1376 return ERROR_SUCCESS
;
1378 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1379 if (r
!= ERROR_SUCCESS
)
1382 r
= MSI_IterateRecords( view
, NULL
, load_feature
, package
);
1383 if (r
!= ERROR_SUCCESS
)
1386 r
= MSI_IterateRecords( view
, NULL
, find_feature_children
, package
);
1387 msiobj_release( &view
->hdr
);
1392 static LPWSTR
folder_split_path(LPWSTR p
, WCHAR ch
)
1403 static UINT
load_file(MSIRECORD
*row
, LPVOID param
)
1405 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1409 /* fill in the data */
1411 file
= msi_alloc_zero( sizeof (MSIFILE
) );
1413 return ERROR_NOT_ENOUGH_MEMORY
;
1415 file
->File
= msi_dup_record_field( row
, 1 );
1417 component
= MSI_RecordGetString( row
, 2 );
1418 file
->Component
= get_loaded_component( package
, component
);
1420 if (!file
->Component
)
1421 ERR("Unfound Component %s\n",debugstr_w(component
));
1423 file
->FileName
= msi_dup_record_field( row
, 3 );
1424 reduce_to_longfilename( file
->FileName
);
1426 file
->ShortName
= msi_dup_record_field( row
, 3 );
1427 file
->LongName
= strdupW( folder_split_path(file
->ShortName
, '|'));
1429 file
->FileSize
= MSI_RecordGetInteger( row
, 4 );
1430 file
->Version
= msi_dup_record_field( row
, 5 );
1431 file
->Language
= msi_dup_record_field( row
, 6 );
1432 file
->Attributes
= MSI_RecordGetInteger( row
, 7 );
1433 file
->Sequence
= MSI_RecordGetInteger( row
, 8 );
1435 file
->state
= msifs_invalid
;
1437 /* if the compressed bits are not set in the file attributes,
1438 * then read the information from the package word count property
1440 if (file
->Attributes
& msidbFileAttributesCompressed
)
1442 file
->IsCompressed
= TRUE
;
1444 else if (file
->Attributes
& msidbFileAttributesNoncompressed
)
1446 file
->IsCompressed
= FALSE
;
1450 file
->IsCompressed
= package
->WordCount
& MSIWORDCOUNT_COMPRESSED
;
1453 TRACE("File Loaded (%s)\n",debugstr_w(file
->File
));
1455 list_add_tail( &package
->files
, &file
->entry
);
1457 return ERROR_SUCCESS
;
1460 static UINT
load_all_files(MSIPACKAGE
*package
)
1464 static const WCHAR Query
[] =
1465 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1466 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1467 '`','S','e','q','u','e','n','c','e','`', 0};
1469 if (!list_empty(&package
->files
))
1470 return ERROR_SUCCESS
;
1472 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
1473 if (rc
!= ERROR_SUCCESS
)
1474 return ERROR_SUCCESS
;
1476 rc
= MSI_IterateRecords(view
, NULL
, load_file
, package
);
1477 msiobj_release(&view
->hdr
);
1479 return ERROR_SUCCESS
;
1482 static UINT
load_folder( MSIRECORD
*row
, LPVOID param
)
1484 MSIPACKAGE
*package
= param
;
1485 static const WCHAR szDot
[] = { '.',0 };
1486 static WCHAR szEmpty
[] = { 0 };
1487 LPWSTR p
, tgt_short
, tgt_long
, src_short
, src_long
;
1490 folder
= msi_alloc_zero( sizeof (MSIFOLDER
) );
1492 return ERROR_NOT_ENOUGH_MEMORY
;
1494 folder
->Directory
= msi_dup_record_field( row
, 1 );
1496 TRACE("%s\n", debugstr_w(folder
->Directory
));
1498 p
= msi_dup_record_field(row
, 3);
1500 /* split src and target dir */
1502 src_short
= folder_split_path( p
, ':' );
1504 /* split the long and short paths */
1505 tgt_long
= folder_split_path( tgt_short
, '|' );
1506 src_long
= folder_split_path( src_short
, '|' );
1508 /* check for no-op dirs */
1509 if (!lstrcmpW(szDot
, tgt_short
))
1510 tgt_short
= szEmpty
;
1511 if (!lstrcmpW(szDot
, src_short
))
1512 src_short
= szEmpty
;
1515 tgt_long
= tgt_short
;
1518 src_short
= tgt_short
;
1519 src_long
= tgt_long
;
1523 src_long
= src_short
;
1525 /* FIXME: use the target short path too */
1526 folder
->TargetDefault
= strdupW(tgt_long
);
1527 folder
->SourceShortPath
= strdupW(src_short
);
1528 folder
->SourceLongPath
= strdupW(src_long
);
1531 TRACE("TargetDefault = %s\n",debugstr_w( folder
->TargetDefault
));
1532 TRACE("SourceLong = %s\n", debugstr_w( folder
->SourceLongPath
));
1533 TRACE("SourceShort = %s\n", debugstr_w( folder
->SourceShortPath
));
1535 folder
->Parent
= msi_dup_record_field( row
, 2 );
1537 folder
->Property
= msi_dup_property( package
, folder
->Directory
);
1539 list_add_tail( &package
->folders
, &folder
->entry
);
1541 TRACE("returning %p\n", folder
);
1543 return ERROR_SUCCESS
;
1546 static UINT
load_all_folders( MSIPACKAGE
*package
)
1548 static const WCHAR query
[] = {
1549 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1550 '`','D','i','r','e','c','t','o','r','y','`',0 };
1554 if (!list_empty(&package
->folders
))
1555 return ERROR_SUCCESS
;
1557 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1558 if (r
!= ERROR_SUCCESS
)
1561 r
= MSI_IterateRecords(view
, NULL
, load_folder
, package
);
1562 msiobj_release(&view
->hdr
);
1567 * I am not doing any of the costing functionality yet.
1568 * Mostly looking at doing the Component and Feature loading
1570 * The native MSI does A LOT of modification to tables here. Mostly adding
1571 * a lot of temporary columns to the Feature and Component tables.
1573 * note: Native msi also tracks the short filename. But I am only going to
1574 * track the long ones. Also looking at this directory table
1575 * it appears that the directory table does not get the parents
1576 * resolved base on property only based on their entries in the
1579 static UINT
ACTION_CostInitialize(MSIPACKAGE
*package
)
1581 static const WCHAR szCosting
[] =
1582 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1583 static const WCHAR szZero
[] = { '0', 0 };
1585 MSI_SetPropertyW(package
, szCosting
, szZero
);
1586 MSI_SetPropertyW(package
, cszRootDrive
, c_colon
);
1588 load_all_components( package
);
1589 load_all_features( package
);
1590 load_all_files( package
);
1591 load_all_folders( package
);
1593 return ERROR_SUCCESS
;
1596 static UINT
execute_script(MSIPACKAGE
*package
, UINT script
)
1599 UINT rc
= ERROR_SUCCESS
;
1601 TRACE("Executing Script %i\n",script
);
1603 if (!package
->script
)
1605 ERR("no script!\n");
1606 return ERROR_FUNCTION_FAILED
;
1609 for (i
= 0; i
< package
->script
->ActionCount
[script
]; i
++)
1612 action
= package
->script
->Actions
[script
][i
];
1613 ui_actionstart(package
, action
);
1614 TRACE("Executing Action (%s)\n",debugstr_w(action
));
1615 rc
= ACTION_PerformAction(package
, action
, script
, TRUE
);
1616 if (rc
!= ERROR_SUCCESS
)
1619 msi_free_action_script(package
, script
);
1623 static UINT
ACTION_FileCost(MSIPACKAGE
*package
)
1625 return ERROR_SUCCESS
;
1628 static void ACTION_GetComponentInstallStates(MSIPACKAGE
*package
)
1632 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1636 if (!comp
->ComponentId
)
1639 res
= MsiGetComponentPathW( package
->ProductCode
,
1640 comp
->ComponentId
, NULL
, NULL
);
1642 res
= INSTALLSTATE_ABSENT
;
1643 comp
->Installed
= res
;
1647 /* scan for and update current install states */
1648 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE
*package
)
1651 MSIFEATURE
*feature
;
1653 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1656 INSTALLSTATE res
= INSTALLSTATE_ABSENT
;
1658 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1660 comp
= cl
->component
;
1662 if (!comp
->ComponentId
)
1664 res
= INSTALLSTATE_ABSENT
;
1668 if (res
== INSTALLSTATE_ABSENT
)
1669 res
= comp
->Installed
;
1672 if (res
== comp
->Installed
)
1675 if (res
!= INSTALLSTATE_DEFAULT
&& res
!= INSTALLSTATE_LOCAL
&&
1676 res
!= INSTALLSTATE_SOURCE
)
1678 res
= INSTALLSTATE_INCOMPLETE
;
1682 feature
->Installed
= res
;
1686 static BOOL
process_state_property (MSIPACKAGE
* package
, LPCWSTR property
,
1689 static const WCHAR all
[]={'A','L','L',0};
1691 MSIFEATURE
*feature
;
1693 override
= msi_dup_property( package
, property
);
1697 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1699 if (strcmpiW(override
,all
)==0)
1700 msi_feature_set_state( feature
, state
);
1703 LPWSTR ptr
= override
;
1704 LPWSTR ptr2
= strchrW(override
,',');
1708 if ((ptr2
&& strncmpW(ptr
,feature
->Feature
, ptr2
-ptr
)==0)
1709 || (!ptr2
&& strcmpW(ptr
,feature
->Feature
)==0))
1711 msi_feature_set_state( feature
, state
);
1717 ptr2
= strchrW(ptr
,',');
1729 UINT
MSI_SetFeatureStates(MSIPACKAGE
*package
)
1732 static const WCHAR szlevel
[] =
1733 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1734 static const WCHAR szAddLocal
[] =
1735 {'A','D','D','L','O','C','A','L',0};
1736 static const WCHAR szRemove
[] =
1737 {'R','E','M','O','V','E',0};
1738 static const WCHAR szReinstall
[] =
1739 {'R','E','I','N','S','T','A','L','L',0};
1740 BOOL override
= FALSE
;
1741 MSICOMPONENT
* component
;
1742 MSIFEATURE
*feature
;
1745 /* I do not know if this is where it should happen.. but */
1747 TRACE("Checking Install Level\n");
1749 install_level
= msi_get_property_int( package
, szlevel
, 1 );
1751 /* ok here is the _real_ rub
1752 * all these activation/deactivation things happen in order and things
1753 * later on the list override things earlier on the list.
1754 * 1) INSTALLLEVEL processing
1764 * 11) FILEADDDEFAULT
1765 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1766 * ignored for all the features. seems strange, especially since it is not
1767 * documented anywhere, but it is how it works.
1769 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1770 * REMOVE are the big ones, since we don't handle administrative installs
1773 override
|= process_state_property(package
,szAddLocal
,INSTALLSTATE_LOCAL
);
1774 override
|= process_state_property(package
,szRemove
,INSTALLSTATE_ABSENT
);
1775 override
|= process_state_property(package
,szReinstall
,INSTALLSTATE_LOCAL
);
1779 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1781 BOOL feature_state
= ((feature
->Level
> 0) &&
1782 (feature
->Level
<= install_level
));
1784 if ((feature_state
) && (feature
->Action
== INSTALLSTATE_UNKNOWN
))
1786 if (feature
->Attributes
& msidbFeatureAttributesFavorSource
)
1787 msi_feature_set_state( feature
, INSTALLSTATE_SOURCE
);
1788 else if (feature
->Attributes
& msidbFeatureAttributesFavorAdvertise
)
1789 msi_feature_set_state( feature
, INSTALLSTATE_ADVERTISED
);
1791 msi_feature_set_state( feature
, INSTALLSTATE_LOCAL
);
1795 /* disable child features of unselected parent features */
1796 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1800 if (feature
->Level
> 0 && feature
->Level
<= install_level
)
1803 LIST_FOR_EACH_ENTRY( fl
, &feature
->Children
, FeatureList
, entry
)
1804 msi_feature_set_state( fl
->feature
, INSTALLSTATE_UNKNOWN
);
1809 /* set the Preselected Property */
1810 static const WCHAR szPreselected
[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1811 static const WCHAR szOne
[] = { '1', 0 };
1813 MSI_SetPropertyW(package
,szPreselected
,szOne
);
1817 * now we want to enable or disable components base on feature
1820 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1824 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1825 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
);
1827 /* features with components that have compressed files are made local */
1828 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1830 if (cl
->component
->Enabled
&&
1831 cl
->component
->ForceLocalState
&&
1832 feature
->Action
== INSTALLSTATE_SOURCE
)
1834 msi_feature_set_state( feature
, INSTALLSTATE_LOCAL
);
1839 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1841 component
= cl
->component
;
1843 if (!component
->Enabled
)
1846 switch (feature
->Action
)
1848 case INSTALLSTATE_ADVERTISED
:
1849 component
->hasAdvertiseFeature
= 1;
1851 case INSTALLSTATE_SOURCE
:
1852 component
->hasSourceFeature
= 1;
1854 case INSTALLSTATE_LOCAL
:
1855 component
->hasLocalFeature
= 1;
1857 case INSTALLSTATE_DEFAULT
:
1858 if (feature
->Attributes
& msidbFeatureAttributesFavorAdvertise
)
1859 component
->hasAdvertiseFeature
= 1;
1860 else if (feature
->Attributes
& msidbFeatureAttributesFavorSource
)
1861 component
->hasSourceFeature
= 1;
1863 component
->hasLocalFeature
= 1;
1871 LIST_FOR_EACH_ENTRY( component
, &package
->components
, MSICOMPONENT
, entry
)
1873 /* if the component isn't enabled, leave it alone */
1874 if (!component
->Enabled
)
1877 /* check if it's local or source */
1878 if (!(component
->Attributes
& msidbComponentAttributesOptional
) &&
1879 (component
->hasLocalFeature
|| component
->hasSourceFeature
))
1881 if ((component
->Attributes
& msidbComponentAttributesSourceOnly
) &&
1882 !component
->ForceLocalState
)
1883 msi_component_set_state( component
, INSTALLSTATE_SOURCE
);
1885 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1889 /* if any feature is local, the component must be local too */
1890 if (component
->hasLocalFeature
)
1892 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1896 if (component
->hasSourceFeature
)
1898 msi_component_set_state( component
, INSTALLSTATE_SOURCE
);
1902 if (component
->hasAdvertiseFeature
)
1904 msi_component_set_state( component
, INSTALLSTATE_ADVERTISED
);
1908 TRACE("nobody wants component %s\n", debugstr_w(component
->Component
));
1911 LIST_FOR_EACH_ENTRY( component
, &package
->components
, MSICOMPONENT
, entry
)
1913 if (component
->Action
== INSTALLSTATE_DEFAULT
)
1915 TRACE("%s was default, setting to local\n", debugstr_w(component
->Component
));
1916 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1919 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1920 debugstr_w(component
->Component
), component
->Installed
, component
->Action
);
1924 return ERROR_SUCCESS
;
1927 static UINT
ITERATE_CostFinalizeDirectories(MSIRECORD
*row
, LPVOID param
)
1929 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1934 name
= MSI_RecordGetString(row
,1);
1936 f
= get_loaded_folder(package
, name
);
1937 if (!f
) return ERROR_SUCCESS
;
1939 /* reset the ResolvedTarget */
1940 msi_free(f
->ResolvedTarget
);
1941 f
->ResolvedTarget
= NULL
;
1943 /* This helper function now does ALL the work */
1944 TRACE("Dir %s ...\n",debugstr_w(name
));
1945 path
= resolve_folder(package
,name
,FALSE
,TRUE
,TRUE
,NULL
);
1946 TRACE("resolves to %s\n",debugstr_w(path
));
1949 return ERROR_SUCCESS
;
1952 static UINT
ITERATE_CostFinalizeConditions(MSIRECORD
*row
, LPVOID param
)
1954 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1956 MSIFEATURE
*feature
;
1958 name
= MSI_RecordGetString( row
, 1 );
1960 feature
= get_loaded_feature( package
, name
);
1962 ERR("FAILED to find loaded feature %s\n",debugstr_w(name
));
1966 Condition
= MSI_RecordGetString(row
,3);
1968 if (MSI_EvaluateConditionW(package
,Condition
) == MSICONDITION_TRUE
)
1970 int level
= MSI_RecordGetInteger(row
,2);
1971 TRACE("Reseting feature %s to level %i\n", debugstr_w(name
), level
);
1972 feature
->Level
= level
;
1975 return ERROR_SUCCESS
;
1978 static LPWSTR
msi_get_disk_file_version( LPCWSTR filename
)
1980 static const WCHAR name_fmt
[] =
1981 {'%','u','.','%','u','.','%','u','.','%','u',0};
1982 static WCHAR name
[] = {'\\',0};
1983 VS_FIXEDFILEINFO
*lpVer
;
1984 WCHAR filever
[0x100];
1990 TRACE("%s\n", debugstr_w(filename
));
1992 versize
= GetFileVersionInfoSizeW( filename
, &handle
);
1996 version
= msi_alloc( versize
);
1997 GetFileVersionInfoW( filename
, 0, versize
, version
);
1999 if (!VerQueryValueW( version
, name
, (LPVOID
*)&lpVer
, &sz
))
2001 msi_free( version
);
2005 sprintfW( filever
, name_fmt
,
2006 HIWORD(lpVer
->dwFileVersionMS
),
2007 LOWORD(lpVer
->dwFileVersionMS
),
2008 HIWORD(lpVer
->dwFileVersionLS
),
2009 LOWORD(lpVer
->dwFileVersionLS
));
2011 msi_free( version
);
2013 return strdupW( filever
);
2016 static UINT
msi_check_file_install_states( MSIPACKAGE
*package
)
2018 LPWSTR file_version
;
2021 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2023 MSICOMPONENT
* comp
= file
->Component
;
2029 if (file
->IsCompressed
)
2030 comp
->ForceLocalState
= TRUE
;
2032 /* calculate target */
2033 p
= resolve_folder(package
, comp
->Directory
, FALSE
, FALSE
, TRUE
, NULL
);
2035 msi_free(file
->TargetPath
);
2037 TRACE("file %s is named %s\n",
2038 debugstr_w(file
->File
), debugstr_w(file
->FileName
));
2040 file
->TargetPath
= build_directory_name(2, p
, file
->FileName
);
2044 TRACE("file %s resolves to %s\n",
2045 debugstr_w(file
->File
), debugstr_w(file
->TargetPath
));
2047 /* don't check files of components that aren't installed */
2048 if (comp
->Installed
== INSTALLSTATE_UNKNOWN
||
2049 comp
->Installed
== INSTALLSTATE_ABSENT
)
2051 file
->state
= msifs_missing
; /* assume files are missing */
2055 if (GetFileAttributesW(file
->TargetPath
) == INVALID_FILE_ATTRIBUTES
)
2057 file
->state
= msifs_missing
;
2058 comp
->Cost
+= file
->FileSize
;
2059 comp
->Installed
= INSTALLSTATE_INCOMPLETE
;
2063 if (file
->Version
&&
2064 (file_version
= msi_get_disk_file_version( file
->TargetPath
)))
2066 TRACE("new %s old %s\n", debugstr_w(file
->Version
),
2067 debugstr_w(file_version
));
2068 /* FIXME: seems like a bad way to compare version numbers */
2069 if (lstrcmpiW(file_version
, file
->Version
)<0)
2071 file
->state
= msifs_overwrite
;
2072 comp
->Cost
+= file
->FileSize
;
2073 comp
->Installed
= INSTALLSTATE_INCOMPLETE
;
2076 file
->state
= msifs_present
;
2077 msi_free( file_version
);
2080 file
->state
= msifs_present
;
2083 return ERROR_SUCCESS
;
2087 * A lot is done in this function aside from just the costing.
2088 * The costing needs to be implemented at some point but for now I am going
2089 * to focus on the directory building
2092 static UINT
ACTION_CostFinalize(MSIPACKAGE
*package
)
2094 static const WCHAR ExecSeqQuery
[] =
2095 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2096 '`','D','i','r','e','c','t','o','r','y','`',0};
2097 static const WCHAR ConditionQuery
[] =
2098 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2099 '`','C','o','n','d','i','t','i','o','n','`',0};
2100 static const WCHAR szCosting
[] =
2101 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2102 static const WCHAR szlevel
[] =
2103 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2104 static const WCHAR szOne
[] = { '1', 0 };
2110 if ( 1 == msi_get_property_int( package
, szCosting
, 0 ) )
2111 return ERROR_SUCCESS
;
2113 TRACE("Building Directory properties\n");
2115 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2116 if (rc
== ERROR_SUCCESS
)
2118 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CostFinalizeDirectories
,
2120 msiobj_release(&view
->hdr
);
2123 /* read components states from the registry */
2124 ACTION_GetComponentInstallStates(package
);
2126 TRACE("File calculations\n");
2127 msi_check_file_install_states( package
);
2129 TRACE("Evaluating Condition Table\n");
2131 rc
= MSI_DatabaseOpenViewW(package
->db
, ConditionQuery
, &view
);
2132 if (rc
== ERROR_SUCCESS
)
2134 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CostFinalizeConditions
,
2136 msiobj_release(&view
->hdr
);
2139 TRACE("Enabling or Disabling Components\n");
2140 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2142 if (MSI_EvaluateConditionW(package
, comp
->Condition
) == MSICONDITION_FALSE
)
2144 TRACE("Disabling component %s\n", debugstr_w(comp
->Component
));
2145 comp
->Enabled
= FALSE
;
2149 MSI_SetPropertyW(package
,szCosting
,szOne
);
2150 /* set default run level if not set */
2151 level
= msi_dup_property( package
, szlevel
);
2153 MSI_SetPropertyW(package
,szlevel
, szOne
);
2156 ACTION_UpdateFeatureInstallStates(package
);
2158 return MSI_SetFeatureStates(package
);
2161 /* OK this value is "interpreted" and then formatted based on the
2162 first few characters */
2163 static LPSTR
parse_value(MSIPACKAGE
*package
, LPCWSTR value
, DWORD
*type
,
2167 if (value
[0]=='#' && value
[1]!='#' && value
[1]!='%')
2173 LPWSTR deformated
= NULL
;
2176 deformat_string(package
, &value
[2], &deformated
);
2178 /* binary value type */
2182 *size
= (strlenW(ptr
)/2)+1;
2184 *size
= strlenW(ptr
)/2;
2186 data
= msi_alloc(*size
);
2192 /* if uneven pad with a zero in front */
2198 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2200 TRACE("Uneven byte count\n");
2208 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2211 msi_free(deformated
);
2213 TRACE("Data %i bytes(%i)\n",*size
,count
);
2220 deformat_string(package
, &value
[1], &deformated
);
2223 *size
= sizeof(DWORD
);
2224 data
= msi_alloc(*size
);
2230 if ( (*p
< '0') || (*p
> '9') )
2236 if (deformated
[0] == '-')
2239 TRACE("DWORD %i\n",*(LPDWORD
)data
);
2241 msi_free(deformated
);
2246 static const WCHAR szMulti
[] = {'[','~',']',0};
2255 *type
=REG_EXPAND_SZ
;
2263 if (strstrW(value
,szMulti
))
2264 *type
= REG_MULTI_SZ
;
2266 *size
= deformat_string(package
, ptr
,(LPWSTR
*)&data
);
2271 static UINT
ITERATE_WriteRegistryValues(MSIRECORD
*row
, LPVOID param
)
2273 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
2274 static const WCHAR szHCR
[] =
2275 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2276 'R','O','O','T','\\',0};
2277 static const WCHAR szHCU
[] =
2278 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2279 'U','S','E','R','\\',0};
2280 static const WCHAR szHLM
[] =
2281 {'H','K','E','Y','_','L','O','C','A','L','_',
2282 'M','A','C','H','I','N','E','\\',0};
2283 static const WCHAR szHU
[] =
2284 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2286 LPSTR value_data
= NULL
;
2287 HKEY root_key
, hkey
;
2290 LPCWSTR szRoot
, component
, name
, key
, value
;
2295 BOOL check_first
= FALSE
;
2298 ui_progress(package
,2,0,0,0);
2305 component
= MSI_RecordGetString(row
, 6);
2306 comp
= get_loaded_component(package
,component
);
2308 return ERROR_SUCCESS
;
2310 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2312 TRACE("Skipping write due to disabled component %s\n",
2313 debugstr_w(component
));
2315 comp
->Action
= comp
->Installed
;
2317 return ERROR_SUCCESS
;
2320 comp
->Action
= INSTALLSTATE_LOCAL
;
2322 name
= MSI_RecordGetString(row
, 4);
2323 if( MSI_RecordIsNull(row
,5) && name
)
2325 /* null values can have special meanings */
2326 if (name
[0]=='-' && name
[1] == 0)
2327 return ERROR_SUCCESS
;
2328 else if ((name
[0]=='+' && name
[1] == 0) ||
2329 (name
[0] == '*' && name
[1] == 0))
2334 root
= MSI_RecordGetInteger(row
,2);
2335 key
= MSI_RecordGetString(row
, 3);
2337 /* get the root key */
2342 static const WCHAR szALLUSER
[] = {'A','L','L','U','S','E','R','S',0};
2343 LPWSTR all_users
= msi_dup_property( package
, szALLUSER
);
2344 if (all_users
&& all_users
[0] == '1')
2346 root_key
= HKEY_LOCAL_MACHINE
;
2351 root_key
= HKEY_CURRENT_USER
;
2354 msi_free(all_users
);
2357 case 0: root_key
= HKEY_CLASSES_ROOT
;
2360 case 1: root_key
= HKEY_CURRENT_USER
;
2363 case 2: root_key
= HKEY_LOCAL_MACHINE
;
2366 case 3: root_key
= HKEY_USERS
;
2370 ERR("Unknown root %i\n",root
);
2376 return ERROR_SUCCESS
;
2378 deformat_string(package
, key
, &deformated
);
2379 size
= strlenW(deformated
) + strlenW(szRoot
) + 1;
2380 uikey
= msi_alloc(size
*sizeof(WCHAR
));
2381 strcpyW(uikey
,szRoot
);
2382 strcatW(uikey
,deformated
);
2384 if (RegCreateKeyW( root_key
, deformated
, &hkey
))
2386 ERR("Could not create key %s\n",debugstr_w(deformated
));
2387 msi_free(deformated
);
2389 return ERROR_SUCCESS
;
2391 msi_free(deformated
);
2393 value
= MSI_RecordGetString(row
,5);
2395 value_data
= parse_value(package
, value
, &type
, &size
);
2398 static const WCHAR szEmpty
[] = {0};
2399 value_data
= (LPSTR
)strdupW(szEmpty
);
2404 deformat_string(package
, name
, &deformated
);
2406 /* get the double nulls to terminate SZ_MULTI */
2407 if (type
== REG_MULTI_SZ
)
2408 size
+=sizeof(WCHAR
);
2412 TRACE("Setting value %s of %s\n",debugstr_w(deformated
),
2414 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
)value_data
, size
);
2419 rc
= RegQueryValueExW(hkey
, deformated
, NULL
, NULL
, NULL
, &sz
);
2420 if (rc
== ERROR_SUCCESS
|| rc
== ERROR_MORE_DATA
)
2422 TRACE("value %s of %s checked already exists\n",
2423 debugstr_w(deformated
), debugstr_w(uikey
));
2427 TRACE("Checked and setting value %s of %s\n",
2428 debugstr_w(deformated
), debugstr_w(uikey
));
2429 if (deformated
|| size
)
2430 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
) value_data
, size
);
2435 uirow
= MSI_CreateRecord(3);
2436 MSI_RecordSetStringW(uirow
,2,deformated
);
2437 MSI_RecordSetStringW(uirow
,1,uikey
);
2440 MSI_RecordSetStringW(uirow
,3,(LPWSTR
)value_data
);
2442 MSI_RecordSetStringW(uirow
,3,value
);
2444 ui_actiondata(package
,szWriteRegistryValues
,uirow
);
2445 msiobj_release( &uirow
->hdr
);
2447 msi_free(value_data
);
2448 msi_free(deformated
);
2451 return ERROR_SUCCESS
;
2454 static UINT
ACTION_WriteRegistryValues(MSIPACKAGE
*package
)
2458 static const WCHAR ExecSeqQuery
[] =
2459 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2460 '`','R','e','g','i','s','t','r','y','`',0 };
2462 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2463 if (rc
!= ERROR_SUCCESS
)
2464 return ERROR_SUCCESS
;
2466 /* increment progress bar each time action data is sent */
2467 ui_progress(package
,1,REG_PROGRESS_VALUE
,1,0);
2469 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteRegistryValues
, package
);
2471 msiobj_release(&view
->hdr
);
2475 static UINT
ACTION_InstallInitialize(MSIPACKAGE
*package
)
2477 package
->script
->CurrentlyScripting
= TRUE
;
2479 return ERROR_SUCCESS
;
2483 static UINT
ACTION_InstallValidate(MSIPACKAGE
*package
)
2488 static const WCHAR q1
[]=
2489 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2490 '`','R','e','g','i','s','t','r','y','`',0};
2493 MSIFEATURE
*feature
;
2496 TRACE("InstallValidate\n");
2498 rc
= MSI_DatabaseOpenViewW(package
->db
, q1
, &view
);
2499 if (rc
== ERROR_SUCCESS
)
2501 MSI_IterateRecords( view
, &progress
, NULL
, package
);
2502 msiobj_release( &view
->hdr
);
2503 total
+= progress
* REG_PROGRESS_VALUE
;
2506 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2507 total
+= COMPONENT_PROGRESS_VALUE
;
2509 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2510 total
+= file
->FileSize
;
2512 ui_progress(package
,0,total
,0,0);
2514 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2516 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2517 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
,
2518 feature
->ActionRequest
);
2521 return ERROR_SUCCESS
;
2524 static UINT
ITERATE_LaunchConditions(MSIRECORD
*row
, LPVOID param
)
2526 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
2527 LPCWSTR cond
= NULL
;
2528 LPCWSTR message
= NULL
;
2531 static const WCHAR title
[]=
2532 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2534 cond
= MSI_RecordGetString(row
,1);
2536 r
= MSI_EvaluateConditionW(package
,cond
);
2537 if (r
== MSICONDITION_FALSE
)
2539 if ((gUILevel
& INSTALLUILEVEL_MASK
) != INSTALLUILEVEL_NONE
)
2542 message
= MSI_RecordGetString(row
,2);
2543 deformat_string(package
,message
,&deformated
);
2544 MessageBoxW(NULL
,deformated
,title
,MB_OK
);
2545 msi_free(deformated
);
2548 return ERROR_INSTALL_FAILURE
;
2551 return ERROR_SUCCESS
;
2554 static UINT
ACTION_LaunchConditions(MSIPACKAGE
*package
)
2557 MSIQUERY
* view
= NULL
;
2558 static const WCHAR ExecSeqQuery
[] =
2559 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2560 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2562 TRACE("Checking launch conditions\n");
2564 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2565 if (rc
!= ERROR_SUCCESS
)
2566 return ERROR_SUCCESS
;
2568 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_LaunchConditions
, package
);
2569 msiobj_release(&view
->hdr
);
2574 static LPWSTR
resolve_keypath( MSIPACKAGE
* package
, MSICOMPONENT
*cmp
)
2578 return resolve_folder(package
,cmp
->Directory
,FALSE
,FALSE
,TRUE
,NULL
);
2580 if (cmp
->Attributes
& msidbComponentAttributesRegistryKeyPath
)
2582 MSIRECORD
* row
= 0;
2584 LPWSTR deformated
,buffer
,deformated_name
;
2586 static const WCHAR ExecSeqQuery
[] =
2587 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2588 '`','R','e','g','i','s','t','r','y','`',' ',
2589 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2590 ' ','=',' ' ,'\'','%','s','\'',0 };
2591 static const WCHAR fmt
[]={'%','0','2','i',':','\\','%','s','\\',0};
2592 static const WCHAR fmt2
[]=
2593 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2595 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
,cmp
->KeyPath
);
2599 root
= MSI_RecordGetInteger(row
,2);
2600 key
= MSI_RecordGetString(row
, 3);
2601 name
= MSI_RecordGetString(row
, 4);
2602 deformat_string(package
, key
, &deformated
);
2603 deformat_string(package
, name
, &deformated_name
);
2605 len
= strlenW(deformated
) + 6;
2606 if (deformated_name
)
2607 len
+=strlenW(deformated_name
);
2609 buffer
= msi_alloc( len
*sizeof(WCHAR
));
2611 if (deformated_name
)
2612 sprintfW(buffer
,fmt2
,root
,deformated
,deformated_name
);
2614 sprintfW(buffer
,fmt
,root
,deformated
);
2616 msi_free(deformated
);
2617 msi_free(deformated_name
);
2618 msiobj_release(&row
->hdr
);
2622 else if (cmp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2624 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2629 MSIFILE
*file
= get_loaded_file( package
, cmp
->KeyPath
);
2632 return strdupW( file
->TargetPath
);
2637 static HKEY
openSharedDLLsKey(void)
2640 static const WCHAR path
[] =
2641 {'S','o','f','t','w','a','r','e','\\',
2642 'M','i','c','r','o','s','o','f','t','\\',
2643 'W','i','n','d','o','w','s','\\',
2644 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2645 'S','h','a','r','e','d','D','L','L','s',0};
2647 RegCreateKeyW(HKEY_LOCAL_MACHINE
,path
,&hkey
);
2651 static UINT
ACTION_GetSharedDLLsCount(LPCWSTR dll
)
2656 DWORD sz
= sizeof(count
);
2659 hkey
= openSharedDLLsKey();
2660 rc
= RegQueryValueExW(hkey
, dll
, NULL
, &type
, (LPBYTE
)&count
, &sz
);
2661 if (rc
!= ERROR_SUCCESS
)
2667 static UINT
ACTION_WriteSharedDLLsCount(LPCWSTR path
, UINT count
)
2671 hkey
= openSharedDLLsKey();
2673 msi_reg_set_val_dword( hkey
, path
, count
);
2675 RegDeleteValueW(hkey
,path
);
2681 * Return TRUE if the count should be written out and FALSE if not
2683 static void ACTION_RefCountComponent( MSIPACKAGE
* package
, MSICOMPONENT
*comp
)
2685 MSIFEATURE
*feature
;
2689 /* only refcount DLLs */
2690 if (comp
->KeyPath
== NULL
||
2691 comp
->Attributes
& msidbComponentAttributesRegistryKeyPath
||
2692 comp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2696 count
= ACTION_GetSharedDLLsCount( comp
->FullKeypath
);
2697 write
= (count
> 0);
2699 if (comp
->Attributes
& msidbComponentAttributesSharedDllRefCount
)
2703 /* increment counts */
2704 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2708 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
))
2711 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2713 if ( cl
->component
== comp
)
2718 /* decrement counts */
2719 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2723 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ABSENT
))
2726 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2728 if ( cl
->component
== comp
)
2733 /* ref count all the files in the component */
2738 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2740 if (file
->Component
== comp
)
2741 ACTION_WriteSharedDLLsCount( file
->TargetPath
, count
);
2745 /* add a count for permenent */
2746 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2749 comp
->RefCount
= count
;
2752 ACTION_WriteSharedDLLsCount( comp
->FullKeypath
, comp
->RefCount
);
2756 * Ok further analysis makes me think that this work is
2757 * actually done in the PublishComponents and PublishFeatures
2758 * step, and not here. It appears like the keypath and all that is
2759 * resolved in this step, however actually written in the Publish steps.
2760 * But we will leave it here for now because it is unclear
2762 static UINT
ACTION_ProcessComponents(MSIPACKAGE
*package
)
2764 WCHAR squished_pc
[GUID_SIZE
];
2765 WCHAR squished_cc
[GUID_SIZE
];
2768 HKEY hkey
=0,hkey2
=0;
2770 /* writes the Component and Features values to the registry */
2772 rc
= MSIREG_OpenComponents(&hkey
);
2773 if (rc
!= ERROR_SUCCESS
)
2776 squash_guid(package
->ProductCode
,squished_pc
);
2777 ui_progress(package
,1,COMPONENT_PROGRESS_VALUE
,1,0);
2779 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2783 ui_progress(package
,2,0,0,0);
2784 if (!comp
->ComponentId
)
2787 squash_guid(comp
->ComponentId
,squished_cc
);
2789 msi_free(comp
->FullKeypath
);
2790 comp
->FullKeypath
= resolve_keypath( package
, comp
);
2792 /* do the refcounting */
2793 ACTION_RefCountComponent( package
, comp
);
2795 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2796 debugstr_w(comp
->Component
),
2797 debugstr_w(squished_cc
),
2798 debugstr_w(comp
->FullKeypath
),
2801 * Write the keypath out if the component is to be registered
2802 * and delete the key if the component is to be deregistered
2804 if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2806 rc
= RegCreateKeyW(hkey
,squished_cc
,&hkey2
);
2807 if (rc
!= ERROR_SUCCESS
)
2810 if (!comp
->FullKeypath
)
2813 msi_reg_set_val_str( hkey2
, squished_pc
, comp
->FullKeypath
);
2815 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2817 static const WCHAR szPermKey
[] =
2818 { '0','0','0','0','0','0','0','0','0','0','0','0',
2819 '0','0','0','0','0','0','0','0','0','0','0','0',
2820 '0','0','0','0','0','0','0','0',0 };
2822 msi_reg_set_val_str( hkey2
, szPermKey
, comp
->FullKeypath
);
2827 else if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ABSENT
))
2831 rc
= RegOpenKeyW(hkey
,squished_cc
,&hkey2
);
2832 if (rc
!= ERROR_SUCCESS
)
2835 RegDeleteValueW(hkey2
,squished_pc
);
2837 /* if the key is empty delete it */
2838 res
= RegEnumKeyExW(hkey2
,0,NULL
,0,0,NULL
,0,NULL
);
2840 if (res
== ERROR_NO_MORE_ITEMS
)
2841 RegDeleteKeyW(hkey
,squished_cc
);
2846 uirow
= MSI_CreateRecord(3);
2847 MSI_RecordSetStringW(uirow
,1,package
->ProductCode
);
2848 MSI_RecordSetStringW(uirow
,2,comp
->ComponentId
);
2849 MSI_RecordSetStringW(uirow
,3,comp
->FullKeypath
);
2850 ui_actiondata(package
,szProcessComponents
,uirow
);
2851 msiobj_release( &uirow
->hdr
);
2865 static BOOL CALLBACK
Typelib_EnumResNameProc( HMODULE hModule
, LPCWSTR lpszType
,
2866 LPWSTR lpszName
, LONG_PTR lParam
)
2869 typelib_struct
*tl_struct
= (typelib_struct
*) lParam
;
2870 static const WCHAR fmt
[] = {'%','s','\\','%','i',0};
2874 if (!IS_INTRESOURCE(lpszName
))
2876 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName
));
2880 sz
= strlenW(tl_struct
->source
)+4;
2881 sz
*= sizeof(WCHAR
);
2883 if ((INT_PTR
)lpszName
== 1)
2884 tl_struct
->path
= strdupW(tl_struct
->source
);
2887 tl_struct
->path
= msi_alloc(sz
);
2888 sprintfW(tl_struct
->path
,fmt
,tl_struct
->source
, lpszName
);
2891 TRACE("trying %s\n", debugstr_w(tl_struct
->path
));
2892 res
= LoadTypeLib(tl_struct
->path
,&tl_struct
->ptLib
);
2893 if (!SUCCEEDED(res
))
2895 msi_free(tl_struct
->path
);
2896 tl_struct
->path
= NULL
;
2901 ITypeLib_GetLibAttr(tl_struct
->ptLib
, &attr
);
2902 if (IsEqualGUID(&(tl_struct
->clsid
),&(attr
->guid
)))
2904 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2908 msi_free(tl_struct
->path
);
2909 tl_struct
->path
= NULL
;
2911 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2912 ITypeLib_Release(tl_struct
->ptLib
);
2917 static UINT
ITERATE_RegisterTypeLibraries(MSIRECORD
*row
, LPVOID param
)
2919 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
2923 typelib_struct tl_struct
;
2925 static const WCHAR szTYPELIB
[] = {'T','Y','P','E','L','I','B',0};
2927 component
= MSI_RecordGetString(row
,3);
2928 comp
= get_loaded_component(package
,component
);
2930 return ERROR_SUCCESS
;
2932 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2934 TRACE("Skipping typelib reg due to disabled component\n");
2936 comp
->Action
= comp
->Installed
;
2938 return ERROR_SUCCESS
;
2941 comp
->Action
= INSTALLSTATE_LOCAL
;
2943 file
= get_loaded_file( package
, comp
->KeyPath
);
2945 return ERROR_SUCCESS
;
2947 module
= LoadLibraryExW( file
->TargetPath
, NULL
, LOAD_LIBRARY_AS_DATAFILE
);
2951 guid
= MSI_RecordGetString(row
,1);
2952 CLSIDFromString((LPWSTR
)guid
, &tl_struct
.clsid
);
2953 tl_struct
.source
= strdupW( file
->TargetPath
);
2954 tl_struct
.path
= NULL
;
2956 EnumResourceNamesW(module
, szTYPELIB
, Typelib_EnumResNameProc
,
2957 (LONG_PTR
)&tl_struct
);
2965 helpid
= MSI_RecordGetString(row
,6);
2968 help
= resolve_folder(package
,helpid
,FALSE
,FALSE
,TRUE
,NULL
);
2969 res
= RegisterTypeLib(tl_struct
.ptLib
,tl_struct
.path
,help
);
2972 if (!SUCCEEDED(res
))
2973 ERR("Failed to register type library %s\n",
2974 debugstr_w(tl_struct
.path
));
2977 ui_actiondata(package
,szRegisterTypeLibraries
,row
);
2979 TRACE("Registered %s\n", debugstr_w(tl_struct
.path
));
2982 ITypeLib_Release(tl_struct
.ptLib
);
2983 msi_free(tl_struct
.path
);
2986 ERR("Failed to load type library %s\n",
2987 debugstr_w(tl_struct
.source
));
2989 FreeLibrary(module
);
2990 msi_free(tl_struct
.source
);
2993 ERR("Could not load file! %s\n", debugstr_w(file
->TargetPath
));
2995 return ERROR_SUCCESS
;
2998 static UINT
ACTION_RegisterTypeLibraries(MSIPACKAGE
*package
)
3001 * OK this is a bit confusing.. I am given a _Component key and I believe
3002 * that the file that is being registered as a type library is the "key file
3003 * of that component" which I interpret to mean "The file in the KeyPath of
3008 static const WCHAR Query
[] =
3009 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3010 '`','T','y','p','e','L','i','b','`',0};
3012 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3013 if (rc
!= ERROR_SUCCESS
)
3014 return ERROR_SUCCESS
;
3016 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_RegisterTypeLibraries
, package
);
3017 msiobj_release(&view
->hdr
);
3021 static UINT
ITERATE_CreateShortcuts(MSIRECORD
*row
, LPVOID param
)
3023 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3024 LPWSTR target_file
, target_folder
, filename
;
3025 LPCWSTR buffer
, extension
;
3027 static const WCHAR szlnk
[]={'.','l','n','k',0};
3028 IShellLinkW
*sl
= NULL
;
3029 IPersistFile
*pf
= NULL
;
3032 buffer
= MSI_RecordGetString(row
,4);
3033 comp
= get_loaded_component(package
,buffer
);
3035 return ERROR_SUCCESS
;
3037 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3039 TRACE("Skipping shortcut creation due to disabled component\n");
3041 comp
->Action
= comp
->Installed
;
3043 return ERROR_SUCCESS
;
3046 comp
->Action
= INSTALLSTATE_LOCAL
;
3048 ui_actiondata(package
,szCreateShortcuts
,row
);
3050 res
= CoCreateInstance( &CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
,
3051 &IID_IShellLinkW
, (LPVOID
*) &sl
);
3055 ERR("CLSID_ShellLink not available\n");
3059 res
= IShellLinkW_QueryInterface( sl
, &IID_IPersistFile
,(LPVOID
*) &pf
);
3062 ERR("QueryInterface(IID_IPersistFile) failed\n");
3066 buffer
= MSI_RecordGetString(row
,2);
3067 target_folder
= resolve_folder(package
, buffer
,FALSE
,FALSE
,TRUE
,NULL
);
3069 /* may be needed because of a bug somehwere else */
3070 create_full_pathW(target_folder
);
3072 filename
= msi_dup_record_field( row
, 3 );
3073 reduce_to_longfilename(filename
);
3075 extension
= strchrW(filename
,'.');
3076 if (!extension
|| strcmpiW(extension
,szlnk
))
3078 int len
= strlenW(filename
);
3079 filename
= msi_realloc(filename
, len
* sizeof(WCHAR
) + sizeof(szlnk
));
3080 memcpy(filename
+ len
, szlnk
, sizeof(szlnk
));
3082 target_file
= build_directory_name(2, target_folder
, filename
);
3083 msi_free(target_folder
);
3086 buffer
= MSI_RecordGetString(row
,5);
3087 if (strchrW(buffer
,'['))
3090 deformat_string(package
,buffer
,&deformated
);
3091 IShellLinkW_SetPath(sl
,deformated
);
3092 msi_free(deformated
);
3096 FIXME("poorly handled shortcut format, advertised shortcut\n");
3097 IShellLinkW_SetPath(sl
,comp
->FullKeypath
);
3100 if (!MSI_RecordIsNull(row
,6))
3103 buffer
= MSI_RecordGetString(row
,6);
3104 deformat_string(package
,buffer
,&deformated
);
3105 IShellLinkW_SetArguments(sl
,deformated
);
3106 msi_free(deformated
);
3109 if (!MSI_RecordIsNull(row
,7))
3111 buffer
= MSI_RecordGetString(row
,7);
3112 IShellLinkW_SetDescription(sl
,buffer
);
3115 if (!MSI_RecordIsNull(row
,8))
3116 IShellLinkW_SetHotkey(sl
,MSI_RecordGetInteger(row
,8));
3118 if (!MSI_RecordIsNull(row
,9))
3123 buffer
= MSI_RecordGetString(row
,9);
3125 Path
= build_icon_path(package
,buffer
);
3126 index
= MSI_RecordGetInteger(row
,10);
3128 /* no value means 0 */
3129 if (index
== MSI_NULL_INTEGER
)
3132 IShellLinkW_SetIconLocation(sl
,Path
,index
);
3136 if (!MSI_RecordIsNull(row
,11))
3137 IShellLinkW_SetShowCmd(sl
,MSI_RecordGetInteger(row
,11));
3139 if (!MSI_RecordIsNull(row
,12))
3142 buffer
= MSI_RecordGetString(row
,12);
3143 Path
= resolve_folder(package
, buffer
, FALSE
, FALSE
, TRUE
, NULL
);
3145 IShellLinkW_SetWorkingDirectory(sl
,Path
);
3149 TRACE("Writing shortcut to %s\n",debugstr_w(target_file
));
3150 IPersistFile_Save(pf
,target_file
,FALSE
);
3152 msi_free(target_file
);
3156 IPersistFile_Release( pf
);
3158 IShellLinkW_Release( sl
);
3160 return ERROR_SUCCESS
;
3163 static UINT
ACTION_CreateShortcuts(MSIPACKAGE
*package
)
3168 static const WCHAR Query
[] =
3169 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3170 '`','S','h','o','r','t','c','u','t','`',0};
3172 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3173 if (rc
!= ERROR_SUCCESS
)
3174 return ERROR_SUCCESS
;
3176 res
= CoInitialize( NULL
);
3179 ERR("CoInitialize failed\n");
3180 return ERROR_FUNCTION_FAILED
;
3183 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateShortcuts
, package
);
3184 msiobj_release(&view
->hdr
);
3191 static UINT
ITERATE_PublishProduct(MSIRECORD
*row
, LPVOID param
)
3193 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
3202 FileName
= MSI_RecordGetString(row
,1);
3205 ERR("Unable to get FileName\n");
3206 return ERROR_SUCCESS
;
3209 FilePath
= build_icon_path(package
,FileName
);
3211 TRACE("Creating icon file at %s\n",debugstr_w(FilePath
));
3213 the_file
= CreateFileW(FilePath
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
,
3214 FILE_ATTRIBUTE_NORMAL
, NULL
);
3216 if (the_file
== INVALID_HANDLE_VALUE
)
3218 ERR("Unable to create file %s\n",debugstr_w(FilePath
));
3220 return ERROR_SUCCESS
;
3227 rc
= MSI_RecordReadStream(row
,2,buffer
,&sz
);
3228 if (rc
!= ERROR_SUCCESS
)
3230 ERR("Failed to get stream\n");
3231 CloseHandle(the_file
);
3232 DeleteFileW(FilePath
);
3235 WriteFile(the_file
,buffer
,sz
,&write
,NULL
);
3236 } while (sz
== 1024);
3240 CloseHandle(the_file
);
3242 uirow
= MSI_CreateRecord(1);
3243 MSI_RecordSetStringW(uirow
,1,FileName
);
3244 ui_actiondata(package
,szPublishProduct
,uirow
);
3245 msiobj_release( &uirow
->hdr
);
3247 return ERROR_SUCCESS
;
3250 static BOOL
msi_check_publish(MSIPACKAGE
*package
)
3252 MSIFEATURE
*feature
;
3254 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3256 if (feature
->ActionRequest
== INSTALLSTATE_LOCAL
)
3264 * 99% of the work done here is only done for
3265 * advertised installs. However this is where the
3266 * Icon table is processed and written out
3267 * so that is what I am going to do here.
3269 static UINT
ACTION_PublishProduct(MSIPACKAGE
*package
)
3273 MSISOURCELISTINFO
*info
;
3275 static const WCHAR Query
[]=
3276 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3277 '`','I','c','o','n','`',0};
3278 /* for registry stuff */
3281 HKEY hudkey
=0, props
=0;
3282 static const WCHAR szProductLanguage
[] =
3283 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3284 static const WCHAR szARPProductIcon
[] =
3285 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3286 static const WCHAR szProductVersion
[] =
3287 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3291 MSIHANDLE hDb
, hSumInfo
;
3293 /* FIXME: also need to publish if the product is in advertise mode */
3294 if (!msi_check_publish(package
))
3295 return ERROR_SUCCESS
;
3297 /* write out icon files */
3299 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3300 if (rc
== ERROR_SUCCESS
)
3302 MSI_IterateRecords(view
, NULL
, ITERATE_PublishProduct
, package
);
3303 msiobj_release(&view
->hdr
);
3306 /* ok there is a lot more done here but i need to figure out what */
3308 rc
= MSIREG_OpenProductsKey(package
->ProductCode
,&hkey
,TRUE
);
3309 if (rc
!= ERROR_SUCCESS
)
3312 rc
= MSIREG_OpenUserProductsKey(package
->ProductCode
,&hukey
,TRUE
);
3313 if (rc
!= ERROR_SUCCESS
)
3316 rc
= MSIREG_OpenUserDataProductKey(package
->ProductCode
,&hudkey
,TRUE
);
3317 if (rc
!= ERROR_SUCCESS
)
3320 rc
= MSIREG_OpenInstallPropertiesKey(package
->ProductCode
,&props
,TRUE
);
3321 if (rc
!= ERROR_SUCCESS
)
3324 buffer
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTNAMEW
);
3325 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PRODUCTNAMEW
, buffer
);
3328 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
3329 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
3331 buffer
= msi_dup_property( package
, szARPProductIcon
);
3334 LPWSTR path
= build_icon_path(package
,buffer
);
3335 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PRODUCTICONW
, path
);
3340 buffer
= msi_dup_property( package
, szProductVersion
);
3343 DWORD verdword
= msi_version_str_to_dword(buffer
);
3344 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
3348 /* FIXME: Need to write more keys to the user registry */
3350 hDb
= alloc_msihandle( &package
->db
->hdr
);
3352 rc
= ERROR_NOT_ENOUGH_MEMORY
;
3355 rc
= MsiGetSummaryInformationW(hDb
, NULL
, 0, &hSumInfo
);
3356 MsiCloseHandle(hDb
);
3357 if (rc
== ERROR_SUCCESS
)
3359 WCHAR guidbuffer
[0x200];
3361 rc
= MsiSummaryInfoGetPropertyW(hSumInfo
, 9, NULL
, NULL
, NULL
,
3363 if (rc
== ERROR_SUCCESS
)
3365 WCHAR squashed
[GUID_SIZE
];
3366 /* for now we only care about the first guid */
3367 LPWSTR ptr
= strchrW(guidbuffer
,';');
3369 squash_guid(guidbuffer
,squashed
);
3370 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PACKAGECODEW
, squashed
);
3374 ERR("Unable to query Revision_Number...\n");
3377 MsiCloseHandle(hSumInfo
);
3381 ERR("Unable to open Summary Information\n");
3385 /* publish the SourceList info */
3386 LIST_FOR_EACH_ENTRY(info
, &package
->sourcelist_info
, MSISOURCELISTINFO
, entry
)
3388 MsiSourceListSetInfoW(package
->ProductCode
, NULL
,
3389 info
->context
, info
->options
,
3390 info
->property
, info
->value
);
3393 LIST_FOR_EACH_ENTRY(disk
, &package
->sourcelist_media
, MSIMEDIADISK
, entry
)
3395 MsiSourceListAddMediaDiskW(package
->ProductCode
, NULL
,
3396 disk
->context
, disk
->options
,
3397 disk
->disk_id
, disk
->volume_label
, disk
->disk_prompt
);
3403 RegCloseKey(hudkey
);
3409 static UINT
ITERATE_WriteIniValues(MSIRECORD
*row
, LPVOID param
)
3411 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3412 LPCWSTR component
,section
,key
,value
,identifier
,filename
,dirproperty
;
3413 LPWSTR deformated_section
, deformated_key
, deformated_value
;
3414 LPWSTR folder
, fullname
= NULL
;
3418 static const WCHAR szWindowsFolder
[] =
3419 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3421 component
= MSI_RecordGetString(row
, 8);
3422 comp
= get_loaded_component(package
,component
);
3424 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3426 TRACE("Skipping ini file due to disabled component %s\n",
3427 debugstr_w(component
));
3429 comp
->Action
= comp
->Installed
;
3431 return ERROR_SUCCESS
;
3434 comp
->Action
= INSTALLSTATE_LOCAL
;
3436 identifier
= MSI_RecordGetString(row
,1);
3437 filename
= MSI_RecordGetString(row
,2);
3438 dirproperty
= MSI_RecordGetString(row
,3);
3439 section
= MSI_RecordGetString(row
,4);
3440 key
= MSI_RecordGetString(row
,5);
3441 value
= MSI_RecordGetString(row
,6);
3442 action
= MSI_RecordGetInteger(row
,7);
3444 deformat_string(package
,section
,&deformated_section
);
3445 deformat_string(package
,key
,&deformated_key
);
3446 deformat_string(package
,value
,&deformated_value
);
3450 folder
= resolve_folder(package
, dirproperty
, FALSE
, FALSE
, TRUE
, NULL
);
3452 folder
= msi_dup_property( package
, dirproperty
);
3455 folder
= msi_dup_property( package
, szWindowsFolder
);
3459 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty
));
3463 fullname
= build_directory_name(2, folder
, filename
);
3467 TRACE("Adding value %s to section %s in %s\n",
3468 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3469 debugstr_w(fullname
));
3470 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3471 deformated_value
, fullname
);
3473 else if (action
== 1)
3476 GetPrivateProfileStringW(deformated_section
, deformated_key
, NULL
,
3477 returned
, 10, fullname
);
3478 if (returned
[0] == 0)
3480 TRACE("Adding value %s to section %s in %s\n",
3481 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3482 debugstr_w(fullname
));
3484 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3485 deformated_value
, fullname
);
3488 else if (action
== 3)
3489 FIXME("Append to existing section not yet implemented\n");
3491 uirow
= MSI_CreateRecord(4);
3492 MSI_RecordSetStringW(uirow
,1,identifier
);
3493 MSI_RecordSetStringW(uirow
,2,deformated_section
);
3494 MSI_RecordSetStringW(uirow
,3,deformated_key
);
3495 MSI_RecordSetStringW(uirow
,4,deformated_value
);
3496 ui_actiondata(package
,szWriteIniValues
,uirow
);
3497 msiobj_release( &uirow
->hdr
);
3501 msi_free(deformated_key
);
3502 msi_free(deformated_value
);
3503 msi_free(deformated_section
);
3504 return ERROR_SUCCESS
;
3507 static UINT
ACTION_WriteIniValues(MSIPACKAGE
*package
)
3511 static const WCHAR ExecSeqQuery
[] =
3512 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3513 '`','I','n','i','F','i','l','e','`',0};
3515 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3516 if (rc
!= ERROR_SUCCESS
)
3518 TRACE("no IniFile table\n");
3519 return ERROR_SUCCESS
;
3522 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteIniValues
, package
);
3523 msiobj_release(&view
->hdr
);
3527 static UINT
ITERATE_SelfRegModules(MSIRECORD
*row
, LPVOID param
)
3529 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3534 static const WCHAR ExeStr
[] =
3535 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3536 static const WCHAR close
[] = {'\"',0};
3538 PROCESS_INFORMATION info
;
3543 memset(&si
,0,sizeof(STARTUPINFOW
));
3545 filename
= MSI_RecordGetString(row
,1);
3546 file
= get_loaded_file( package
, filename
);
3550 ERR("Unable to find file id %s\n",debugstr_w(filename
));
3551 return ERROR_SUCCESS
;
3554 len
= strlenW(ExeStr
) + strlenW( file
->TargetPath
) + 2;
3556 FullName
= msi_alloc(len
*sizeof(WCHAR
));
3557 strcpyW(FullName
,ExeStr
);
3558 strcatW( FullName
, file
->TargetPath
);
3559 strcatW(FullName
,close
);
3561 TRACE("Registering %s\n",debugstr_w(FullName
));
3562 brc
= CreateProcessW(NULL
, FullName
, NULL
, NULL
, FALSE
, 0, NULL
, c_colon
,
3566 msi_dialog_check_messages(info
.hProcess
);
3571 uirow
= MSI_CreateRecord( 2 );
3572 uipath
= strdupW( file
->TargetPath
);
3573 p
= strrchrW(uipath
,'\\');
3576 MSI_RecordSetStringW( uirow
, 1, &p
[1] );
3577 MSI_RecordSetStringW( uirow
, 2, uipath
);
3578 ui_actiondata( package
, szSelfRegModules
, uirow
);
3579 msiobj_release( &uirow
->hdr
);
3581 /* FIXME: call ui_progress? */
3583 return ERROR_SUCCESS
;
3586 static UINT
ACTION_SelfRegModules(MSIPACKAGE
*package
)
3590 static const WCHAR ExecSeqQuery
[] =
3591 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3592 '`','S','e','l','f','R','e','g','`',0};
3594 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3595 if (rc
!= ERROR_SUCCESS
)
3597 TRACE("no SelfReg table\n");
3598 return ERROR_SUCCESS
;
3601 MSI_IterateRecords(view
, NULL
, ITERATE_SelfRegModules
, package
);
3602 msiobj_release(&view
->hdr
);
3604 return ERROR_SUCCESS
;
3607 static UINT
ACTION_PublishFeatures(MSIPACKAGE
*package
)
3609 MSIFEATURE
*feature
;
3614 if (!msi_check_publish(package
))
3615 return ERROR_SUCCESS
;
3617 rc
= MSIREG_OpenFeaturesKey(package
->ProductCode
,&hkey
,TRUE
);
3618 if (rc
!= ERROR_SUCCESS
)
3621 rc
= MSIREG_OpenUserFeaturesKey(package
->ProductCode
,&hukey
,TRUE
);
3622 if (rc
!= ERROR_SUCCESS
)
3625 /* here the guids are base 85 encoded */
3626 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
3632 BOOL absent
= FALSE
;
3635 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
) &&
3636 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_SOURCE
) &&
3637 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ADVERTISED
))
3641 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3645 if (feature
->Feature_Parent
)
3646 size
+= strlenW( feature
->Feature_Parent
)+2;
3648 data
= msi_alloc(size
* sizeof(WCHAR
));
3651 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3653 MSICOMPONENT
* component
= cl
->component
;
3657 if (component
->ComponentId
)
3659 TRACE("From %s\n",debugstr_w(component
->ComponentId
));
3660 CLSIDFromString(component
->ComponentId
, &clsid
);
3661 encode_base85_guid(&clsid
,buf
);
3662 TRACE("to %s\n",debugstr_w(buf
));
3666 if (feature
->Feature_Parent
)
3668 static const WCHAR sep
[] = {'\2',0};
3670 strcatW(data
,feature
->Feature_Parent
);
3673 msi_reg_set_val_str( hkey
, feature
->Feature
, data
);
3677 if (feature
->Feature_Parent
)
3678 size
= strlenW(feature
->Feature_Parent
)*sizeof(WCHAR
);
3681 RegSetValueExW(hukey
,feature
->Feature
,0,REG_SZ
,
3682 (LPBYTE
)feature
->Feature_Parent
,size
);
3686 size
+= 2*sizeof(WCHAR
);
3687 data
= msi_alloc(size
);
3690 if (feature
->Feature_Parent
)
3691 strcpyW( &data
[1], feature
->Feature_Parent
);
3692 RegSetValueExW(hukey
,feature
->Feature
,0,REG_SZ
,
3698 uirow
= MSI_CreateRecord( 1 );
3699 MSI_RecordSetStringW( uirow
, 1, feature
->Feature
);
3700 ui_actiondata( package
, szPublishFeatures
, uirow
);
3701 msiobj_release( &uirow
->hdr
);
3702 /* FIXME: call ui_progress? */
3711 static UINT
msi_unpublish_feature(MSIPACKAGE
*package
, MSIFEATURE
*feature
)
3716 TRACE("unpublishing feature %s\n", debugstr_w(feature
->Feature
));
3718 r
= MSIREG_OpenUserFeaturesKey(package
->ProductCode
, &hkey
, FALSE
);
3719 if (r
== ERROR_SUCCESS
)
3721 RegDeleteValueW(hkey
, feature
->Feature
);
3725 r
= MSIREG_OpenUserDataFeaturesKey(package
->ProductCode
, &hkey
, FALSE
);
3726 if (r
== ERROR_SUCCESS
)
3728 RegDeleteValueW(hkey
, feature
->Feature
);
3732 return ERROR_SUCCESS
;
3735 static UINT
ACTION_UnpublishFeatures(MSIPACKAGE
*package
)
3737 MSIFEATURE
*feature
;
3739 if (msi_check_publish(package
))
3740 return ERROR_SUCCESS
;
3742 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3744 msi_unpublish_feature(package
, feature
);
3747 return ERROR_SUCCESS
;
3750 static UINT
msi_get_local_package_name( LPWSTR path
)
3752 static const WCHAR szInstaller
[] = {
3753 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3754 static const WCHAR fmt
[] = { '%','x','.','m','s','i',0};
3758 time
= GetTickCount();
3759 GetWindowsDirectoryW( path
, MAX_PATH
);
3760 lstrcatW( path
, szInstaller
);
3761 CreateDirectoryW( path
, NULL
);
3763 len
= lstrlenW(path
);
3764 for (i
=0; i
<0x10000; i
++)
3766 snprintfW( &path
[len
], MAX_PATH
- len
, fmt
, (time
+i
)&0xffff );
3767 handle
= CreateFileW( path
, GENERIC_WRITE
, 0, NULL
,
3768 CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, 0 );
3769 if (handle
!= INVALID_HANDLE_VALUE
)
3771 CloseHandle(handle
);
3774 if (GetLastError() != ERROR_FILE_EXISTS
&&
3775 GetLastError() != ERROR_SHARING_VIOLATION
)
3776 return ERROR_FUNCTION_FAILED
;
3779 return ERROR_SUCCESS
;
3782 static UINT
msi_make_package_local( MSIPACKAGE
*package
, HKEY hkey
)
3784 static const WCHAR szOriginalDatabase
[] =
3785 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3786 WCHAR packagefile
[MAX_PATH
];
3790 r
= msi_get_local_package_name( packagefile
);
3791 if (r
!= ERROR_SUCCESS
)
3794 TRACE("Copying to local package %s\n",debugstr_w(packagefile
));
3796 msiFilePath
= msi_dup_property( package
, szOriginalDatabase
);
3797 r
= CopyFileW( msiFilePath
, packagefile
, FALSE
);
3801 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3802 debugstr_w(msiFilePath
), debugstr_w(packagefile
), GetLastError());
3803 msi_free( msiFilePath
);
3804 return ERROR_FUNCTION_FAILED
;
3806 msi_free( msiFilePath
);
3808 /* FIXME: maybe set this key in ACTION_RegisterProduct instead */
3809 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_LOCALPACKAGEW
, packagefile
);
3810 return ERROR_SUCCESS
;
3813 static UINT
msi_write_uninstall_property_vals( MSIPACKAGE
*package
, HKEY hkey
)
3815 LPWSTR prop
, val
, key
;
3816 static const LPCSTR propval
[] = {
3817 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3818 "ARPCONTACT", "Contact",
3819 "ARPCOMMENTS", "Comments",
3820 "ProductName", "DisplayName",
3821 "ProductVersion", "DisplayVersion",
3822 "ARPHELPLINK", "HelpLink",
3823 "ARPHELPTELEPHONE", "HelpTelephone",
3824 "ARPINSTALLLOCATION", "InstallLocation",
3825 "SourceDir", "InstallSource",
3826 "Manufacturer", "Publisher",
3827 "ARPREADME", "Readme",
3829 "ARPURLINFOABOUT", "URLInfoAbout",
3830 "ARPURLUPDATEINFO", "URLUpdateInfo",
3833 const LPCSTR
*p
= propval
;
3837 prop
= strdupAtoW( *p
++ );
3838 key
= strdupAtoW( *p
++ );
3839 val
= msi_dup_property( package
, prop
);
3840 msi_reg_set_val_str( hkey
, key
, val
);
3845 return ERROR_SUCCESS
;
3848 static UINT
ACTION_RegisterProduct(MSIPACKAGE
*package
)
3851 HKEY hudkey
=0, props
=0;
3852 LPWSTR buffer
= NULL
;
3855 static const WCHAR szWindowsInstaller
[] =
3856 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3857 static const WCHAR szUpgradeCode
[] =
3858 {'U','p','g','r','a','d','e','C','o','d','e',0};
3859 static const WCHAR modpath_fmt
[] =
3860 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3861 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3862 static const WCHAR szModifyPath
[] =
3863 {'M','o','d','i','f','y','P','a','t','h',0};
3864 static const WCHAR szUninstallString
[] =
3865 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3866 static const WCHAR szEstimatedSize
[] =
3867 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3868 static const WCHAR szProductLanguage
[] =
3869 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3870 static const WCHAR szProductVersion
[] =
3871 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3874 static const WCHAR date_fmt
[] = {'%','i','%','i','%','i',0};
3875 LPWSTR upgrade_code
;
3878 /* FIXME: also need to publish if the product is in advertise mode */
3879 if (!msi_check_publish(package
))
3880 return ERROR_SUCCESS
;
3882 rc
= MSIREG_OpenUninstallKey(package
->ProductCode
,&hkey
,TRUE
);
3883 if (rc
!= ERROR_SUCCESS
)
3886 /* dump all the info i can grab */
3887 /* FIXME: Flesh out more information */
3889 msi_write_uninstall_property_vals( package
, hkey
);
3891 msi_reg_set_val_dword( hkey
, szWindowsInstaller
, 1 );
3893 msi_make_package_local( package
, hkey
);
3895 /* do ModifyPath and UninstallString */
3896 size
= deformat_string(package
,modpath_fmt
,&buffer
);
3897 RegSetValueExW(hkey
,szModifyPath
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
3898 RegSetValueExW(hkey
,szUninstallString
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
3901 /* FIXME: Write real Estimated Size when we have it */
3902 msi_reg_set_val_dword( hkey
, szEstimatedSize
, 0 );
3904 GetLocalTime(&systime
);
3905 sprintfW(szDate
,date_fmt
,systime
.wYear
,systime
.wMonth
,systime
.wDay
);
3906 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_INSTALLDATEW
, szDate
);
3908 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
3909 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
3911 buffer
= msi_dup_property( package
, szProductVersion
);
3914 DWORD verdword
= msi_version_str_to_dword(buffer
);
3916 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
3917 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMAJORW
, verdword
>>24 );
3918 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMINORW
, (verdword
>>16)&0x00FF );
3922 /* Handle Upgrade Codes */
3923 upgrade_code
= msi_dup_property( package
, szUpgradeCode
);
3928 MSIREG_OpenUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
3929 squash_guid(package
->ProductCode
,squashed
);
3930 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
3932 MSIREG_OpenUserUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
3933 squash_guid(package
->ProductCode
,squashed
);
3934 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
3937 msi_free(upgrade_code
);
3942 rc
= MSIREG_OpenUserDataProductKey(package
->ProductCode
, &hudkey
, TRUE
);
3943 if (rc
!= ERROR_SUCCESS
)
3946 RegCloseKey(hudkey
);
3948 rc
= MSIREG_OpenInstallPropertiesKey(package
->ProductCode
, &props
, TRUE
);
3949 if (rc
!= ERROR_SUCCESS
)
3952 msi_reg_set_val_dword( props
, szWindowsInstaller
, 1 );
3955 return ERROR_SUCCESS
;
3958 static UINT
ACTION_InstallExecute(MSIPACKAGE
*package
)
3960 return execute_script(package
,INSTALL_SCRIPT
);
3963 static UINT
msi_unpublish_product(MSIPACKAGE
*package
)
3965 LPWSTR remove
= NULL
;
3966 LPWSTR
*features
= NULL
;
3967 BOOL full_uninstall
= TRUE
;
3968 MSIFEATURE
*feature
;
3970 static const WCHAR szRemove
[] = {'R','E','M','O','V','E',0};
3971 static const WCHAR szAll
[] = {'A','L','L',0};
3973 remove
= msi_dup_property(package
, szRemove
);
3975 return ERROR_SUCCESS
;
3977 features
= msi_split_string(remove
, ',');
3981 ERR("REMOVE feature list is empty!\n");
3982 return ERROR_FUNCTION_FAILED
;
3985 if (!lstrcmpW(features
[0], szAll
))
3986 full_uninstall
= TRUE
;
3989 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3991 if (feature
->Action
!= INSTALLSTATE_ABSENT
)
3992 full_uninstall
= FALSE
;
3996 if (!full_uninstall
)
3999 MSIREG_DeleteProductKey(package
->ProductCode
);
4000 MSIREG_DeleteUserProductKey(package
->ProductCode
);
4001 MSIREG_DeleteUserDataProductKey(package
->ProductCode
);
4002 MSIREG_DeleteUserFeaturesKey(package
->ProductCode
);
4007 return ERROR_SUCCESS
;
4010 static UINT
ACTION_InstallFinalize(MSIPACKAGE
*package
)
4014 rc
= msi_unpublish_product(package
);
4015 if (rc
!= ERROR_SUCCESS
)
4018 /* turn off scheduling */
4019 package
->script
->CurrentlyScripting
= FALSE
;
4021 /* first do the same as an InstallExecute */
4022 rc
= ACTION_InstallExecute(package
);
4023 if (rc
!= ERROR_SUCCESS
)
4026 /* then handle Commit Actions */
4027 rc
= execute_script(package
,COMMIT_SCRIPT
);
4032 UINT
ACTION_ForceReboot(MSIPACKAGE
*package
)
4034 static const WCHAR RunOnce
[] = {
4035 'S','o','f','t','w','a','r','e','\\',
4036 'M','i','c','r','o','s','o','f','t','\\',
4037 'W','i','n','d','o','w','s','\\',
4038 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4039 'R','u','n','O','n','c','e',0};
4040 static const WCHAR InstallRunOnce
[] = {
4041 'S','o','f','t','w','a','r','e','\\',
4042 'M','i','c','r','o','s','o','f','t','\\',
4043 'W','i','n','d','o','w','s','\\',
4044 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4045 'I','n','s','t','a','l','l','e','r','\\',
4046 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4048 static const WCHAR msiexec_fmt
[] = {
4050 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4051 '\"','%','s','\"',0};
4052 static const WCHAR install_fmt
[] = {
4053 '/','I',' ','\"','%','s','\"',' ',
4054 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4055 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4056 WCHAR buffer
[256], sysdir
[MAX_PATH
];
4058 WCHAR squished_pc
[100];
4060 squash_guid(package
->ProductCode
,squished_pc
);
4062 GetSystemDirectoryW(sysdir
, sizeof(sysdir
)/sizeof(sysdir
[0]));
4063 RegCreateKeyW(HKEY_LOCAL_MACHINE
,RunOnce
,&hkey
);
4064 snprintfW(buffer
,sizeof(buffer
)/sizeof(buffer
[0]),msiexec_fmt
,sysdir
,
4067 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
4070 TRACE("Reboot command %s\n",debugstr_w(buffer
));
4072 RegCreateKeyW(HKEY_LOCAL_MACHINE
,InstallRunOnce
,&hkey
);
4073 sprintfW(buffer
,install_fmt
,package
->ProductCode
,squished_pc
);
4075 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
4078 return ERROR_INSTALL_SUSPEND
;
4081 static UINT
msi_set_sourcedir_props(MSIPACKAGE
*package
)
4086 p
= strrchrW( package
->PackagePath
, '\\' );
4088 return ERROR_SUCCESS
;
4090 len
= p
- package
->PackagePath
+ 2;
4091 source
= msi_alloc( len
* sizeof(WCHAR
) );
4092 lstrcpynW( source
, package
->PackagePath
, len
);
4094 MSI_SetPropertyW( package
, cszSourceDir
, source
);
4095 MSI_SetPropertyW( package
, cszSOURCEDIR
, source
);
4099 return ERROR_SUCCESS
;
4102 static UINT
ACTION_ResolveSource(MSIPACKAGE
* package
)
4108 * We are currently doing what should be done here in the top level Install
4109 * however for Administrative and uninstalls this step will be needed
4111 if (!package
->PackagePath
)
4112 return ERROR_SUCCESS
;
4114 msi_set_sourcedir_props(package
);
4116 attrib
= GetFileAttributesW(package
->PackagePath
);
4117 if (attrib
== INVALID_FILE_ATTRIBUTES
)
4123 rc
= MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
4124 MSIINSTALLCONTEXT_USERMANAGED
, MSICODE_PRODUCT
,
4125 INSTALLPROPERTY_DISKPROMPTW
,NULL
,&size
);
4126 if (rc
== ERROR_MORE_DATA
)
4128 prompt
= msi_alloc(size
* sizeof(WCHAR
));
4129 MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
4130 MSIINSTALLCONTEXT_USERMANAGED
, MSICODE_PRODUCT
,
4131 INSTALLPROPERTY_DISKPROMPTW
,prompt
,&size
);
4134 prompt
= strdupW(package
->PackagePath
);
4136 msg
= generate_error_string(package
,1302,1,prompt
);
4137 while(attrib
== INVALID_FILE_ATTRIBUTES
)
4139 rc
= MessageBoxW(NULL
,msg
,NULL
,MB_OKCANCEL
);
4142 rc
= ERROR_INSTALL_USEREXIT
;
4145 attrib
= GetFileAttributesW(package
->PackagePath
);
4151 return ERROR_SUCCESS
;
4156 static UINT
ACTION_RegisterUser(MSIPACKAGE
*package
)
4163 static const WCHAR szPropKeys
[][80] =
4165 {'P','r','o','d','u','c','t','I','D',0},
4166 {'U','S','E','R','N','A','M','E',0},
4167 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4171 static const WCHAR szRegKeys
[][80] =
4173 {'P','r','o','d','u','c','t','I','D',0},
4174 {'R','e','g','O','w','n','e','r',0},
4175 {'R','e','g','C','o','m','p','a','n','y',0},
4179 productid
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTIDW
);
4181 return ERROR_SUCCESS
;
4183 rc
= MSIREG_OpenUninstallKey(package
->ProductCode
,&hkey
,TRUE
);
4184 if (rc
!= ERROR_SUCCESS
)
4187 for( i
= 0; szPropKeys
[i
][0]; i
++ )
4189 buffer
= msi_dup_property( package
, szPropKeys
[i
] );
4190 msi_reg_set_val_str( hkey
, szRegKeys
[i
], buffer
);
4195 msi_free(productid
);
4198 /* FIXME: call ui_actiondata */
4200 return ERROR_SUCCESS
;
4204 static UINT
ACTION_ExecuteAction(MSIPACKAGE
*package
)
4208 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
4209 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
4214 static UINT
ITERATE_PublishComponent(MSIRECORD
*rec
, LPVOID param
)
4216 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4217 LPCWSTR compgroupid
=NULL
;
4218 LPCWSTR feature
=NULL
;
4219 LPCWSTR text
= NULL
;
4220 LPCWSTR qualifier
= NULL
;
4221 LPCWSTR component
= NULL
;
4222 LPWSTR advertise
= NULL
;
4223 LPWSTR output
= NULL
;
4225 UINT rc
= ERROR_SUCCESS
;
4230 component
= MSI_RecordGetString(rec
,3);
4231 comp
= get_loaded_component(package
,component
);
4233 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
) &&
4234 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_SOURCE
) &&
4235 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ADVERTISED
))
4237 TRACE("Skipping: Component %s not scheduled for install\n",
4238 debugstr_w(component
));
4240 return ERROR_SUCCESS
;
4243 compgroupid
= MSI_RecordGetString(rec
,1);
4244 qualifier
= MSI_RecordGetString(rec
,2);
4246 rc
= MSIREG_OpenUserComponentsKey(compgroupid
, &hkey
, TRUE
);
4247 if (rc
!= ERROR_SUCCESS
)
4250 text
= MSI_RecordGetString(rec
,4);
4251 feature
= MSI_RecordGetString(rec
,5);
4253 advertise
= create_component_advertise_string(package
, comp
, feature
);
4255 sz
= strlenW(advertise
);
4258 sz
+= lstrlenW(text
);
4261 sz
*= sizeof(WCHAR
);
4263 output
= msi_alloc_zero(sz
);
4264 strcpyW(output
,advertise
);
4265 msi_free(advertise
);
4268 strcatW(output
,text
);
4270 msi_reg_set_val_multi_str( hkey
, qualifier
, output
);
4277 uirow
= MSI_CreateRecord( 2 );
4278 MSI_RecordSetStringW( uirow
, 1, compgroupid
);
4279 MSI_RecordSetStringW( uirow
, 2, qualifier
);
4280 ui_actiondata( package
, szPublishComponents
, uirow
);
4281 msiobj_release( &uirow
->hdr
);
4282 /* FIXME: call ui_progress? */
4288 * At present I am ignorning the advertised components part of this and only
4289 * focusing on the qualified component sets
4291 static UINT
ACTION_PublishComponents(MSIPACKAGE
*package
)
4295 static const WCHAR ExecSeqQuery
[] =
4296 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4297 '`','P','u','b','l','i','s','h',
4298 'C','o','m','p','o','n','e','n','t','`',0};
4300 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4301 if (rc
!= ERROR_SUCCESS
)
4302 return ERROR_SUCCESS
;
4304 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_PublishComponent
, package
);
4305 msiobj_release(&view
->hdr
);
4310 static UINT
ITERATE_InstallService(MSIRECORD
*rec
, LPVOID param
)
4312 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4315 SC_HANDLE hscm
, service
= NULL
;
4316 LPCWSTR name
, disp
, comp
, depends
, pass
;
4317 LPCWSTR load_order
, serv_name
, key
;
4318 DWORD serv_type
, start_type
;
4321 static const WCHAR query
[] =
4322 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4323 '`','C','o','m','p','o','n','e','n','t','`',' ',
4324 'W','H','E','R','E',' ',
4325 '`','C','o','m','p','o','n','e','n','t','`',' ',
4326 '=','\'','%','s','\'',0};
4328 hscm
= OpenSCManagerW(NULL
, SERVICES_ACTIVE_DATABASEW
, GENERIC_WRITE
);
4331 ERR("Failed to open the SC Manager!\n");
4335 start_type
= MSI_RecordGetInteger(rec
, 5);
4336 if (start_type
== SERVICE_BOOT_START
|| start_type
== SERVICE_SYSTEM_START
)
4339 depends
= MSI_RecordGetString(rec
, 8);
4340 if (depends
&& *depends
)
4341 FIXME("Dependency list unhandled!\n");
4343 name
= MSI_RecordGetString(rec
, 2);
4344 disp
= MSI_RecordGetString(rec
, 3);
4345 serv_type
= MSI_RecordGetInteger(rec
, 4);
4346 err_control
= MSI_RecordGetInteger(rec
, 6);
4347 load_order
= MSI_RecordGetString(rec
, 7);
4348 serv_name
= MSI_RecordGetString(rec
, 9);
4349 pass
= MSI_RecordGetString(rec
, 10);
4350 comp
= MSI_RecordGetString(rec
, 12);
4352 /* fetch the service path */
4353 row
= MSI_QueryGetRecord(package
->db
, query
, comp
);
4356 ERR("Control query failed!\n");
4360 key
= MSI_RecordGetString(row
, 6);
4361 msiobj_release(&row
->hdr
);
4363 file
= get_loaded_file(package
, key
);
4366 ERR("Failed to load the service file\n");
4370 service
= CreateServiceW(hscm
, name
, disp
, GENERIC_ALL
, serv_type
,
4371 start_type
, err_control
, file
->TargetPath
,
4372 load_order
, NULL
, NULL
, serv_name
, pass
);
4375 if (GetLastError() != ERROR_SERVICE_EXISTS
)
4376 ERR("Failed to create service %s: %d\n", debugstr_w(name
), GetLastError());
4380 CloseServiceHandle(service
);
4381 CloseServiceHandle(hscm
);
4383 return ERROR_SUCCESS
;
4386 static UINT
ACTION_InstallServices( MSIPACKAGE
*package
)
4390 static const WCHAR ExecSeqQuery
[] =
4391 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4392 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4394 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4395 if (rc
!= ERROR_SUCCESS
)
4396 return ERROR_SUCCESS
;
4398 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallService
, package
);
4399 msiobj_release(&view
->hdr
);
4404 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4405 static LPCWSTR
*msi_service_args_to_vector(LPCWSTR name
, LPWSTR args
, DWORD
*numargs
)
4411 static const WCHAR separator
[] = {'[','~',']',0};
4414 sep_len
= sizeof(separator
) / sizeof(WCHAR
) - 1;
4419 vector
= msi_alloc(sizeof(LPWSTR
));
4427 vector
[*numargs
- 1] = p
;
4429 if ((q
= strstrW(p
, separator
)))
4433 vector
= msi_realloc(vector
, (*numargs
+ 1) * sizeof(LPWSTR
));
4444 static MSICOMPONENT
*msi_find_component( MSIPACKAGE
*package
, LPCWSTR component
)
4448 LIST_FOR_EACH_ENTRY(comp
, &package
->components
, MSICOMPONENT
, entry
)
4450 if (!lstrcmpW(comp
->Component
, component
))
4457 static UINT
ITERATE_StartService(MSIRECORD
*rec
, LPVOID param
)
4459 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4461 SC_HANDLE scm
, service
= NULL
;
4462 LPCWSTR name
, *vector
= NULL
;
4464 DWORD event
, numargs
;
4465 UINT r
= ERROR_FUNCTION_FAILED
;
4467 comp
= msi_find_component(package
, MSI_RecordGetString(rec
, 6));
4468 if (!comp
|| comp
->Action
== INSTALLSTATE_UNKNOWN
|| comp
->Action
== INSTALLSTATE_ABSENT
)
4469 return ERROR_SUCCESS
;
4471 name
= MSI_RecordGetString(rec
, 2);
4472 event
= MSI_RecordGetInteger(rec
, 3);
4473 args
= strdupW(MSI_RecordGetString(rec
, 4));
4475 if (!(event
& msidbServiceControlEventStart
))
4476 return ERROR_SUCCESS
;
4478 scm
= OpenSCManagerW(NULL
, NULL
, SC_MANAGER_CONNECT
);
4481 ERR("Failed to open the service control manager\n");
4485 service
= OpenServiceW(scm
, name
, SERVICE_START
);
4488 ERR("Failed to open service %s\n", debugstr_w(name
));
4492 vector
= msi_service_args_to_vector(name
, args
, &numargs
);
4494 if (!StartServiceW(service
, numargs
, vector
))
4496 ERR("Failed to start service %s\n", debugstr_w(name
));
4503 CloseServiceHandle(service
);
4504 CloseServiceHandle(scm
);
4511 static UINT
ACTION_StartServices( MSIPACKAGE
*package
)
4516 static const WCHAR query
[] = {
4517 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4518 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4520 rc
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
4521 if (rc
!= ERROR_SUCCESS
)
4522 return ERROR_SUCCESS
;
4524 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_StartService
, package
);
4525 msiobj_release(&view
->hdr
);
4530 static MSIFILE
*msi_find_file( MSIPACKAGE
*package
, LPCWSTR filename
)
4534 LIST_FOR_EACH_ENTRY(file
, &package
->files
, MSIFILE
, entry
)
4536 if (!lstrcmpW(file
->File
, filename
))
4543 static UINT
ITERATE_InstallODBCDriver( MSIRECORD
*rec
, LPVOID param
)
4545 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4546 LPWSTR driver
, driver_path
, ptr
;
4547 WCHAR outpath
[MAX_PATH
];
4548 MSIFILE
*driver_file
, *setup_file
;
4551 UINT r
= ERROR_SUCCESS
;
4553 static const WCHAR driver_fmt
[] = {
4554 'D','r','i','v','e','r','=','%','s',0};
4555 static const WCHAR setup_fmt
[] = {
4556 'S','e','t','u','p','=','%','s',0};
4557 static const WCHAR usage_fmt
[] = {
4558 'F','i','l','e','U','s','a','g','e','=','1',0};
4560 desc
= MSI_RecordGetString(rec
, 3);
4562 driver_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 4));
4563 setup_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 5));
4565 if (!driver_file
|| !setup_file
)
4567 ERR("ODBC Driver entry not found!\n");
4568 return ERROR_FUNCTION_FAILED
;
4571 len
= lstrlenW(desc
) + lstrlenW(driver_fmt
) + lstrlenW(driver_file
->FileName
) +
4572 lstrlenW(setup_fmt
) + lstrlenW(setup_file
->FileName
) +
4573 lstrlenW(usage_fmt
) + 1;
4574 driver
= msi_alloc(len
* sizeof(WCHAR
));
4576 return ERROR_OUTOFMEMORY
;
4579 lstrcpyW(ptr
, desc
);
4580 ptr
+= lstrlenW(ptr
) + 1;
4582 sprintfW(ptr
, driver_fmt
, driver_file
->FileName
);
4583 ptr
+= lstrlenW(ptr
) + 1;
4585 sprintfW(ptr
, setup_fmt
, setup_file
->FileName
);
4586 ptr
+= lstrlenW(ptr
) + 1;
4588 lstrcpyW(ptr
, usage_fmt
);
4589 ptr
+= lstrlenW(ptr
) + 1;
4592 driver_path
= strdupW(driver_file
->TargetPath
);
4593 ptr
= strrchrW(driver_path
, '\\');
4594 if (ptr
) *ptr
= '\0';
4596 if (!SQLInstallDriverExW(driver
, driver_path
, outpath
, MAX_PATH
,
4597 NULL
, ODBC_INSTALL_COMPLETE
, &usage
))
4599 ERR("Failed to install SQL driver!\n");
4600 r
= ERROR_FUNCTION_FAILED
;
4604 msi_free(driver_path
);
4609 static UINT
ITERATE_InstallODBCTranslator( MSIRECORD
*rec
, LPVOID param
)
4611 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4612 LPWSTR translator
, translator_path
, ptr
;
4613 WCHAR outpath
[MAX_PATH
];
4614 MSIFILE
*translator_file
, *setup_file
;
4617 UINT r
= ERROR_SUCCESS
;
4619 static const WCHAR translator_fmt
[] = {
4620 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4621 static const WCHAR setup_fmt
[] = {
4622 'S','e','t','u','p','=','%','s',0};
4624 desc
= MSI_RecordGetString(rec
, 3);
4626 translator_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 4));
4627 setup_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 5));
4629 if (!translator_file
|| !setup_file
)
4631 ERR("ODBC Translator entry not found!\n");
4632 return ERROR_FUNCTION_FAILED
;
4635 len
= lstrlenW(desc
) + lstrlenW(translator_fmt
) + lstrlenW(translator_file
->FileName
) +
4636 lstrlenW(setup_fmt
) + lstrlenW(setup_file
->FileName
) + 1;
4637 translator
= msi_alloc(len
* sizeof(WCHAR
));
4639 return ERROR_OUTOFMEMORY
;
4642 lstrcpyW(ptr
, desc
);
4643 ptr
+= lstrlenW(ptr
) + 1;
4645 sprintfW(ptr
, translator_fmt
, translator_file
->FileName
);
4646 ptr
+= lstrlenW(ptr
) + 1;
4648 sprintfW(ptr
, setup_fmt
, setup_file
->FileName
);
4649 ptr
+= lstrlenW(ptr
) + 1;
4652 translator_path
= strdupW(translator_file
->TargetPath
);
4653 ptr
= strrchrW(translator_path
, '\\');
4654 if (ptr
) *ptr
= '\0';
4656 if (!SQLInstallTranslatorExW(translator
, translator_path
, outpath
, MAX_PATH
,
4657 NULL
, ODBC_INSTALL_COMPLETE
, &usage
))
4659 ERR("Failed to install SQL translator!\n");
4660 r
= ERROR_FUNCTION_FAILED
;
4663 msi_free(translator
);
4664 msi_free(translator_path
);
4669 static UINT
ITERATE_InstallODBCDataSource( MSIRECORD
*rec
, LPVOID param
)
4672 LPCWSTR desc
, driver
;
4673 WORD request
= ODBC_ADD_SYS_DSN
;
4676 UINT r
= ERROR_SUCCESS
;
4678 static const WCHAR attrs_fmt
[] = {
4679 'D','S','N','=','%','s',0 };
4681 desc
= MSI_RecordGetString(rec
, 3);
4682 driver
= MSI_RecordGetString(rec
, 4);
4683 registration
= MSI_RecordGetInteger(rec
, 5);
4685 if (registration
== msidbODBCDataSourceRegistrationPerMachine
) request
= ODBC_ADD_SYS_DSN
;
4686 else if (registration
== msidbODBCDataSourceRegistrationPerUser
) request
= ODBC_ADD_DSN
;
4688 len
= lstrlenW(attrs_fmt
) + lstrlenW(desc
) + 1 + 1;
4689 attrs
= msi_alloc(len
* sizeof(WCHAR
));
4691 return ERROR_OUTOFMEMORY
;
4693 sprintfW(attrs
, attrs_fmt
, desc
);
4694 attrs
[len
- 1] = '\0';
4696 if (!SQLConfigDataSourceW(NULL
, request
, driver
, attrs
))
4698 ERR("Failed to install SQL data source!\n");
4699 r
= ERROR_FUNCTION_FAILED
;
4707 static UINT
ACTION_InstallODBC( MSIPACKAGE
*package
)
4712 static const WCHAR driver_query
[] = {
4713 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4714 'O','D','B','C','D','r','i','v','e','r',0 };
4716 static const WCHAR translator_query
[] = {
4717 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4718 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4720 static const WCHAR source_query
[] = {
4721 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4722 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4724 rc
= MSI_DatabaseOpenViewW(package
->db
, driver_query
, &view
);
4725 if (rc
!= ERROR_SUCCESS
)
4726 return ERROR_SUCCESS
;
4728 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCDriver
, package
);
4729 msiobj_release(&view
->hdr
);
4731 rc
= MSI_DatabaseOpenViewW(package
->db
, translator_query
, &view
);
4732 if (rc
!= ERROR_SUCCESS
)
4733 return ERROR_SUCCESS
;
4735 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCTranslator
, package
);
4736 msiobj_release(&view
->hdr
);
4738 rc
= MSI_DatabaseOpenViewW(package
->db
, source_query
, &view
);
4739 if (rc
!= ERROR_SUCCESS
)
4740 return ERROR_SUCCESS
;
4742 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCDataSource
, package
);
4743 msiobj_release(&view
->hdr
);
4748 #define ENV_ACT_SETALWAYS 0x1
4749 #define ENV_ACT_SETABSENT 0x2
4750 #define ENV_ACT_REMOVE 0x4
4751 #define ENV_ACT_REMOVEMATCH 0x8
4753 #define ENV_MOD_MACHINE 0x20000000
4754 #define ENV_MOD_APPEND 0x40000000
4755 #define ENV_MOD_PREFIX 0x80000000
4756 #define ENV_MOD_MASK 0xC0000000
4758 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4760 static LONG
env_set_flags( LPCWSTR
*name
, LPWSTR
*value
, DWORD
*flags
)
4762 LPCWSTR cptr
= *name
;
4763 LPWSTR ptr
= *value
;
4765 static const WCHAR prefix
[] = {'[','~',']',0};
4771 *flags
|= ENV_ACT_SETALWAYS
;
4772 else if (*cptr
== '+')
4773 *flags
|= ENV_ACT_SETABSENT
;
4774 else if (*cptr
== '-')
4775 *flags
|= ENV_ACT_REMOVE
;
4776 else if (*cptr
== '!')
4777 *flags
|= ENV_ACT_REMOVEMATCH
;
4778 else if (*cptr
== '*')
4779 *flags
|= ENV_MOD_MACHINE
;
4789 ERR("Missing environment variable\n");
4790 return ERROR_FUNCTION_FAILED
;
4793 if (!strncmpW(ptr
, prefix
, lstrlenW(prefix
)))
4795 *flags
|= ENV_MOD_PREFIX
;
4796 *value
+= lstrlenW(prefix
);
4800 ptr
+= lstrlenW(ptr
) - lstrlenW(prefix
) - 1;
4801 if (!lstrcmpW(ptr
, prefix
))
4803 *flags
|= ENV_MOD_APPEND
;
4809 check_flag_combo(*flags
, ENV_ACT_SETALWAYS
| ENV_ACT_SETABSENT
) ||
4810 check_flag_combo(*flags
, ENV_ACT_REMOVEMATCH
| ENV_ACT_SETABSENT
) ||
4811 check_flag_combo(*flags
, ENV_ACT_REMOVEMATCH
| ENV_ACT_SETALWAYS
) ||
4812 check_flag_combo(*flags
, ENV_ACT_SETABSENT
| ENV_MOD_MASK
))
4814 ERR("Invalid flags: %08x\n", *flags
);
4815 return ERROR_FUNCTION_FAILED
;
4818 return ERROR_SUCCESS
;
4821 static UINT
ITERATE_WriteEnvironmentString( MSIRECORD
*rec
, LPVOID param
)
4823 MSIPACKAGE
*package
= param
;
4824 LPCWSTR name
, value
, comp
;
4825 LPWSTR data
= NULL
, newval
= NULL
;
4826 LPWSTR deformatted
, ptr
;
4827 DWORD flags
, type
, size
;
4829 HKEY env
= NULL
, root
= HKEY_CURRENT_USER
;
4831 static const WCHAR environment
[] =
4832 {'S','y','s','t','e','m','\\',
4833 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4834 'C','o','n','t','r','o','l','\\',
4835 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4836 'E','n','v','i','r','o','n','m','e','n','t',0};
4837 static const WCHAR semicolon
[] = {';',0};
4839 name
= MSI_RecordGetString(rec
, 2);
4840 value
= MSI_RecordGetString(rec
, 3);
4841 comp
= MSI_RecordGetString(rec
, 4);
4843 deformat_string(package
, value
, &deformatted
);
4845 return ERROR_OUTOFMEMORY
;
4847 res
= env_set_flags(&name
, &deformatted
, &flags
);
4848 if (res
!= ERROR_SUCCESS
)
4851 value
= deformatted
;
4853 if (flags
& ENV_MOD_MACHINE
)
4854 root
= HKEY_LOCAL_MACHINE
;
4856 res
= RegOpenKeyExW(root
, environment
, 0, KEY_ALL_ACCESS
, &env
);
4857 if (res
!= ERROR_SUCCESS
)
4860 if (flags
& ENV_ACT_REMOVE
)
4861 FIXME("Not removing environment variable on uninstall!\n");
4864 res
= RegQueryValueExW(env
, name
, NULL
, &type
, NULL
, &size
);
4865 if ((res
!= ERROR_SUCCESS
&& res
!= ERROR_FILE_NOT_FOUND
) ||
4866 (res
== ERROR_SUCCESS
&& type
!= REG_SZ
))
4869 if (res
!= ERROR_FILE_NOT_FOUND
)
4871 if (flags
& ENV_ACT_SETABSENT
)
4873 res
= ERROR_SUCCESS
;
4877 data
= msi_alloc(size
);
4881 return ERROR_OUTOFMEMORY
;
4884 res
= RegQueryValueExW(env
, name
, NULL
, &type
, (LPVOID
)data
, &size
);
4885 if (res
!= ERROR_SUCCESS
)
4888 if (flags
& ENV_ACT_REMOVEMATCH
&& (!value
|| !lstrcmpW(data
, value
)))
4890 res
= RegDeleteKeyW(env
, name
);
4894 size
= (lstrlenW(value
) + 1 + size
) * sizeof(WCHAR
);
4895 newval
= msi_alloc(size
);
4899 res
= ERROR_OUTOFMEMORY
;
4903 if (!(flags
& ENV_MOD_MASK
))
4904 lstrcpyW(newval
, value
);
4907 if (flags
& ENV_MOD_PREFIX
)
4909 lstrcpyW(newval
, value
);
4910 lstrcatW(newval
, semicolon
);
4911 ptr
= newval
+ lstrlenW(value
) + 1;
4914 lstrcpyW(ptr
, data
);
4916 if (flags
& ENV_MOD_APPEND
)
4918 lstrcatW(newval
, semicolon
);
4919 lstrcatW(newval
, value
);
4925 size
= (lstrlenW(value
) + 1) * sizeof(WCHAR
);
4926 newval
= msi_alloc(size
);
4929 res
= ERROR_OUTOFMEMORY
;
4933 lstrcpyW(newval
, value
);
4936 TRACE("setting %s to %s\n", debugstr_w(name
), debugstr_w(newval
));
4937 res
= RegSetValueExW(env
, name
, 0, type
, (LPVOID
)newval
, size
);
4940 if (env
) RegCloseKey(env
);
4941 msi_free(deformatted
);
4947 static UINT
ACTION_WriteEnvironmentStrings( MSIPACKAGE
*package
)
4951 static const WCHAR ExecSeqQuery
[] =
4952 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4953 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
4954 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4955 if (rc
!= ERROR_SUCCESS
)
4956 return ERROR_SUCCESS
;
4958 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteEnvironmentString
, package
);
4959 msiobj_release(&view
->hdr
);
4964 static UINT
msi_unimplemented_action_stub( MSIPACKAGE
*package
,
4965 LPCSTR action
, LPCWSTR table
)
4967 static const WCHAR query
[] = {
4968 'S','E','L','E','C','T',' ','*',' ',
4969 'F','R','O','M',' ','`','%','s','`',0 };
4970 MSIQUERY
*view
= NULL
;
4974 r
= MSI_OpenQuery( package
->db
, &view
, query
, table
);
4975 if (r
== ERROR_SUCCESS
)
4977 r
= MSI_IterateRecords(view
, &count
, NULL
, package
);
4978 msiobj_release(&view
->hdr
);
4982 FIXME("%s -> %u ignored %s table values\n",
4983 action
, count
, debugstr_w(table
));
4985 return ERROR_SUCCESS
;
4988 static UINT
ACTION_AllocateRegistrySpace( MSIPACKAGE
*package
)
4990 TRACE("%p\n", package
);
4991 return ERROR_SUCCESS
;
4994 static UINT
ACTION_RemoveIniValues( MSIPACKAGE
*package
)
4996 static const WCHAR table
[] =
4997 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
4998 return msi_unimplemented_action_stub( package
, "RemoveIniValues", table
);
5001 static UINT
ACTION_MoveFiles( MSIPACKAGE
*package
)
5003 static const WCHAR table
[] = { 'M','o','v','e','F','i','l','e',0 };
5004 return msi_unimplemented_action_stub( package
, "MoveFiles", table
);
5007 static UINT
ACTION_PatchFiles( MSIPACKAGE
*package
)
5009 static const WCHAR table
[] = { 'P','a','t','c','h',0 };
5010 return msi_unimplemented_action_stub( package
, "PatchFiles", table
);
5013 static UINT
ACTION_BindImage( MSIPACKAGE
*package
)
5015 static const WCHAR table
[] = { 'B','i','n','d','I','m','a','g','e',0 };
5016 return msi_unimplemented_action_stub( package
, "BindImage", table
);
5019 static UINT
ACTION_IsolateComponents( MSIPACKAGE
*package
)
5021 static const WCHAR table
[] = {
5022 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5023 return msi_unimplemented_action_stub( package
, "IsolateComponents", table
);
5026 static UINT
ACTION_MigrateFeatureStates( MSIPACKAGE
*package
)
5028 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
5029 return msi_unimplemented_action_stub( package
, "MigrateFeatureStates", table
);
5032 static UINT
ACTION_SelfUnregModules( MSIPACKAGE
*package
)
5034 static const WCHAR table
[] = { 'S','e','l','f','R','e','g',0 };
5035 return msi_unimplemented_action_stub( package
, "SelfUnregModules", table
);
5038 static UINT
ACTION_StopServices( MSIPACKAGE
*package
)
5040 static const WCHAR table
[] = {
5041 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5042 return msi_unimplemented_action_stub( package
, "StopServices", table
);
5045 static UINT
ACTION_DeleteServices( MSIPACKAGE
*package
)
5047 static const WCHAR table
[] = {
5048 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5049 return msi_unimplemented_action_stub( package
, "DeleteServices", table
);
5051 static UINT
ACTION_ValidateProductID( MSIPACKAGE
*package
)
5053 static const WCHAR table
[] = {
5054 'P','r','o','d','u','c','t','I','D',0 };
5055 return msi_unimplemented_action_stub( package
, "ValidateProductID", table
);
5058 static UINT
ACTION_RemoveEnvironmentStrings( MSIPACKAGE
*package
)
5060 static const WCHAR table
[] = {
5061 'E','n','v','i','r','o','n','m','e','n','t',0 };
5062 return msi_unimplemented_action_stub( package
, "RemoveEnvironmentStrings", table
);
5065 static UINT
ACTION_MsiPublishAssemblies( MSIPACKAGE
*package
)
5067 static const WCHAR table
[] = {
5068 'M','s','i','A','s','s','e','m','b','l','y',0 };
5069 return msi_unimplemented_action_stub( package
, "MsiPublishAssemblies", table
);
5072 static UINT
ACTION_MsiUnpublishAssemblies( MSIPACKAGE
*package
)
5074 static const WCHAR table
[] = {
5075 'M','s','i','A','s','s','e','m','b','l','y',0 };
5076 return msi_unimplemented_action_stub( package
, "MsiUnpublishAssemblies", table
);
5079 static UINT
ACTION_UnregisterFonts( MSIPACKAGE
*package
)
5081 static const WCHAR table
[] = { 'F','o','n','t',0 };
5082 return msi_unimplemented_action_stub( package
, "UnregisterFonts", table
);
5085 static UINT
ACTION_CCPSearch( MSIPACKAGE
*package
)
5087 static const WCHAR table
[] = { 'C','C','P','S','e','a','r','c','h',0 };
5088 return msi_unimplemented_action_stub( package
, "CCPSearch", table
);
5091 static UINT
ACTION_RMCCPSearch( MSIPACKAGE
*package
)
5093 static const WCHAR table
[] = { 'C','C','P','S','e','a','r','c','h',0 };
5094 return msi_unimplemented_action_stub( package
, "RMCCPSearch", table
);
5097 static UINT
ACTION_RegisterComPlus( MSIPACKAGE
*package
)
5099 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
5100 return msi_unimplemented_action_stub( package
, "RegisterComPlus", table
);
5103 static UINT
ACTION_UnregisterComPlus( MSIPACKAGE
*package
)
5105 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
5106 return msi_unimplemented_action_stub( package
, "UnregisterComPlus", table
);
5109 static UINT
ACTION_InstallSFPCatalogFile( MSIPACKAGE
*package
)
5111 static const WCHAR table
[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5112 return msi_unimplemented_action_stub( package
, "InstallSFPCatalogFile", table
);
5115 static UINT
ACTION_RemoveDuplicateFiles( MSIPACKAGE
*package
)
5117 static const WCHAR table
[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5118 return msi_unimplemented_action_stub( package
, "RemoveDuplicateFiles", table
);
5121 static UINT
ACTION_RemoveExistingProducts( MSIPACKAGE
*package
)
5123 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
5124 return msi_unimplemented_action_stub( package
, "RemoveExistingProducts", table
);
5127 static UINT
ACTION_RemoveFolders( MSIPACKAGE
*package
)
5129 static const WCHAR table
[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5130 return msi_unimplemented_action_stub( package
, "RemoveFolders", table
);
5133 static UINT
ACTION_RemoveODBC( MSIPACKAGE
*package
)
5135 static const WCHAR table
[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5136 return msi_unimplemented_action_stub( package
, "RemoveODBC", table
);
5139 static UINT
ACTION_RemoveRegistryValues( MSIPACKAGE
*package
)
5141 static const WCHAR table
[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5142 return msi_unimplemented_action_stub( package
, "RemoveRegistryValues", table
);
5145 static UINT
ACTION_RemoveShortcuts( MSIPACKAGE
*package
)
5147 static const WCHAR table
[] = { 'S','h','o','r','t','c','u','t',0 };
5148 return msi_unimplemented_action_stub( package
, "RemoveShortcuts", table
);
5151 static UINT
ACTION_UnpublishComponents( MSIPACKAGE
*package
)
5153 static const WCHAR table
[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5154 return msi_unimplemented_action_stub( package
, "UnpublishComponents", table
);
5157 static UINT
ACTION_UnregisterClassInfo( MSIPACKAGE
*package
)
5159 static const WCHAR table
[] = { 'A','p','p','I','d',0 };
5160 return msi_unimplemented_action_stub( package
, "UnregisterClassInfo", table
);
5163 static UINT
ACTION_UnregisterExtensionInfo( MSIPACKAGE
*package
)
5165 static const WCHAR table
[] = { 'E','x','t','e','n','s','i','o','n',0 };
5166 return msi_unimplemented_action_stub( package
, "UnregisterExtensionInfo", table
);
5169 static UINT
ACTION_UnregisterMIMEInfo( MSIPACKAGE
*package
)
5171 static const WCHAR table
[] = { 'M','I','M','E',0 };
5172 return msi_unimplemented_action_stub( package
, "UnregisterMIMEInfo", table
);
5175 static UINT
ACTION_UnregisterProgIdInfo( MSIPACKAGE
*package
)
5177 static const WCHAR table
[] = { 'P','r','o','g','I','d',0 };
5178 return msi_unimplemented_action_stub( package
, "UnregisterProgIdInfo", table
);
5181 static UINT
ACTION_UnregisterTypeLibraries( MSIPACKAGE
*package
)
5183 static const WCHAR table
[] = { 'T','y','p','e','L','i','b',0 };
5184 return msi_unimplemented_action_stub( package
, "UnregisterTypeLibraries", table
);
5187 static const struct _actions StandardActions
[] = {
5188 { szAllocateRegistrySpace
, ACTION_AllocateRegistrySpace
},
5189 { szAppSearch
, ACTION_AppSearch
},
5190 { szBindImage
, ACTION_BindImage
},
5191 { szCCPSearch
, ACTION_CCPSearch
},
5192 { szCostFinalize
, ACTION_CostFinalize
},
5193 { szCostInitialize
, ACTION_CostInitialize
},
5194 { szCreateFolders
, ACTION_CreateFolders
},
5195 { szCreateShortcuts
, ACTION_CreateShortcuts
},
5196 { szDeleteServices
, ACTION_DeleteServices
},
5197 { szDisableRollback
, NULL
},
5198 { szDuplicateFiles
, ACTION_DuplicateFiles
},
5199 { szExecuteAction
, ACTION_ExecuteAction
},
5200 { szFileCost
, ACTION_FileCost
},
5201 { szFindRelatedProducts
, ACTION_FindRelatedProducts
},
5202 { szForceReboot
, ACTION_ForceReboot
},
5203 { szInstallAdminPackage
, NULL
},
5204 { szInstallExecute
, ACTION_InstallExecute
},
5205 { szInstallExecuteAgain
, ACTION_InstallExecute
},
5206 { szInstallFiles
, ACTION_InstallFiles
},
5207 { szInstallFinalize
, ACTION_InstallFinalize
},
5208 { szInstallInitialize
, ACTION_InstallInitialize
},
5209 { szInstallSFPCatalogFile
, ACTION_InstallSFPCatalogFile
},
5210 { szInstallValidate
, ACTION_InstallValidate
},
5211 { szIsolateComponents
, ACTION_IsolateComponents
},
5212 { szLaunchConditions
, ACTION_LaunchConditions
},
5213 { szMigrateFeatureStates
, ACTION_MigrateFeatureStates
},
5214 { szMoveFiles
, ACTION_MoveFiles
},
5215 { szMsiPublishAssemblies
, ACTION_MsiPublishAssemblies
},
5216 { szMsiUnpublishAssemblies
, ACTION_MsiUnpublishAssemblies
},
5217 { szInstallODBC
, ACTION_InstallODBC
},
5218 { szInstallServices
, ACTION_InstallServices
},
5219 { szPatchFiles
, ACTION_PatchFiles
},
5220 { szProcessComponents
, ACTION_ProcessComponents
},
5221 { szPublishComponents
, ACTION_PublishComponents
},
5222 { szPublishFeatures
, ACTION_PublishFeatures
},
5223 { szPublishProduct
, ACTION_PublishProduct
},
5224 { szRegisterClassInfo
, ACTION_RegisterClassInfo
},
5225 { szRegisterComPlus
, ACTION_RegisterComPlus
},
5226 { szRegisterExtensionInfo
, ACTION_RegisterExtensionInfo
},
5227 { szRegisterFonts
, ACTION_RegisterFonts
},
5228 { szRegisterMIMEInfo
, ACTION_RegisterMIMEInfo
},
5229 { szRegisterProduct
, ACTION_RegisterProduct
},
5230 { szRegisterProgIdInfo
, ACTION_RegisterProgIdInfo
},
5231 { szRegisterTypeLibraries
, ACTION_RegisterTypeLibraries
},
5232 { szRegisterUser
, ACTION_RegisterUser
},
5233 { szRemoveDuplicateFiles
, ACTION_RemoveDuplicateFiles
},
5234 { szRemoveEnvironmentStrings
, ACTION_RemoveEnvironmentStrings
},
5235 { szRemoveExistingProducts
, ACTION_RemoveExistingProducts
},
5236 { szRemoveFiles
, ACTION_RemoveFiles
},
5237 { szRemoveFolders
, ACTION_RemoveFolders
},
5238 { szRemoveIniValues
, ACTION_RemoveIniValues
},
5239 { szRemoveODBC
, ACTION_RemoveODBC
},
5240 { szRemoveRegistryValues
, ACTION_RemoveRegistryValues
},
5241 { szRemoveShortcuts
, ACTION_RemoveShortcuts
},
5242 { szResolveSource
, ACTION_ResolveSource
},
5243 { szRMCCPSearch
, ACTION_RMCCPSearch
},
5244 { szScheduleReboot
, NULL
},
5245 { szSelfRegModules
, ACTION_SelfRegModules
},
5246 { szSelfUnregModules
, ACTION_SelfUnregModules
},
5247 { szSetODBCFolders
, NULL
},
5248 { szStartServices
, ACTION_StartServices
},
5249 { szStopServices
, ACTION_StopServices
},
5250 { szUnpublishComponents
, ACTION_UnpublishComponents
},
5251 { szUnpublishFeatures
, ACTION_UnpublishFeatures
},
5252 { szUnregisterClassInfo
, ACTION_UnregisterClassInfo
},
5253 { szUnregisterComPlus
, ACTION_UnregisterComPlus
},
5254 { szUnregisterExtensionInfo
, ACTION_UnregisterExtensionInfo
},
5255 { szUnregisterFonts
, ACTION_UnregisterFonts
},
5256 { szUnregisterMIMEInfo
, ACTION_UnregisterMIMEInfo
},
5257 { szUnregisterProgIdInfo
, ACTION_UnregisterProgIdInfo
},
5258 { szUnregisterTypeLibraries
, ACTION_UnregisterTypeLibraries
},
5259 { szValidateProductID
, ACTION_ValidateProductID
},
5260 { szWriteEnvironmentStrings
, ACTION_WriteEnvironmentStrings
},
5261 { szWriteIniValues
, ACTION_WriteIniValues
},
5262 { szWriteRegistryValues
, ACTION_WriteRegistryValues
},