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
31 #include "wine/debug.h"
36 #include "wine/unicode.h"
39 #define REG_PROGRESS_VALUE 13200
40 #define COMPONENT_PROGRESS_VALUE 24000
42 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
47 static UINT
ACTION_ProcessExecSequence(MSIPACKAGE
*package
, BOOL UIran
);
48 static UINT
ACTION_ProcessUISequence(MSIPACKAGE
*package
);
49 static UINT
ACTION_PerformActionSequence(MSIPACKAGE
*package
, UINT seq
, BOOL UI
);
52 * consts and values used
54 static const WCHAR c_colon
[] = {'C',':','\\',0};
56 static const WCHAR szCreateFolders
[] =
57 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
58 static const WCHAR szCostFinalize
[] =
59 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
60 const WCHAR szInstallFiles
[] =
61 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
62 const WCHAR szDuplicateFiles
[] =
63 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
64 static const WCHAR szWriteRegistryValues
[] =
65 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
66 'V','a','l','u','e','s',0};
67 static const WCHAR szCostInitialize
[] =
68 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
69 static const WCHAR szFileCost
[] =
70 {'F','i','l','e','C','o','s','t',0};
71 static const WCHAR szInstallInitialize
[] =
72 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
73 static const WCHAR szInstallValidate
[] =
74 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
75 static const WCHAR szLaunchConditions
[] =
76 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
77 static const WCHAR szProcessComponents
[] =
78 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
79 static const WCHAR szRegisterTypeLibraries
[] =
80 {'R','e','g','i','s','t','e','r','T','y','p','e',
81 'L','i','b','r','a','r','i','e','s',0};
82 const WCHAR szRegisterClassInfo
[] =
83 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
84 const WCHAR szRegisterProgIdInfo
[] =
85 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
86 static const WCHAR szCreateShortcuts
[] =
87 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
88 static const WCHAR szPublishProduct
[] =
89 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
90 static const WCHAR szWriteIniValues
[] =
91 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
92 static const WCHAR szSelfRegModules
[] =
93 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
94 static const WCHAR szPublishFeatures
[] =
95 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
96 static const WCHAR szRegisterProduct
[] =
97 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
98 static const WCHAR szInstallExecute
[] =
99 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
100 static const WCHAR szInstallExecuteAgain
[] =
101 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
102 'A','g','a','i','n',0};
103 static const WCHAR szInstallFinalize
[] =
104 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
105 static const WCHAR szForceReboot
[] =
106 {'F','o','r','c','e','R','e','b','o','o','t',0};
107 static const WCHAR szResolveSource
[] =
108 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
109 static const WCHAR szAppSearch
[] =
110 {'A','p','p','S','e','a','r','c','h',0};
111 static const WCHAR szAllocateRegistrySpace
[] =
112 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
113 'S','p','a','c','e',0};
114 static const WCHAR szBindImage
[] =
115 {'B','i','n','d','I','m','a','g','e',0};
116 static const WCHAR szCCPSearch
[] =
117 {'C','C','P','S','e','a','r','c','h',0};
118 static const WCHAR szDeleteServices
[] =
119 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
120 static const WCHAR szDisableRollback
[] =
121 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
122 static const WCHAR szExecuteAction
[] =
123 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
124 const WCHAR szFindRelatedProducts
[] =
125 {'F','i','n','d','R','e','l','a','t','e','d',
126 'P','r','o','d','u','c','t','s',0};
127 static const WCHAR szInstallAdminPackage
[] =
128 {'I','n','s','t','a','l','l','A','d','m','i','n',
129 'P','a','c','k','a','g','e',0};
130 static const WCHAR szInstallSFPCatalogFile
[] =
131 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
133 static const WCHAR szIsolateComponents
[] =
134 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
135 const WCHAR szMigrateFeatureStates
[] =
136 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
137 'S','t','a','t','e','s',0};
138 const WCHAR szMoveFiles
[] =
139 {'M','o','v','e','F','i','l','e','s',0};
140 static const WCHAR szMsiPublishAssemblies
[] =
141 {'M','s','i','P','u','b','l','i','s','h',
142 'A','s','s','e','m','b','l','i','e','s',0};
143 static const WCHAR szMsiUnpublishAssemblies
[] =
144 {'M','s','i','U','n','p','u','b','l','i','s','h',
145 'A','s','s','e','m','b','l','i','e','s',0};
146 static const WCHAR szInstallODBC
[] =
147 {'I','n','s','t','a','l','l','O','D','B','C',0};
148 static const WCHAR szInstallServices
[] =
149 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
150 const WCHAR szPatchFiles
[] =
151 {'P','a','t','c','h','F','i','l','e','s',0};
152 static const WCHAR szPublishComponents
[] =
153 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
154 static const WCHAR szRegisterComPlus
[] =
155 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
156 const WCHAR szRegisterExtensionInfo
[] =
157 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
159 static const WCHAR szRegisterFonts
[] =
160 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
161 const WCHAR szRegisterMIMEInfo
[] =
162 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
163 static const WCHAR szRegisterUser
[] =
164 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
165 const WCHAR szRemoveDuplicateFiles
[] =
166 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
167 'F','i','l','e','s',0};
168 static const WCHAR szRemoveEnvironmentStrings
[] =
169 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
170 'S','t','r','i','n','g','s',0};
171 const WCHAR szRemoveExistingProducts
[] =
172 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
173 'P','r','o','d','u','c','t','s',0};
174 const WCHAR szRemoveFiles
[] =
175 {'R','e','m','o','v','e','F','i','l','e','s',0};
176 static const WCHAR szRemoveFolders
[] =
177 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
178 static const WCHAR szRemoveIniValues
[] =
179 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
180 static const WCHAR szRemoveODBC
[] =
181 {'R','e','m','o','v','e','O','D','B','C',0};
182 static const WCHAR szRemoveRegistryValues
[] =
183 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
184 'V','a','l','u','e','s',0};
185 static const WCHAR szRemoveShortcuts
[] =
186 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
187 static const WCHAR szRMCCPSearch
[] =
188 {'R','M','C','C','P','S','e','a','r','c','h',0};
189 static const WCHAR szScheduleReboot
[] =
190 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
191 static const WCHAR szSelfUnregModules
[] =
192 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
193 static const WCHAR szSetODBCFolders
[] =
194 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
195 static const WCHAR szStartServices
[] =
196 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
197 static const WCHAR szStopServices
[] =
198 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
199 static const WCHAR szUnpublishComponents
[] =
200 {'U','n','p','u','b','l','i','s','h',
201 'C','o','m','p','o','n','e','n','t','s',0};
202 static const WCHAR szUnpublishFeatures
[] =
203 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
204 const WCHAR szUnregisterClassInfo
[] =
205 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
207 static const WCHAR szUnregisterComPlus
[] =
208 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
209 const WCHAR szUnregisterExtensionInfo
[] =
210 {'U','n','r','e','g','i','s','t','e','r',
211 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
212 static const WCHAR szUnregisterFonts
[] =
213 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
214 const WCHAR szUnregisterMIMEInfo
[] =
215 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
216 const WCHAR szUnregisterProgIdInfo
[] =
217 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
219 static const WCHAR szUnregisterTypeLibraries
[] =
220 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
221 'L','i','b','r','a','r','i','e','s',0};
222 static const WCHAR szValidateProductID
[] =
223 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
224 static const WCHAR szWriteEnvironmentStrings
[] =
225 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
226 'S','t','r','i','n','g','s',0};
228 /* action handlers */
229 typedef UINT (*STANDARDACTIONHANDLER
)(MSIPACKAGE
*);
233 STANDARDACTIONHANDLER handler
;
236 static const struct _actions StandardActions
[];
239 /********************************************************
241 ********************************************************/
243 static void ui_actionstart(MSIPACKAGE
*package
, LPCWSTR action
)
245 static const WCHAR Query_t
[] =
246 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
247 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
248 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
249 ' ','\'','%','s','\'',0};
252 row
= MSI_QueryGetRecord( package
->db
, Query_t
, action
);
255 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONSTART
, row
);
256 msiobj_release(&row
->hdr
);
259 static void ui_actioninfo(MSIPACKAGE
*package
, LPCWSTR action
, BOOL start
,
263 static const WCHAR template_s
[]=
264 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
266 static const WCHAR template_e
[]=
267 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
268 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
270 static const WCHAR format
[] =
271 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
275 GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, NULL
, format
, timet
, 0x100);
277 sprintfW(message
,template_s
,timet
,action
);
279 sprintfW(message
,template_e
,timet
,action
,rc
);
281 row
= MSI_CreateRecord(1);
282 MSI_RecordSetStringW(row
,1,message
);
284 MSI_ProcessMessage(package
, INSTALLMESSAGE_INFO
, row
);
285 msiobj_release(&row
->hdr
);
288 UINT
msi_parse_command_line( MSIPACKAGE
*package
, LPCWSTR szCommandLine
)
293 LPWSTR prop
= NULL
, val
= NULL
;
296 return ERROR_SUCCESS
;
308 TRACE("Looking at %s\n",debugstr_w(ptr
));
310 ptr2
= strchrW(ptr
,'=');
313 ERR("command line contains unknown string : %s\n", debugstr_w(ptr
));
320 prop
= msi_alloc((len
+1)*sizeof(WCHAR
));
321 memcpy(prop
,ptr
,len
*sizeof(WCHAR
));
327 while (*ptr
&& (quote
|| (!quote
&& *ptr
!=' ')))
340 val
= msi_alloc((len
+1)*sizeof(WCHAR
));
341 memcpy(val
,ptr2
,len
*sizeof(WCHAR
));
344 if (lstrlenW(prop
) > 0)
346 TRACE("Found commandline property (%s) = (%s)\n",
347 debugstr_w(prop
), debugstr_w(val
));
348 MSI_SetPropertyW(package
,prop
,val
);
354 return ERROR_SUCCESS
;
358 static LPWSTR
* msi_split_string( LPCWSTR str
, WCHAR sep
)
361 LPWSTR p
, *ret
= NULL
;
367 /* count the number of substrings */
368 for ( pc
= str
, count
= 0; pc
; count
++ )
370 pc
= strchrW( pc
, sep
);
375 /* allocate space for an array of substring pointers and the substrings */
376 ret
= msi_alloc( (count
+1) * sizeof (LPWSTR
) +
377 (lstrlenW(str
)+1) * sizeof(WCHAR
) );
381 /* copy the string and set the pointers */
382 p
= (LPWSTR
) &ret
[count
+1];
384 for( count
= 0; (ret
[count
] = p
); count
++ )
386 p
= strchrW( p
, sep
);
394 static UINT
msi_check_transform_applicable( MSIPACKAGE
*package
, IStorage
*patch
)
396 WCHAR szProductCode
[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
397 LPWSTR prod_code
, patch_product
;
400 prod_code
= msi_dup_property( package
, szProductCode
);
401 patch_product
= msi_get_suminfo_product( patch
);
403 TRACE("db = %s patch = %s\n", debugstr_w(prod_code
), debugstr_w(patch_product
));
405 if ( strstrW( patch_product
, prod_code
) )
408 ret
= ERROR_FUNCTION_FAILED
;
410 msi_free( patch_product
);
411 msi_free( prod_code
);
416 static UINT
msi_apply_substorage_transform( MSIPACKAGE
*package
,
417 MSIDATABASE
*patch_db
, LPCWSTR name
)
419 UINT ret
= ERROR_FUNCTION_FAILED
;
420 IStorage
*stg
= NULL
;
423 TRACE("%p %s\n", package
, debugstr_w(name
) );
427 ERR("expected a colon in %s\n", debugstr_w(name
));
428 return ERROR_FUNCTION_FAILED
;
431 r
= IStorage_OpenStorage( patch_db
->storage
, name
, NULL
, STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
434 ret
= msi_check_transform_applicable( package
, stg
);
435 if (ret
== ERROR_SUCCESS
)
436 msi_table_apply_transform( package
->db
, stg
);
438 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name
));
439 IStorage_Release( stg
);
442 ERR("failed to open substorage %s\n", debugstr_w(name
));
444 return ERROR_SUCCESS
;
447 static UINT
msi_check_patch_applicable( MSIPACKAGE
*package
, MSISUMMARYINFO
*si
)
449 static const WCHAR szProdCode
[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
450 LPWSTR guid_list
, *guids
, product_code
;
451 UINT i
, ret
= ERROR_FUNCTION_FAILED
;
453 product_code
= msi_dup_property( package
, szProdCode
);
456 /* FIXME: the property ProductCode should be written into the DB somewhere */
457 ERR("no product code to check\n");
458 return ERROR_SUCCESS
;
461 guid_list
= msi_suminfo_dup_string( si
, PID_TEMPLATE
);
462 guids
= msi_split_string( guid_list
, ';' );
463 for ( i
= 0; guids
[i
] && ret
!= ERROR_SUCCESS
; i
++ )
465 if (!lstrcmpW( guids
[i
], product_code
))
469 msi_free( guid_list
);
470 msi_free( product_code
);
475 static UINT
msi_parse_patch_summary( MSIPACKAGE
*package
, MSIDATABASE
*patch_db
)
478 LPWSTR str
, *substorage
;
479 UINT i
, r
= ERROR_SUCCESS
;
481 si
= MSI_GetSummaryInformationW( patch_db
->storage
, 0 );
483 return ERROR_FUNCTION_FAILED
;
485 msi_check_patch_applicable( package
, si
);
487 /* enumerate the substorage */
488 str
= msi_suminfo_dup_string( si
, PID_LASTAUTHOR
);
489 substorage
= msi_split_string( str
, ';' );
490 for ( i
= 0; substorage
&& substorage
[i
] && r
== ERROR_SUCCESS
; i
++ )
491 r
= msi_apply_substorage_transform( package
, patch_db
, substorage
[i
] );
492 msi_free( substorage
);
495 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
497 msiobj_release( &si
->hdr
);
502 static UINT
msi_apply_patch_package( MSIPACKAGE
*package
, LPCWSTR file
)
504 MSIDATABASE
*patch_db
= NULL
;
507 TRACE("%p %s\n", package
, debugstr_w( file
) );
510 * We probably want to make sure we only open a patch collection here.
511 * Patch collections (.msp) and databases (.msi) have different GUIDs
512 * but currently MSI_OpenDatabaseW will accept both.
514 r
= MSI_OpenDatabaseW( file
, MSIDBOPEN_READONLY
, &patch_db
);
515 if ( r
!= ERROR_SUCCESS
)
517 ERR("failed to open patch collection %s\n", debugstr_w( file
) );
521 msi_parse_patch_summary( package
, patch_db
);
524 * There might be a CAB file in the patch package,
525 * so append it to the list of storage to search for streams.
527 append_storage_to_db( package
->db
, patch_db
->storage
);
529 msiobj_release( &patch_db
->hdr
);
531 return ERROR_SUCCESS
;
534 /* get the PATCH property, and apply all the patches it specifies */
535 static UINT
msi_apply_patches( MSIPACKAGE
*package
)
537 static const WCHAR szPatch
[] = { 'P','A','T','C','H',0 };
538 LPWSTR patch_list
, *patches
;
539 UINT i
, r
= ERROR_SUCCESS
;
541 patch_list
= msi_dup_property( package
, szPatch
);
543 TRACE("patches to be applied: %s\n", debugstr_w( patch_list
) );
545 patches
= msi_split_string( patch_list
, ';' );
546 for( i
=0; patches
&& patches
[i
] && r
== ERROR_SUCCESS
; i
++ )
547 r
= msi_apply_patch_package( package
, patches
[i
] );
550 msi_free( patch_list
);
555 static UINT
msi_apply_transforms( MSIPACKAGE
*package
)
557 static const WCHAR szTransforms
[] = {
558 'T','R','A','N','S','F','O','R','M','S',0 };
559 LPWSTR xform_list
, *xforms
;
560 UINT i
, r
= ERROR_SUCCESS
;
562 xform_list
= msi_dup_property( package
, szTransforms
);
563 xforms
= msi_split_string( xform_list
, ';' );
565 for( i
=0; xforms
&& xforms
[i
] && r
== ERROR_SUCCESS
; i
++ )
567 if (xforms
[i
][0] == ':')
568 r
= msi_apply_substorage_transform( package
, package
->db
, xforms
[i
] );
570 r
= MSI_DatabaseApplyTransformW( package
->db
, xforms
[i
], 0 );
574 msi_free( xform_list
);
579 static BOOL
ui_sequence_exists( MSIPACKAGE
*package
)
584 static const WCHAR ExecSeqQuery
[] =
585 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
586 '`','I','n','s','t','a','l','l',
587 'U','I','S','e','q','u','e','n','c','e','`',
588 ' ','W','H','E','R','E',' ',
589 '`','S','e','q','u','e','n','c','e','`',' ',
590 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
591 '`','S','e','q','u','e','n','c','e','`',0};
593 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
594 if (rc
== ERROR_SUCCESS
)
596 msiobj_release(&view
->hdr
);
603 static UINT
msi_set_sourcedir_props(MSIPACKAGE
*package
, BOOL replace
)
606 LPWSTR source
, check
;
609 static const WCHAR szOriginalDatabase
[] =
610 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
612 db
= msi_dup_property( package
, szOriginalDatabase
);
614 return ERROR_OUTOFMEMORY
;
616 p
= strrchrW( db
, '\\' );
619 p
= strrchrW( db
, '/' );
623 return ERROR_SUCCESS
;
628 source
= msi_alloc( len
* sizeof(WCHAR
) );
629 lstrcpynW( source
, db
, len
);
631 check
= msi_dup_property( package
, cszSourceDir
);
632 if (!check
|| replace
)
633 MSI_SetPropertyW( package
, cszSourceDir
, source
);
637 check
= msi_dup_property( package
, cszSOURCEDIR
);
638 if (!check
|| replace
)
639 MSI_SetPropertyW( package
, cszSOURCEDIR
, source
);
645 return ERROR_SUCCESS
;
648 /****************************************************
649 * TOP level entry points
650 *****************************************************/
652 UINT
MSI_InstallPackage( MSIPACKAGE
*package
, LPCWSTR szPackagePath
,
653 LPCWSTR szCommandLine
)
656 BOOL ui
= FALSE
, ui_exists
;
657 static const WCHAR szUILevel
[] = {'U','I','L','e','v','e','l',0};
658 static const WCHAR szAction
[] = {'A','C','T','I','O','N',0};
659 static const WCHAR szInstall
[] = {'I','N','S','T','A','L','L',0};
661 MSI_SetPropertyW(package
, szAction
, szInstall
);
663 package
->script
= msi_alloc_zero(sizeof(MSISCRIPT
));
665 package
->script
->InWhatSequence
= SEQUENCE_INSTALL
;
672 dir
= strdupW(szPackagePath
);
673 p
= strrchrW(dir
, '\\');
677 file
= szPackagePath
+ (p
- dir
);
682 dir
= msi_alloc(MAX_PATH
*sizeof(WCHAR
));
683 GetCurrentDirectoryW(MAX_PATH
, dir
);
684 lstrcatW(dir
, cszbs
);
685 file
= szPackagePath
;
688 msi_free( package
->PackagePath
);
689 package
->PackagePath
= msi_alloc((lstrlenW(dir
) + lstrlenW(file
) + 1) * sizeof(WCHAR
));
690 if (!package
->PackagePath
)
693 return ERROR_OUTOFMEMORY
;
696 lstrcpyW(package
->PackagePath
, dir
);
697 lstrcatW(package
->PackagePath
, file
);
700 msi_set_sourcedir_props(package
, FALSE
);
703 msi_parse_command_line( package
, szCommandLine
);
705 msi_apply_transforms( package
);
706 msi_apply_patches( package
);
708 /* properties may have been added by a transform */
709 msi_clone_properties( package
);
711 if ( (msi_get_property_int(package
, szUILevel
, 0) & INSTALLUILEVEL_MASK
) >= INSTALLUILEVEL_REDUCED
)
713 package
->script
->InWhatSequence
|= SEQUENCE_UI
;
714 rc
= ACTION_ProcessUISequence(package
);
716 ui_exists
= ui_sequence_exists(package
);
717 if (rc
== ERROR_SUCCESS
|| !ui_exists
)
719 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
720 rc
= ACTION_ProcessExecSequence(package
,ui_exists
);
724 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
726 package
->script
->CurrentlyScripting
= FALSE
;
728 /* process the ending type action */
729 if (rc
== ERROR_SUCCESS
)
730 ACTION_PerformActionSequence(package
,-1,ui
);
731 else if (rc
== ERROR_INSTALL_USEREXIT
)
732 ACTION_PerformActionSequence(package
,-2,ui
);
733 else if (rc
== ERROR_INSTALL_SUSPEND
)
734 ACTION_PerformActionSequence(package
,-4,ui
);
736 ACTION_PerformActionSequence(package
,-3,ui
);
738 /* finish up running custom actions */
739 ACTION_FinishCustomActions(package
);
744 static UINT
ACTION_PerformActionSequence(MSIPACKAGE
*package
, UINT seq
, BOOL UI
)
746 UINT rc
= ERROR_SUCCESS
;
748 static const WCHAR ExecSeqQuery
[] =
749 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
750 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
751 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
752 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
754 static const WCHAR UISeqQuery
[] =
755 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
756 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
757 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
758 ' ', '=',' ','%','i',0};
761 row
= MSI_QueryGetRecord(package
->db
, UISeqQuery
, seq
);
763 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
, seq
);
767 LPCWSTR action
, cond
;
769 TRACE("Running the actions\n");
771 /* check conditions */
772 cond
= MSI_RecordGetString(row
,2);
774 /* this is a hack to skip errors in the condition code */
775 if (MSI_EvaluateConditionW(package
, cond
) == MSICONDITION_FALSE
)
778 action
= MSI_RecordGetString(row
,1);
781 ERR("failed to fetch action\n");
782 rc
= ERROR_FUNCTION_FAILED
;
787 rc
= ACTION_PerformUIAction(package
,action
,-1);
789 rc
= ACTION_PerformAction(package
,action
,-1,FALSE
);
791 msiobj_release(&row
->hdr
);
802 } iterate_action_param
;
804 static UINT
ITERATE_Actions(MSIRECORD
*row
, LPVOID param
)
806 iterate_action_param
*iap
= (iterate_action_param
*)param
;
808 LPCWSTR cond
, action
;
810 action
= MSI_RecordGetString(row
,1);
813 ERR("Error is retrieving action name\n");
814 return ERROR_FUNCTION_FAILED
;
817 /* check conditions */
818 cond
= MSI_RecordGetString(row
,2);
820 /* this is a hack to skip errors in the condition code */
821 if (MSI_EvaluateConditionW(iap
->package
, cond
) == MSICONDITION_FALSE
)
823 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action
));
824 return ERROR_SUCCESS
;
828 rc
= ACTION_PerformUIAction(iap
->package
,action
,-1);
830 rc
= ACTION_PerformAction(iap
->package
,action
,-1,FALSE
);
832 msi_dialog_check_messages( NULL
);
834 if (iap
->package
->CurrentInstallState
!= ERROR_SUCCESS
)
835 rc
= iap
->package
->CurrentInstallState
;
837 if (rc
== ERROR_FUNCTION_NOT_CALLED
)
840 if (rc
!= ERROR_SUCCESS
)
841 ERR("Execution halted, action %s returned %i\n", debugstr_w(action
), rc
);
846 UINT
MSI_Sequence( MSIPACKAGE
*package
, LPCWSTR szTable
, INT iSequenceMode
)
850 static const WCHAR query
[] =
851 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
853 ' ','W','H','E','R','E',' ',
854 '`','S','e','q','u','e','n','c','e','`',' ',
855 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
856 '`','S','e','q','u','e','n','c','e','`',0};
857 iterate_action_param iap
;
860 * FIXME: probably should be checking UILevel in the
861 * ACTION_PerformUIAction/ACTION_PerformAction
862 * rather than saving the UI level here. Those
863 * two functions can be merged too.
865 iap
.package
= package
;
868 TRACE("%p %s %i\n", package
, debugstr_w(szTable
), iSequenceMode
);
870 r
= MSI_OpenQuery( package
->db
, &view
, query
, szTable
);
871 if (r
== ERROR_SUCCESS
)
873 r
= MSI_IterateRecords( view
, NULL
, ITERATE_Actions
, &iap
);
874 msiobj_release(&view
->hdr
);
880 static UINT
ACTION_ProcessExecSequence(MSIPACKAGE
*package
, BOOL UIran
)
884 static const WCHAR ExecSeqQuery
[] =
885 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
886 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
887 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
888 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
889 'O','R','D','E','R',' ', 'B','Y',' ',
890 '`','S','e','q','u','e','n','c','e','`',0 };
892 static const WCHAR IVQuery
[] =
893 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
894 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
895 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
896 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
897 ' ','\'', 'I','n','s','t','a','l','l',
898 'V','a','l','i','d','a','t','e','\'', 0};
900 iterate_action_param iap
;
902 iap
.package
= package
;
905 if (package
->script
->ExecuteSequenceRun
)
907 TRACE("Execute Sequence already Run\n");
908 return ERROR_SUCCESS
;
911 package
->script
->ExecuteSequenceRun
= TRUE
;
913 /* get the sequence number */
916 row
= MSI_QueryGetRecord(package
->db
, IVQuery
);
918 return ERROR_FUNCTION_FAILED
;
919 seq
= MSI_RecordGetInteger(row
,1);
920 msiobj_release(&row
->hdr
);
923 rc
= MSI_OpenQuery(package
->db
, &view
, ExecSeqQuery
, seq
);
924 if (rc
== ERROR_SUCCESS
)
926 TRACE("Running the actions\n");
928 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, &iap
);
929 msiobj_release(&view
->hdr
);
935 static UINT
ACTION_ProcessUISequence(MSIPACKAGE
*package
)
939 static const WCHAR ExecSeqQuery
[] =
940 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
941 '`','I','n','s','t','a','l','l',
942 'U','I','S','e','q','u','e','n','c','e','`',
943 ' ','W','H','E','R','E',' ',
944 '`','S','e','q','u','e','n','c','e','`',' ',
945 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
946 '`','S','e','q','u','e','n','c','e','`',0};
947 iterate_action_param iap
;
949 iap
.package
= package
;
952 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
954 if (rc
== ERROR_SUCCESS
)
956 TRACE("Running the actions\n");
958 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, &iap
);
959 msiobj_release(&view
->hdr
);
965 /********************************************************
966 * ACTION helper functions and functions that perform the actions
967 *******************************************************/
968 static BOOL
ACTION_HandleStandardAction(MSIPACKAGE
*package
, LPCWSTR action
,
969 UINT
* rc
, BOOL force
)
975 if (!run
&& !package
->script
->CurrentlyScripting
)
980 if (strcmpW(action
,szInstallFinalize
) == 0 ||
981 strcmpW(action
,szInstallExecute
) == 0 ||
982 strcmpW(action
,szInstallExecuteAgain
) == 0)
987 while (StandardActions
[i
].action
!= NULL
)
989 if (strcmpW(StandardActions
[i
].action
, action
)==0)
993 ui_actioninfo(package
, action
, TRUE
, 0);
994 *rc
= schedule_action(package
,INSTALL_SCRIPT
,action
);
995 ui_actioninfo(package
, action
, FALSE
, *rc
);
999 ui_actionstart(package
, action
);
1000 if (StandardActions
[i
].handler
)
1002 *rc
= StandardActions
[i
].handler(package
);
1006 FIXME("unhandled standard action %s\n",debugstr_w(action
));
1007 *rc
= ERROR_SUCCESS
;
1018 static BOOL
ACTION_HandleCustomAction( MSIPACKAGE
* package
, LPCWSTR action
,
1019 UINT
* rc
, UINT script
, BOOL force
)
1024 arc
= ACTION_CustomAction(package
, action
, script
, force
);
1026 if (arc
!= ERROR_CALL_NOT_IMPLEMENTED
)
1035 * A lot of actions are really important even if they don't do anything
1036 * explicit... Lots of properties are set at the beginning of the installation
1037 * CostFinalize does a bunch of work to translate the directories and such
1039 * But until I get write access to the database that is hard, so I am going to
1040 * hack it to see if I can get something to run.
1042 UINT
ACTION_PerformAction(MSIPACKAGE
*package
, const WCHAR
*action
, UINT script
, BOOL force
)
1044 UINT rc
= ERROR_SUCCESS
;
1047 TRACE("Performing action (%s)\n",debugstr_w(action
));
1049 handled
= ACTION_HandleStandardAction(package
, action
, &rc
, force
);
1052 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, script
, force
);
1056 FIXME("unhandled msi action %s\n",debugstr_w(action
));
1057 rc
= ERROR_FUNCTION_NOT_CALLED
;
1063 UINT
ACTION_PerformUIAction(MSIPACKAGE
*package
, const WCHAR
*action
, UINT script
)
1065 UINT rc
= ERROR_SUCCESS
;
1066 BOOL handled
= FALSE
;
1068 TRACE("Performing action (%s)\n",debugstr_w(action
));
1070 handled
= ACTION_HandleStandardAction(package
, action
, &rc
,TRUE
);
1073 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, script
, FALSE
);
1075 if( !handled
&& ACTION_DialogBox(package
,action
) == ERROR_SUCCESS
)
1080 FIXME("unhandled msi action %s\n",debugstr_w(action
));
1081 rc
= ERROR_FUNCTION_NOT_CALLED
;
1089 * Actual Action Handlers
1092 static UINT
ITERATE_CreateFolders(MSIRECORD
*row
, LPVOID param
)
1094 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1100 dir
= MSI_RecordGetString(row
,1);
1103 ERR("Unable to get folder id\n");
1104 return ERROR_SUCCESS
;
1107 full_path
= resolve_folder(package
,dir
,FALSE
,FALSE
,TRUE
,&folder
);
1110 ERR("Unable to resolve folder id %s\n",debugstr_w(dir
));
1111 return ERROR_SUCCESS
;
1114 TRACE("Folder is %s\n",debugstr_w(full_path
));
1117 uirow
= MSI_CreateRecord(1);
1118 MSI_RecordSetStringW(uirow
,1,full_path
);
1119 ui_actiondata(package
,szCreateFolders
,uirow
);
1120 msiobj_release( &uirow
->hdr
);
1122 if (folder
->State
== 0)
1123 create_full_pathW(full_path
);
1127 msi_free(full_path
);
1128 return ERROR_SUCCESS
;
1131 /* FIXME: probably should merge this with the above function */
1132 static UINT
msi_create_directory( MSIPACKAGE
* package
, LPCWSTR dir
)
1134 UINT rc
= ERROR_SUCCESS
;
1136 LPWSTR install_path
;
1138 install_path
= resolve_folder(package
, dir
, FALSE
, FALSE
, TRUE
, &folder
);
1140 return ERROR_FUNCTION_FAILED
;
1142 /* create the path */
1143 if (folder
->State
== 0)
1145 create_full_pathW(install_path
);
1148 msi_free(install_path
);
1153 UINT
msi_create_component_directories( MSIPACKAGE
*package
)
1157 /* create all the folders required by the components are going to install */
1158 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1160 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
1162 msi_create_directory( package
, comp
->Directory
);
1165 return ERROR_SUCCESS
;
1169 * Also we cannot enable/disable components either, so for now I am just going
1170 * to do all the directories for all the components.
1172 static UINT
ACTION_CreateFolders(MSIPACKAGE
*package
)
1174 static const WCHAR ExecSeqQuery
[] =
1175 {'S','E','L','E','C','T',' ',
1176 '`','D','i','r','e','c','t','o','r','y','_','`',
1177 ' ','F','R','O','M',' ',
1178 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1182 /* create all the empty folders specified in the CreateFolder table */
1183 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
1184 if (rc
!= ERROR_SUCCESS
)
1185 return ERROR_SUCCESS
;
1187 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateFolders
, package
);
1188 msiobj_release(&view
->hdr
);
1190 msi_create_component_directories( package
);
1195 static UINT
load_component( MSIRECORD
*row
, LPVOID param
)
1197 MSIPACKAGE
*package
= param
;
1200 comp
= msi_alloc_zero( sizeof(MSICOMPONENT
) );
1202 return ERROR_FUNCTION_FAILED
;
1204 list_add_tail( &package
->components
, &comp
->entry
);
1206 /* fill in the data */
1207 comp
->Component
= msi_dup_record_field( row
, 1 );
1209 TRACE("Loading Component %s\n", debugstr_w(comp
->Component
));
1211 comp
->ComponentId
= msi_dup_record_field( row
, 2 );
1212 comp
->Directory
= msi_dup_record_field( row
, 3 );
1213 comp
->Attributes
= MSI_RecordGetInteger(row
,4);
1214 comp
->Condition
= msi_dup_record_field( row
, 5 );
1215 comp
->KeyPath
= msi_dup_record_field( row
, 6 );
1217 comp
->Installed
= INSTALLSTATE_UNKNOWN
;
1218 msi_component_set_state( comp
, INSTALLSTATE_UNKNOWN
);
1220 return ERROR_SUCCESS
;
1223 static UINT
load_all_components( MSIPACKAGE
*package
)
1225 static const WCHAR query
[] = {
1226 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1227 '`','C','o','m','p','o','n','e','n','t','`',0 };
1231 if (!list_empty(&package
->components
))
1232 return ERROR_SUCCESS
;
1234 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1235 if (r
!= ERROR_SUCCESS
)
1238 r
= MSI_IterateRecords(view
, NULL
, load_component
, package
);
1239 msiobj_release(&view
->hdr
);
1244 MSIPACKAGE
*package
;
1245 MSIFEATURE
*feature
;
1248 static UINT
add_feature_component( MSIFEATURE
*feature
, MSICOMPONENT
*comp
)
1252 cl
= msi_alloc( sizeof (*cl
) );
1254 return ERROR_NOT_ENOUGH_MEMORY
;
1255 cl
->component
= comp
;
1256 list_add_tail( &feature
->Components
, &cl
->entry
);
1258 return ERROR_SUCCESS
;
1261 static UINT
add_feature_child( MSIFEATURE
*parent
, MSIFEATURE
*child
)
1265 fl
= msi_alloc( sizeof(*fl
) );
1267 return ERROR_NOT_ENOUGH_MEMORY
;
1268 fl
->feature
= child
;
1269 list_add_tail( &parent
->Children
, &fl
->entry
);
1271 return ERROR_SUCCESS
;
1274 static UINT
iterate_load_featurecomponents(MSIRECORD
*row
, LPVOID param
)
1276 _ilfs
* ilfs
= (_ilfs
*)param
;
1280 component
= MSI_RecordGetString(row
,1);
1282 /* check to see if the component is already loaded */
1283 comp
= get_loaded_component( ilfs
->package
, component
);
1286 ERR("unknown component %s\n", debugstr_w(component
));
1287 return ERROR_FUNCTION_FAILED
;
1290 add_feature_component( ilfs
->feature
, comp
);
1291 comp
->Enabled
= TRUE
;
1293 return ERROR_SUCCESS
;
1296 static MSIFEATURE
*find_feature_by_name( MSIPACKAGE
*package
, LPCWSTR name
)
1298 MSIFEATURE
*feature
;
1300 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1302 if ( !lstrcmpW( feature
->Feature
, name
) )
1309 static UINT
load_feature(MSIRECORD
* row
, LPVOID param
)
1311 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1312 MSIFEATURE
* feature
;
1313 static const WCHAR Query1
[] =
1314 {'S','E','L','E','C','T',' ',
1315 '`','C','o','m','p','o','n','e','n','t','_','`',
1316 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1317 'C','o','m','p','o','n','e','n','t','s','`',' ',
1318 'W','H','E','R','E',' ',
1319 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1324 /* fill in the data */
1326 feature
= msi_alloc_zero( sizeof (MSIFEATURE
) );
1328 return ERROR_NOT_ENOUGH_MEMORY
;
1330 list_init( &feature
->Children
);
1331 list_init( &feature
->Components
);
1333 feature
->Feature
= msi_dup_record_field( row
, 1 );
1335 TRACE("Loading feature %s\n",debugstr_w(feature
->Feature
));
1337 feature
->Feature_Parent
= msi_dup_record_field( row
, 2 );
1338 feature
->Title
= msi_dup_record_field( row
, 3 );
1339 feature
->Description
= msi_dup_record_field( row
, 4 );
1341 if (!MSI_RecordIsNull(row
,5))
1342 feature
->Display
= MSI_RecordGetInteger(row
,5);
1344 feature
->Level
= MSI_RecordGetInteger(row
,6);
1345 feature
->Directory
= msi_dup_record_field( row
, 7 );
1346 feature
->Attributes
= MSI_RecordGetInteger(row
,8);
1348 feature
->Installed
= INSTALLSTATE_UNKNOWN
;
1349 msi_feature_set_state( feature
, INSTALLSTATE_UNKNOWN
);
1351 list_add_tail( &package
->features
, &feature
->entry
);
1353 /* load feature components */
1355 rc
= MSI_OpenQuery( package
->db
, &view
, Query1
, feature
->Feature
);
1356 if (rc
!= ERROR_SUCCESS
)
1357 return ERROR_SUCCESS
;
1359 ilfs
.package
= package
;
1360 ilfs
.feature
= feature
;
1362 MSI_IterateRecords(view
, NULL
, iterate_load_featurecomponents
, &ilfs
);
1363 msiobj_release(&view
->hdr
);
1365 return ERROR_SUCCESS
;
1368 static UINT
find_feature_children(MSIRECORD
* row
, LPVOID param
)
1370 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1371 MSIFEATURE
*parent
, *child
;
1373 child
= find_feature_by_name( package
, MSI_RecordGetString( row
, 1 ) );
1375 return ERROR_FUNCTION_FAILED
;
1377 if (!child
->Feature_Parent
)
1378 return ERROR_SUCCESS
;
1380 parent
= find_feature_by_name( package
, child
->Feature_Parent
);
1382 return ERROR_FUNCTION_FAILED
;
1384 add_feature_child( parent
, child
);
1385 return ERROR_SUCCESS
;
1388 static UINT
load_all_features( MSIPACKAGE
*package
)
1390 static const WCHAR query
[] = {
1391 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1392 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1393 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1397 if (!list_empty(&package
->features
))
1398 return ERROR_SUCCESS
;
1400 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1401 if (r
!= ERROR_SUCCESS
)
1404 r
= MSI_IterateRecords( view
, NULL
, load_feature
, package
);
1405 if (r
!= ERROR_SUCCESS
)
1408 r
= MSI_IterateRecords( view
, NULL
, find_feature_children
, package
);
1409 msiobj_release( &view
->hdr
);
1414 static LPWSTR
folder_split_path(LPWSTR p
, WCHAR ch
)
1425 static UINT
load_file_hash(MSIPACKAGE
*package
, MSIFILE
*file
)
1427 static const WCHAR query
[] = {
1428 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1429 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1430 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1431 MSIQUERY
*view
= NULL
;
1432 MSIRECORD
*row
= NULL
;
1435 TRACE("%s\n", debugstr_w(file
->File
));
1437 r
= MSI_OpenQuery(package
->db
, &view
, query
, file
->File
);
1438 if (r
!= ERROR_SUCCESS
)
1441 r
= MSI_ViewExecute(view
, NULL
);
1442 if (r
!= ERROR_SUCCESS
)
1445 r
= MSI_ViewFetch(view
, &row
);
1446 if (r
!= ERROR_SUCCESS
)
1449 file
->hash
.dwFileHashInfoSize
= sizeof(MSIFILEHASHINFO
);
1450 file
->hash
.dwData
[0] = MSI_RecordGetInteger(row
, 3);
1451 file
->hash
.dwData
[1] = MSI_RecordGetInteger(row
, 4);
1452 file
->hash
.dwData
[2] = MSI_RecordGetInteger(row
, 5);
1453 file
->hash
.dwData
[3] = MSI_RecordGetInteger(row
, 6);
1456 if (view
) msiobj_release(&view
->hdr
);
1457 if (row
) msiobj_release(&row
->hdr
);
1461 static UINT
load_file(MSIRECORD
*row
, LPVOID param
)
1463 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1467 /* fill in the data */
1469 file
= msi_alloc_zero( sizeof (MSIFILE
) );
1471 return ERROR_NOT_ENOUGH_MEMORY
;
1473 file
->File
= msi_dup_record_field( row
, 1 );
1475 component
= MSI_RecordGetString( row
, 2 );
1476 file
->Component
= get_loaded_component( package
, component
);
1478 if (!file
->Component
)
1479 ERR("Unfound Component %s\n",debugstr_w(component
));
1481 file
->FileName
= msi_dup_record_field( row
, 3 );
1482 reduce_to_longfilename( file
->FileName
);
1484 file
->ShortName
= msi_dup_record_field( row
, 3 );
1485 file
->LongName
= strdupW( folder_split_path(file
->ShortName
, '|'));
1487 file
->FileSize
= MSI_RecordGetInteger( row
, 4 );
1488 file
->Version
= msi_dup_record_field( row
, 5 );
1489 file
->Language
= msi_dup_record_field( row
, 6 );
1490 file
->Attributes
= MSI_RecordGetInteger( row
, 7 );
1491 file
->Sequence
= MSI_RecordGetInteger( row
, 8 );
1493 file
->state
= msifs_invalid
;
1495 /* if the compressed bits are not set in the file attributes,
1496 * then read the information from the package word count property
1498 if (file
->Attributes
& msidbFileAttributesCompressed
)
1500 file
->IsCompressed
= TRUE
;
1502 else if (file
->Attributes
& msidbFileAttributesNoncompressed
)
1504 file
->IsCompressed
= FALSE
;
1508 file
->IsCompressed
= package
->WordCount
& MSIWORDCOUNT_COMPRESSED
;
1511 load_file_hash(package
, file
);
1513 TRACE("File Loaded (%s)\n",debugstr_w(file
->File
));
1515 list_add_tail( &package
->files
, &file
->entry
);
1517 return ERROR_SUCCESS
;
1520 static UINT
load_all_files(MSIPACKAGE
*package
)
1524 static const WCHAR Query
[] =
1525 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1526 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1527 '`','S','e','q','u','e','n','c','e','`', 0};
1529 if (!list_empty(&package
->files
))
1530 return ERROR_SUCCESS
;
1532 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
1533 if (rc
!= ERROR_SUCCESS
)
1534 return ERROR_SUCCESS
;
1536 rc
= MSI_IterateRecords(view
, NULL
, load_file
, package
);
1537 msiobj_release(&view
->hdr
);
1539 return ERROR_SUCCESS
;
1542 static UINT
load_folder( MSIRECORD
*row
, LPVOID param
)
1544 MSIPACKAGE
*package
= param
;
1545 static const WCHAR szDot
[] = { '.',0 };
1546 static WCHAR szEmpty
[] = { 0 };
1547 LPWSTR p
, tgt_short
, tgt_long
, src_short
, src_long
;
1550 folder
= msi_alloc_zero( sizeof (MSIFOLDER
) );
1552 return ERROR_NOT_ENOUGH_MEMORY
;
1554 folder
->Directory
= msi_dup_record_field( row
, 1 );
1556 TRACE("%s\n", debugstr_w(folder
->Directory
));
1558 p
= msi_dup_record_field(row
, 3);
1560 /* split src and target dir */
1562 src_short
= folder_split_path( p
, ':' );
1564 /* split the long and short paths */
1565 tgt_long
= folder_split_path( tgt_short
, '|' );
1566 src_long
= folder_split_path( src_short
, '|' );
1568 /* check for no-op dirs */
1569 if (!lstrcmpW(szDot
, tgt_short
))
1570 tgt_short
= szEmpty
;
1571 if (!lstrcmpW(szDot
, src_short
))
1572 src_short
= szEmpty
;
1575 tgt_long
= tgt_short
;
1578 src_short
= tgt_short
;
1579 src_long
= tgt_long
;
1583 src_long
= src_short
;
1585 /* FIXME: use the target short path too */
1586 folder
->TargetDefault
= strdupW(tgt_long
);
1587 folder
->SourceShortPath
= strdupW(src_short
);
1588 folder
->SourceLongPath
= strdupW(src_long
);
1591 TRACE("TargetDefault = %s\n",debugstr_w( folder
->TargetDefault
));
1592 TRACE("SourceLong = %s\n", debugstr_w( folder
->SourceLongPath
));
1593 TRACE("SourceShort = %s\n", debugstr_w( folder
->SourceShortPath
));
1595 folder
->Parent
= msi_dup_record_field( row
, 2 );
1597 folder
->Property
= msi_dup_property( package
, folder
->Directory
);
1599 list_add_tail( &package
->folders
, &folder
->entry
);
1601 TRACE("returning %p\n", folder
);
1603 return ERROR_SUCCESS
;
1606 static UINT
load_all_folders( MSIPACKAGE
*package
)
1608 static const WCHAR query
[] = {
1609 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1610 '`','D','i','r','e','c','t','o','r','y','`',0 };
1614 if (!list_empty(&package
->folders
))
1615 return ERROR_SUCCESS
;
1617 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1618 if (r
!= ERROR_SUCCESS
)
1621 r
= MSI_IterateRecords(view
, NULL
, load_folder
, package
);
1622 msiobj_release(&view
->hdr
);
1627 * I am not doing any of the costing functionality yet.
1628 * Mostly looking at doing the Component and Feature loading
1630 * The native MSI does A LOT of modification to tables here. Mostly adding
1631 * a lot of temporary columns to the Feature and Component tables.
1633 * note: Native msi also tracks the short filename. But I am only going to
1634 * track the long ones. Also looking at this directory table
1635 * it appears that the directory table does not get the parents
1636 * resolved base on property only based on their entries in the
1639 static UINT
ACTION_CostInitialize(MSIPACKAGE
*package
)
1641 static const WCHAR szCosting
[] =
1642 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1643 static const WCHAR szZero
[] = { '0', 0 };
1645 MSI_SetPropertyW(package
, szCosting
, szZero
);
1646 MSI_SetPropertyW(package
, cszRootDrive
, c_colon
);
1648 load_all_components( package
);
1649 load_all_features( package
);
1650 load_all_files( package
);
1651 load_all_folders( package
);
1653 return ERROR_SUCCESS
;
1656 static UINT
execute_script(MSIPACKAGE
*package
, UINT script
)
1659 UINT rc
= ERROR_SUCCESS
;
1661 TRACE("Executing Script %i\n",script
);
1663 if (!package
->script
)
1665 ERR("no script!\n");
1666 return ERROR_FUNCTION_FAILED
;
1669 for (i
= 0; i
< package
->script
->ActionCount
[script
]; i
++)
1672 action
= package
->script
->Actions
[script
][i
];
1673 ui_actionstart(package
, action
);
1674 TRACE("Executing Action (%s)\n",debugstr_w(action
));
1675 rc
= ACTION_PerformAction(package
, action
, script
, TRUE
);
1676 if (rc
!= ERROR_SUCCESS
)
1679 msi_free_action_script(package
, script
);
1683 static UINT
ACTION_FileCost(MSIPACKAGE
*package
)
1685 return ERROR_SUCCESS
;
1688 static void ACTION_GetComponentInstallStates(MSIPACKAGE
*package
)
1692 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1696 if (!comp
->ComponentId
)
1699 res
= MsiGetComponentPathW( package
->ProductCode
,
1700 comp
->ComponentId
, NULL
, NULL
);
1702 res
= INSTALLSTATE_ABSENT
;
1703 comp
->Installed
= res
;
1707 /* scan for and update current install states */
1708 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE
*package
)
1711 MSIFEATURE
*feature
;
1713 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1716 INSTALLSTATE res
= INSTALLSTATE_ABSENT
;
1718 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1720 comp
= cl
->component
;
1722 if (!comp
->ComponentId
)
1724 res
= INSTALLSTATE_ABSENT
;
1728 if (res
== INSTALLSTATE_ABSENT
)
1729 res
= comp
->Installed
;
1732 if (res
== comp
->Installed
)
1735 if (res
!= INSTALLSTATE_DEFAULT
&& res
!= INSTALLSTATE_LOCAL
&&
1736 res
!= INSTALLSTATE_SOURCE
)
1738 res
= INSTALLSTATE_INCOMPLETE
;
1742 feature
->Installed
= res
;
1746 static BOOL
process_state_property (MSIPACKAGE
* package
, LPCWSTR property
,
1749 static const WCHAR all
[]={'A','L','L',0};
1751 MSIFEATURE
*feature
;
1753 override
= msi_dup_property( package
, property
);
1757 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1759 if (strcmpiW(override
,all
)==0)
1760 msi_feature_set_state( feature
, state
);
1763 LPWSTR ptr
= override
;
1764 LPWSTR ptr2
= strchrW(override
,',');
1768 if ((ptr2
&& strncmpW(ptr
,feature
->Feature
, ptr2
-ptr
)==0)
1769 || (!ptr2
&& strcmpW(ptr
,feature
->Feature
)==0))
1771 msi_feature_set_state( feature
, state
);
1777 ptr2
= strchrW(ptr
,',');
1789 UINT
MSI_SetFeatureStates(MSIPACKAGE
*package
)
1792 static const WCHAR szlevel
[] =
1793 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1794 static const WCHAR szAddLocal
[] =
1795 {'A','D','D','L','O','C','A','L',0};
1796 static const WCHAR szAddSource
[] =
1797 {'A','D','D','S','O','U','R','C','E',0};
1798 static const WCHAR szRemove
[] =
1799 {'R','E','M','O','V','E',0};
1800 static const WCHAR szReinstall
[] =
1801 {'R','E','I','N','S','T','A','L','L',0};
1802 BOOL override
= FALSE
;
1803 MSICOMPONENT
* component
;
1804 MSIFEATURE
*feature
;
1807 /* I do not know if this is where it should happen.. but */
1809 TRACE("Checking Install Level\n");
1811 install_level
= msi_get_property_int( package
, szlevel
, 1 );
1813 /* ok here is the _real_ rub
1814 * all these activation/deactivation things happen in order and things
1815 * later on the list override things earlier on the list.
1816 * 1) INSTALLLEVEL processing
1826 * 11) FILEADDDEFAULT
1827 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1828 * ignored for all the features. seems strange, especially since it is not
1829 * documented anywhere, but it is how it works.
1831 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1832 * REMOVE are the big ones, since we don't handle administrative installs
1835 override
|= process_state_property(package
,szAddLocal
,INSTALLSTATE_LOCAL
);
1836 override
|= process_state_property(package
,szRemove
,INSTALLSTATE_ABSENT
);
1837 override
|= process_state_property(package
,szAddSource
,INSTALLSTATE_SOURCE
);
1838 override
|= process_state_property(package
,szReinstall
,INSTALLSTATE_LOCAL
);
1842 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1844 BOOL feature_state
= ((feature
->Level
> 0) &&
1845 (feature
->Level
<= install_level
));
1847 if ((feature_state
) && (feature
->Action
== INSTALLSTATE_UNKNOWN
))
1849 if (feature
->Attributes
& msidbFeatureAttributesFavorSource
)
1850 msi_feature_set_state( feature
, INSTALLSTATE_SOURCE
);
1851 else if (feature
->Attributes
& msidbFeatureAttributesFavorAdvertise
)
1852 msi_feature_set_state( feature
, INSTALLSTATE_ADVERTISED
);
1854 msi_feature_set_state( feature
, INSTALLSTATE_LOCAL
);
1858 /* disable child features of unselected parent features */
1859 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1863 if (feature
->Level
> 0 && feature
->Level
<= install_level
)
1866 LIST_FOR_EACH_ENTRY( fl
, &feature
->Children
, FeatureList
, entry
)
1867 msi_feature_set_state( fl
->feature
, INSTALLSTATE_UNKNOWN
);
1872 /* set the Preselected Property */
1873 static const WCHAR szPreselected
[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1874 static const WCHAR szOne
[] = { '1', 0 };
1876 MSI_SetPropertyW(package
,szPreselected
,szOne
);
1880 * now we want to enable or disable components base on feature
1883 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1887 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1888 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
);
1890 /* features with components that have compressed files are made local */
1891 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1893 if (cl
->component
->Enabled
&&
1894 cl
->component
->ForceLocalState
&&
1895 feature
->Action
== INSTALLSTATE_SOURCE
)
1897 msi_feature_set_state( feature
, INSTALLSTATE_LOCAL
);
1902 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1904 component
= cl
->component
;
1906 if (!component
->Enabled
)
1909 switch (feature
->Action
)
1911 case INSTALLSTATE_ABSENT
:
1912 component
->anyAbsent
= 1;
1914 case INSTALLSTATE_ADVERTISED
:
1915 component
->hasAdvertiseFeature
= 1;
1917 case INSTALLSTATE_SOURCE
:
1918 component
->hasSourceFeature
= 1;
1920 case INSTALLSTATE_LOCAL
:
1921 component
->hasLocalFeature
= 1;
1923 case INSTALLSTATE_DEFAULT
:
1924 if (feature
->Attributes
& msidbFeatureAttributesFavorAdvertise
)
1925 component
->hasAdvertiseFeature
= 1;
1926 else if (feature
->Attributes
& msidbFeatureAttributesFavorSource
)
1927 component
->hasSourceFeature
= 1;
1929 component
->hasLocalFeature
= 1;
1937 LIST_FOR_EACH_ENTRY( component
, &package
->components
, MSICOMPONENT
, entry
)
1939 /* if the component isn't enabled, leave it alone */
1940 if (!component
->Enabled
)
1943 /* check if it's local or source */
1944 if (!(component
->Attributes
& msidbComponentAttributesOptional
) &&
1945 (component
->hasLocalFeature
|| component
->hasSourceFeature
))
1947 if ((component
->Attributes
& msidbComponentAttributesSourceOnly
) &&
1948 !component
->ForceLocalState
)
1949 msi_component_set_state( component
, INSTALLSTATE_SOURCE
);
1951 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1955 /* if any feature is local, the component must be local too */
1956 if (component
->hasLocalFeature
)
1958 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1962 if (component
->hasSourceFeature
)
1964 msi_component_set_state( component
, INSTALLSTATE_SOURCE
);
1968 if (component
->hasAdvertiseFeature
)
1970 msi_component_set_state( component
, INSTALLSTATE_ADVERTISED
);
1974 TRACE("nobody wants component %s\n", debugstr_w(component
->Component
));
1975 if (component
->anyAbsent
)
1976 msi_component_set_state(component
, INSTALLSTATE_ABSENT
);
1979 LIST_FOR_EACH_ENTRY( component
, &package
->components
, MSICOMPONENT
, entry
)
1981 if (component
->Action
== INSTALLSTATE_DEFAULT
)
1983 TRACE("%s was default, setting to local\n", debugstr_w(component
->Component
));
1984 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1987 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1988 debugstr_w(component
->Component
), component
->Installed
, component
->Action
);
1992 return ERROR_SUCCESS
;
1995 static UINT
ITERATE_CostFinalizeDirectories(MSIRECORD
*row
, LPVOID param
)
1997 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
2002 name
= MSI_RecordGetString(row
,1);
2004 f
= get_loaded_folder(package
, name
);
2005 if (!f
) return ERROR_SUCCESS
;
2007 /* reset the ResolvedTarget */
2008 msi_free(f
->ResolvedTarget
);
2009 f
->ResolvedTarget
= NULL
;
2011 /* This helper function now does ALL the work */
2012 TRACE("Dir %s ...\n",debugstr_w(name
));
2013 path
= resolve_folder(package
,name
,FALSE
,TRUE
,TRUE
,NULL
);
2014 TRACE("resolves to %s\n",debugstr_w(path
));
2017 return ERROR_SUCCESS
;
2020 static UINT
ITERATE_CostFinalizeConditions(MSIRECORD
*row
, LPVOID param
)
2022 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
2024 MSIFEATURE
*feature
;
2026 name
= MSI_RecordGetString( row
, 1 );
2028 feature
= get_loaded_feature( package
, name
);
2030 ERR("FAILED to find loaded feature %s\n",debugstr_w(name
));
2034 Condition
= MSI_RecordGetString(row
,3);
2036 if (MSI_EvaluateConditionW(package
,Condition
) == MSICONDITION_TRUE
)
2038 int level
= MSI_RecordGetInteger(row
,2);
2039 TRACE("Reseting feature %s to level %i\n", debugstr_w(name
), level
);
2040 feature
->Level
= level
;
2043 return ERROR_SUCCESS
;
2046 static LPWSTR
msi_get_disk_file_version( LPCWSTR filename
)
2048 static const WCHAR name_fmt
[] =
2049 {'%','u','.','%','u','.','%','u','.','%','u',0};
2050 static WCHAR name
[] = {'\\',0};
2051 VS_FIXEDFILEINFO
*lpVer
;
2052 WCHAR filever
[0x100];
2058 TRACE("%s\n", debugstr_w(filename
));
2060 versize
= GetFileVersionInfoSizeW( filename
, &handle
);
2064 version
= msi_alloc( versize
);
2065 GetFileVersionInfoW( filename
, 0, versize
, version
);
2067 if (!VerQueryValueW( version
, name
, (LPVOID
*)&lpVer
, &sz
))
2069 msi_free( version
);
2073 sprintfW( filever
, name_fmt
,
2074 HIWORD(lpVer
->dwFileVersionMS
),
2075 LOWORD(lpVer
->dwFileVersionMS
),
2076 HIWORD(lpVer
->dwFileVersionLS
),
2077 LOWORD(lpVer
->dwFileVersionLS
));
2079 msi_free( version
);
2081 return strdupW( filever
);
2084 static UINT
msi_check_file_install_states( MSIPACKAGE
*package
)
2086 LPWSTR file_version
;
2089 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2091 MSICOMPONENT
* comp
= file
->Component
;
2097 if (file
->IsCompressed
)
2098 comp
->ForceLocalState
= TRUE
;
2100 /* calculate target */
2101 p
= resolve_folder(package
, comp
->Directory
, FALSE
, FALSE
, TRUE
, NULL
);
2103 msi_free(file
->TargetPath
);
2105 TRACE("file %s is named %s\n",
2106 debugstr_w(file
->File
), debugstr_w(file
->FileName
));
2108 file
->TargetPath
= build_directory_name(2, p
, file
->FileName
);
2112 TRACE("file %s resolves to %s\n",
2113 debugstr_w(file
->File
), debugstr_w(file
->TargetPath
));
2115 /* don't check files of components that aren't installed */
2116 if (comp
->Installed
== INSTALLSTATE_UNKNOWN
||
2117 comp
->Installed
== INSTALLSTATE_ABSENT
)
2119 file
->state
= msifs_missing
; /* assume files are missing */
2123 if (GetFileAttributesW(file
->TargetPath
) == INVALID_FILE_ATTRIBUTES
)
2125 file
->state
= msifs_missing
;
2126 comp
->Cost
+= file
->FileSize
;
2127 comp
->Installed
= INSTALLSTATE_INCOMPLETE
;
2131 if (file
->Version
&&
2132 (file_version
= msi_get_disk_file_version( file
->TargetPath
)))
2134 TRACE("new %s old %s\n", debugstr_w(file
->Version
),
2135 debugstr_w(file_version
));
2136 /* FIXME: seems like a bad way to compare version numbers */
2137 if (lstrcmpiW(file_version
, file
->Version
)<0)
2139 file
->state
= msifs_overwrite
;
2140 comp
->Cost
+= file
->FileSize
;
2141 comp
->Installed
= INSTALLSTATE_INCOMPLETE
;
2144 file
->state
= msifs_present
;
2145 msi_free( file_version
);
2148 file
->state
= msifs_present
;
2151 return ERROR_SUCCESS
;
2155 * A lot is done in this function aside from just the costing.
2156 * The costing needs to be implemented at some point but for now I am going
2157 * to focus on the directory building
2160 static UINT
ACTION_CostFinalize(MSIPACKAGE
*package
)
2162 static const WCHAR ExecSeqQuery
[] =
2163 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2164 '`','D','i','r','e','c','t','o','r','y','`',0};
2165 static const WCHAR ConditionQuery
[] =
2166 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2167 '`','C','o','n','d','i','t','i','o','n','`',0};
2168 static const WCHAR szCosting
[] =
2169 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2170 static const WCHAR szlevel
[] =
2171 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2172 static const WCHAR szOne
[] = { '1', 0 };
2178 if ( 1 == msi_get_property_int( package
, szCosting
, 0 ) )
2179 return ERROR_SUCCESS
;
2181 TRACE("Building Directory properties\n");
2183 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2184 if (rc
== ERROR_SUCCESS
)
2186 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CostFinalizeDirectories
,
2188 msiobj_release(&view
->hdr
);
2191 /* read components states from the registry */
2192 ACTION_GetComponentInstallStates(package
);
2194 TRACE("File calculations\n");
2195 msi_check_file_install_states( package
);
2197 TRACE("Evaluating Condition Table\n");
2199 rc
= MSI_DatabaseOpenViewW(package
->db
, ConditionQuery
, &view
);
2200 if (rc
== ERROR_SUCCESS
)
2202 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CostFinalizeConditions
,
2204 msiobj_release(&view
->hdr
);
2207 TRACE("Enabling or Disabling Components\n");
2208 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2210 if (MSI_EvaluateConditionW(package
, comp
->Condition
) == MSICONDITION_FALSE
)
2212 TRACE("Disabling component %s\n", debugstr_w(comp
->Component
));
2213 comp
->Enabled
= FALSE
;
2217 MSI_SetPropertyW(package
,szCosting
,szOne
);
2218 /* set default run level if not set */
2219 level
= msi_dup_property( package
, szlevel
);
2221 MSI_SetPropertyW(package
,szlevel
, szOne
);
2224 ACTION_UpdateFeatureInstallStates(package
);
2226 return MSI_SetFeatureStates(package
);
2229 /* OK this value is "interpreted" and then formatted based on the
2230 first few characters */
2231 static LPSTR
parse_value(MSIPACKAGE
*package
, LPCWSTR value
, DWORD
*type
,
2236 if (value
[0]=='#' && value
[1]!='#' && value
[1]!='%')
2242 LPWSTR deformated
= NULL
;
2245 deformat_string(package
, &value
[2], &deformated
);
2247 /* binary value type */
2251 *size
= (strlenW(ptr
)/2)+1;
2253 *size
= strlenW(ptr
)/2;
2255 data
= msi_alloc(*size
);
2261 /* if uneven pad with a zero in front */
2267 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2269 TRACE("Uneven byte count\n");
2277 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2280 msi_free(deformated
);
2282 TRACE("Data %i bytes(%i)\n",*size
,count
);
2289 deformat_string(package
, &value
[1], &deformated
);
2292 *size
= sizeof(DWORD
);
2293 data
= msi_alloc(*size
);
2299 if ( (*p
< '0') || (*p
> '9') )
2305 if (deformated
[0] == '-')
2308 TRACE("DWORD %i\n",*(LPDWORD
)data
);
2310 msi_free(deformated
);
2315 static const WCHAR szMulti
[] = {'[','~',']',0};
2324 *type
=REG_EXPAND_SZ
;
2332 if (strstrW(value
,szMulti
))
2333 *type
= REG_MULTI_SZ
;
2335 /* remove initial delimiter */
2336 if (!strncmpW(value
, szMulti
, 3))
2339 *size
= deformat_string(package
, ptr
,(LPWSTR
*)&data
);
2341 /* add double NULL terminator */
2342 if (*type
== REG_MULTI_SZ
)
2344 *size
+= 2 * sizeof(WCHAR
); /* two NULL terminators */
2345 data
= msi_realloc_zero(data
, *size
);
2351 static UINT
ITERATE_WriteRegistryValues(MSIRECORD
*row
, LPVOID param
)
2353 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
2354 static const WCHAR szHCR
[] =
2355 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2356 'R','O','O','T','\\',0};
2357 static const WCHAR szHCU
[] =
2358 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2359 'U','S','E','R','\\',0};
2360 static const WCHAR szHLM
[] =
2361 {'H','K','E','Y','_','L','O','C','A','L','_',
2362 'M','A','C','H','I','N','E','\\',0};
2363 static const WCHAR szHU
[] =
2364 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2366 LPSTR value_data
= NULL
;
2367 HKEY root_key
, hkey
;
2370 LPCWSTR szRoot
, component
, name
, key
, value
;
2375 BOOL check_first
= FALSE
;
2378 ui_progress(package
,2,0,0,0);
2385 component
= MSI_RecordGetString(row
, 6);
2386 comp
= get_loaded_component(package
,component
);
2388 return ERROR_SUCCESS
;
2390 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2392 TRACE("Skipping write due to disabled component %s\n",
2393 debugstr_w(component
));
2395 comp
->Action
= comp
->Installed
;
2397 return ERROR_SUCCESS
;
2400 comp
->Action
= INSTALLSTATE_LOCAL
;
2402 name
= MSI_RecordGetString(row
, 4);
2403 if( MSI_RecordIsNull(row
,5) && name
)
2405 /* null values can have special meanings */
2406 if (name
[0]=='-' && name
[1] == 0)
2407 return ERROR_SUCCESS
;
2408 else if ((name
[0]=='+' && name
[1] == 0) ||
2409 (name
[0] == '*' && name
[1] == 0))
2414 root
= MSI_RecordGetInteger(row
,2);
2415 key
= MSI_RecordGetString(row
, 3);
2417 /* get the root key */
2422 static const WCHAR szALLUSER
[] = {'A','L','L','U','S','E','R','S',0};
2423 LPWSTR all_users
= msi_dup_property( package
, szALLUSER
);
2424 if (all_users
&& all_users
[0] == '1')
2426 root_key
= HKEY_LOCAL_MACHINE
;
2431 root_key
= HKEY_CURRENT_USER
;
2434 msi_free(all_users
);
2437 case 0: root_key
= HKEY_CLASSES_ROOT
;
2440 case 1: root_key
= HKEY_CURRENT_USER
;
2443 case 2: root_key
= HKEY_LOCAL_MACHINE
;
2446 case 3: root_key
= HKEY_USERS
;
2450 ERR("Unknown root %i\n",root
);
2456 return ERROR_SUCCESS
;
2458 deformat_string(package
, key
, &deformated
);
2459 size
= strlenW(deformated
) + strlenW(szRoot
) + 1;
2460 uikey
= msi_alloc(size
*sizeof(WCHAR
));
2461 strcpyW(uikey
,szRoot
);
2462 strcatW(uikey
,deformated
);
2464 if (RegCreateKeyW( root_key
, deformated
, &hkey
))
2466 ERR("Could not create key %s\n",debugstr_w(deformated
));
2467 msi_free(deformated
);
2469 return ERROR_SUCCESS
;
2471 msi_free(deformated
);
2473 value
= MSI_RecordGetString(row
,5);
2475 value_data
= parse_value(package
, value
, &type
, &size
);
2478 static const WCHAR szEmpty
[] = {0};
2479 value_data
= (LPSTR
)strdupW(szEmpty
);
2484 deformat_string(package
, name
, &deformated
);
2488 TRACE("Setting value %s of %s\n",debugstr_w(deformated
),
2490 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
)value_data
, size
);
2495 rc
= RegQueryValueExW(hkey
, deformated
, NULL
, NULL
, NULL
, &sz
);
2496 if (rc
== ERROR_SUCCESS
|| rc
== ERROR_MORE_DATA
)
2498 TRACE("value %s of %s checked already exists\n",
2499 debugstr_w(deformated
), debugstr_w(uikey
));
2503 TRACE("Checked and setting value %s of %s\n",
2504 debugstr_w(deformated
), debugstr_w(uikey
));
2505 if (deformated
|| size
)
2506 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
) value_data
, size
);
2511 uirow
= MSI_CreateRecord(3);
2512 MSI_RecordSetStringW(uirow
,2,deformated
);
2513 MSI_RecordSetStringW(uirow
,1,uikey
);
2516 MSI_RecordSetStringW(uirow
,3,(LPWSTR
)value_data
);
2518 MSI_RecordSetStringW(uirow
,3,value
);
2520 ui_actiondata(package
,szWriteRegistryValues
,uirow
);
2521 msiobj_release( &uirow
->hdr
);
2523 msi_free(value_data
);
2524 msi_free(deformated
);
2527 return ERROR_SUCCESS
;
2530 static UINT
ACTION_WriteRegistryValues(MSIPACKAGE
*package
)
2534 static const WCHAR ExecSeqQuery
[] =
2535 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2536 '`','R','e','g','i','s','t','r','y','`',0 };
2538 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2539 if (rc
!= ERROR_SUCCESS
)
2540 return ERROR_SUCCESS
;
2542 /* increment progress bar each time action data is sent */
2543 ui_progress(package
,1,REG_PROGRESS_VALUE
,1,0);
2545 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteRegistryValues
, package
);
2547 msiobj_release(&view
->hdr
);
2551 static UINT
ACTION_InstallInitialize(MSIPACKAGE
*package
)
2553 package
->script
->CurrentlyScripting
= TRUE
;
2555 return ERROR_SUCCESS
;
2559 static UINT
ACTION_InstallValidate(MSIPACKAGE
*package
)
2564 static const WCHAR q1
[]=
2565 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2566 '`','R','e','g','i','s','t','r','y','`',0};
2569 MSIFEATURE
*feature
;
2572 TRACE("InstallValidate\n");
2574 rc
= MSI_DatabaseOpenViewW(package
->db
, q1
, &view
);
2575 if (rc
== ERROR_SUCCESS
)
2577 MSI_IterateRecords( view
, &progress
, NULL
, package
);
2578 msiobj_release( &view
->hdr
);
2579 total
+= progress
* REG_PROGRESS_VALUE
;
2582 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2583 total
+= COMPONENT_PROGRESS_VALUE
;
2585 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2586 total
+= file
->FileSize
;
2588 ui_progress(package
,0,total
,0,0);
2590 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2592 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2593 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
,
2594 feature
->ActionRequest
);
2597 return ERROR_SUCCESS
;
2600 static UINT
ITERATE_LaunchConditions(MSIRECORD
*row
, LPVOID param
)
2602 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
2603 LPCWSTR cond
= NULL
;
2604 LPCWSTR message
= NULL
;
2607 static const WCHAR title
[]=
2608 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2610 cond
= MSI_RecordGetString(row
,1);
2612 r
= MSI_EvaluateConditionW(package
,cond
);
2613 if (r
== MSICONDITION_FALSE
)
2615 if ((gUILevel
& INSTALLUILEVEL_MASK
) != INSTALLUILEVEL_NONE
)
2618 message
= MSI_RecordGetString(row
,2);
2619 deformat_string(package
,message
,&deformated
);
2620 MessageBoxW(NULL
,deformated
,title
,MB_OK
);
2621 msi_free(deformated
);
2624 return ERROR_INSTALL_FAILURE
;
2627 return ERROR_SUCCESS
;
2630 static UINT
ACTION_LaunchConditions(MSIPACKAGE
*package
)
2633 MSIQUERY
* view
= NULL
;
2634 static const WCHAR ExecSeqQuery
[] =
2635 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2636 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2638 TRACE("Checking launch conditions\n");
2640 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2641 if (rc
!= ERROR_SUCCESS
)
2642 return ERROR_SUCCESS
;
2644 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_LaunchConditions
, package
);
2645 msiobj_release(&view
->hdr
);
2650 static LPWSTR
resolve_keypath( MSIPACKAGE
* package
, MSICOMPONENT
*cmp
)
2654 return resolve_folder(package
,cmp
->Directory
,FALSE
,FALSE
,TRUE
,NULL
);
2656 if (cmp
->Attributes
& msidbComponentAttributesRegistryKeyPath
)
2658 MSIRECORD
* row
= 0;
2660 LPWSTR deformated
,buffer
,deformated_name
;
2662 static const WCHAR ExecSeqQuery
[] =
2663 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2664 '`','R','e','g','i','s','t','r','y','`',' ',
2665 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2666 ' ','=',' ' ,'\'','%','s','\'',0 };
2667 static const WCHAR fmt
[]={'%','0','2','i',':','\\','%','s','\\',0};
2668 static const WCHAR fmt2
[]=
2669 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2671 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
,cmp
->KeyPath
);
2675 root
= MSI_RecordGetInteger(row
,2);
2676 key
= MSI_RecordGetString(row
, 3);
2677 name
= MSI_RecordGetString(row
, 4);
2678 deformat_string(package
, key
, &deformated
);
2679 deformat_string(package
, name
, &deformated_name
);
2681 len
= strlenW(deformated
) + 6;
2682 if (deformated_name
)
2683 len
+=strlenW(deformated_name
);
2685 buffer
= msi_alloc( len
*sizeof(WCHAR
));
2687 if (deformated_name
)
2688 sprintfW(buffer
,fmt2
,root
,deformated
,deformated_name
);
2690 sprintfW(buffer
,fmt
,root
,deformated
);
2692 msi_free(deformated
);
2693 msi_free(deformated_name
);
2694 msiobj_release(&row
->hdr
);
2698 else if (cmp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2700 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2705 MSIFILE
*file
= get_loaded_file( package
, cmp
->KeyPath
);
2708 return strdupW( file
->TargetPath
);
2713 static HKEY
openSharedDLLsKey(void)
2716 static const WCHAR path
[] =
2717 {'S','o','f','t','w','a','r','e','\\',
2718 'M','i','c','r','o','s','o','f','t','\\',
2719 'W','i','n','d','o','w','s','\\',
2720 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2721 'S','h','a','r','e','d','D','L','L','s',0};
2723 RegCreateKeyW(HKEY_LOCAL_MACHINE
,path
,&hkey
);
2727 static UINT
ACTION_GetSharedDLLsCount(LPCWSTR dll
)
2732 DWORD sz
= sizeof(count
);
2735 hkey
= openSharedDLLsKey();
2736 rc
= RegQueryValueExW(hkey
, dll
, NULL
, &type
, (LPBYTE
)&count
, &sz
);
2737 if (rc
!= ERROR_SUCCESS
)
2743 static UINT
ACTION_WriteSharedDLLsCount(LPCWSTR path
, UINT count
)
2747 hkey
= openSharedDLLsKey();
2749 msi_reg_set_val_dword( hkey
, path
, count
);
2751 RegDeleteValueW(hkey
,path
);
2757 * Return TRUE if the count should be written out and FALSE if not
2759 static void ACTION_RefCountComponent( MSIPACKAGE
* package
, MSICOMPONENT
*comp
)
2761 MSIFEATURE
*feature
;
2765 /* only refcount DLLs */
2766 if (comp
->KeyPath
== NULL
||
2767 comp
->Attributes
& msidbComponentAttributesRegistryKeyPath
||
2768 comp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2772 count
= ACTION_GetSharedDLLsCount( comp
->FullKeypath
);
2773 write
= (count
> 0);
2775 if (comp
->Attributes
& msidbComponentAttributesSharedDllRefCount
)
2779 /* increment counts */
2780 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2784 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
))
2787 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2789 if ( cl
->component
== comp
)
2794 /* decrement counts */
2795 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2799 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ABSENT
))
2802 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2804 if ( cl
->component
== comp
)
2809 /* ref count all the files in the component */
2814 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2816 if (file
->Component
== comp
)
2817 ACTION_WriteSharedDLLsCount( file
->TargetPath
, count
);
2821 /* add a count for permenent */
2822 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2825 comp
->RefCount
= count
;
2828 ACTION_WriteSharedDLLsCount( comp
->FullKeypath
, comp
->RefCount
);
2832 * Ok further analysis makes me think that this work is
2833 * actually done in the PublishComponents and PublishFeatures
2834 * step, and not here. It appears like the keypath and all that is
2835 * resolved in this step, however actually written in the Publish steps.
2836 * But we will leave it here for now because it is unclear
2838 static UINT
ACTION_ProcessComponents(MSIPACKAGE
*package
)
2840 WCHAR squished_pc
[GUID_SIZE
];
2841 WCHAR squished_cc
[GUID_SIZE
];
2844 HKEY hkey
=0,hkey2
=0;
2848 /* writes the Component and Features values to the registry */
2850 rc
= MSIREG_OpenComponents(&hkey
);
2851 if (rc
!= ERROR_SUCCESS
)
2854 squash_guid(package
->ProductCode
,squished_pc
);
2855 ui_progress(package
,1,COMPONENT_PROGRESS_VALUE
,1,0);
2857 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2861 ui_progress(package
,2,0,0,0);
2862 if (!comp
->ComponentId
)
2865 squash_guid(comp
->ComponentId
,squished_cc
);
2867 msi_free(comp
->FullKeypath
);
2868 comp
->FullKeypath
= resolve_keypath( package
, comp
);
2870 /* do the refcounting */
2871 ACTION_RefCountComponent( package
, comp
);
2873 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2874 debugstr_w(comp
->Component
),
2875 debugstr_w(squished_cc
),
2876 debugstr_w(comp
->FullKeypath
),
2879 * Write the keypath out if the component is to be registered
2880 * and delete the key if the component is to be deregistered
2882 if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2884 rc
= RegCreateKeyW(hkey
,squished_cc
,&hkey2
);
2885 if (rc
!= ERROR_SUCCESS
)
2888 if (!comp
->FullKeypath
)
2891 msi_reg_set_val_str( hkey2
, squished_pc
, comp
->FullKeypath
);
2893 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2895 static const WCHAR szPermKey
[] =
2896 { '0','0','0','0','0','0','0','0','0','0','0','0',
2897 '0','0','0','0','0','0','0','0','0','0','0','0',
2898 '0','0','0','0','0','0','0','0',0 };
2900 msi_reg_set_val_str( hkey2
, szPermKey
, comp
->FullKeypath
);
2905 rc
= MSIREG_OpenUserDataComponentKey(comp
->ComponentId
, &hkey2
, TRUE
);
2906 if (rc
!= ERROR_SUCCESS
)
2909 msi_reg_set_val_str(hkey2
, squished_pc
, comp
->FullKeypath
);
2912 else if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ABSENT
))
2916 rc
= RegOpenKeyW(hkey
,squished_cc
,&hkey2
);
2917 if (rc
!= ERROR_SUCCESS
)
2920 RegDeleteValueW(hkey2
,squished_pc
);
2922 /* if the key is empty delete it */
2923 res
= RegEnumKeyExW(hkey2
,0,NULL
,0,0,NULL
,0,NULL
);
2925 if (res
== ERROR_NO_MORE_ITEMS
)
2926 RegDeleteKeyW(hkey
,squished_cc
);
2928 MSIREG_DeleteUserDataComponentKey(comp
->ComponentId
);
2932 uirow
= MSI_CreateRecord(3);
2933 MSI_RecordSetStringW(uirow
,1,package
->ProductCode
);
2934 MSI_RecordSetStringW(uirow
,2,comp
->ComponentId
);
2935 MSI_RecordSetStringW(uirow
,3,comp
->FullKeypath
);
2936 ui_actiondata(package
,szProcessComponents
,uirow
);
2937 msiobj_release( &uirow
->hdr
);
2951 static BOOL CALLBACK
Typelib_EnumResNameProc( HMODULE hModule
, LPCWSTR lpszType
,
2952 LPWSTR lpszName
, LONG_PTR lParam
)
2955 typelib_struct
*tl_struct
= (typelib_struct
*) lParam
;
2956 static const WCHAR fmt
[] = {'%','s','\\','%','i',0};
2960 if (!IS_INTRESOURCE(lpszName
))
2962 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName
));
2966 sz
= strlenW(tl_struct
->source
)+4;
2967 sz
*= sizeof(WCHAR
);
2969 if ((INT_PTR
)lpszName
== 1)
2970 tl_struct
->path
= strdupW(tl_struct
->source
);
2973 tl_struct
->path
= msi_alloc(sz
);
2974 sprintfW(tl_struct
->path
,fmt
,tl_struct
->source
, lpszName
);
2977 TRACE("trying %s\n", debugstr_w(tl_struct
->path
));
2978 res
= LoadTypeLib(tl_struct
->path
,&tl_struct
->ptLib
);
2979 if (!SUCCEEDED(res
))
2981 msi_free(tl_struct
->path
);
2982 tl_struct
->path
= NULL
;
2987 ITypeLib_GetLibAttr(tl_struct
->ptLib
, &attr
);
2988 if (IsEqualGUID(&(tl_struct
->clsid
),&(attr
->guid
)))
2990 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2994 msi_free(tl_struct
->path
);
2995 tl_struct
->path
= NULL
;
2997 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2998 ITypeLib_Release(tl_struct
->ptLib
);
3003 static UINT
ITERATE_RegisterTypeLibraries(MSIRECORD
*row
, LPVOID param
)
3005 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
3009 typelib_struct tl_struct
;
3011 static const WCHAR szTYPELIB
[] = {'T','Y','P','E','L','I','B',0};
3013 component
= MSI_RecordGetString(row
,3);
3014 comp
= get_loaded_component(package
,component
);
3016 return ERROR_SUCCESS
;
3018 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3020 TRACE("Skipping typelib reg due to disabled component\n");
3022 comp
->Action
= comp
->Installed
;
3024 return ERROR_SUCCESS
;
3027 comp
->Action
= INSTALLSTATE_LOCAL
;
3029 file
= get_loaded_file( package
, comp
->KeyPath
);
3031 return ERROR_SUCCESS
;
3033 module
= LoadLibraryExW( file
->TargetPath
, NULL
, LOAD_LIBRARY_AS_DATAFILE
);
3037 guid
= MSI_RecordGetString(row
,1);
3038 CLSIDFromString((LPWSTR
)guid
, &tl_struct
.clsid
);
3039 tl_struct
.source
= strdupW( file
->TargetPath
);
3040 tl_struct
.path
= NULL
;
3042 EnumResourceNamesW(module
, szTYPELIB
, Typelib_EnumResNameProc
,
3043 (LONG_PTR
)&tl_struct
);
3051 helpid
= MSI_RecordGetString(row
,6);
3054 help
= resolve_folder(package
,helpid
,FALSE
,FALSE
,TRUE
,NULL
);
3055 res
= RegisterTypeLib(tl_struct
.ptLib
,tl_struct
.path
,help
);
3058 if (!SUCCEEDED(res
))
3059 ERR("Failed to register type library %s\n",
3060 debugstr_w(tl_struct
.path
));
3063 ui_actiondata(package
,szRegisterTypeLibraries
,row
);
3065 TRACE("Registered %s\n", debugstr_w(tl_struct
.path
));
3068 ITypeLib_Release(tl_struct
.ptLib
);
3069 msi_free(tl_struct
.path
);
3072 ERR("Failed to load type library %s\n",
3073 debugstr_w(tl_struct
.source
));
3075 FreeLibrary(module
);
3076 msi_free(tl_struct
.source
);
3079 ERR("Could not load file! %s\n", debugstr_w(file
->TargetPath
));
3081 return ERROR_SUCCESS
;
3084 static UINT
ACTION_RegisterTypeLibraries(MSIPACKAGE
*package
)
3087 * OK this is a bit confusing.. I am given a _Component key and I believe
3088 * that the file that is being registered as a type library is the "key file
3089 * of that component" which I interpret to mean "The file in the KeyPath of
3094 static const WCHAR Query
[] =
3095 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3096 '`','T','y','p','e','L','i','b','`',0};
3098 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3099 if (rc
!= ERROR_SUCCESS
)
3100 return ERROR_SUCCESS
;
3102 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_RegisterTypeLibraries
, package
);
3103 msiobj_release(&view
->hdr
);
3107 static UINT
ITERATE_CreateShortcuts(MSIRECORD
*row
, LPVOID param
)
3109 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3110 LPWSTR target_file
, target_folder
, filename
;
3111 LPCWSTR buffer
, extension
;
3113 static const WCHAR szlnk
[]={'.','l','n','k',0};
3114 IShellLinkW
*sl
= NULL
;
3115 IPersistFile
*pf
= NULL
;
3118 buffer
= MSI_RecordGetString(row
,4);
3119 comp
= get_loaded_component(package
,buffer
);
3121 return ERROR_SUCCESS
;
3123 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3125 TRACE("Skipping shortcut creation due to disabled component\n");
3127 comp
->Action
= comp
->Installed
;
3129 return ERROR_SUCCESS
;
3132 comp
->Action
= INSTALLSTATE_LOCAL
;
3134 ui_actiondata(package
,szCreateShortcuts
,row
);
3136 res
= CoCreateInstance( &CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
,
3137 &IID_IShellLinkW
, (LPVOID
*) &sl
);
3141 ERR("CLSID_ShellLink not available\n");
3145 res
= IShellLinkW_QueryInterface( sl
, &IID_IPersistFile
,(LPVOID
*) &pf
);
3148 ERR("QueryInterface(IID_IPersistFile) failed\n");
3152 buffer
= MSI_RecordGetString(row
,2);
3153 target_folder
= resolve_folder(package
, buffer
,FALSE
,FALSE
,TRUE
,NULL
);
3155 /* may be needed because of a bug somehwere else */
3156 create_full_pathW(target_folder
);
3158 filename
= msi_dup_record_field( row
, 3 );
3159 reduce_to_longfilename(filename
);
3161 extension
= strchrW(filename
,'.');
3162 if (!extension
|| strcmpiW(extension
,szlnk
))
3164 int len
= strlenW(filename
);
3165 filename
= msi_realloc(filename
, len
* sizeof(WCHAR
) + sizeof(szlnk
));
3166 memcpy(filename
+ len
, szlnk
, sizeof(szlnk
));
3168 target_file
= build_directory_name(2, target_folder
, filename
);
3169 msi_free(target_folder
);
3172 buffer
= MSI_RecordGetString(row
,5);
3173 if (strchrW(buffer
,'['))
3176 deformat_string(package
,buffer
,&deformated
);
3177 IShellLinkW_SetPath(sl
,deformated
);
3178 msi_free(deformated
);
3182 FIXME("poorly handled shortcut format, advertised shortcut\n");
3183 IShellLinkW_SetPath(sl
,comp
->FullKeypath
);
3186 if (!MSI_RecordIsNull(row
,6))
3189 buffer
= MSI_RecordGetString(row
,6);
3190 deformat_string(package
,buffer
,&deformated
);
3191 IShellLinkW_SetArguments(sl
,deformated
);
3192 msi_free(deformated
);
3195 if (!MSI_RecordIsNull(row
,7))
3197 buffer
= MSI_RecordGetString(row
,7);
3198 IShellLinkW_SetDescription(sl
,buffer
);
3201 if (!MSI_RecordIsNull(row
,8))
3202 IShellLinkW_SetHotkey(sl
,MSI_RecordGetInteger(row
,8));
3204 if (!MSI_RecordIsNull(row
,9))
3209 buffer
= MSI_RecordGetString(row
,9);
3211 Path
= build_icon_path(package
,buffer
);
3212 index
= MSI_RecordGetInteger(row
,10);
3214 /* no value means 0 */
3215 if (index
== MSI_NULL_INTEGER
)
3218 IShellLinkW_SetIconLocation(sl
,Path
,index
);
3222 if (!MSI_RecordIsNull(row
,11))
3223 IShellLinkW_SetShowCmd(sl
,MSI_RecordGetInteger(row
,11));
3225 if (!MSI_RecordIsNull(row
,12))
3228 buffer
= MSI_RecordGetString(row
,12);
3229 Path
= resolve_folder(package
, buffer
, FALSE
, FALSE
, TRUE
, NULL
);
3231 IShellLinkW_SetWorkingDirectory(sl
,Path
);
3235 TRACE("Writing shortcut to %s\n",debugstr_w(target_file
));
3236 IPersistFile_Save(pf
,target_file
,FALSE
);
3238 msi_free(target_file
);
3242 IPersistFile_Release( pf
);
3244 IShellLinkW_Release( sl
);
3246 return ERROR_SUCCESS
;
3249 static UINT
ACTION_CreateShortcuts(MSIPACKAGE
*package
)
3254 static const WCHAR Query
[] =
3255 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3256 '`','S','h','o','r','t','c','u','t','`',0};
3258 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3259 if (rc
!= ERROR_SUCCESS
)
3260 return ERROR_SUCCESS
;
3262 res
= CoInitialize( NULL
);
3265 ERR("CoInitialize failed\n");
3266 return ERROR_FUNCTION_FAILED
;
3269 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateShortcuts
, package
);
3270 msiobj_release(&view
->hdr
);
3277 static UINT
ITERATE_PublishProduct(MSIRECORD
*row
, LPVOID param
)
3279 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
3288 FileName
= MSI_RecordGetString(row
,1);
3291 ERR("Unable to get FileName\n");
3292 return ERROR_SUCCESS
;
3295 FilePath
= build_icon_path(package
,FileName
);
3297 TRACE("Creating icon file at %s\n",debugstr_w(FilePath
));
3299 the_file
= CreateFileW(FilePath
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
,
3300 FILE_ATTRIBUTE_NORMAL
, NULL
);
3302 if (the_file
== INVALID_HANDLE_VALUE
)
3304 ERR("Unable to create file %s\n",debugstr_w(FilePath
));
3306 return ERROR_SUCCESS
;
3313 rc
= MSI_RecordReadStream(row
,2,buffer
,&sz
);
3314 if (rc
!= ERROR_SUCCESS
)
3316 ERR("Failed to get stream\n");
3317 CloseHandle(the_file
);
3318 DeleteFileW(FilePath
);
3321 WriteFile(the_file
,buffer
,sz
,&write
,NULL
);
3322 } while (sz
== 1024);
3326 CloseHandle(the_file
);
3328 uirow
= MSI_CreateRecord(1);
3329 MSI_RecordSetStringW(uirow
,1,FileName
);
3330 ui_actiondata(package
,szPublishProduct
,uirow
);
3331 msiobj_release( &uirow
->hdr
);
3333 return ERROR_SUCCESS
;
3336 static BOOL
msi_check_publish(MSIPACKAGE
*package
)
3338 MSIFEATURE
*feature
;
3340 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3342 if (feature
->ActionRequest
== INSTALLSTATE_LOCAL
)
3350 * 99% of the work done here is only done for
3351 * advertised installs. However this is where the
3352 * Icon table is processed and written out
3353 * so that is what I am going to do here.
3355 static UINT
ACTION_PublishProduct(MSIPACKAGE
*package
)
3360 MSISOURCELISTINFO
*info
;
3362 static const WCHAR Query
[]=
3363 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3364 '`','I','c','o','n','`',0};
3365 /* for registry stuff */
3368 HKEY hudkey
=0, props
=0;
3370 static const WCHAR szProductLanguage
[] =
3371 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3372 static const WCHAR szARPProductIcon
[] =
3373 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3374 static const WCHAR szProductVersion
[] =
3375 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3376 static const WCHAR szSourceList
[] =
3377 {'S','o','u','r','c','e','L','i','s','t',0};
3378 static const WCHAR szEmpty
[] = {0};
3382 MSIHANDLE hDb
, hSumInfo
;
3384 /* FIXME: also need to publish if the product is in advertise mode */
3385 if (!msi_check_publish(package
))
3386 return ERROR_SUCCESS
;
3388 /* write out icon files */
3390 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3391 if (rc
== ERROR_SUCCESS
)
3393 MSI_IterateRecords(view
, NULL
, ITERATE_PublishProduct
, package
);
3394 msiobj_release(&view
->hdr
);
3397 /* ok there is a lot more done here but i need to figure out what */
3399 rc
= MSIREG_OpenProductsKey(package
->ProductCode
,&hkey
,TRUE
);
3400 if (rc
!= ERROR_SUCCESS
)
3403 rc
= MSIREG_OpenUserProductsKey(package
->ProductCode
,&hukey
,TRUE
);
3404 if (rc
!= ERROR_SUCCESS
)
3407 rc
= RegCreateKeyW(hukey
, szSourceList
, &source
);
3408 if (rc
!= ERROR_SUCCESS
)
3411 RegCloseKey(source
);
3413 rc
= MSIREG_OpenUserDataProductKey(package
->ProductCode
,&hudkey
,TRUE
);
3414 if (rc
!= ERROR_SUCCESS
)
3417 rc
= MSIREG_OpenInstallPropertiesKey(package
->ProductCode
,&props
,TRUE
);
3418 if (rc
!= ERROR_SUCCESS
)
3421 buffer
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTNAMEW
);
3422 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PRODUCTNAMEW
, buffer
);
3425 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
3426 msi_reg_set_val_dword( hukey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
3428 packname
= strrchrW( package
->PackagePath
, '\\' ) + 1;
3429 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PACKAGENAMEW
, packname
);
3432 msi_reg_set_val_dword( hukey
, INSTALLPROPERTY_AUTHORIZED_LUA_APPW
, 0 );
3433 msi_reg_set_val_dword( props
, INSTALLPROPERTY_INSTANCETYPEW
, 0 );
3435 buffer
= msi_dup_property( package
, szARPProductIcon
);
3438 LPWSTR path
= build_icon_path(package
,buffer
);
3439 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PRODUCTICONW
, path
);
3444 buffer
= msi_dup_property( package
, szProductVersion
);
3447 DWORD verdword
= msi_version_str_to_dword(buffer
);
3448 msi_reg_set_val_dword( hukey
, INSTALLPROPERTY_VERSIONW
, verdword
);
3452 buffer
= strrchrW( package
->PackagePath
, '\\') + 1;
3453 rc
= MsiSourceListSetInfoW( package
->ProductCode
, NULL
,
3454 MSIINSTALLCONTEXT_USERUNMANAGED
, MSICODE_PRODUCT
,
3455 INSTALLPROPERTY_PACKAGENAMEW
, buffer
);
3456 if (rc
!= ERROR_SUCCESS
)
3459 rc
= MsiSourceListSetInfoW( package
->ProductCode
, NULL
,
3460 MSIINSTALLCONTEXT_USERUNMANAGED
, MSICODE_PRODUCT
,
3461 INSTALLPROPERTY_MEDIAPACKAGEPATHW
, szEmpty
);
3462 if (rc
!= ERROR_SUCCESS
)
3465 rc
= MsiSourceListSetInfoW( package
->ProductCode
, NULL
,
3466 MSIINSTALLCONTEXT_USERUNMANAGED
, MSICODE_PRODUCT
,
3467 INSTALLPROPERTY_DISKPROMPTW
, szEmpty
);
3468 if (rc
!= ERROR_SUCCESS
)
3471 /* FIXME: Need to write more keys to the user registry */
3473 hDb
= alloc_msihandle( &package
->db
->hdr
);
3475 rc
= ERROR_NOT_ENOUGH_MEMORY
;
3478 rc
= MsiGetSummaryInformationW(hDb
, NULL
, 0, &hSumInfo
);
3479 MsiCloseHandle(hDb
);
3480 if (rc
== ERROR_SUCCESS
)
3482 WCHAR guidbuffer
[0x200];
3484 rc
= MsiSummaryInfoGetPropertyW(hSumInfo
, 9, NULL
, NULL
, NULL
,
3486 if (rc
== ERROR_SUCCESS
)
3488 /* for now we only care about the first guid */
3489 LPWSTR ptr
= strchrW(guidbuffer
,';');
3491 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PACKAGECODEW
, guidbuffer
);
3495 ERR("Unable to query Revision_Number...\n");
3498 MsiCloseHandle(hSumInfo
);
3502 ERR("Unable to open Summary Information\n");
3506 /* publish the SourceList info */
3507 LIST_FOR_EACH_ENTRY(info
, &package
->sourcelist_info
, MSISOURCELISTINFO
, entry
)
3509 if (!lstrcmpW(info
->property
, INSTALLPROPERTY_LASTUSEDSOURCEW
))
3510 msi_set_last_used_source(package
->ProductCode
, NULL
, info
->context
,
3511 info
->options
, info
->value
);
3513 MsiSourceListSetInfoW(package
->ProductCode
, NULL
,
3514 info
->context
, info
->options
,
3515 info
->property
, info
->value
);
3518 LIST_FOR_EACH_ENTRY(disk
, &package
->sourcelist_media
, MSIMEDIADISK
, entry
)
3520 MsiSourceListAddMediaDiskW(package
->ProductCode
, NULL
,
3521 disk
->context
, disk
->options
,
3522 disk
->disk_id
, disk
->volume_label
, disk
->disk_prompt
);
3528 RegCloseKey(hudkey
);
3534 static UINT
ITERATE_WriteIniValues(MSIRECORD
*row
, LPVOID param
)
3536 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3537 LPCWSTR component
,section
,key
,value
,identifier
,filename
,dirproperty
;
3538 LPWSTR deformated_section
, deformated_key
, deformated_value
;
3539 LPWSTR folder
, fullname
= NULL
;
3543 static const WCHAR szWindowsFolder
[] =
3544 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3546 component
= MSI_RecordGetString(row
, 8);
3547 comp
= get_loaded_component(package
,component
);
3549 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3551 TRACE("Skipping ini file due to disabled component %s\n",
3552 debugstr_w(component
));
3554 comp
->Action
= comp
->Installed
;
3556 return ERROR_SUCCESS
;
3559 comp
->Action
= INSTALLSTATE_LOCAL
;
3561 identifier
= MSI_RecordGetString(row
,1);
3562 filename
= MSI_RecordGetString(row
,2);
3563 dirproperty
= MSI_RecordGetString(row
,3);
3564 section
= MSI_RecordGetString(row
,4);
3565 key
= MSI_RecordGetString(row
,5);
3566 value
= MSI_RecordGetString(row
,6);
3567 action
= MSI_RecordGetInteger(row
,7);
3569 deformat_string(package
,section
,&deformated_section
);
3570 deformat_string(package
,key
,&deformated_key
);
3571 deformat_string(package
,value
,&deformated_value
);
3575 folder
= resolve_folder(package
, dirproperty
, FALSE
, FALSE
, TRUE
, NULL
);
3577 folder
= msi_dup_property( package
, dirproperty
);
3580 folder
= msi_dup_property( package
, szWindowsFolder
);
3584 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty
));
3588 fullname
= build_directory_name(2, folder
, filename
);
3592 TRACE("Adding value %s to section %s in %s\n",
3593 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3594 debugstr_w(fullname
));
3595 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3596 deformated_value
, fullname
);
3598 else if (action
== 1)
3601 GetPrivateProfileStringW(deformated_section
, deformated_key
, NULL
,
3602 returned
, 10, fullname
);
3603 if (returned
[0] == 0)
3605 TRACE("Adding value %s to section %s in %s\n",
3606 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3607 debugstr_w(fullname
));
3609 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3610 deformated_value
, fullname
);
3613 else if (action
== 3)
3614 FIXME("Append to existing section not yet implemented\n");
3616 uirow
= MSI_CreateRecord(4);
3617 MSI_RecordSetStringW(uirow
,1,identifier
);
3618 MSI_RecordSetStringW(uirow
,2,deformated_section
);
3619 MSI_RecordSetStringW(uirow
,3,deformated_key
);
3620 MSI_RecordSetStringW(uirow
,4,deformated_value
);
3621 ui_actiondata(package
,szWriteIniValues
,uirow
);
3622 msiobj_release( &uirow
->hdr
);
3626 msi_free(deformated_key
);
3627 msi_free(deformated_value
);
3628 msi_free(deformated_section
);
3629 return ERROR_SUCCESS
;
3632 static UINT
ACTION_WriteIniValues(MSIPACKAGE
*package
)
3636 static const WCHAR ExecSeqQuery
[] =
3637 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3638 '`','I','n','i','F','i','l','e','`',0};
3640 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3641 if (rc
!= ERROR_SUCCESS
)
3643 TRACE("no IniFile table\n");
3644 return ERROR_SUCCESS
;
3647 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteIniValues
, package
);
3648 msiobj_release(&view
->hdr
);
3652 static UINT
ITERATE_SelfRegModules(MSIRECORD
*row
, LPVOID param
)
3654 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3659 static const WCHAR ExeStr
[] =
3660 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3661 static const WCHAR close
[] = {'\"',0};
3663 PROCESS_INFORMATION info
;
3668 memset(&si
,0,sizeof(STARTUPINFOW
));
3670 filename
= MSI_RecordGetString(row
,1);
3671 file
= get_loaded_file( package
, filename
);
3675 ERR("Unable to find file id %s\n",debugstr_w(filename
));
3676 return ERROR_SUCCESS
;
3679 len
= strlenW(ExeStr
) + strlenW( file
->TargetPath
) + 2;
3681 FullName
= msi_alloc(len
*sizeof(WCHAR
));
3682 strcpyW(FullName
,ExeStr
);
3683 strcatW( FullName
, file
->TargetPath
);
3684 strcatW(FullName
,close
);
3686 TRACE("Registering %s\n",debugstr_w(FullName
));
3687 brc
= CreateProcessW(NULL
, FullName
, NULL
, NULL
, FALSE
, 0, NULL
, c_colon
,
3691 msi_dialog_check_messages(info
.hProcess
);
3696 uirow
= MSI_CreateRecord( 2 );
3697 uipath
= strdupW( file
->TargetPath
);
3698 p
= strrchrW(uipath
,'\\');
3701 MSI_RecordSetStringW( uirow
, 1, &p
[1] );
3702 MSI_RecordSetStringW( uirow
, 2, uipath
);
3703 ui_actiondata( package
, szSelfRegModules
, uirow
);
3704 msiobj_release( &uirow
->hdr
);
3706 /* FIXME: call ui_progress? */
3708 return ERROR_SUCCESS
;
3711 static UINT
ACTION_SelfRegModules(MSIPACKAGE
*package
)
3715 static const WCHAR ExecSeqQuery
[] =
3716 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3717 '`','S','e','l','f','R','e','g','`',0};
3719 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3720 if (rc
!= ERROR_SUCCESS
)
3722 TRACE("no SelfReg table\n");
3723 return ERROR_SUCCESS
;
3726 MSI_IterateRecords(view
, NULL
, ITERATE_SelfRegModules
, package
);
3727 msiobj_release(&view
->hdr
);
3729 return ERROR_SUCCESS
;
3732 static UINT
ACTION_PublishFeatures(MSIPACKAGE
*package
)
3734 MSIFEATURE
*feature
;
3740 if (!msi_check_publish(package
))
3741 return ERROR_SUCCESS
;
3743 rc
= MSIREG_OpenFeaturesKey(package
->ProductCode
,&hkey
,TRUE
);
3744 if (rc
!= ERROR_SUCCESS
)
3747 rc
= MSIREG_OpenUserFeaturesKey(package
->ProductCode
,&hukey
,TRUE
);
3748 if (rc
!= ERROR_SUCCESS
)
3751 rc
= MSIREG_OpenUserDataFeaturesKey(package
->ProductCode
, &userdata
, TRUE
);
3752 if (rc
!= ERROR_SUCCESS
)
3755 /* here the guids are base 85 encoded */
3756 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
3762 BOOL absent
= FALSE
;
3765 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
) &&
3766 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_SOURCE
) &&
3767 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ADVERTISED
))
3771 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3775 if (feature
->Feature_Parent
)
3776 size
+= strlenW( feature
->Feature_Parent
)+2;
3778 data
= msi_alloc(size
* sizeof(WCHAR
));
3781 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3783 MSICOMPONENT
* component
= cl
->component
;
3787 if (component
->ComponentId
)
3789 TRACE("From %s\n",debugstr_w(component
->ComponentId
));
3790 CLSIDFromString(component
->ComponentId
, &clsid
);
3791 encode_base85_guid(&clsid
,buf
);
3792 TRACE("to %s\n",debugstr_w(buf
));
3797 if (feature
->Feature_Parent
)
3799 static const WCHAR sep
[] = {'\2',0};
3801 strcatW(data
,feature
->Feature_Parent
);
3804 msi_reg_set_val_str( hkey
, feature
->Feature
, data
);
3805 msi_reg_set_val_str( userdata
, feature
->Feature
, data
);
3809 if (feature
->Feature_Parent
)
3810 size
= strlenW(feature
->Feature_Parent
)*sizeof(WCHAR
);
3813 RegSetValueExW(hukey
,feature
->Feature
,0,REG_SZ
,
3814 (LPBYTE
)feature
->Feature_Parent
,size
);
3818 size
+= 2*sizeof(WCHAR
);
3819 data
= msi_alloc(size
);
3822 if (feature
->Feature_Parent
)
3823 strcpyW( &data
[1], feature
->Feature_Parent
);
3824 RegSetValueExW(hukey
,feature
->Feature
,0,REG_SZ
,
3830 uirow
= MSI_CreateRecord( 1 );
3831 MSI_RecordSetStringW( uirow
, 1, feature
->Feature
);
3832 ui_actiondata( package
, szPublishFeatures
, uirow
);
3833 msiobj_release( &uirow
->hdr
);
3834 /* FIXME: call ui_progress? */
3843 static UINT
msi_unpublish_feature(MSIPACKAGE
*package
, MSIFEATURE
*feature
)
3848 TRACE("unpublishing feature %s\n", debugstr_w(feature
->Feature
));
3850 r
= MSIREG_OpenUserFeaturesKey(package
->ProductCode
, &hkey
, FALSE
);
3851 if (r
== ERROR_SUCCESS
)
3853 RegDeleteValueW(hkey
, feature
->Feature
);
3857 r
= MSIREG_OpenUserDataFeaturesKey(package
->ProductCode
, &hkey
, FALSE
);
3858 if (r
== ERROR_SUCCESS
)
3860 RegDeleteValueW(hkey
, feature
->Feature
);
3864 return ERROR_SUCCESS
;
3867 static BOOL
msi_check_unpublish(MSIPACKAGE
*package
)
3869 MSIFEATURE
*feature
;
3871 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3873 if (feature
->ActionRequest
!= INSTALLSTATE_ABSENT
)
3880 static UINT
ACTION_UnpublishFeatures(MSIPACKAGE
*package
)
3882 MSIFEATURE
*feature
;
3884 if (!msi_check_unpublish(package
))
3885 return ERROR_SUCCESS
;
3887 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3889 msi_unpublish_feature(package
, feature
);
3892 return ERROR_SUCCESS
;
3895 static UINT
msi_get_local_package_name( LPWSTR path
)
3897 static const WCHAR szInstaller
[] = {
3898 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3899 static const WCHAR fmt
[] = { '%','x','.','m','s','i',0};
3903 time
= GetTickCount();
3904 GetWindowsDirectoryW( path
, MAX_PATH
);
3905 lstrcatW( path
, szInstaller
);
3906 CreateDirectoryW( path
, NULL
);
3908 len
= lstrlenW(path
);
3909 for (i
=0; i
<0x10000; i
++)
3911 snprintfW( &path
[len
], MAX_PATH
- len
, fmt
, (time
+i
)&0xffff );
3912 handle
= CreateFileW( path
, GENERIC_WRITE
, 0, NULL
,
3913 CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, 0 );
3914 if (handle
!= INVALID_HANDLE_VALUE
)
3916 CloseHandle(handle
);
3919 if (GetLastError() != ERROR_FILE_EXISTS
&&
3920 GetLastError() != ERROR_SHARING_VIOLATION
)
3921 return ERROR_FUNCTION_FAILED
;
3924 return ERROR_SUCCESS
;
3927 static UINT
msi_make_package_local( MSIPACKAGE
*package
, HKEY hkey
)
3929 WCHAR packagefile
[MAX_PATH
];
3933 r
= msi_get_local_package_name( packagefile
);
3934 if (r
!= ERROR_SUCCESS
)
3937 TRACE("Copying to local package %s\n",debugstr_w(packagefile
));
3939 r
= CopyFileW( package
->db
->path
, packagefile
, FALSE
);
3943 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3944 debugstr_w(package
->db
->path
), debugstr_w(packagefile
), GetLastError());
3945 return ERROR_FUNCTION_FAILED
;
3948 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_LOCALPACKAGEW
, packagefile
);
3950 r
= MSIREG_OpenInstallPropertiesKey(package
->ProductCode
, &props
, TRUE
);
3951 if (r
!= ERROR_SUCCESS
)
3954 msi_reg_set_val_str(props
, INSTALLPROPERTY_LOCALPACKAGEW
, packagefile
);
3956 return ERROR_SUCCESS
;
3959 static UINT
msi_write_uninstall_property_vals( MSIPACKAGE
*package
, HKEY hkey
)
3961 LPWSTR prop
, val
, key
;
3962 static const LPCSTR propval
[] = {
3963 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3964 "ARPCONTACT", "Contact",
3965 "ARPCOMMENTS", "Comments",
3966 "ProductName", "DisplayName",
3967 "ProductVersion", "DisplayVersion",
3968 "ARPHELPLINK", "HelpLink",
3969 "ARPHELPTELEPHONE", "HelpTelephone",
3970 "ARPINSTALLLOCATION", "InstallLocation",
3971 "SourceDir", "InstallSource",
3972 "Manufacturer", "Publisher",
3973 "ARPREADME", "Readme",
3975 "ARPURLINFOABOUT", "URLInfoAbout",
3976 "ARPURLUPDATEINFO", "URLUpdateInfo",
3979 const LPCSTR
*p
= propval
;
3983 prop
= strdupAtoW( *p
++ );
3984 key
= strdupAtoW( *p
++ );
3985 val
= msi_dup_property( package
, prop
);
3986 msi_reg_set_val_str( hkey
, key
, val
);
3991 return ERROR_SUCCESS
;
3994 static UINT
ACTION_RegisterProduct(MSIPACKAGE
*package
)
3997 HKEY hudkey
=0, props
=0;
3998 LPWSTR buffer
= NULL
;
4001 static const WCHAR szWindowsInstaller
[] =
4002 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4003 static const WCHAR szUpgradeCode
[] =
4004 {'U','p','g','r','a','d','e','C','o','d','e',0};
4005 static const WCHAR modpath_fmt
[] =
4006 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4007 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4008 static const WCHAR szModifyPath
[] =
4009 {'M','o','d','i','f','y','P','a','t','h',0};
4010 static const WCHAR szUninstallString
[] =
4011 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4012 static const WCHAR szEstimatedSize
[] =
4013 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4014 static const WCHAR szProductLanguage
[] =
4015 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4016 static const WCHAR szProductVersion
[] =
4017 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4018 static const WCHAR szProductName
[] =
4019 {'P','r','o','d','u','c','t','N','a','m','e',0};
4020 static const WCHAR szDisplayName
[] =
4021 {'D','i','s','p','l','a','y','N','a','m','e',0};
4022 static const WCHAR szDisplayVersion
[] =
4023 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4024 static const WCHAR szManufacturer
[] =
4025 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4028 static const WCHAR date_fmt
[] = {'%','i','%','0','2','i','%','0','2','i',0};
4029 LPWSTR upgrade_code
;
4032 /* FIXME: also need to publish if the product is in advertise mode */
4033 if (!msi_check_publish(package
))
4034 return ERROR_SUCCESS
;
4036 rc
= MSIREG_OpenUninstallKey(package
->ProductCode
,&hkey
,TRUE
);
4037 if (rc
!= ERROR_SUCCESS
)
4040 rc
= MSIREG_OpenInstallPropertiesKey(package
->ProductCode
, &props
, TRUE
);
4041 if (rc
!= ERROR_SUCCESS
)
4044 /* dump all the info i can grab */
4045 /* FIXME: Flesh out more information */
4047 msi_write_uninstall_property_vals( package
, hkey
);
4049 msi_reg_set_val_dword( hkey
, szWindowsInstaller
, 1 );
4051 msi_make_package_local( package
, hkey
);
4053 /* do ModifyPath and UninstallString */
4054 size
= deformat_string(package
,modpath_fmt
,&buffer
);
4055 RegSetValueExW(hkey
,szModifyPath
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
4056 RegSetValueExW(hkey
,szUninstallString
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
4059 /* FIXME: Write real Estimated Size when we have it */
4060 msi_reg_set_val_dword( hkey
, szEstimatedSize
, 0 );
4062 buffer
= msi_dup_property( package
, szProductName
);
4063 msi_reg_set_val_str( props
, szDisplayName
, buffer
);
4066 buffer
= msi_dup_property( package
, cszSourceDir
);
4067 msi_reg_set_val_str( props
, INSTALLPROPERTY_INSTALLSOURCEW
, buffer
);
4070 buffer
= msi_dup_property( package
, szManufacturer
);
4071 msi_reg_set_val_str( props
, INSTALLPROPERTY_PUBLISHERW
, buffer
);
4074 GetLocalTime(&systime
);
4075 sprintfW(szDate
,date_fmt
,systime
.wYear
,systime
.wMonth
,systime
.wDay
);
4076 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_INSTALLDATEW
, szDate
);
4077 msi_reg_set_val_str( props
, INSTALLPROPERTY_INSTALLDATEW
, szDate
);
4079 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
4080 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
4082 buffer
= msi_dup_property( package
, szProductVersion
);
4083 msi_reg_set_val_str( props
, szDisplayVersion
, buffer
);
4086 DWORD verdword
= msi_version_str_to_dword(buffer
);
4088 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
4089 msi_reg_set_val_dword( props
, INSTALLPROPERTY_VERSIONW
, verdword
);
4090 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMAJORW
, verdword
>>24 );
4091 msi_reg_set_val_dword( props
, INSTALLPROPERTY_VERSIONMAJORW
, verdword
>>24 );
4092 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMINORW
, (verdword
>>16)&0x00FF );
4093 msi_reg_set_val_dword( props
, INSTALLPROPERTY_VERSIONMINORW
, (verdword
>>16)&0x00FF );
4097 /* Handle Upgrade Codes */
4098 upgrade_code
= msi_dup_property( package
, szUpgradeCode
);
4103 MSIREG_OpenUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
4104 squash_guid(package
->ProductCode
,squashed
);
4105 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
4107 MSIREG_OpenUserUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
4108 squash_guid(package
->ProductCode
,squashed
);
4109 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
4112 msi_free(upgrade_code
);
4117 rc
= MSIREG_OpenUserDataProductKey(package
->ProductCode
, &hudkey
, TRUE
);
4118 if (rc
!= ERROR_SUCCESS
)
4121 RegCloseKey(hudkey
);
4123 msi_reg_set_val_dword( props
, szWindowsInstaller
, 1 );
4126 return ERROR_SUCCESS
;
4129 static UINT
ACTION_InstallExecute(MSIPACKAGE
*package
)
4131 return execute_script(package
,INSTALL_SCRIPT
);
4134 static UINT
msi_unpublish_product(MSIPACKAGE
*package
)
4136 LPWSTR remove
= NULL
;
4137 LPWSTR
*features
= NULL
;
4138 BOOL full_uninstall
= TRUE
;
4139 MSIFEATURE
*feature
;
4141 static const WCHAR szRemove
[] = {'R','E','M','O','V','E',0};
4142 static const WCHAR szAll
[] = {'A','L','L',0};
4144 remove
= msi_dup_property(package
, szRemove
);
4146 return ERROR_SUCCESS
;
4148 features
= msi_split_string(remove
, ',');
4152 ERR("REMOVE feature list is empty!\n");
4153 return ERROR_FUNCTION_FAILED
;
4156 if (!lstrcmpW(features
[0], szAll
))
4157 full_uninstall
= TRUE
;
4160 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
4162 if (feature
->Action
!= INSTALLSTATE_ABSENT
)
4163 full_uninstall
= FALSE
;
4167 if (!full_uninstall
)
4170 MSIREG_DeleteProductKey(package
->ProductCode
);
4171 MSIREG_DeleteUserProductKey(package
->ProductCode
);
4172 MSIREG_DeleteUserDataProductKey(package
->ProductCode
);
4173 MSIREG_DeleteUserFeaturesKey(package
->ProductCode
);
4174 MSIREG_DeleteUninstallKey(package
->ProductCode
);
4179 return ERROR_SUCCESS
;
4182 static UINT
ACTION_InstallFinalize(MSIPACKAGE
*package
)
4186 rc
= msi_unpublish_product(package
);
4187 if (rc
!= ERROR_SUCCESS
)
4190 /* turn off scheduling */
4191 package
->script
->CurrentlyScripting
= FALSE
;
4193 /* first do the same as an InstallExecute */
4194 rc
= ACTION_InstallExecute(package
);
4195 if (rc
!= ERROR_SUCCESS
)
4198 /* then handle Commit Actions */
4199 rc
= execute_script(package
,COMMIT_SCRIPT
);
4204 UINT
ACTION_ForceReboot(MSIPACKAGE
*package
)
4206 static const WCHAR RunOnce
[] = {
4207 'S','o','f','t','w','a','r','e','\\',
4208 'M','i','c','r','o','s','o','f','t','\\',
4209 'W','i','n','d','o','w','s','\\',
4210 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4211 'R','u','n','O','n','c','e',0};
4212 static const WCHAR InstallRunOnce
[] = {
4213 'S','o','f','t','w','a','r','e','\\',
4214 'M','i','c','r','o','s','o','f','t','\\',
4215 'W','i','n','d','o','w','s','\\',
4216 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4217 'I','n','s','t','a','l','l','e','r','\\',
4218 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4220 static const WCHAR msiexec_fmt
[] = {
4222 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4223 '\"','%','s','\"',0};
4224 static const WCHAR install_fmt
[] = {
4225 '/','I',' ','\"','%','s','\"',' ',
4226 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4227 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4228 WCHAR buffer
[256], sysdir
[MAX_PATH
];
4230 WCHAR squished_pc
[100];
4232 squash_guid(package
->ProductCode
,squished_pc
);
4234 GetSystemDirectoryW(sysdir
, sizeof(sysdir
)/sizeof(sysdir
[0]));
4235 RegCreateKeyW(HKEY_LOCAL_MACHINE
,RunOnce
,&hkey
);
4236 snprintfW(buffer
,sizeof(buffer
)/sizeof(buffer
[0]),msiexec_fmt
,sysdir
,
4239 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
4242 TRACE("Reboot command %s\n",debugstr_w(buffer
));
4244 RegCreateKeyW(HKEY_LOCAL_MACHINE
,InstallRunOnce
,&hkey
);
4245 sprintfW(buffer
,install_fmt
,package
->ProductCode
,squished_pc
);
4247 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
4250 return ERROR_INSTALL_SUSPEND
;
4253 static UINT
ACTION_ResolveSource(MSIPACKAGE
* package
)
4259 * We are currently doing what should be done here in the top level Install
4260 * however for Administrative and uninstalls this step will be needed
4262 if (!package
->PackagePath
)
4263 return ERROR_SUCCESS
;
4265 msi_set_sourcedir_props(package
, TRUE
);
4267 attrib
= GetFileAttributesW(package
->db
->path
);
4268 if (attrib
== INVALID_FILE_ATTRIBUTES
)
4274 rc
= MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
4275 MSIINSTALLCONTEXT_USERUNMANAGED
, MSICODE_PRODUCT
,
4276 INSTALLPROPERTY_DISKPROMPTW
,NULL
,&size
);
4277 if (rc
== ERROR_MORE_DATA
)
4279 prompt
= msi_alloc(size
* sizeof(WCHAR
));
4280 MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
4281 MSIINSTALLCONTEXT_USERUNMANAGED
, MSICODE_PRODUCT
,
4282 INSTALLPROPERTY_DISKPROMPTW
,prompt
,&size
);
4285 prompt
= strdupW(package
->db
->path
);
4287 msg
= generate_error_string(package
,1302,1,prompt
);
4288 while(attrib
== INVALID_FILE_ATTRIBUTES
)
4290 rc
= MessageBoxW(NULL
,msg
,NULL
,MB_OKCANCEL
);
4293 rc
= ERROR_INSTALL_USEREXIT
;
4296 attrib
= GetFileAttributesW(package
->db
->path
);
4302 return ERROR_SUCCESS
;
4307 static UINT
ACTION_RegisterUser(MSIPACKAGE
*package
)
4314 static const WCHAR szPropKeys
[][80] =
4316 {'P','r','o','d','u','c','t','I','D',0},
4317 {'U','S','E','R','N','A','M','E',0},
4318 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4322 static const WCHAR szRegKeys
[][80] =
4324 {'P','r','o','d','u','c','t','I','D',0},
4325 {'R','e','g','O','w','n','e','r',0},
4326 {'R','e','g','C','o','m','p','a','n','y',0},
4330 if (msi_check_unpublish(package
))
4332 MSIREG_DeleteUserDataProductKey(package
->ProductCode
);
4333 return ERROR_SUCCESS
;
4336 productid
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTIDW
);
4338 return ERROR_SUCCESS
;
4340 rc
= MSIREG_OpenInstallPropertiesKey(package
->ProductCode
, &hkey
, TRUE
);
4341 if (rc
!= ERROR_SUCCESS
)
4344 for( i
= 0; szPropKeys
[i
][0]; i
++ )
4346 buffer
= msi_dup_property( package
, szPropKeys
[i
] );
4347 msi_reg_set_val_str( hkey
, szRegKeys
[i
], buffer
);
4352 msi_free(productid
);
4355 /* FIXME: call ui_actiondata */
4361 static UINT
ACTION_ExecuteAction(MSIPACKAGE
*package
)
4365 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
4366 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
4371 static UINT
ITERATE_PublishComponent(MSIRECORD
*rec
, LPVOID param
)
4373 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4374 LPCWSTR compgroupid
=NULL
;
4375 LPCWSTR feature
=NULL
;
4376 LPCWSTR text
= NULL
;
4377 LPCWSTR qualifier
= NULL
;
4378 LPCWSTR component
= NULL
;
4379 LPWSTR advertise
= NULL
;
4380 LPWSTR output
= NULL
;
4382 UINT rc
= ERROR_SUCCESS
;
4387 component
= MSI_RecordGetString(rec
,3);
4388 comp
= get_loaded_component(package
,component
);
4390 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
) &&
4391 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_SOURCE
) &&
4392 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ADVERTISED
))
4394 TRACE("Skipping: Component %s not scheduled for install\n",
4395 debugstr_w(component
));
4397 return ERROR_SUCCESS
;
4400 compgroupid
= MSI_RecordGetString(rec
,1);
4401 qualifier
= MSI_RecordGetString(rec
,2);
4403 rc
= MSIREG_OpenUserComponentsKey(compgroupid
, &hkey
, TRUE
);
4404 if (rc
!= ERROR_SUCCESS
)
4407 text
= MSI_RecordGetString(rec
,4);
4408 feature
= MSI_RecordGetString(rec
,5);
4410 advertise
= create_component_advertise_string(package
, comp
, feature
);
4412 sz
= strlenW(advertise
);
4415 sz
+= lstrlenW(text
);
4418 sz
*= sizeof(WCHAR
);
4420 output
= msi_alloc_zero(sz
);
4421 strcpyW(output
,advertise
);
4422 msi_free(advertise
);
4425 strcatW(output
,text
);
4427 msi_reg_set_val_multi_str( hkey
, qualifier
, output
);
4434 uirow
= MSI_CreateRecord( 2 );
4435 MSI_RecordSetStringW( uirow
, 1, compgroupid
);
4436 MSI_RecordSetStringW( uirow
, 2, qualifier
);
4437 ui_actiondata( package
, szPublishComponents
, uirow
);
4438 msiobj_release( &uirow
->hdr
);
4439 /* FIXME: call ui_progress? */
4445 * At present I am ignorning the advertised components part of this and only
4446 * focusing on the qualified component sets
4448 static UINT
ACTION_PublishComponents(MSIPACKAGE
*package
)
4452 static const WCHAR ExecSeqQuery
[] =
4453 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4454 '`','P','u','b','l','i','s','h',
4455 'C','o','m','p','o','n','e','n','t','`',0};
4457 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4458 if (rc
!= ERROR_SUCCESS
)
4459 return ERROR_SUCCESS
;
4461 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_PublishComponent
, package
);
4462 msiobj_release(&view
->hdr
);
4467 static UINT
ITERATE_InstallService(MSIRECORD
*rec
, LPVOID param
)
4469 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4472 SC_HANDLE hscm
, service
= NULL
;
4473 LPCWSTR comp
, depends
, pass
;
4475 LPCWSTR load_order
, serv_name
, key
;
4476 DWORD serv_type
, start_type
;
4479 static const WCHAR query
[] =
4480 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4481 '`','C','o','m','p','o','n','e','n','t','`',' ',
4482 'W','H','E','R','E',' ',
4483 '`','C','o','m','p','o','n','e','n','t','`',' ',
4484 '=','\'','%','s','\'',0};
4486 hscm
= OpenSCManagerW(NULL
, SERVICES_ACTIVE_DATABASEW
, GENERIC_WRITE
);
4489 ERR("Failed to open the SC Manager!\n");
4493 start_type
= MSI_RecordGetInteger(rec
, 5);
4494 if (start_type
== SERVICE_BOOT_START
|| start_type
== SERVICE_SYSTEM_START
)
4497 depends
= MSI_RecordGetString(rec
, 8);
4498 if (depends
&& *depends
)
4499 FIXME("Dependency list unhandled!\n");
4501 deformat_string(package
, MSI_RecordGetString(rec
, 2), &name
);
4502 deformat_string(package
, MSI_RecordGetString(rec
, 3), &disp
);
4503 serv_type
= MSI_RecordGetInteger(rec
, 4);
4504 err_control
= MSI_RecordGetInteger(rec
, 6);
4505 load_order
= MSI_RecordGetString(rec
, 7);
4506 serv_name
= MSI_RecordGetString(rec
, 9);
4507 pass
= MSI_RecordGetString(rec
, 10);
4508 comp
= MSI_RecordGetString(rec
, 12);
4510 /* fetch the service path */
4511 row
= MSI_QueryGetRecord(package
->db
, query
, comp
);
4514 ERR("Control query failed!\n");
4518 key
= MSI_RecordGetString(row
, 6);
4520 file
= get_loaded_file(package
, key
);
4521 msiobj_release(&row
->hdr
);
4524 ERR("Failed to load the service file\n");
4528 service
= CreateServiceW(hscm
, name
, disp
, GENERIC_ALL
, serv_type
,
4529 start_type
, err_control
, file
->TargetPath
,
4530 load_order
, NULL
, NULL
, serv_name
, pass
);
4533 if (GetLastError() != ERROR_SERVICE_EXISTS
)
4534 ERR("Failed to create service %s: %d\n", debugstr_w(name
), GetLastError());
4538 CloseServiceHandle(service
);
4539 CloseServiceHandle(hscm
);
4543 return ERROR_SUCCESS
;
4546 static UINT
ACTION_InstallServices( MSIPACKAGE
*package
)
4550 static const WCHAR ExecSeqQuery
[] =
4551 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4552 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4554 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4555 if (rc
!= ERROR_SUCCESS
)
4556 return ERROR_SUCCESS
;
4558 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallService
, package
);
4559 msiobj_release(&view
->hdr
);
4564 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4565 static LPCWSTR
*msi_service_args_to_vector(LPWSTR args
, DWORD
*numargs
)
4567 LPCWSTR
*vector
, *temp_vector
;
4571 static const WCHAR separator
[] = {'[','~',']',0};
4574 sep_len
= sizeof(separator
) / sizeof(WCHAR
) - 1;
4579 vector
= msi_alloc(sizeof(LPWSTR
));
4587 vector
[*numargs
- 1] = p
;
4589 if ((q
= strstrW(p
, separator
)))
4593 temp_vector
= msi_realloc(vector
, (*numargs
+ 1) * sizeof(LPWSTR
));
4599 vector
= temp_vector
;
4608 static UINT
ITERATE_StartService(MSIRECORD
*rec
, LPVOID param
)
4610 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4612 SC_HANDLE scm
, service
= NULL
;
4613 LPCWSTR name
, *vector
= NULL
;
4615 DWORD event
, numargs
;
4616 UINT r
= ERROR_FUNCTION_FAILED
;
4618 comp
= get_loaded_component(package
, MSI_RecordGetString(rec
, 6));
4619 if (!comp
|| comp
->Action
== INSTALLSTATE_UNKNOWN
|| comp
->Action
== INSTALLSTATE_ABSENT
)
4620 return ERROR_SUCCESS
;
4622 name
= MSI_RecordGetString(rec
, 2);
4623 event
= MSI_RecordGetInteger(rec
, 3);
4624 args
= strdupW(MSI_RecordGetString(rec
, 4));
4626 if (!(event
& msidbServiceControlEventStart
))
4627 return ERROR_SUCCESS
;
4629 scm
= OpenSCManagerW(NULL
, NULL
, SC_MANAGER_CONNECT
);
4632 ERR("Failed to open the service control manager\n");
4636 service
= OpenServiceW(scm
, name
, SERVICE_START
);
4639 ERR("Failed to open service %s\n", debugstr_w(name
));
4643 vector
= msi_service_args_to_vector(args
, &numargs
);
4645 if (!StartServiceW(service
, numargs
, vector
))
4647 ERR("Failed to start service %s\n", debugstr_w(name
));
4654 CloseServiceHandle(service
);
4655 CloseServiceHandle(scm
);
4662 static UINT
ACTION_StartServices( MSIPACKAGE
*package
)
4667 static const WCHAR query
[] = {
4668 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4669 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4671 rc
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
4672 if (rc
!= ERROR_SUCCESS
)
4673 return ERROR_SUCCESS
;
4675 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_StartService
, package
);
4676 msiobj_release(&view
->hdr
);
4681 static MSIFILE
*msi_find_file( MSIPACKAGE
*package
, LPCWSTR filename
)
4685 LIST_FOR_EACH_ENTRY(file
, &package
->files
, MSIFILE
, entry
)
4687 if (!lstrcmpW(file
->File
, filename
))
4694 static UINT
ITERATE_InstallODBCDriver( MSIRECORD
*rec
, LPVOID param
)
4696 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4697 LPWSTR driver
, driver_path
, ptr
;
4698 WCHAR outpath
[MAX_PATH
];
4699 MSIFILE
*driver_file
, *setup_file
;
4702 UINT r
= ERROR_SUCCESS
;
4704 static const WCHAR driver_fmt
[] = {
4705 'D','r','i','v','e','r','=','%','s',0};
4706 static const WCHAR setup_fmt
[] = {
4707 'S','e','t','u','p','=','%','s',0};
4708 static const WCHAR usage_fmt
[] = {
4709 'F','i','l','e','U','s','a','g','e','=','1',0};
4711 desc
= MSI_RecordGetString(rec
, 3);
4713 driver_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 4));
4714 setup_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 5));
4716 if (!driver_file
|| !setup_file
)
4718 ERR("ODBC Driver entry not found!\n");
4719 return ERROR_FUNCTION_FAILED
;
4722 len
= lstrlenW(desc
) + lstrlenW(driver_fmt
) + lstrlenW(driver_file
->FileName
) +
4723 lstrlenW(setup_fmt
) + lstrlenW(setup_file
->FileName
) +
4724 lstrlenW(usage_fmt
) + 1;
4725 driver
= msi_alloc(len
* sizeof(WCHAR
));
4727 return ERROR_OUTOFMEMORY
;
4730 lstrcpyW(ptr
, desc
);
4731 ptr
+= lstrlenW(ptr
) + 1;
4733 sprintfW(ptr
, driver_fmt
, driver_file
->FileName
);
4734 ptr
+= lstrlenW(ptr
) + 1;
4736 sprintfW(ptr
, setup_fmt
, setup_file
->FileName
);
4737 ptr
+= lstrlenW(ptr
) + 1;
4739 lstrcpyW(ptr
, usage_fmt
);
4740 ptr
+= lstrlenW(ptr
) + 1;
4743 driver_path
= strdupW(driver_file
->TargetPath
);
4744 ptr
= strrchrW(driver_path
, '\\');
4745 if (ptr
) *ptr
= '\0';
4747 if (!SQLInstallDriverExW(driver
, driver_path
, outpath
, MAX_PATH
,
4748 NULL
, ODBC_INSTALL_COMPLETE
, &usage
))
4750 ERR("Failed to install SQL driver!\n");
4751 r
= ERROR_FUNCTION_FAILED
;
4755 msi_free(driver_path
);
4760 static UINT
ITERATE_InstallODBCTranslator( MSIRECORD
*rec
, LPVOID param
)
4762 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4763 LPWSTR translator
, translator_path
, ptr
;
4764 WCHAR outpath
[MAX_PATH
];
4765 MSIFILE
*translator_file
, *setup_file
;
4768 UINT r
= ERROR_SUCCESS
;
4770 static const WCHAR translator_fmt
[] = {
4771 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4772 static const WCHAR setup_fmt
[] = {
4773 'S','e','t','u','p','=','%','s',0};
4775 desc
= MSI_RecordGetString(rec
, 3);
4777 translator_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 4));
4778 setup_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 5));
4780 if (!translator_file
|| !setup_file
)
4782 ERR("ODBC Translator entry not found!\n");
4783 return ERROR_FUNCTION_FAILED
;
4786 len
= lstrlenW(desc
) + lstrlenW(translator_fmt
) + lstrlenW(translator_file
->FileName
) +
4787 lstrlenW(setup_fmt
) + lstrlenW(setup_file
->FileName
) + 1;
4788 translator
= msi_alloc(len
* sizeof(WCHAR
));
4790 return ERROR_OUTOFMEMORY
;
4793 lstrcpyW(ptr
, desc
);
4794 ptr
+= lstrlenW(ptr
) + 1;
4796 sprintfW(ptr
, translator_fmt
, translator_file
->FileName
);
4797 ptr
+= lstrlenW(ptr
) + 1;
4799 sprintfW(ptr
, setup_fmt
, setup_file
->FileName
);
4800 ptr
+= lstrlenW(ptr
) + 1;
4803 translator_path
= strdupW(translator_file
->TargetPath
);
4804 ptr
= strrchrW(translator_path
, '\\');
4805 if (ptr
) *ptr
= '\0';
4807 if (!SQLInstallTranslatorExW(translator
, translator_path
, outpath
, MAX_PATH
,
4808 NULL
, ODBC_INSTALL_COMPLETE
, &usage
))
4810 ERR("Failed to install SQL translator!\n");
4811 r
= ERROR_FUNCTION_FAILED
;
4814 msi_free(translator
);
4815 msi_free(translator_path
);
4820 static UINT
ITERATE_InstallODBCDataSource( MSIRECORD
*rec
, LPVOID param
)
4823 LPCWSTR desc
, driver
;
4824 WORD request
= ODBC_ADD_SYS_DSN
;
4827 UINT r
= ERROR_SUCCESS
;
4829 static const WCHAR attrs_fmt
[] = {
4830 'D','S','N','=','%','s',0 };
4832 desc
= MSI_RecordGetString(rec
, 3);
4833 driver
= MSI_RecordGetString(rec
, 4);
4834 registration
= MSI_RecordGetInteger(rec
, 5);
4836 if (registration
== msidbODBCDataSourceRegistrationPerMachine
) request
= ODBC_ADD_SYS_DSN
;
4837 else if (registration
== msidbODBCDataSourceRegistrationPerUser
) request
= ODBC_ADD_DSN
;
4839 len
= lstrlenW(attrs_fmt
) + lstrlenW(desc
) + 1 + 1;
4840 attrs
= msi_alloc(len
* sizeof(WCHAR
));
4842 return ERROR_OUTOFMEMORY
;
4844 sprintfW(attrs
, attrs_fmt
, desc
);
4845 attrs
[len
- 1] = '\0';
4847 if (!SQLConfigDataSourceW(NULL
, request
, driver
, attrs
))
4849 ERR("Failed to install SQL data source!\n");
4850 r
= ERROR_FUNCTION_FAILED
;
4858 static UINT
ACTION_InstallODBC( MSIPACKAGE
*package
)
4863 static const WCHAR driver_query
[] = {
4864 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4865 'O','D','B','C','D','r','i','v','e','r',0 };
4867 static const WCHAR translator_query
[] = {
4868 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4869 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4871 static const WCHAR source_query
[] = {
4872 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4873 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4875 rc
= MSI_DatabaseOpenViewW(package
->db
, driver_query
, &view
);
4876 if (rc
!= ERROR_SUCCESS
)
4877 return ERROR_SUCCESS
;
4879 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCDriver
, package
);
4880 msiobj_release(&view
->hdr
);
4882 rc
= MSI_DatabaseOpenViewW(package
->db
, translator_query
, &view
);
4883 if (rc
!= ERROR_SUCCESS
)
4884 return ERROR_SUCCESS
;
4886 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCTranslator
, package
);
4887 msiobj_release(&view
->hdr
);
4889 rc
= MSI_DatabaseOpenViewW(package
->db
, source_query
, &view
);
4890 if (rc
!= ERROR_SUCCESS
)
4891 return ERROR_SUCCESS
;
4893 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCDataSource
, package
);
4894 msiobj_release(&view
->hdr
);
4899 #define ENV_ACT_SETALWAYS 0x1
4900 #define ENV_ACT_SETABSENT 0x2
4901 #define ENV_ACT_REMOVE 0x4
4902 #define ENV_ACT_REMOVEMATCH 0x8
4904 #define ENV_MOD_MACHINE 0x20000000
4905 #define ENV_MOD_APPEND 0x40000000
4906 #define ENV_MOD_PREFIX 0x80000000
4907 #define ENV_MOD_MASK 0xC0000000
4909 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4911 static LONG
env_set_flags( LPCWSTR
*name
, LPCWSTR
*value
, DWORD
*flags
)
4913 LPCWSTR cptr
= *name
;
4914 LPCWSTR ptr
= *value
;
4916 static const WCHAR prefix
[] = {'[','~',']',0};
4917 static const int prefix_len
= 3;
4923 *flags
|= ENV_ACT_SETALWAYS
;
4924 else if (*cptr
== '+')
4925 *flags
|= ENV_ACT_SETABSENT
;
4926 else if (*cptr
== '-')
4927 *flags
|= ENV_ACT_REMOVE
;
4928 else if (*cptr
== '!')
4929 *flags
|= ENV_ACT_REMOVEMATCH
;
4930 else if (*cptr
== '*')
4931 *flags
|= ENV_MOD_MACHINE
;
4941 ERR("Missing environment variable\n");
4942 return ERROR_FUNCTION_FAILED
;
4945 if (!strncmpW(ptr
, prefix
, prefix_len
))
4947 *flags
|= ENV_MOD_APPEND
;
4948 *value
+= lstrlenW(prefix
);
4950 else if (lstrlenW(*value
) >= prefix_len
)
4952 ptr
+= lstrlenW(ptr
) - prefix_len
;
4953 if (!lstrcmpW(ptr
, prefix
))
4955 *flags
|= ENV_MOD_PREFIX
;
4956 /* the "[~]" will be removed by deformat_string */;
4961 check_flag_combo(*flags
, ENV_ACT_SETALWAYS
| ENV_ACT_SETABSENT
) ||
4962 check_flag_combo(*flags
, ENV_ACT_REMOVEMATCH
| ENV_ACT_SETABSENT
) ||
4963 check_flag_combo(*flags
, ENV_ACT_REMOVEMATCH
| ENV_ACT_SETALWAYS
) ||
4964 check_flag_combo(*flags
, ENV_ACT_SETABSENT
| ENV_MOD_MASK
))
4966 ERR("Invalid flags: %08x\n", *flags
);
4967 return ERROR_FUNCTION_FAILED
;
4970 return ERROR_SUCCESS
;
4973 static UINT
ITERATE_WriteEnvironmentString( MSIRECORD
*rec
, LPVOID param
)
4975 MSIPACKAGE
*package
= param
;
4976 LPCWSTR name
, value
, comp
;
4977 LPWSTR data
= NULL
, newval
= NULL
;
4978 LPWSTR deformatted
= NULL
, ptr
;
4979 DWORD flags
, type
, size
;
4981 HKEY env
= NULL
, root
;
4982 LPCWSTR environment
;
4984 static const WCHAR user_env
[] =
4985 {'E','n','v','i','r','o','n','m','e','n','t',0};
4986 static const WCHAR machine_env
[] =
4987 {'S','y','s','t','e','m','\\',
4988 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4989 'C','o','n','t','r','o','l','\\',
4990 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4991 'E','n','v','i','r','o','n','m','e','n','t',0};
4992 static const WCHAR semicolon
[] = {';',0};
4994 name
= MSI_RecordGetString(rec
, 2);
4995 value
= MSI_RecordGetString(rec
, 3);
4996 comp
= MSI_RecordGetString(rec
, 4);
4998 res
= env_set_flags(&name
, &value
, &flags
);
4999 if (res
!= ERROR_SUCCESS
)
5002 deformat_string(package
, value
, &deformatted
);
5005 res
= ERROR_OUTOFMEMORY
;
5009 value
= deformatted
;
5011 if (flags
& ENV_MOD_MACHINE
)
5013 environment
= machine_env
;
5014 root
= HKEY_LOCAL_MACHINE
;
5018 environment
= user_env
;
5019 root
= HKEY_CURRENT_USER
;
5022 res
= RegCreateKeyExW(root
, environment
, 0, NULL
, 0,
5023 KEY_ALL_ACCESS
, NULL
, &env
, NULL
);
5024 if (res
!= ERROR_SUCCESS
)
5027 if (flags
& ENV_ACT_REMOVE
)
5028 FIXME("Not removing environment variable on uninstall!\n");
5031 res
= RegQueryValueExW(env
, name
, NULL
, &type
, NULL
, &size
);
5032 if ((res
!= ERROR_SUCCESS
&& res
!= ERROR_FILE_NOT_FOUND
) ||
5033 (res
== ERROR_SUCCESS
&& type
!= REG_SZ
&& type
!= REG_EXPAND_SZ
))
5036 if (res
!= ERROR_FILE_NOT_FOUND
)
5038 if (flags
& ENV_ACT_SETABSENT
)
5040 res
= ERROR_SUCCESS
;
5044 data
= msi_alloc(size
);
5048 return ERROR_OUTOFMEMORY
;
5051 res
= RegQueryValueExW(env
, name
, NULL
, &type
, (LPVOID
)data
, &size
);
5052 if (res
!= ERROR_SUCCESS
)
5055 if (flags
& ENV_ACT_REMOVEMATCH
&& (!value
|| !lstrcmpW(data
, value
)))
5057 res
= RegDeleteKeyW(env
, name
);
5061 size
= (lstrlenW(value
) + 1 + size
) * sizeof(WCHAR
);
5062 newval
= msi_alloc(size
);
5066 res
= ERROR_OUTOFMEMORY
;
5070 if (!(flags
& ENV_MOD_MASK
))
5071 lstrcpyW(newval
, value
);
5074 if (flags
& ENV_MOD_PREFIX
)
5076 lstrcpyW(newval
, value
);
5077 lstrcatW(newval
, semicolon
);
5078 ptr
= newval
+ lstrlenW(value
) + 1;
5081 lstrcpyW(ptr
, data
);
5083 if (flags
& ENV_MOD_APPEND
)
5085 lstrcatW(newval
, semicolon
);
5086 lstrcatW(newval
, value
);
5092 size
= (lstrlenW(value
) + 1) * sizeof(WCHAR
);
5093 newval
= msi_alloc(size
);
5096 res
= ERROR_OUTOFMEMORY
;
5100 lstrcpyW(newval
, value
);
5103 TRACE("setting %s to %s\n", debugstr_w(name
), debugstr_w(newval
));
5104 res
= RegSetValueExW(env
, name
, 0, type
, (LPVOID
)newval
, size
);
5107 if (env
) RegCloseKey(env
);
5108 msi_free(deformatted
);
5114 static UINT
ACTION_WriteEnvironmentStrings( MSIPACKAGE
*package
)
5118 static const WCHAR ExecSeqQuery
[] =
5119 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5120 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5121 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
5122 if (rc
!= ERROR_SUCCESS
)
5123 return ERROR_SUCCESS
;
5125 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteEnvironmentString
, package
);
5126 msiobj_release(&view
->hdr
);
5131 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5142 static BOOL
msi_move_file(LPCWSTR source
, LPCWSTR dest
, int options
)
5146 if (GetFileAttributesW(source
) == FILE_ATTRIBUTE_DIRECTORY
||
5147 GetFileAttributesW(dest
) == FILE_ATTRIBUTE_DIRECTORY
)
5149 WARN("Source or dest is directory, not moving\n");
5153 if (options
== msidbMoveFileOptionsMove
)
5155 TRACE("moving %s -> %s\n", debugstr_w(source
), debugstr_w(dest
));
5156 ret
= MoveFileExW(source
, dest
, MOVEFILE_REPLACE_EXISTING
);
5159 WARN("MoveFile failed: %d\n", GetLastError());
5165 TRACE("copying %s -> %s\n", debugstr_w(source
), debugstr_w(dest
));
5166 ret
= CopyFileW(source
, dest
, FALSE
);
5169 WARN("CopyFile failed: %d\n", GetLastError());
5177 static LPWSTR
wildcard_to_file(LPWSTR wildcard
, LPWSTR filename
)
5180 DWORD dirlen
, pathlen
;
5182 ptr
= strrchrW(wildcard
, '\\');
5183 dirlen
= ptr
- wildcard
+ 1;
5185 pathlen
= dirlen
+ lstrlenW(filename
) + 1;
5186 path
= msi_alloc(pathlen
* sizeof(WCHAR
));
5188 lstrcpynW(path
, wildcard
, dirlen
+ 1);
5189 lstrcatW(path
, filename
);
5194 static void free_file_entry(FILE_LIST
*file
)
5196 msi_free(file
->source
);
5197 msi_free(file
->dest
);
5201 static void free_list(FILE_LIST
*list
)
5203 while (!list_empty(&list
->entry
))
5205 FILE_LIST
*file
= LIST_ENTRY(list_head(&list
->entry
), FILE_LIST
, entry
);
5207 list_remove(&file
->entry
);
5208 free_file_entry(file
);
5212 static BOOL
add_wildcard(FILE_LIST
*files
, LPWSTR source
, LPWSTR dest
)
5214 FILE_LIST
*new, *file
;
5215 LPWSTR ptr
, filename
;
5218 new = msi_alloc_zero(sizeof(FILE_LIST
));
5222 new->source
= strdupW(source
);
5223 ptr
= strrchrW(dest
, '\\') + 1;
5224 filename
= strrchrW(new->source
, '\\') + 1;
5226 new->sourcename
= filename
;
5229 new->destname
= ptr
;
5231 new->destname
= new->sourcename
;
5233 size
= (ptr
- dest
) + lstrlenW(filename
) + 1;
5234 new->dest
= msi_alloc(size
* sizeof(WCHAR
));
5237 free_file_entry(new);
5241 lstrcpynW(new->dest
, dest
, ptr
- dest
+ 1);
5242 lstrcatW(new->dest
, filename
);
5244 if (list_empty(&files
->entry
))
5246 list_add_head(&files
->entry
, &new->entry
);
5250 LIST_FOR_EACH_ENTRY(file
, &files
->entry
, FILE_LIST
, entry
)
5252 if (lstrcmpW(source
, file
->source
) < 0)
5254 list_add_before(&file
->entry
, &new->entry
);
5259 list_add_after(&file
->entry
, &new->entry
);
5263 static BOOL
move_files_wildcard(LPWSTR source
, LPWSTR dest
, int options
)
5265 WIN32_FIND_DATAW wfd
;
5269 FILE_LIST files
, *file
;
5272 hfile
= FindFirstFileW(source
, &wfd
);
5273 if (hfile
== INVALID_HANDLE_VALUE
) return FALSE
;
5275 list_init(&files
.entry
);
5277 for (res
= TRUE
; res
; res
= FindNextFileW(hfile
, &wfd
))
5279 if (is_dot_dir(wfd
.cFileName
)) continue;
5281 path
= wildcard_to_file(source
, wfd
.cFileName
);
5288 add_wildcard(&files
, path
, dest
);
5292 /* only the first wildcard match gets renamed to dest */
5293 file
= LIST_ENTRY(list_head(&files
.entry
), FILE_LIST
, entry
);
5294 size
= (strrchrW(file
->dest
, '\\') - file
->dest
) + lstrlenW(file
->destname
) + 2;
5295 file
->dest
= msi_realloc(file
->dest
, size
* sizeof(WCHAR
));
5302 lstrcpyW(strrchrW(file
->dest
, '\\') + 1, file
->destname
);
5304 while (!list_empty(&files
.entry
))
5306 file
= LIST_ENTRY(list_head(&files
.entry
), FILE_LIST
, entry
);
5308 msi_move_file((LPCWSTR
)file
->source
, (LPCWSTR
)file
->dest
, options
);
5310 list_remove(&file
->entry
);
5311 free_file_entry(file
);
5322 static UINT
ITERATE_MoveFiles( MSIRECORD
*rec
, LPVOID param
)
5324 MSIPACKAGE
*package
= param
;
5326 LPCWSTR sourcename
, destname
;
5327 LPWSTR sourcedir
= NULL
, destdir
= NULL
;
5328 LPWSTR source
= NULL
, dest
= NULL
;
5331 BOOL ret
, wildcards
;
5333 static const WCHAR backslash
[] = {'\\',0};
5335 comp
= get_loaded_component(package
, MSI_RecordGetString(rec
, 2));
5336 if (!comp
|| !comp
->Enabled
||
5337 !(comp
->Action
& (INSTALLSTATE_LOCAL
| INSTALLSTATE_SOURCE
)))
5339 TRACE("Component not set for install, not moving file\n");
5340 return ERROR_SUCCESS
;
5343 sourcename
= MSI_RecordGetString(rec
, 3);
5344 destname
= MSI_RecordGetString(rec
, 4);
5345 options
= MSI_RecordGetInteger(rec
, 7);
5347 sourcedir
= msi_dup_property(package
, MSI_RecordGetString(rec
, 5));
5351 destdir
= msi_dup_property(package
, MSI_RecordGetString(rec
, 6));
5357 if (GetFileAttributesW(sourcedir
) == INVALID_FILE_ATTRIBUTES
)
5360 source
= strdupW(sourcedir
);
5366 size
= lstrlenW(sourcedir
) + lstrlenW(sourcename
) + 2;
5367 source
= msi_alloc(size
* sizeof(WCHAR
));
5371 lstrcpyW(source
, sourcedir
);
5372 if (source
[lstrlenW(source
) - 1] != '\\')
5373 lstrcatW(source
, backslash
);
5374 lstrcatW(source
, sourcename
);
5377 wildcards
= strchrW(source
, '*') || strchrW(source
, '?');
5379 if (!destname
&& !wildcards
)
5381 destname
= strdupW(sourcename
);
5388 size
= lstrlenW(destname
);
5390 size
+= lstrlenW(destdir
) + 2;
5391 dest
= msi_alloc(size
* sizeof(WCHAR
));
5395 lstrcpyW(dest
, destdir
);
5396 if (dest
[lstrlenW(dest
) - 1] != '\\')
5397 lstrcatW(dest
, backslash
);
5400 lstrcatW(dest
, destname
);
5402 if (GetFileAttributesW(destdir
) == INVALID_FILE_ATTRIBUTES
)
5404 ret
= CreateDirectoryW(destdir
, NULL
);
5407 WARN("CreateDirectory failed: %d\n", GetLastError());
5408 return ERROR_SUCCESS
;
5413 msi_move_file(source
, dest
, options
);
5415 move_files_wildcard(source
, dest
, options
);
5418 msi_free(sourcedir
);
5423 return ERROR_SUCCESS
;
5426 static UINT
ACTION_MoveFiles( MSIPACKAGE
*package
)
5431 static const WCHAR ExecSeqQuery
[] =
5432 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5433 '`','M','o','v','e','F','i','l','e','`',0};
5435 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
5436 if (rc
!= ERROR_SUCCESS
)
5437 return ERROR_SUCCESS
;
5439 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_MoveFiles
, package
);
5440 msiobj_release(&view
->hdr
);
5445 static UINT
msi_unimplemented_action_stub( MSIPACKAGE
*package
,
5446 LPCSTR action
, LPCWSTR table
)
5448 static const WCHAR query
[] = {
5449 'S','E','L','E','C','T',' ','*',' ',
5450 'F','R','O','M',' ','`','%','s','`',0 };
5451 MSIQUERY
*view
= NULL
;
5455 r
= MSI_OpenQuery( package
->db
, &view
, query
, table
);
5456 if (r
== ERROR_SUCCESS
)
5458 r
= MSI_IterateRecords(view
, &count
, NULL
, package
);
5459 msiobj_release(&view
->hdr
);
5463 FIXME("%s -> %u ignored %s table values\n",
5464 action
, count
, debugstr_w(table
));
5466 return ERROR_SUCCESS
;
5469 static UINT
ACTION_AllocateRegistrySpace( MSIPACKAGE
*package
)
5471 TRACE("%p\n", package
);
5472 return ERROR_SUCCESS
;
5475 static UINT
ACTION_RemoveIniValues( MSIPACKAGE
*package
)
5477 static const WCHAR table
[] =
5478 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5479 return msi_unimplemented_action_stub( package
, "RemoveIniValues", table
);
5482 static UINT
ACTION_PatchFiles( MSIPACKAGE
*package
)
5484 static const WCHAR table
[] = { 'P','a','t','c','h',0 };
5485 return msi_unimplemented_action_stub( package
, "PatchFiles", table
);
5488 static UINT
ACTION_BindImage( MSIPACKAGE
*package
)
5490 static const WCHAR table
[] = { 'B','i','n','d','I','m','a','g','e',0 };
5491 return msi_unimplemented_action_stub( package
, "BindImage", table
);
5494 static UINT
ACTION_IsolateComponents( MSIPACKAGE
*package
)
5496 static const WCHAR table
[] = {
5497 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5498 return msi_unimplemented_action_stub( package
, "IsolateComponents", table
);
5501 static UINT
ACTION_MigrateFeatureStates( MSIPACKAGE
*package
)
5503 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
5504 return msi_unimplemented_action_stub( package
, "MigrateFeatureStates", table
);
5507 static UINT
ACTION_SelfUnregModules( MSIPACKAGE
*package
)
5509 static const WCHAR table
[] = { 'S','e','l','f','R','e','g',0 };
5510 return msi_unimplemented_action_stub( package
, "SelfUnregModules", table
);
5513 static UINT
ACTION_StopServices( MSIPACKAGE
*package
)
5515 static const WCHAR table
[] = {
5516 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5517 return msi_unimplemented_action_stub( package
, "StopServices", table
);
5520 static UINT
ACTION_DeleteServices( MSIPACKAGE
*package
)
5522 static const WCHAR table
[] = {
5523 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5524 return msi_unimplemented_action_stub( package
, "DeleteServices", table
);
5526 static UINT
ACTION_ValidateProductID( MSIPACKAGE
*package
)
5528 static const WCHAR table
[] = {
5529 'P','r','o','d','u','c','t','I','D',0 };
5530 return msi_unimplemented_action_stub( package
, "ValidateProductID", table
);
5533 static UINT
ACTION_RemoveEnvironmentStrings( MSIPACKAGE
*package
)
5535 static const WCHAR table
[] = {
5536 'E','n','v','i','r','o','n','m','e','n','t',0 };
5537 return msi_unimplemented_action_stub( package
, "RemoveEnvironmentStrings", table
);
5540 static UINT
ACTION_MsiPublishAssemblies( MSIPACKAGE
*package
)
5542 static const WCHAR table
[] = {
5543 'M','s','i','A','s','s','e','m','b','l','y',0 };
5544 return msi_unimplemented_action_stub( package
, "MsiPublishAssemblies", table
);
5547 static UINT
ACTION_MsiUnpublishAssemblies( MSIPACKAGE
*package
)
5549 static const WCHAR table
[] = {
5550 'M','s','i','A','s','s','e','m','b','l','y',0 };
5551 return msi_unimplemented_action_stub( package
, "MsiUnpublishAssemblies", table
);
5554 static UINT
ACTION_UnregisterFonts( MSIPACKAGE
*package
)
5556 static const WCHAR table
[] = { 'F','o','n','t',0 };
5557 return msi_unimplemented_action_stub( package
, "UnregisterFonts", table
);
5560 static UINT
ACTION_RMCCPSearch( MSIPACKAGE
*package
)
5562 static const WCHAR table
[] = { 'C','C','P','S','e','a','r','c','h',0 };
5563 return msi_unimplemented_action_stub( package
, "RMCCPSearch", table
);
5566 static UINT
ACTION_RegisterComPlus( MSIPACKAGE
*package
)
5568 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
5569 return msi_unimplemented_action_stub( package
, "RegisterComPlus", table
);
5572 static UINT
ACTION_UnregisterComPlus( MSIPACKAGE
*package
)
5574 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
5575 return msi_unimplemented_action_stub( package
, "UnregisterComPlus", table
);
5578 static UINT
ACTION_InstallSFPCatalogFile( MSIPACKAGE
*package
)
5580 static const WCHAR table
[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5581 return msi_unimplemented_action_stub( package
, "InstallSFPCatalogFile", table
);
5584 static UINT
ACTION_RemoveDuplicateFiles( MSIPACKAGE
*package
)
5586 static const WCHAR table
[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5587 return msi_unimplemented_action_stub( package
, "RemoveDuplicateFiles", table
);
5590 static UINT
ACTION_RemoveExistingProducts( MSIPACKAGE
*package
)
5592 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
5593 return msi_unimplemented_action_stub( package
, "RemoveExistingProducts", table
);
5596 static UINT
ACTION_RemoveFolders( MSIPACKAGE
*package
)
5598 static const WCHAR table
[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5599 return msi_unimplemented_action_stub( package
, "RemoveFolders", table
);
5602 static UINT
ACTION_RemoveODBC( MSIPACKAGE
*package
)
5604 static const WCHAR table
[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5605 return msi_unimplemented_action_stub( package
, "RemoveODBC", table
);
5608 static UINT
ACTION_RemoveRegistryValues( MSIPACKAGE
*package
)
5610 static const WCHAR table
[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5611 return msi_unimplemented_action_stub( package
, "RemoveRegistryValues", table
);
5614 static UINT
ACTION_RemoveShortcuts( MSIPACKAGE
*package
)
5616 static const WCHAR table
[] = { 'S','h','o','r','t','c','u','t',0 };
5617 return msi_unimplemented_action_stub( package
, "RemoveShortcuts", table
);
5620 static UINT
ACTION_UnpublishComponents( MSIPACKAGE
*package
)
5622 static const WCHAR table
[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5623 return msi_unimplemented_action_stub( package
, "UnpublishComponents", table
);
5626 static UINT
ACTION_UnregisterClassInfo( MSIPACKAGE
*package
)
5628 static const WCHAR table
[] = { 'A','p','p','I','d',0 };
5629 return msi_unimplemented_action_stub( package
, "UnregisterClassInfo", table
);
5632 static UINT
ACTION_UnregisterExtensionInfo( MSIPACKAGE
*package
)
5634 static const WCHAR table
[] = { 'E','x','t','e','n','s','i','o','n',0 };
5635 return msi_unimplemented_action_stub( package
, "UnregisterExtensionInfo", table
);
5638 static UINT
ACTION_UnregisterMIMEInfo( MSIPACKAGE
*package
)
5640 static const WCHAR table
[] = { 'M','I','M','E',0 };
5641 return msi_unimplemented_action_stub( package
, "UnregisterMIMEInfo", table
);
5644 static UINT
ACTION_UnregisterProgIdInfo( MSIPACKAGE
*package
)
5646 static const WCHAR table
[] = { 'P','r','o','g','I','d',0 };
5647 return msi_unimplemented_action_stub( package
, "UnregisterProgIdInfo", table
);
5650 static UINT
ACTION_UnregisterTypeLibraries( MSIPACKAGE
*package
)
5652 static const WCHAR table
[] = { 'T','y','p','e','L','i','b',0 };
5653 return msi_unimplemented_action_stub( package
, "UnregisterTypeLibraries", table
);
5656 static const struct _actions StandardActions
[] = {
5657 { szAllocateRegistrySpace
, ACTION_AllocateRegistrySpace
},
5658 { szAppSearch
, ACTION_AppSearch
},
5659 { szBindImage
, ACTION_BindImage
},
5660 { szCCPSearch
, ACTION_CCPSearch
},
5661 { szCostFinalize
, ACTION_CostFinalize
},
5662 { szCostInitialize
, ACTION_CostInitialize
},
5663 { szCreateFolders
, ACTION_CreateFolders
},
5664 { szCreateShortcuts
, ACTION_CreateShortcuts
},
5665 { szDeleteServices
, ACTION_DeleteServices
},
5666 { szDisableRollback
, NULL
},
5667 { szDuplicateFiles
, ACTION_DuplicateFiles
},
5668 { szExecuteAction
, ACTION_ExecuteAction
},
5669 { szFileCost
, ACTION_FileCost
},
5670 { szFindRelatedProducts
, ACTION_FindRelatedProducts
},
5671 { szForceReboot
, ACTION_ForceReboot
},
5672 { szInstallAdminPackage
, NULL
},
5673 { szInstallExecute
, ACTION_InstallExecute
},
5674 { szInstallExecuteAgain
, ACTION_InstallExecute
},
5675 { szInstallFiles
, ACTION_InstallFiles
},
5676 { szInstallFinalize
, ACTION_InstallFinalize
},
5677 { szInstallInitialize
, ACTION_InstallInitialize
},
5678 { szInstallSFPCatalogFile
, ACTION_InstallSFPCatalogFile
},
5679 { szInstallValidate
, ACTION_InstallValidate
},
5680 { szIsolateComponents
, ACTION_IsolateComponents
},
5681 { szLaunchConditions
, ACTION_LaunchConditions
},
5682 { szMigrateFeatureStates
, ACTION_MigrateFeatureStates
},
5683 { szMoveFiles
, ACTION_MoveFiles
},
5684 { szMsiPublishAssemblies
, ACTION_MsiPublishAssemblies
},
5685 { szMsiUnpublishAssemblies
, ACTION_MsiUnpublishAssemblies
},
5686 { szInstallODBC
, ACTION_InstallODBC
},
5687 { szInstallServices
, ACTION_InstallServices
},
5688 { szPatchFiles
, ACTION_PatchFiles
},
5689 { szProcessComponents
, ACTION_ProcessComponents
},
5690 { szPublishComponents
, ACTION_PublishComponents
},
5691 { szPublishFeatures
, ACTION_PublishFeatures
},
5692 { szPublishProduct
, ACTION_PublishProduct
},
5693 { szRegisterClassInfo
, ACTION_RegisterClassInfo
},
5694 { szRegisterComPlus
, ACTION_RegisterComPlus
},
5695 { szRegisterExtensionInfo
, ACTION_RegisterExtensionInfo
},
5696 { szRegisterFonts
, ACTION_RegisterFonts
},
5697 { szRegisterMIMEInfo
, ACTION_RegisterMIMEInfo
},
5698 { szRegisterProduct
, ACTION_RegisterProduct
},
5699 { szRegisterProgIdInfo
, ACTION_RegisterProgIdInfo
},
5700 { szRegisterTypeLibraries
, ACTION_RegisterTypeLibraries
},
5701 { szRegisterUser
, ACTION_RegisterUser
},
5702 { szRemoveDuplicateFiles
, ACTION_RemoveDuplicateFiles
},
5703 { szRemoveEnvironmentStrings
, ACTION_RemoveEnvironmentStrings
},
5704 { szRemoveExistingProducts
, ACTION_RemoveExistingProducts
},
5705 { szRemoveFiles
, ACTION_RemoveFiles
},
5706 { szRemoveFolders
, ACTION_RemoveFolders
},
5707 { szRemoveIniValues
, ACTION_RemoveIniValues
},
5708 { szRemoveODBC
, ACTION_RemoveODBC
},
5709 { szRemoveRegistryValues
, ACTION_RemoveRegistryValues
},
5710 { szRemoveShortcuts
, ACTION_RemoveShortcuts
},
5711 { szResolveSource
, ACTION_ResolveSource
},
5712 { szRMCCPSearch
, ACTION_RMCCPSearch
},
5713 { szScheduleReboot
, NULL
},
5714 { szSelfRegModules
, ACTION_SelfRegModules
},
5715 { szSelfUnregModules
, ACTION_SelfUnregModules
},
5716 { szSetODBCFolders
, NULL
},
5717 { szStartServices
, ACTION_StartServices
},
5718 { szStopServices
, ACTION_StopServices
},
5719 { szUnpublishComponents
, ACTION_UnpublishComponents
},
5720 { szUnpublishFeatures
, ACTION_UnpublishFeatures
},
5721 { szUnregisterClassInfo
, ACTION_UnregisterClassInfo
},
5722 { szUnregisterComPlus
, ACTION_UnregisterComPlus
},
5723 { szUnregisterExtensionInfo
, ACTION_UnregisterExtensionInfo
},
5724 { szUnregisterFonts
, ACTION_UnregisterFonts
},
5725 { szUnregisterMIMEInfo
, ACTION_UnregisterMIMEInfo
},
5726 { szUnregisterProgIdInfo
, ACTION_UnregisterProgIdInfo
},
5727 { szUnregisterTypeLibraries
, ACTION_UnregisterTypeLibraries
},
5728 { szValidateProductID
, ACTION_ValidateProductID
},
5729 { szWriteEnvironmentStrings
, ACTION_WriteEnvironmentStrings
},
5730 { szWriteIniValues
, ACTION_WriteIniValues
},
5731 { szWriteRegistryValues
, ACTION_WriteRegistryValues
},