2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
26 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
39 #include "wine/debug.h"
44 #include "wine/unicode.h"
47 #define REG_PROGRESS_VALUE 13200
48 #define COMPONENT_PROGRESS_VALUE 24000
50 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
55 static UINT
ACTION_ProcessExecSequence(MSIPACKAGE
*package
, BOOL UIran
);
56 static UINT
ACTION_ProcessUISequence(MSIPACKAGE
*package
);
57 static UINT
ACTION_PerformActionSequence(MSIPACKAGE
*package
, UINT seq
, BOOL UI
);
60 * consts and values used
62 static const WCHAR c_colon
[] = {'C',':','\\',0};
64 static const WCHAR szCreateFolders
[] =
65 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
66 static const WCHAR szCostFinalize
[] =
67 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
68 const WCHAR szInstallFiles
[] =
69 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
70 const WCHAR szDuplicateFiles
[] =
71 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
72 static const WCHAR szWriteRegistryValues
[] =
73 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
74 'V','a','l','u','e','s',0};
75 static const WCHAR szCostInitialize
[] =
76 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
77 static const WCHAR szFileCost
[] =
78 {'F','i','l','e','C','o','s','t',0};
79 static const WCHAR szInstallInitialize
[] =
80 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
81 static const WCHAR szInstallValidate
[] =
82 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
83 static const WCHAR szLaunchConditions
[] =
84 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
85 static const WCHAR szProcessComponents
[] =
86 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
87 static const WCHAR szRegisterTypeLibraries
[] =
88 {'R','e','g','i','s','t','e','r','T','y','p','e',
89 'L','i','b','r','a','r','i','e','s',0};
90 const WCHAR szRegisterClassInfo
[] =
91 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
92 const WCHAR szRegisterProgIdInfo
[] =
93 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
94 static const WCHAR szCreateShortcuts
[] =
95 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
96 static const WCHAR szPublishProduct
[] =
97 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
98 static const WCHAR szWriteIniValues
[] =
99 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
100 static const WCHAR szSelfRegModules
[] =
101 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
102 static const WCHAR szPublishFeatures
[] =
103 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
104 static const WCHAR szRegisterProduct
[] =
105 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
106 static const WCHAR szInstallExecute
[] =
107 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
108 static const WCHAR szInstallExecuteAgain
[] =
109 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
110 'A','g','a','i','n',0};
111 static const WCHAR szInstallFinalize
[] =
112 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
113 static const WCHAR szForceReboot
[] =
114 {'F','o','r','c','e','R','e','b','o','o','t',0};
115 static const WCHAR szResolveSource
[] =
116 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
117 static const WCHAR szAppSearch
[] =
118 {'A','p','p','S','e','a','r','c','h',0};
119 static const WCHAR szAllocateRegistrySpace
[] =
120 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
121 'S','p','a','c','e',0};
122 static const WCHAR szBindImage
[] =
123 {'B','i','n','d','I','m','a','g','e',0};
124 static const WCHAR szCCPSearch
[] =
125 {'C','C','P','S','e','a','r','c','h',0};
126 static const WCHAR szDeleteServices
[] =
127 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
128 static const WCHAR szDisableRollback
[] =
129 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
130 static const WCHAR szExecuteAction
[] =
131 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
132 const WCHAR szFindRelatedProducts
[] =
133 {'F','i','n','d','R','e','l','a','t','e','d',
134 'P','r','o','d','u','c','t','s',0};
135 static const WCHAR szInstallAdminPackage
[] =
136 {'I','n','s','t','a','l','l','A','d','m','i','n',
137 'P','a','c','k','a','g','e',0};
138 static const WCHAR szInstallSFPCatalogFile
[] =
139 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
141 static const WCHAR szIsolateComponents
[] =
142 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
143 const WCHAR szMigrateFeatureStates
[] =
144 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
145 'S','t','a','t','e','s',0};
146 const WCHAR szMoveFiles
[] =
147 {'M','o','v','e','F','i','l','e','s',0};
148 static const WCHAR szMsiPublishAssemblies
[] =
149 {'M','s','i','P','u','b','l','i','s','h',
150 'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szMsiUnpublishAssemblies
[] =
152 {'M','s','i','U','n','p','u','b','l','i','s','h',
153 'A','s','s','e','m','b','l','i','e','s',0};
154 static const WCHAR szInstallODBC
[] =
155 {'I','n','s','t','a','l','l','O','D','B','C',0};
156 static const WCHAR szInstallServices
[] =
157 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
158 const WCHAR szPatchFiles
[] =
159 {'P','a','t','c','h','F','i','l','e','s',0};
160 static const WCHAR szPublishComponents
[] =
161 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
162 static const WCHAR szRegisterComPlus
[] =
163 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
164 const WCHAR szRegisterExtensionInfo
[] =
165 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
167 static const WCHAR szRegisterFonts
[] =
168 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
169 const WCHAR szRegisterMIMEInfo
[] =
170 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
171 static const WCHAR szRegisterUser
[] =
172 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
173 const WCHAR szRemoveDuplicateFiles
[] =
174 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
175 'F','i','l','e','s',0};
176 static const WCHAR szRemoveEnvironmentStrings
[] =
177 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
178 'S','t','r','i','n','g','s',0};
179 const WCHAR szRemoveExistingProducts
[] =
180 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
181 'P','r','o','d','u','c','t','s',0};
182 const WCHAR szRemoveFiles
[] =
183 {'R','e','m','o','v','e','F','i','l','e','s',0};
184 static const WCHAR szRemoveFolders
[] =
185 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
186 static const WCHAR szRemoveIniValues
[] =
187 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
188 static const WCHAR szRemoveODBC
[] =
189 {'R','e','m','o','v','e','O','D','B','C',0};
190 static const WCHAR szRemoveRegistryValues
[] =
191 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
192 'V','a','l','u','e','s',0};
193 static const WCHAR szRemoveShortcuts
[] =
194 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
195 static const WCHAR szRMCCPSearch
[] =
196 {'R','M','C','C','P','S','e','a','r','c','h',0};
197 static const WCHAR szScheduleReboot
[] =
198 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
199 static const WCHAR szSelfUnregModules
[] =
200 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
201 static const WCHAR szSetODBCFolders
[] =
202 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
203 static const WCHAR szStartServices
[] =
204 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
205 static const WCHAR szStopServices
[] =
206 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
207 static const WCHAR szUnpublishComponents
[] =
208 {'U','n','p','u','b','l','i','s','h',
209 'C','o','m','p','o','n','e','n','t','s',0};
210 static const WCHAR szUnpublishFeatures
[] =
211 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
212 const WCHAR szUnregisterClassInfo
[] =
213 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
215 static const WCHAR szUnregisterComPlus
[] =
216 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
217 const WCHAR szUnregisterExtensionInfo
[] =
218 {'U','n','r','e','g','i','s','t','e','r',
219 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
220 static const WCHAR szUnregisterFonts
[] =
221 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
222 const WCHAR szUnregisterMIMEInfo
[] =
223 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
224 const WCHAR szUnregisterProgIdInfo
[] =
225 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
227 static const WCHAR szUnregisterTypeLibraries
[] =
228 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
229 'L','i','b','r','a','r','i','e','s',0};
230 static const WCHAR szValidateProductID
[] =
231 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
232 static const WCHAR szWriteEnvironmentStrings
[] =
233 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
234 'S','t','r','i','n','g','s',0};
236 /* action handlers */
237 typedef UINT (*STANDARDACTIONHANDLER
)(MSIPACKAGE
*);
241 STANDARDACTIONHANDLER handler
;
244 static const struct _actions StandardActions
[];
247 /********************************************************
249 ********************************************************/
251 static void ui_actionstart(MSIPACKAGE
*package
, LPCWSTR action
)
253 static const WCHAR Query_t
[] =
254 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
255 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
256 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
257 ' ','\'','%','s','\'',0};
260 row
= MSI_QueryGetRecord( package
->db
, Query_t
, action
);
263 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONSTART
, row
);
264 msiobj_release(&row
->hdr
);
267 static void ui_actioninfo(MSIPACKAGE
*package
, LPCWSTR action
, BOOL start
,
271 static const WCHAR template_s
[]=
272 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
274 static const WCHAR template_e
[]=
275 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
276 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
278 static const WCHAR format
[] =
279 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
283 GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, NULL
, format
, timet
, 0x100);
285 sprintfW(message
,template_s
,timet
,action
);
287 sprintfW(message
,template_e
,timet
,action
,rc
);
289 row
= MSI_CreateRecord(1);
290 MSI_RecordSetStringW(row
,1,message
);
292 MSI_ProcessMessage(package
, INSTALLMESSAGE_INFO
, row
);
293 msiobj_release(&row
->hdr
);
296 UINT
msi_parse_command_line( MSIPACKAGE
*package
, LPCWSTR szCommandLine
)
301 LPWSTR prop
= NULL
, val
= NULL
;
304 return ERROR_SUCCESS
;
316 TRACE("Looking at %s\n",debugstr_w(ptr
));
318 ptr2
= strchrW(ptr
,'=');
321 ERR("command line contains unknown string : %s\n", debugstr_w(ptr
));
328 prop
= msi_alloc((len
+1)*sizeof(WCHAR
));
329 memcpy(prop
,ptr
,len
*sizeof(WCHAR
));
335 while (*ptr
&& (quote
|| (!quote
&& *ptr
!=' ')))
348 val
= msi_alloc((len
+1)*sizeof(WCHAR
));
349 memcpy(val
,ptr2
,len
*sizeof(WCHAR
));
352 if (lstrlenW(prop
) > 0)
354 TRACE("Found commandline property (%s) = (%s)\n",
355 debugstr_w(prop
), debugstr_w(val
));
356 MSI_SetPropertyW(package
,prop
,val
);
362 return ERROR_SUCCESS
;
366 static LPWSTR
* msi_split_string( LPCWSTR str
, WCHAR sep
)
369 LPWSTR p
, *ret
= NULL
;
375 /* count the number of substrings */
376 for ( pc
= str
, count
= 0; pc
; count
++ )
378 pc
= strchrW( pc
, sep
);
383 /* allocate space for an array of substring pointers and the substrings */
384 ret
= msi_alloc( (count
+1) * sizeof (LPWSTR
) +
385 (lstrlenW(str
)+1) * sizeof(WCHAR
) );
389 /* copy the string and set the pointers */
390 p
= (LPWSTR
) &ret
[count
+1];
392 for( count
= 0; (ret
[count
] = p
); count
++ )
394 p
= strchrW( p
, sep
);
402 static UINT
msi_check_transform_applicable( MSIPACKAGE
*package
, IStorage
*patch
)
404 WCHAR szProductCode
[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
405 LPWSTR prod_code
, patch_product
;
408 prod_code
= msi_dup_property( package
, szProductCode
);
409 patch_product
= msi_get_suminfo_product( patch
);
411 TRACE("db = %s patch = %s\n", debugstr_w(prod_code
), debugstr_w(patch_product
));
413 if ( strstrW( patch_product
, prod_code
) )
416 ret
= ERROR_FUNCTION_FAILED
;
418 msi_free( patch_product
);
419 msi_free( prod_code
);
424 static UINT
msi_apply_substorage_transform( MSIPACKAGE
*package
,
425 MSIDATABASE
*patch_db
, LPCWSTR name
)
427 UINT ret
= ERROR_FUNCTION_FAILED
;
428 IStorage
*stg
= NULL
;
431 TRACE("%p %s\n", package
, debugstr_w(name
) );
435 ERR("expected a colon in %s\n", debugstr_w(name
));
436 return ERROR_FUNCTION_FAILED
;
439 r
= IStorage_OpenStorage( patch_db
->storage
, name
, NULL
, STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
442 ret
= msi_check_transform_applicable( package
, stg
);
443 if (ret
== ERROR_SUCCESS
)
444 msi_table_apply_transform( package
->db
, stg
);
446 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name
));
447 IStorage_Release( stg
);
450 ERR("failed to open substorage %s\n", debugstr_w(name
));
452 return ERROR_SUCCESS
;
455 static UINT
msi_check_patch_applicable( MSIPACKAGE
*package
, MSISUMMARYINFO
*si
)
457 static const WCHAR szProdCode
[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
458 LPWSTR guid_list
, *guids
, product_code
;
459 UINT i
, ret
= ERROR_FUNCTION_FAILED
;
461 product_code
= msi_dup_property( package
, szProdCode
);
464 /* FIXME: the property ProductCode should be written into the DB somewhere */
465 ERR("no product code to check\n");
466 return ERROR_SUCCESS
;
469 guid_list
= msi_suminfo_dup_string( si
, PID_TEMPLATE
);
470 guids
= msi_split_string( guid_list
, ';' );
471 for ( i
= 0; guids
[i
] && ret
!= ERROR_SUCCESS
; i
++ )
473 if (!lstrcmpW( guids
[i
], product_code
))
477 msi_free( guid_list
);
478 msi_free( product_code
);
483 static UINT
msi_parse_patch_summary( MSIPACKAGE
*package
, MSIDATABASE
*patch_db
)
486 LPWSTR str
, *substorage
;
487 UINT i
, r
= ERROR_SUCCESS
;
489 si
= MSI_GetSummaryInformationW( patch_db
->storage
, 0 );
491 return ERROR_FUNCTION_FAILED
;
493 msi_check_patch_applicable( package
, si
);
495 /* enumerate the substorage */
496 str
= msi_suminfo_dup_string( si
, PID_LASTAUTHOR
);
497 substorage
= msi_split_string( str
, ';' );
498 for ( i
= 0; substorage
&& substorage
[i
] && r
== ERROR_SUCCESS
; i
++ )
499 r
= msi_apply_substorage_transform( package
, patch_db
, substorage
[i
] );
500 msi_free( substorage
);
503 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
505 msiobj_release( &si
->hdr
);
510 static UINT
msi_apply_patch_package( MSIPACKAGE
*package
, LPCWSTR file
)
512 MSIDATABASE
*patch_db
= NULL
;
515 TRACE("%p %s\n", package
, debugstr_w( file
) );
518 * We probably want to make sure we only open a patch collection here.
519 * Patch collections (.msp) and databases (.msi) have different GUIDs
520 * but currently MSI_OpenDatabaseW will accept both.
522 r
= MSI_OpenDatabaseW( file
, MSIDBOPEN_READONLY
, &patch_db
);
523 if ( r
!= ERROR_SUCCESS
)
525 ERR("failed to open patch collection %s\n", debugstr_w( file
) );
529 msi_parse_patch_summary( package
, patch_db
);
532 * There might be a CAB file in the patch package,
533 * so append it to the list of storage to search for streams.
535 append_storage_to_db( package
->db
, patch_db
->storage
);
537 msiobj_release( &patch_db
->hdr
);
539 return ERROR_SUCCESS
;
542 /* get the PATCH property, and apply all the patches it specifies */
543 static UINT
msi_apply_patches( MSIPACKAGE
*package
)
545 static const WCHAR szPatch
[] = { 'P','A','T','C','H',0 };
546 LPWSTR patch_list
, *patches
;
547 UINT i
, r
= ERROR_SUCCESS
;
549 patch_list
= msi_dup_property( package
, szPatch
);
551 TRACE("patches to be applied: %s\n", debugstr_w( patch_list
) );
553 patches
= msi_split_string( patch_list
, ';' );
554 for( i
=0; patches
&& patches
[i
] && r
== ERROR_SUCCESS
; i
++ )
555 r
= msi_apply_patch_package( package
, patches
[i
] );
558 msi_free( patch_list
);
563 static UINT
msi_apply_transforms( MSIPACKAGE
*package
)
565 static const WCHAR szTransforms
[] = {
566 'T','R','A','N','S','F','O','R','M','S',0 };
567 LPWSTR xform_list
, *xforms
;
568 UINT i
, r
= ERROR_SUCCESS
;
570 xform_list
= msi_dup_property( package
, szTransforms
);
571 xforms
= msi_split_string( xform_list
, ';' );
573 for( i
=0; xforms
&& xforms
[i
] && r
== ERROR_SUCCESS
; i
++ )
575 if (xforms
[i
][0] == ':')
576 r
= msi_apply_substorage_transform( package
, package
->db
, &xforms
[i
][1] );
578 r
= MSI_DatabaseApplyTransformW( package
->db
, xforms
[i
], 0 );
582 msi_free( xform_list
);
587 BOOL
ui_sequence_exists( MSIPACKAGE
*package
)
592 static const WCHAR ExecSeqQuery
[] =
593 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
594 '`','I','n','s','t','a','l','l',
595 'U','I','S','e','q','u','e','n','c','e','`',
596 ' ','W','H','E','R','E',' ',
597 '`','S','e','q','u','e','n','c','e','`',' ',
598 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
599 '`','S','e','q','u','e','n','c','e','`',0};
601 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
602 if (rc
== ERROR_SUCCESS
)
604 msiobj_release(&view
->hdr
);
611 static UINT
msi_set_sourcedir_props(MSIPACKAGE
*package
, BOOL replace
)
614 LPWSTR source
, check
;
617 static const WCHAR szOriginalDatabase
[] =
618 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
620 db
= msi_dup_property( package
, szOriginalDatabase
);
622 return ERROR_OUTOFMEMORY
;
624 p
= strrchrW( db
, '\\' );
627 p
= strrchrW( db
, '/' );
631 return ERROR_SUCCESS
;
636 source
= msi_alloc( len
* sizeof(WCHAR
) );
637 lstrcpynW( source
, db
, len
);
639 check
= msi_dup_property( package
, cszSourceDir
);
640 if (!check
|| replace
)
641 MSI_SetPropertyW( package
, cszSourceDir
, source
);
645 check
= msi_dup_property( package
, cszSOURCEDIR
);
646 if (!check
|| replace
)
647 MSI_SetPropertyW( package
, cszSOURCEDIR
, source
);
653 return ERROR_SUCCESS
;
656 /****************************************************
657 * TOP level entry points
658 *****************************************************/
660 UINT
MSI_InstallPackage( MSIPACKAGE
*package
, LPCWSTR szPackagePath
,
661 LPCWSTR szCommandLine
)
664 BOOL ui
= FALSE
, ui_exists
;
665 static const WCHAR szUILevel
[] = {'U','I','L','e','v','e','l',0};
666 static const WCHAR szAction
[] = {'A','C','T','I','O','N',0};
667 static const WCHAR szInstall
[] = {'I','N','S','T','A','L','L',0};
669 MSI_SetPropertyW(package
, szAction
, szInstall
);
671 package
->script
= msi_alloc_zero(sizeof(MSISCRIPT
));
673 package
->script
->InWhatSequence
= SEQUENCE_INSTALL
;
680 dir
= strdupW(szPackagePath
);
681 p
= strrchrW(dir
, '\\');
685 file
= szPackagePath
+ (p
- dir
);
690 dir
= msi_alloc(MAX_PATH
*sizeof(WCHAR
));
691 GetCurrentDirectoryW(MAX_PATH
, dir
);
692 lstrcatW(dir
, cszbs
);
693 file
= szPackagePath
;
696 msi_free( package
->PackagePath
);
697 package
->PackagePath
= msi_alloc((lstrlenW(dir
) + lstrlenW(file
) + 1) * sizeof(WCHAR
));
698 if (!package
->PackagePath
)
701 return ERROR_OUTOFMEMORY
;
704 lstrcpyW(package
->PackagePath
, dir
);
705 lstrcatW(package
->PackagePath
, file
);
708 msi_set_sourcedir_props(package
, FALSE
);
711 msi_parse_command_line( package
, szCommandLine
);
713 msi_apply_transforms( package
);
714 msi_apply_patches( package
);
716 /* properties may have been added by a transform */
717 msi_clone_properties( package
);
719 if ( (msi_get_property_int(package
, szUILevel
, 0) & INSTALLUILEVEL_MASK
) >= INSTALLUILEVEL_REDUCED
)
721 package
->script
->InWhatSequence
|= SEQUENCE_UI
;
722 rc
= ACTION_ProcessUISequence(package
);
724 ui_exists
= ui_sequence_exists(package
);
725 if (rc
== ERROR_SUCCESS
|| !ui_exists
)
727 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
728 rc
= ACTION_ProcessExecSequence(package
,ui_exists
);
732 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
734 package
->script
->CurrentlyScripting
= FALSE
;
736 /* process the ending type action */
737 if (rc
== ERROR_SUCCESS
)
738 ACTION_PerformActionSequence(package
,-1,ui
);
739 else if (rc
== ERROR_INSTALL_USEREXIT
)
740 ACTION_PerformActionSequence(package
,-2,ui
);
741 else if (rc
== ERROR_INSTALL_SUSPEND
)
742 ACTION_PerformActionSequence(package
,-4,ui
);
744 ACTION_PerformActionSequence(package
,-3,ui
);
746 /* finish up running custom actions */
747 ACTION_FinishCustomActions(package
);
752 static UINT
ACTION_PerformActionSequence(MSIPACKAGE
*package
, UINT seq
, BOOL UI
)
754 UINT rc
= ERROR_SUCCESS
;
756 static const WCHAR ExecSeqQuery
[] =
757 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
758 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
759 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
760 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
762 static const WCHAR UISeqQuery
[] =
763 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
764 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
765 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
766 ' ', '=',' ','%','i',0};
769 row
= MSI_QueryGetRecord(package
->db
, UISeqQuery
, seq
);
771 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
, seq
);
775 LPCWSTR action
, cond
;
777 TRACE("Running the actions\n");
779 /* check conditions */
780 cond
= MSI_RecordGetString(row
,2);
782 /* this is a hack to skip errors in the condition code */
783 if (MSI_EvaluateConditionW(package
, cond
) == MSICONDITION_FALSE
)
786 action
= MSI_RecordGetString(row
,1);
789 ERR("failed to fetch action\n");
790 rc
= ERROR_FUNCTION_FAILED
;
795 rc
= ACTION_PerformUIAction(package
,action
,-1);
797 rc
= ACTION_PerformAction(package
,action
,-1,FALSE
);
799 msiobj_release(&row
->hdr
);
810 } iterate_action_param
;
812 static UINT
ITERATE_Actions(MSIRECORD
*row
, LPVOID param
)
814 iterate_action_param
*iap
= (iterate_action_param
*)param
;
816 LPCWSTR cond
, action
;
818 action
= MSI_RecordGetString(row
,1);
821 ERR("Error is retrieving action name\n");
822 return ERROR_FUNCTION_FAILED
;
825 /* check conditions */
826 cond
= MSI_RecordGetString(row
,2);
828 /* this is a hack to skip errors in the condition code */
829 if (MSI_EvaluateConditionW(iap
->package
, cond
) == MSICONDITION_FALSE
)
831 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action
));
832 return ERROR_SUCCESS
;
836 rc
= ACTION_PerformUIAction(iap
->package
,action
,-1);
838 rc
= ACTION_PerformAction(iap
->package
,action
,-1,FALSE
);
840 msi_dialog_check_messages( NULL
);
842 if (iap
->package
->CurrentInstallState
!= ERROR_SUCCESS
)
843 rc
= iap
->package
->CurrentInstallState
;
845 if (rc
== ERROR_FUNCTION_NOT_CALLED
)
848 if (rc
!= ERROR_SUCCESS
)
849 ERR("Execution halted, action %s returned %i\n", debugstr_w(action
), rc
);
854 UINT
MSI_Sequence( MSIPACKAGE
*package
, LPCWSTR szTable
, INT iSequenceMode
)
858 static const WCHAR query
[] =
859 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
861 ' ','W','H','E','R','E',' ',
862 '`','S','e','q','u','e','n','c','e','`',' ',
863 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
864 '`','S','e','q','u','e','n','c','e','`',0};
865 iterate_action_param iap
;
868 * FIXME: probably should be checking UILevel in the
869 * ACTION_PerformUIAction/ACTION_PerformAction
870 * rather than saving the UI level here. Those
871 * two functions can be merged too.
873 iap
.package
= package
;
876 TRACE("%p %s %i\n", package
, debugstr_w(szTable
), iSequenceMode
);
878 r
= MSI_OpenQuery( package
->db
, &view
, query
, szTable
);
879 if (r
== ERROR_SUCCESS
)
881 r
= MSI_IterateRecords( view
, NULL
, ITERATE_Actions
, &iap
);
882 msiobj_release(&view
->hdr
);
888 static UINT
ACTION_ProcessExecSequence(MSIPACKAGE
*package
, BOOL UIran
)
892 static const WCHAR ExecSeqQuery
[] =
893 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
894 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
895 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
896 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
897 'O','R','D','E','R',' ', 'B','Y',' ',
898 '`','S','e','q','u','e','n','c','e','`',0 };
900 static const WCHAR IVQuery
[] =
901 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
902 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
903 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
904 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
905 ' ','\'', 'I','n','s','t','a','l','l',
906 'V','a','l','i','d','a','t','e','\'', 0};
908 iterate_action_param iap
;
910 iap
.package
= package
;
913 if (package
->script
->ExecuteSequenceRun
)
915 TRACE("Execute Sequence already Run\n");
916 return ERROR_SUCCESS
;
919 package
->script
->ExecuteSequenceRun
= TRUE
;
921 /* get the sequence number */
924 row
= MSI_QueryGetRecord(package
->db
, IVQuery
);
926 return ERROR_FUNCTION_FAILED
;
927 seq
= MSI_RecordGetInteger(row
,1);
928 msiobj_release(&row
->hdr
);
931 rc
= MSI_OpenQuery(package
->db
, &view
, ExecSeqQuery
, seq
);
932 if (rc
== ERROR_SUCCESS
)
934 TRACE("Running the actions\n");
936 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, &iap
);
937 msiobj_release(&view
->hdr
);
943 static UINT
ACTION_ProcessUISequence(MSIPACKAGE
*package
)
947 static const WCHAR ExecSeqQuery
[] =
948 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
949 '`','I','n','s','t','a','l','l',
950 'U','I','S','e','q','u','e','n','c','e','`',
951 ' ','W','H','E','R','E',' ',
952 '`','S','e','q','u','e','n','c','e','`',' ',
953 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
954 '`','S','e','q','u','e','n','c','e','`',0};
955 iterate_action_param iap
;
957 iap
.package
= package
;
960 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
962 if (rc
== ERROR_SUCCESS
)
964 TRACE("Running the actions\n");
966 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, &iap
);
967 msiobj_release(&view
->hdr
);
973 /********************************************************
974 * ACTION helper functions and functions that perform the actions
975 *******************************************************/
976 static BOOL
ACTION_HandleStandardAction(MSIPACKAGE
*package
, LPCWSTR action
,
977 UINT
* rc
, BOOL force
)
983 if (!run
&& !package
->script
->CurrentlyScripting
)
988 if (strcmpW(action
,szInstallFinalize
) == 0 ||
989 strcmpW(action
,szInstallExecute
) == 0 ||
990 strcmpW(action
,szInstallExecuteAgain
) == 0)
995 while (StandardActions
[i
].action
!= NULL
)
997 if (strcmpW(StandardActions
[i
].action
, action
)==0)
1001 ui_actioninfo(package
, action
, TRUE
, 0);
1002 *rc
= schedule_action(package
,INSTALL_SCRIPT
,action
);
1003 ui_actioninfo(package
, action
, FALSE
, *rc
);
1007 ui_actionstart(package
, action
);
1008 if (StandardActions
[i
].handler
)
1010 *rc
= StandardActions
[i
].handler(package
);
1014 FIXME("unhandled standard action %s\n",debugstr_w(action
));
1015 *rc
= ERROR_SUCCESS
;
1026 static BOOL
ACTION_HandleCustomAction( MSIPACKAGE
* package
, LPCWSTR action
,
1027 UINT
* rc
, UINT script
, BOOL force
)
1032 arc
= ACTION_CustomAction(package
, action
, script
, force
);
1034 if (arc
!= ERROR_CALL_NOT_IMPLEMENTED
)
1043 * A lot of actions are really important even if they don't do anything
1044 * explicit... Lots of properties are set at the beginning of the installation
1045 * CostFinalize does a bunch of work to translate the directories and such
1047 * But until I get write access to the database that is hard, so I am going to
1048 * hack it to see if I can get something to run.
1050 UINT
ACTION_PerformAction(MSIPACKAGE
*package
, const WCHAR
*action
, UINT script
, BOOL force
)
1052 UINT rc
= ERROR_SUCCESS
;
1055 TRACE("Performing action (%s)\n",debugstr_w(action
));
1057 handled
= ACTION_HandleStandardAction(package
, action
, &rc
, force
);
1060 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, script
, force
);
1064 FIXME("unhandled msi action %s\n",debugstr_w(action
));
1065 rc
= ERROR_FUNCTION_NOT_CALLED
;
1071 UINT
ACTION_PerformUIAction(MSIPACKAGE
*package
, const WCHAR
*action
, UINT script
)
1073 UINT rc
= ERROR_SUCCESS
;
1074 BOOL handled
= FALSE
;
1076 TRACE("Performing action (%s)\n",debugstr_w(action
));
1078 handled
= ACTION_HandleStandardAction(package
, action
, &rc
,TRUE
);
1081 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, script
, FALSE
);
1083 if( !handled
&& ACTION_DialogBox(package
,action
) == ERROR_SUCCESS
)
1088 FIXME("unhandled msi action %s\n",debugstr_w(action
));
1089 rc
= ERROR_FUNCTION_NOT_CALLED
;
1097 * Actual Action Handlers
1100 static UINT
ITERATE_CreateFolders(MSIRECORD
*row
, LPVOID param
)
1102 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1108 dir
= MSI_RecordGetString(row
,1);
1111 ERR("Unable to get folder id\n");
1112 return ERROR_SUCCESS
;
1115 full_path
= resolve_folder(package
,dir
,FALSE
,FALSE
,TRUE
,&folder
);
1118 ERR("Unable to resolve folder id %s\n",debugstr_w(dir
));
1119 return ERROR_SUCCESS
;
1122 TRACE("Folder is %s\n",debugstr_w(full_path
));
1125 uirow
= MSI_CreateRecord(1);
1126 MSI_RecordSetStringW(uirow
,1,full_path
);
1127 ui_actiondata(package
,szCreateFolders
,uirow
);
1128 msiobj_release( &uirow
->hdr
);
1130 if (folder
->State
== 0)
1131 create_full_pathW(full_path
);
1135 msi_free(full_path
);
1136 return ERROR_SUCCESS
;
1139 /* FIXME: probably should merge this with the above function */
1140 static UINT
msi_create_directory( MSIPACKAGE
* package
, LPCWSTR dir
)
1142 UINT rc
= ERROR_SUCCESS
;
1144 LPWSTR install_path
;
1146 install_path
= resolve_folder(package
, dir
, FALSE
, FALSE
, TRUE
, &folder
);
1148 return ERROR_FUNCTION_FAILED
;
1150 /* create the path */
1151 if (folder
->State
== 0)
1153 create_full_pathW(install_path
);
1156 msi_free(install_path
);
1161 UINT
msi_create_component_directories( MSIPACKAGE
*package
)
1165 /* create all the folders required by the components are going to install */
1166 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1168 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
1170 msi_create_directory( package
, comp
->Directory
);
1173 return ERROR_SUCCESS
;
1177 * Also we cannot enable/disable components either, so for now I am just going
1178 * to do all the directories for all the components.
1180 static UINT
ACTION_CreateFolders(MSIPACKAGE
*package
)
1182 static const WCHAR ExecSeqQuery
[] =
1183 {'S','E','L','E','C','T',' ',
1184 '`','D','i','r','e','c','t','o','r','y','_','`',
1185 ' ','F','R','O','M',' ',
1186 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1190 /* create all the empty folders specified in the CreateFolder table */
1191 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
1192 if (rc
!= ERROR_SUCCESS
)
1193 return ERROR_SUCCESS
;
1195 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateFolders
, package
);
1196 msiobj_release(&view
->hdr
);
1198 msi_create_component_directories( package
);
1203 static UINT
load_component( MSIRECORD
*row
, LPVOID param
)
1205 MSIPACKAGE
*package
= param
;
1208 comp
= msi_alloc_zero( sizeof(MSICOMPONENT
) );
1210 return ERROR_FUNCTION_FAILED
;
1212 list_add_tail( &package
->components
, &comp
->entry
);
1214 /* fill in the data */
1215 comp
->Component
= msi_dup_record_field( row
, 1 );
1217 TRACE("Loading Component %s\n", debugstr_w(comp
->Component
));
1219 comp
->ComponentId
= msi_dup_record_field( row
, 2 );
1220 comp
->Directory
= msi_dup_record_field( row
, 3 );
1221 comp
->Attributes
= MSI_RecordGetInteger(row
,4);
1222 comp
->Condition
= msi_dup_record_field( row
, 5 );
1223 comp
->KeyPath
= msi_dup_record_field( row
, 6 );
1225 comp
->Installed
= INSTALLSTATE_UNKNOWN
;
1226 msi_component_set_state( comp
, INSTALLSTATE_UNKNOWN
);
1228 return ERROR_SUCCESS
;
1231 static UINT
load_all_components( MSIPACKAGE
*package
)
1233 static const WCHAR query
[] = {
1234 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1235 '`','C','o','m','p','o','n','e','n','t','`',0 };
1239 if (!list_empty(&package
->components
))
1240 return ERROR_SUCCESS
;
1242 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1243 if (r
!= ERROR_SUCCESS
)
1246 r
= MSI_IterateRecords(view
, NULL
, load_component
, package
);
1247 msiobj_release(&view
->hdr
);
1252 MSIPACKAGE
*package
;
1253 MSIFEATURE
*feature
;
1256 static UINT
add_feature_component( MSIFEATURE
*feature
, MSICOMPONENT
*comp
)
1260 cl
= msi_alloc( sizeof (*cl
) );
1262 return ERROR_NOT_ENOUGH_MEMORY
;
1263 cl
->component
= comp
;
1264 list_add_tail( &feature
->Components
, &cl
->entry
);
1266 return ERROR_SUCCESS
;
1269 static UINT
add_feature_child( MSIFEATURE
*parent
, MSIFEATURE
*child
)
1273 fl
= msi_alloc( sizeof(*fl
) );
1275 return ERROR_NOT_ENOUGH_MEMORY
;
1276 fl
->feature
= child
;
1277 list_add_tail( &parent
->Children
, &fl
->entry
);
1279 return ERROR_SUCCESS
;
1282 static UINT
iterate_load_featurecomponents(MSIRECORD
*row
, LPVOID param
)
1284 _ilfs
* ilfs
= (_ilfs
*)param
;
1288 component
= MSI_RecordGetString(row
,1);
1290 /* check to see if the component is already loaded */
1291 comp
= get_loaded_component( ilfs
->package
, component
);
1294 ERR("unknown component %s\n", debugstr_w(component
));
1295 return ERROR_FUNCTION_FAILED
;
1298 add_feature_component( ilfs
->feature
, comp
);
1299 comp
->Enabled
= TRUE
;
1301 return ERROR_SUCCESS
;
1304 static MSIFEATURE
*find_feature_by_name( MSIPACKAGE
*package
, LPCWSTR name
)
1306 MSIFEATURE
*feature
;
1308 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1310 if ( !lstrcmpW( feature
->Feature
, name
) )
1317 static UINT
load_feature(MSIRECORD
* row
, LPVOID param
)
1319 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1320 MSIFEATURE
* feature
;
1321 static const WCHAR Query1
[] =
1322 {'S','E','L','E','C','T',' ',
1323 '`','C','o','m','p','o','n','e','n','t','_','`',
1324 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1325 'C','o','m','p','o','n','e','n','t','s','`',' ',
1326 'W','H','E','R','E',' ',
1327 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1332 /* fill in the data */
1334 feature
= msi_alloc_zero( sizeof (MSIFEATURE
) );
1336 return ERROR_NOT_ENOUGH_MEMORY
;
1338 list_init( &feature
->Children
);
1339 list_init( &feature
->Components
);
1341 feature
->Feature
= msi_dup_record_field( row
, 1 );
1343 TRACE("Loading feature %s\n",debugstr_w(feature
->Feature
));
1345 feature
->Feature_Parent
= msi_dup_record_field( row
, 2 );
1346 feature
->Title
= msi_dup_record_field( row
, 3 );
1347 feature
->Description
= msi_dup_record_field( row
, 4 );
1349 if (!MSI_RecordIsNull(row
,5))
1350 feature
->Display
= MSI_RecordGetInteger(row
,5);
1352 feature
->Level
= MSI_RecordGetInteger(row
,6);
1353 feature
->Directory
= msi_dup_record_field( row
, 7 );
1354 feature
->Attributes
= MSI_RecordGetInteger(row
,8);
1356 feature
->Installed
= INSTALLSTATE_UNKNOWN
;
1357 msi_feature_set_state( feature
, INSTALLSTATE_UNKNOWN
);
1359 list_add_tail( &package
->features
, &feature
->entry
);
1361 /* load feature components */
1363 rc
= MSI_OpenQuery( package
->db
, &view
, Query1
, feature
->Feature
);
1364 if (rc
!= ERROR_SUCCESS
)
1365 return ERROR_SUCCESS
;
1367 ilfs
.package
= package
;
1368 ilfs
.feature
= feature
;
1370 MSI_IterateRecords(view
, NULL
, iterate_load_featurecomponents
, &ilfs
);
1371 msiobj_release(&view
->hdr
);
1373 return ERROR_SUCCESS
;
1376 static UINT
find_feature_children(MSIRECORD
* row
, LPVOID param
)
1378 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1379 MSIFEATURE
*parent
, *child
;
1381 child
= find_feature_by_name( package
, MSI_RecordGetString( row
, 1 ) );
1383 return ERROR_FUNCTION_FAILED
;
1385 if (!child
->Feature_Parent
)
1386 return ERROR_SUCCESS
;
1388 parent
= find_feature_by_name( package
, child
->Feature_Parent
);
1390 return ERROR_FUNCTION_FAILED
;
1392 add_feature_child( parent
, child
);
1393 return ERROR_SUCCESS
;
1396 static UINT
load_all_features( MSIPACKAGE
*package
)
1398 static const WCHAR query
[] = {
1399 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1400 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1401 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1405 if (!list_empty(&package
->features
))
1406 return ERROR_SUCCESS
;
1408 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1409 if (r
!= ERROR_SUCCESS
)
1412 r
= MSI_IterateRecords( view
, NULL
, load_feature
, package
);
1413 if (r
!= ERROR_SUCCESS
)
1416 r
= MSI_IterateRecords( view
, NULL
, find_feature_children
, package
);
1417 msiobj_release( &view
->hdr
);
1422 static LPWSTR
folder_split_path(LPWSTR p
, WCHAR ch
)
1433 static UINT
load_file_hash(MSIPACKAGE
*package
, MSIFILE
*file
)
1435 static const WCHAR query
[] = {
1436 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1437 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1438 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1439 MSIQUERY
*view
= NULL
;
1440 MSIRECORD
*row
= NULL
;
1443 TRACE("%s\n", debugstr_w(file
->File
));
1445 r
= MSI_OpenQuery(package
->db
, &view
, query
, file
->File
);
1446 if (r
!= ERROR_SUCCESS
)
1449 r
= MSI_ViewExecute(view
, NULL
);
1450 if (r
!= ERROR_SUCCESS
)
1453 r
= MSI_ViewFetch(view
, &row
);
1454 if (r
!= ERROR_SUCCESS
)
1457 file
->hash
.dwFileHashInfoSize
= sizeof(MSIFILEHASHINFO
);
1458 file
->hash
.dwData
[0] = MSI_RecordGetInteger(row
, 3);
1459 file
->hash
.dwData
[1] = MSI_RecordGetInteger(row
, 4);
1460 file
->hash
.dwData
[2] = MSI_RecordGetInteger(row
, 5);
1461 file
->hash
.dwData
[3] = MSI_RecordGetInteger(row
, 6);
1464 if (view
) msiobj_release(&view
->hdr
);
1465 if (row
) msiobj_release(&row
->hdr
);
1469 static UINT
load_file(MSIRECORD
*row
, LPVOID param
)
1471 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1475 /* fill in the data */
1477 file
= msi_alloc_zero( sizeof (MSIFILE
) );
1479 return ERROR_NOT_ENOUGH_MEMORY
;
1481 file
->File
= msi_dup_record_field( row
, 1 );
1483 component
= MSI_RecordGetString( row
, 2 );
1484 file
->Component
= get_loaded_component( package
, component
);
1486 if (!file
->Component
)
1487 ERR("Unfound Component %s\n",debugstr_w(component
));
1489 file
->FileName
= msi_dup_record_field( row
, 3 );
1490 reduce_to_longfilename( file
->FileName
);
1492 file
->ShortName
= msi_dup_record_field( row
, 3 );
1493 file
->LongName
= strdupW( folder_split_path(file
->ShortName
, '|'));
1495 file
->FileSize
= MSI_RecordGetInteger( row
, 4 );
1496 file
->Version
= msi_dup_record_field( row
, 5 );
1497 file
->Language
= msi_dup_record_field( row
, 6 );
1498 file
->Attributes
= MSI_RecordGetInteger( row
, 7 );
1499 file
->Sequence
= MSI_RecordGetInteger( row
, 8 );
1501 file
->state
= msifs_invalid
;
1503 /* if the compressed bits are not set in the file attributes,
1504 * then read the information from the package word count property
1506 if (file
->Attributes
& msidbFileAttributesCompressed
)
1508 file
->IsCompressed
= TRUE
;
1510 else if (file
->Attributes
& msidbFileAttributesNoncompressed
)
1512 file
->IsCompressed
= FALSE
;
1516 file
->IsCompressed
= package
->WordCount
& MSIWORDCOUNT_COMPRESSED
;
1519 load_file_hash(package
, file
);
1521 TRACE("File Loaded (%s)\n",debugstr_w(file
->File
));
1523 list_add_tail( &package
->files
, &file
->entry
);
1525 return ERROR_SUCCESS
;
1528 static UINT
load_all_files(MSIPACKAGE
*package
)
1532 static const WCHAR Query
[] =
1533 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1534 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1535 '`','S','e','q','u','e','n','c','e','`', 0};
1537 if (!list_empty(&package
->files
))
1538 return ERROR_SUCCESS
;
1540 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
1541 if (rc
!= ERROR_SUCCESS
)
1542 return ERROR_SUCCESS
;
1544 rc
= MSI_IterateRecords(view
, NULL
, load_file
, package
);
1545 msiobj_release(&view
->hdr
);
1547 return ERROR_SUCCESS
;
1550 static UINT
load_folder( MSIRECORD
*row
, LPVOID param
)
1552 MSIPACKAGE
*package
= param
;
1553 static const WCHAR szDot
[] = { '.',0 };
1554 static WCHAR szEmpty
[] = { 0 };
1555 LPWSTR p
, tgt_short
, tgt_long
, src_short
, src_long
;
1558 folder
= msi_alloc_zero( sizeof (MSIFOLDER
) );
1560 return ERROR_NOT_ENOUGH_MEMORY
;
1562 folder
->Directory
= msi_dup_record_field( row
, 1 );
1564 TRACE("%s\n", debugstr_w(folder
->Directory
));
1566 p
= msi_dup_record_field(row
, 3);
1568 /* split src and target dir */
1570 src_short
= folder_split_path( p
, ':' );
1572 /* split the long and short paths */
1573 tgt_long
= folder_split_path( tgt_short
, '|' );
1574 src_long
= folder_split_path( src_short
, '|' );
1576 /* check for no-op dirs */
1577 if (!lstrcmpW(szDot
, tgt_short
))
1578 tgt_short
= szEmpty
;
1579 if (!lstrcmpW(szDot
, src_short
))
1580 src_short
= szEmpty
;
1583 tgt_long
= tgt_short
;
1586 src_short
= tgt_short
;
1587 src_long
= tgt_long
;
1591 src_long
= src_short
;
1593 /* FIXME: use the target short path too */
1594 folder
->TargetDefault
= strdupW(tgt_long
);
1595 folder
->SourceShortPath
= strdupW(src_short
);
1596 folder
->SourceLongPath
= strdupW(src_long
);
1599 TRACE("TargetDefault = %s\n",debugstr_w( folder
->TargetDefault
));
1600 TRACE("SourceLong = %s\n", debugstr_w( folder
->SourceLongPath
));
1601 TRACE("SourceShort = %s\n", debugstr_w( folder
->SourceShortPath
));
1603 folder
->Parent
= msi_dup_record_field( row
, 2 );
1605 folder
->Property
= msi_dup_property( package
, folder
->Directory
);
1607 list_add_tail( &package
->folders
, &folder
->entry
);
1609 TRACE("returning %p\n", folder
);
1611 return ERROR_SUCCESS
;
1614 static UINT
load_all_folders( MSIPACKAGE
*package
)
1616 static const WCHAR query
[] = {
1617 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1618 '`','D','i','r','e','c','t','o','r','y','`',0 };
1622 if (!list_empty(&package
->folders
))
1623 return ERROR_SUCCESS
;
1625 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1626 if (r
!= ERROR_SUCCESS
)
1629 r
= MSI_IterateRecords(view
, NULL
, load_folder
, package
);
1630 msiobj_release(&view
->hdr
);
1635 * I am not doing any of the costing functionality yet.
1636 * Mostly looking at doing the Component and Feature loading
1638 * The native MSI does A LOT of modification to tables here. Mostly adding
1639 * a lot of temporary columns to the Feature and Component tables.
1641 * note: Native msi also tracks the short filename. But I am only going to
1642 * track the long ones. Also looking at this directory table
1643 * it appears that the directory table does not get the parents
1644 * resolved base on property only based on their entries in the
1647 static UINT
ACTION_CostInitialize(MSIPACKAGE
*package
)
1649 static const WCHAR szCosting
[] =
1650 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1651 static const WCHAR szZero
[] = { '0', 0 };
1653 MSI_SetPropertyW(package
, szCosting
, szZero
);
1654 MSI_SetPropertyW(package
, cszRootDrive
, c_colon
);
1656 load_all_components( package
);
1657 load_all_features( package
);
1658 load_all_files( package
);
1659 load_all_folders( package
);
1661 return ERROR_SUCCESS
;
1664 static UINT
execute_script(MSIPACKAGE
*package
, UINT script
)
1667 UINT rc
= ERROR_SUCCESS
;
1669 TRACE("Executing Script %i\n",script
);
1671 if (!package
->script
)
1673 ERR("no script!\n");
1674 return ERROR_FUNCTION_FAILED
;
1677 for (i
= 0; i
< package
->script
->ActionCount
[script
]; i
++)
1680 action
= package
->script
->Actions
[script
][i
];
1681 ui_actionstart(package
, action
);
1682 TRACE("Executing Action (%s)\n",debugstr_w(action
));
1683 rc
= ACTION_PerformAction(package
, action
, script
, TRUE
);
1684 if (rc
!= ERROR_SUCCESS
)
1687 msi_free_action_script(package
, script
);
1691 static UINT
ACTION_FileCost(MSIPACKAGE
*package
)
1693 return ERROR_SUCCESS
;
1696 static void ACTION_GetComponentInstallStates(MSIPACKAGE
*package
)
1700 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1704 if (!comp
->ComponentId
)
1707 res
= MsiGetComponentPathW( package
->ProductCode
,
1708 comp
->ComponentId
, NULL
, NULL
);
1710 res
= INSTALLSTATE_ABSENT
;
1711 comp
->Installed
= res
;
1715 /* scan for and update current install states */
1716 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE
*package
)
1719 MSIFEATURE
*feature
;
1721 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1724 INSTALLSTATE res
= INSTALLSTATE_ABSENT
;
1726 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1728 comp
= cl
->component
;
1730 if (!comp
->ComponentId
)
1732 res
= INSTALLSTATE_ABSENT
;
1736 if (res
== INSTALLSTATE_ABSENT
)
1737 res
= comp
->Installed
;
1740 if (res
== comp
->Installed
)
1743 if (res
!= INSTALLSTATE_DEFAULT
&& res
!= INSTALLSTATE_LOCAL
&&
1744 res
!= INSTALLSTATE_SOURCE
)
1746 res
= INSTALLSTATE_INCOMPLETE
;
1750 feature
->Installed
= res
;
1754 static BOOL
process_state_property (MSIPACKAGE
* package
, LPCWSTR property
,
1757 static const WCHAR all
[]={'A','L','L',0};
1759 MSIFEATURE
*feature
;
1761 override
= msi_dup_property( package
, property
);
1765 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1767 if (strcmpiW(override
,all
)==0)
1768 msi_feature_set_state( feature
, state
);
1771 LPWSTR ptr
= override
;
1772 LPWSTR ptr2
= strchrW(override
,',');
1776 if ((ptr2
&& strncmpW(ptr
,feature
->Feature
, ptr2
-ptr
)==0)
1777 || (!ptr2
&& strcmpW(ptr
,feature
->Feature
)==0))
1779 msi_feature_set_state( feature
, state
);
1785 ptr2
= strchrW(ptr
,',');
1797 UINT
MSI_SetFeatureStates(MSIPACKAGE
*package
)
1800 static const WCHAR szlevel
[] =
1801 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1802 static const WCHAR szAddLocal
[] =
1803 {'A','D','D','L','O','C','A','L',0};
1804 static const WCHAR szAddSource
[] =
1805 {'A','D','D','S','O','U','R','C','E',0};
1806 static const WCHAR szRemove
[] =
1807 {'R','E','M','O','V','E',0};
1808 static const WCHAR szReinstall
[] =
1809 {'R','E','I','N','S','T','A','L','L',0};
1810 BOOL override
= FALSE
;
1811 MSICOMPONENT
* component
;
1812 MSIFEATURE
*feature
;
1815 /* I do not know if this is where it should happen.. but */
1817 TRACE("Checking Install Level\n");
1819 install_level
= msi_get_property_int( package
, szlevel
, 1 );
1821 /* ok here is the _real_ rub
1822 * all these activation/deactivation things happen in order and things
1823 * later on the list override things earlier on the list.
1824 * 1) INSTALLLEVEL processing
1834 * 11) FILEADDDEFAULT
1835 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1836 * ignored for all the features. seems strange, especially since it is not
1837 * documented anywhere, but it is how it works.
1839 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1840 * REMOVE are the big ones, since we don't handle administrative installs
1843 override
|= process_state_property(package
,szAddLocal
,INSTALLSTATE_LOCAL
);
1844 override
|= process_state_property(package
,szRemove
,INSTALLSTATE_ABSENT
);
1845 override
|= process_state_property(package
,szAddSource
,INSTALLSTATE_SOURCE
);
1846 override
|= process_state_property(package
,szReinstall
,INSTALLSTATE_LOCAL
);
1850 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1852 BOOL feature_state
= ((feature
->Level
> 0) &&
1853 (feature
->Level
<= install_level
));
1855 if ((feature_state
) && (feature
->Action
== INSTALLSTATE_UNKNOWN
))
1857 if (feature
->Attributes
& msidbFeatureAttributesFavorSource
)
1858 msi_feature_set_state( feature
, INSTALLSTATE_SOURCE
);
1859 else if (feature
->Attributes
& msidbFeatureAttributesFavorAdvertise
)
1860 msi_feature_set_state( feature
, INSTALLSTATE_ADVERTISED
);
1862 msi_feature_set_state( feature
, INSTALLSTATE_LOCAL
);
1866 /* disable child features of unselected parent features */
1867 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1871 if (feature
->Level
> 0 && feature
->Level
<= install_level
)
1874 LIST_FOR_EACH_ENTRY( fl
, &feature
->Children
, FeatureList
, entry
)
1875 msi_feature_set_state( fl
->feature
, INSTALLSTATE_UNKNOWN
);
1880 /* set the Preselected Property */
1881 static const WCHAR szPreselected
[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1882 static const WCHAR szOne
[] = { '1', 0 };
1884 MSI_SetPropertyW(package
,szPreselected
,szOne
);
1888 * now we want to enable or disable components base on feature
1891 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1895 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1896 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
);
1898 /* features with components that have compressed files are made local */
1899 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1901 if (cl
->component
->Enabled
&&
1902 cl
->component
->ForceLocalState
&&
1903 feature
->Action
== INSTALLSTATE_SOURCE
)
1905 msi_feature_set_state( feature
, INSTALLSTATE_LOCAL
);
1910 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1912 component
= cl
->component
;
1914 if (!component
->Enabled
)
1917 switch (feature
->Action
)
1919 case INSTALLSTATE_ABSENT
:
1920 component
->anyAbsent
= 1;
1922 case INSTALLSTATE_ADVERTISED
:
1923 component
->hasAdvertiseFeature
= 1;
1925 case INSTALLSTATE_SOURCE
:
1926 component
->hasSourceFeature
= 1;
1928 case INSTALLSTATE_LOCAL
:
1929 component
->hasLocalFeature
= 1;
1931 case INSTALLSTATE_DEFAULT
:
1932 if (feature
->Attributes
& msidbFeatureAttributesFavorAdvertise
)
1933 component
->hasAdvertiseFeature
= 1;
1934 else if (feature
->Attributes
& msidbFeatureAttributesFavorSource
)
1935 component
->hasSourceFeature
= 1;
1937 component
->hasLocalFeature
= 1;
1945 LIST_FOR_EACH_ENTRY( component
, &package
->components
, MSICOMPONENT
, entry
)
1947 /* if the component isn't enabled, leave it alone */
1948 if (!component
->Enabled
)
1951 /* check if it's local or source */
1952 if (!(component
->Attributes
& msidbComponentAttributesOptional
) &&
1953 (component
->hasLocalFeature
|| component
->hasSourceFeature
))
1955 if ((component
->Attributes
& msidbComponentAttributesSourceOnly
) &&
1956 !component
->ForceLocalState
)
1957 msi_component_set_state( component
, INSTALLSTATE_SOURCE
);
1959 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1963 /* if any feature is local, the component must be local too */
1964 if (component
->hasLocalFeature
)
1966 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1970 if (component
->hasSourceFeature
)
1972 msi_component_set_state( component
, INSTALLSTATE_SOURCE
);
1976 if (component
->hasAdvertiseFeature
)
1978 msi_component_set_state( component
, INSTALLSTATE_ADVERTISED
);
1982 TRACE("nobody wants component %s\n", debugstr_w(component
->Component
));
1983 if (component
->anyAbsent
)
1984 msi_component_set_state(component
, INSTALLSTATE_ABSENT
);
1987 LIST_FOR_EACH_ENTRY( component
, &package
->components
, MSICOMPONENT
, entry
)
1989 if (component
->Action
== INSTALLSTATE_DEFAULT
)
1991 TRACE("%s was default, setting to local\n", debugstr_w(component
->Component
));
1992 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1995 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1996 debugstr_w(component
->Component
), component
->Installed
, component
->Action
);
2000 return ERROR_SUCCESS
;
2003 static UINT
ITERATE_CostFinalizeDirectories(MSIRECORD
*row
, LPVOID param
)
2005 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
2010 name
= MSI_RecordGetString(row
,1);
2012 f
= get_loaded_folder(package
, name
);
2013 if (!f
) return ERROR_SUCCESS
;
2015 /* reset the ResolvedTarget */
2016 msi_free(f
->ResolvedTarget
);
2017 f
->ResolvedTarget
= NULL
;
2019 /* This helper function now does ALL the work */
2020 TRACE("Dir %s ...\n",debugstr_w(name
));
2021 path
= resolve_folder(package
,name
,FALSE
,TRUE
,TRUE
,NULL
);
2022 TRACE("resolves to %s\n",debugstr_w(path
));
2025 return ERROR_SUCCESS
;
2028 static UINT
ITERATE_CostFinalizeConditions(MSIRECORD
*row
, LPVOID param
)
2030 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
2032 MSIFEATURE
*feature
;
2034 name
= MSI_RecordGetString( row
, 1 );
2036 feature
= get_loaded_feature( package
, name
);
2038 ERR("FAILED to find loaded feature %s\n",debugstr_w(name
));
2042 Condition
= MSI_RecordGetString(row
,3);
2044 if (MSI_EvaluateConditionW(package
,Condition
) == MSICONDITION_TRUE
)
2046 int level
= MSI_RecordGetInteger(row
,2);
2047 TRACE("Reseting feature %s to level %i\n", debugstr_w(name
), level
);
2048 feature
->Level
= level
;
2051 return ERROR_SUCCESS
;
2054 static LPWSTR
msi_get_disk_file_version( LPCWSTR filename
)
2056 static const WCHAR name_fmt
[] =
2057 {'%','u','.','%','u','.','%','u','.','%','u',0};
2058 static WCHAR name
[] = {'\\',0};
2059 VS_FIXEDFILEINFO
*lpVer
;
2060 WCHAR filever
[0x100];
2066 TRACE("%s\n", debugstr_w(filename
));
2068 versize
= GetFileVersionInfoSizeW( filename
, &handle
);
2072 version
= msi_alloc( versize
);
2073 GetFileVersionInfoW( filename
, 0, versize
, version
);
2075 if (!VerQueryValueW( version
, name
, (LPVOID
*)&lpVer
, &sz
))
2077 msi_free( version
);
2081 sprintfW( filever
, name_fmt
,
2082 HIWORD(lpVer
->dwFileVersionMS
),
2083 LOWORD(lpVer
->dwFileVersionMS
),
2084 HIWORD(lpVer
->dwFileVersionLS
),
2085 LOWORD(lpVer
->dwFileVersionLS
));
2087 msi_free( version
);
2089 return strdupW( filever
);
2092 static UINT
msi_check_file_install_states( MSIPACKAGE
*package
)
2094 LPWSTR file_version
;
2097 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2099 MSICOMPONENT
* comp
= file
->Component
;
2105 if (file
->IsCompressed
)
2106 comp
->ForceLocalState
= TRUE
;
2108 /* calculate target */
2109 p
= resolve_folder(package
, comp
->Directory
, FALSE
, FALSE
, TRUE
, NULL
);
2111 msi_free(file
->TargetPath
);
2113 TRACE("file %s is named %s\n",
2114 debugstr_w(file
->File
), debugstr_w(file
->FileName
));
2116 file
->TargetPath
= build_directory_name(2, p
, file
->FileName
);
2120 TRACE("file %s resolves to %s\n",
2121 debugstr_w(file
->File
), debugstr_w(file
->TargetPath
));
2123 /* don't check files of components that aren't installed */
2124 if (comp
->Installed
== INSTALLSTATE_UNKNOWN
||
2125 comp
->Installed
== INSTALLSTATE_ABSENT
)
2127 file
->state
= msifs_missing
; /* assume files are missing */
2131 if (GetFileAttributesW(file
->TargetPath
) == INVALID_FILE_ATTRIBUTES
)
2133 file
->state
= msifs_missing
;
2134 comp
->Cost
+= file
->FileSize
;
2135 comp
->Installed
= INSTALLSTATE_INCOMPLETE
;
2139 if (file
->Version
&&
2140 (file_version
= msi_get_disk_file_version( file
->TargetPath
)))
2142 TRACE("new %s old %s\n", debugstr_w(file
->Version
),
2143 debugstr_w(file_version
));
2144 /* FIXME: seems like a bad way to compare version numbers */
2145 if (lstrcmpiW(file_version
, file
->Version
)<0)
2147 file
->state
= msifs_overwrite
;
2148 comp
->Cost
+= file
->FileSize
;
2149 comp
->Installed
= INSTALLSTATE_INCOMPLETE
;
2152 file
->state
= msifs_present
;
2153 msi_free( file_version
);
2156 file
->state
= msifs_present
;
2159 return ERROR_SUCCESS
;
2163 * A lot is done in this function aside from just the costing.
2164 * The costing needs to be implemented at some point but for now I am going
2165 * to focus on the directory building
2168 static UINT
ACTION_CostFinalize(MSIPACKAGE
*package
)
2170 static const WCHAR ExecSeqQuery
[] =
2171 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2172 '`','D','i','r','e','c','t','o','r','y','`',0};
2173 static const WCHAR ConditionQuery
[] =
2174 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2175 '`','C','o','n','d','i','t','i','o','n','`',0};
2176 static const WCHAR szCosting
[] =
2177 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2178 static const WCHAR szlevel
[] =
2179 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2180 static const WCHAR szOne
[] = { '1', 0 };
2186 if ( 1 == msi_get_property_int( package
, szCosting
, 0 ) )
2187 return ERROR_SUCCESS
;
2189 TRACE("Building Directory properties\n");
2191 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2192 if (rc
== ERROR_SUCCESS
)
2194 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CostFinalizeDirectories
,
2196 msiobj_release(&view
->hdr
);
2199 /* read components states from the registry */
2200 ACTION_GetComponentInstallStates(package
);
2202 TRACE("File calculations\n");
2203 msi_check_file_install_states( package
);
2205 TRACE("Evaluating Condition Table\n");
2207 rc
= MSI_DatabaseOpenViewW(package
->db
, ConditionQuery
, &view
);
2208 if (rc
== ERROR_SUCCESS
)
2210 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CostFinalizeConditions
,
2212 msiobj_release(&view
->hdr
);
2215 TRACE("Enabling or Disabling Components\n");
2216 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2218 if (MSI_EvaluateConditionW(package
, comp
->Condition
) == MSICONDITION_FALSE
)
2220 TRACE("Disabling component %s\n", debugstr_w(comp
->Component
));
2221 comp
->Enabled
= FALSE
;
2225 MSI_SetPropertyW(package
,szCosting
,szOne
);
2226 /* set default run level if not set */
2227 level
= msi_dup_property( package
, szlevel
);
2229 MSI_SetPropertyW(package
,szlevel
, szOne
);
2232 ACTION_UpdateFeatureInstallStates(package
);
2234 return MSI_SetFeatureStates(package
);
2237 /* OK this value is "interpreted" and then formatted based on the
2238 first few characters */
2239 static LPSTR
parse_value(MSIPACKAGE
*package
, LPCWSTR value
, DWORD
*type
,
2244 if (value
[0]=='#' && value
[1]!='#' && value
[1]!='%')
2250 LPWSTR deformated
= NULL
;
2253 deformat_string(package
, &value
[2], &deformated
);
2255 /* binary value type */
2259 *size
= (strlenW(ptr
)/2)+1;
2261 *size
= strlenW(ptr
)/2;
2263 data
= msi_alloc(*size
);
2269 /* if uneven pad with a zero in front */
2275 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2277 TRACE("Uneven byte count\n");
2285 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2288 msi_free(deformated
);
2290 TRACE("Data %i bytes(%i)\n",*size
,count
);
2297 deformat_string(package
, &value
[1], &deformated
);
2300 *size
= sizeof(DWORD
);
2301 data
= msi_alloc(*size
);
2307 if ( (*p
< '0') || (*p
> '9') )
2313 if (deformated
[0] == '-')
2316 TRACE("DWORD %i\n",*(LPDWORD
)data
);
2318 msi_free(deformated
);
2323 static const WCHAR szMulti
[] = {'[','~',']',0};
2332 *type
=REG_EXPAND_SZ
;
2340 if (strstrW(value
,szMulti
))
2341 *type
= REG_MULTI_SZ
;
2343 /* remove initial delimiter */
2344 if (!strncmpW(value
, szMulti
, 3))
2347 *size
= deformat_string(package
, ptr
,(LPWSTR
*)&data
);
2349 /* add double NULL terminator */
2350 if (*type
== REG_MULTI_SZ
)
2352 *size
+= 2 * sizeof(WCHAR
); /* two NULL terminators */
2353 data
= msi_realloc_zero(data
, *size
);
2359 static UINT
ITERATE_WriteRegistryValues(MSIRECORD
*row
, LPVOID param
)
2361 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
2362 static const WCHAR szHCR
[] =
2363 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2364 'R','O','O','T','\\',0};
2365 static const WCHAR szHCU
[] =
2366 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2367 'U','S','E','R','\\',0};
2368 static const WCHAR szHLM
[] =
2369 {'H','K','E','Y','_','L','O','C','A','L','_',
2370 'M','A','C','H','I','N','E','\\',0};
2371 static const WCHAR szHU
[] =
2372 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2374 LPSTR value_data
= NULL
;
2375 HKEY root_key
, hkey
;
2378 LPCWSTR szRoot
, component
, name
, key
, value
;
2383 BOOL check_first
= FALSE
;
2386 ui_progress(package
,2,0,0,0);
2393 component
= MSI_RecordGetString(row
, 6);
2394 comp
= get_loaded_component(package
,component
);
2396 return ERROR_SUCCESS
;
2398 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2400 TRACE("Skipping write due to disabled component %s\n",
2401 debugstr_w(component
));
2403 comp
->Action
= comp
->Installed
;
2405 return ERROR_SUCCESS
;
2408 comp
->Action
= INSTALLSTATE_LOCAL
;
2410 name
= MSI_RecordGetString(row
, 4);
2411 if( MSI_RecordIsNull(row
,5) && name
)
2413 /* null values can have special meanings */
2414 if (name
[0]=='-' && name
[1] == 0)
2415 return ERROR_SUCCESS
;
2416 else if ((name
[0]=='+' && name
[1] == 0) ||
2417 (name
[0] == '*' && name
[1] == 0))
2422 root
= MSI_RecordGetInteger(row
,2);
2423 key
= MSI_RecordGetString(row
, 3);
2425 /* get the root key */
2430 static const WCHAR szALLUSER
[] = {'A','L','L','U','S','E','R','S',0};
2431 LPWSTR all_users
= msi_dup_property( package
, szALLUSER
);
2432 if (all_users
&& all_users
[0] == '1')
2434 root_key
= HKEY_LOCAL_MACHINE
;
2439 root_key
= HKEY_CURRENT_USER
;
2442 msi_free(all_users
);
2445 case 0: root_key
= HKEY_CLASSES_ROOT
;
2448 case 1: root_key
= HKEY_CURRENT_USER
;
2451 case 2: root_key
= HKEY_LOCAL_MACHINE
;
2454 case 3: root_key
= HKEY_USERS
;
2458 ERR("Unknown root %i\n",root
);
2464 return ERROR_SUCCESS
;
2466 deformat_string(package
, key
, &deformated
);
2467 size
= strlenW(deformated
) + strlenW(szRoot
) + 1;
2468 uikey
= msi_alloc(size
*sizeof(WCHAR
));
2469 strcpyW(uikey
,szRoot
);
2470 strcatW(uikey
,deformated
);
2472 if (RegCreateKeyW( root_key
, deformated
, &hkey
))
2474 ERR("Could not create key %s\n",debugstr_w(deformated
));
2475 msi_free(deformated
);
2477 return ERROR_SUCCESS
;
2479 msi_free(deformated
);
2481 value
= MSI_RecordGetString(row
,5);
2483 value_data
= parse_value(package
, value
, &type
, &size
);
2486 static const WCHAR szEmpty
[] = {0};
2487 value_data
= (LPSTR
)strdupW(szEmpty
);
2492 deformat_string(package
, name
, &deformated
);
2496 TRACE("Setting value %s of %s\n",debugstr_w(deformated
),
2498 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
)value_data
, size
);
2503 rc
= RegQueryValueExW(hkey
, deformated
, NULL
, NULL
, NULL
, &sz
);
2504 if (rc
== ERROR_SUCCESS
|| rc
== ERROR_MORE_DATA
)
2506 TRACE("value %s of %s checked already exists\n",
2507 debugstr_w(deformated
), debugstr_w(uikey
));
2511 TRACE("Checked and setting value %s of %s\n",
2512 debugstr_w(deformated
), debugstr_w(uikey
));
2513 if (deformated
|| size
)
2514 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
) value_data
, size
);
2519 uirow
= MSI_CreateRecord(3);
2520 MSI_RecordSetStringW(uirow
,2,deformated
);
2521 MSI_RecordSetStringW(uirow
,1,uikey
);
2524 MSI_RecordSetStringW(uirow
,3,(LPWSTR
)value_data
);
2526 MSI_RecordSetStringW(uirow
,3,value
);
2528 ui_actiondata(package
,szWriteRegistryValues
,uirow
);
2529 msiobj_release( &uirow
->hdr
);
2531 msi_free(value_data
);
2532 msi_free(deformated
);
2535 return ERROR_SUCCESS
;
2538 static UINT
ACTION_WriteRegistryValues(MSIPACKAGE
*package
)
2542 static const WCHAR ExecSeqQuery
[] =
2543 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2544 '`','R','e','g','i','s','t','r','y','`',0 };
2546 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2547 if (rc
!= ERROR_SUCCESS
)
2548 return ERROR_SUCCESS
;
2550 /* increment progress bar each time action data is sent */
2551 ui_progress(package
,1,REG_PROGRESS_VALUE
,1,0);
2553 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteRegistryValues
, package
);
2555 msiobj_release(&view
->hdr
);
2559 static UINT
ACTION_InstallInitialize(MSIPACKAGE
*package
)
2561 package
->script
->CurrentlyScripting
= TRUE
;
2563 return ERROR_SUCCESS
;
2567 static UINT
ACTION_InstallValidate(MSIPACKAGE
*package
)
2572 static const WCHAR q1
[]=
2573 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2574 '`','R','e','g','i','s','t','r','y','`',0};
2577 MSIFEATURE
*feature
;
2580 TRACE("InstallValidate\n");
2582 rc
= MSI_DatabaseOpenViewW(package
->db
, q1
, &view
);
2583 if (rc
== ERROR_SUCCESS
)
2585 MSI_IterateRecords( view
, &progress
, NULL
, package
);
2586 msiobj_release( &view
->hdr
);
2587 total
+= progress
* REG_PROGRESS_VALUE
;
2590 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2591 total
+= COMPONENT_PROGRESS_VALUE
;
2593 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2594 total
+= file
->FileSize
;
2596 ui_progress(package
,0,total
,0,0);
2598 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2600 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2601 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
,
2602 feature
->ActionRequest
);
2605 return ERROR_SUCCESS
;
2608 static UINT
ITERATE_LaunchConditions(MSIRECORD
*row
, LPVOID param
)
2610 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
2611 LPCWSTR cond
= NULL
;
2612 LPCWSTR message
= NULL
;
2615 static const WCHAR title
[]=
2616 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2618 cond
= MSI_RecordGetString(row
,1);
2620 r
= MSI_EvaluateConditionW(package
,cond
);
2621 if (r
== MSICONDITION_FALSE
)
2623 if ((gUILevel
& INSTALLUILEVEL_MASK
) != INSTALLUILEVEL_NONE
)
2626 message
= MSI_RecordGetString(row
,2);
2627 deformat_string(package
,message
,&deformated
);
2628 MessageBoxW(NULL
,deformated
,title
,MB_OK
);
2629 msi_free(deformated
);
2632 return ERROR_INSTALL_FAILURE
;
2635 return ERROR_SUCCESS
;
2638 static UINT
ACTION_LaunchConditions(MSIPACKAGE
*package
)
2641 MSIQUERY
* view
= NULL
;
2642 static const WCHAR ExecSeqQuery
[] =
2643 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2644 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2646 TRACE("Checking launch conditions\n");
2648 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2649 if (rc
!= ERROR_SUCCESS
)
2650 return ERROR_SUCCESS
;
2652 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_LaunchConditions
, package
);
2653 msiobj_release(&view
->hdr
);
2658 static LPWSTR
resolve_keypath( MSIPACKAGE
* package
, MSICOMPONENT
*cmp
)
2662 return resolve_folder(package
,cmp
->Directory
,FALSE
,FALSE
,TRUE
,NULL
);
2664 if (cmp
->Attributes
& msidbComponentAttributesRegistryKeyPath
)
2666 MSIRECORD
* row
= 0;
2668 LPWSTR deformated
,buffer
,deformated_name
;
2670 static const WCHAR ExecSeqQuery
[] =
2671 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2672 '`','R','e','g','i','s','t','r','y','`',' ',
2673 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2674 ' ','=',' ' ,'\'','%','s','\'',0 };
2675 static const WCHAR fmt
[]={'%','0','2','i',':','\\','%','s','\\',0};
2676 static const WCHAR fmt2
[]=
2677 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2679 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
,cmp
->KeyPath
);
2683 root
= MSI_RecordGetInteger(row
,2);
2684 key
= MSI_RecordGetString(row
, 3);
2685 name
= MSI_RecordGetString(row
, 4);
2686 deformat_string(package
, key
, &deformated
);
2687 deformat_string(package
, name
, &deformated_name
);
2689 len
= strlenW(deformated
) + 6;
2690 if (deformated_name
)
2691 len
+=strlenW(deformated_name
);
2693 buffer
= msi_alloc( len
*sizeof(WCHAR
));
2695 if (deformated_name
)
2696 sprintfW(buffer
,fmt2
,root
,deformated
,deformated_name
);
2698 sprintfW(buffer
,fmt
,root
,deformated
);
2700 msi_free(deformated
);
2701 msi_free(deformated_name
);
2702 msiobj_release(&row
->hdr
);
2706 else if (cmp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2708 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2713 MSIFILE
*file
= get_loaded_file( package
, cmp
->KeyPath
);
2716 return strdupW( file
->TargetPath
);
2721 static HKEY
openSharedDLLsKey(void)
2724 static const WCHAR path
[] =
2725 {'S','o','f','t','w','a','r','e','\\',
2726 'M','i','c','r','o','s','o','f','t','\\',
2727 'W','i','n','d','o','w','s','\\',
2728 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2729 'S','h','a','r','e','d','D','L','L','s',0};
2731 RegCreateKeyW(HKEY_LOCAL_MACHINE
,path
,&hkey
);
2735 static UINT
ACTION_GetSharedDLLsCount(LPCWSTR dll
)
2740 DWORD sz
= sizeof(count
);
2743 hkey
= openSharedDLLsKey();
2744 rc
= RegQueryValueExW(hkey
, dll
, NULL
, &type
, (LPBYTE
)&count
, &sz
);
2745 if (rc
!= ERROR_SUCCESS
)
2751 static UINT
ACTION_WriteSharedDLLsCount(LPCWSTR path
, UINT count
)
2755 hkey
= openSharedDLLsKey();
2757 msi_reg_set_val_dword( hkey
, path
, count
);
2759 RegDeleteValueW(hkey
,path
);
2765 * Return TRUE if the count should be written out and FALSE if not
2767 static void ACTION_RefCountComponent( MSIPACKAGE
* package
, MSICOMPONENT
*comp
)
2769 MSIFEATURE
*feature
;
2773 /* only refcount DLLs */
2774 if (comp
->KeyPath
== NULL
||
2775 comp
->Attributes
& msidbComponentAttributesRegistryKeyPath
||
2776 comp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2780 count
= ACTION_GetSharedDLLsCount( comp
->FullKeypath
);
2781 write
= (count
> 0);
2783 if (comp
->Attributes
& msidbComponentAttributesSharedDllRefCount
)
2787 /* increment counts */
2788 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2792 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
))
2795 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2797 if ( cl
->component
== comp
)
2802 /* decrement counts */
2803 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2807 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ABSENT
))
2810 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2812 if ( cl
->component
== comp
)
2817 /* ref count all the files in the component */
2822 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2824 if (file
->Component
== comp
)
2825 ACTION_WriteSharedDLLsCount( file
->TargetPath
, count
);
2829 /* add a count for permenent */
2830 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2833 comp
->RefCount
= count
;
2836 ACTION_WriteSharedDLLsCount( comp
->FullKeypath
, comp
->RefCount
);
2840 * Ok further analysis makes me think that this work is
2841 * actually done in the PublishComponents and PublishFeatures
2842 * step, and not here. It appears like the keypath and all that is
2843 * resolved in this step, however actually written in the Publish steps.
2844 * But we will leave it here for now because it is unclear
2846 static UINT
ACTION_ProcessComponents(MSIPACKAGE
*package
)
2848 WCHAR squished_pc
[GUID_SIZE
];
2849 WCHAR squished_cc
[GUID_SIZE
];
2852 HKEY hkey
=0,hkey2
=0;
2856 /* writes the Component and Features values to the registry */
2858 rc
= MSIREG_OpenComponents(&hkey
);
2859 if (rc
!= ERROR_SUCCESS
)
2862 squash_guid(package
->ProductCode
,squished_pc
);
2863 ui_progress(package
,1,COMPONENT_PROGRESS_VALUE
,1,0);
2865 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2869 ui_progress(package
,2,0,0,0);
2870 if (!comp
->ComponentId
)
2873 squash_guid(comp
->ComponentId
,squished_cc
);
2875 msi_free(comp
->FullKeypath
);
2876 comp
->FullKeypath
= resolve_keypath( package
, comp
);
2878 /* do the refcounting */
2879 ACTION_RefCountComponent( package
, comp
);
2881 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2882 debugstr_w(comp
->Component
),
2883 debugstr_w(squished_cc
),
2884 debugstr_w(comp
->FullKeypath
),
2887 * Write the keypath out if the component is to be registered
2888 * and delete the key if the component is to be deregistered
2890 if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2892 rc
= RegCreateKeyW(hkey
,squished_cc
,&hkey2
);
2893 if (rc
!= ERROR_SUCCESS
)
2896 if (!comp
->FullKeypath
)
2899 msi_reg_set_val_str( hkey2
, squished_pc
, comp
->FullKeypath
);
2901 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2903 static const WCHAR szPermKey
[] =
2904 { '0','0','0','0','0','0','0','0','0','0','0','0',
2905 '0','0','0','0','0','0','0','0','0','0','0','0',
2906 '0','0','0','0','0','0','0','0',0 };
2908 msi_reg_set_val_str( hkey2
, szPermKey
, comp
->FullKeypath
);
2913 rc
= MSIREG_OpenUserDataComponentKey(comp
->ComponentId
, &hkey2
, TRUE
);
2914 if (rc
!= ERROR_SUCCESS
)
2917 msi_reg_set_val_str(hkey2
, squished_pc
, comp
->FullKeypath
);
2920 else if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ABSENT
))
2924 rc
= RegOpenKeyW(hkey
,squished_cc
,&hkey2
);
2925 if (rc
!= ERROR_SUCCESS
)
2928 RegDeleteValueW(hkey2
,squished_pc
);
2930 /* if the key is empty delete it */
2931 res
= RegEnumKeyExW(hkey2
,0,NULL
,0,0,NULL
,0,NULL
);
2933 if (res
== ERROR_NO_MORE_ITEMS
)
2934 RegDeleteKeyW(hkey
,squished_cc
);
2936 MSIREG_DeleteUserDataComponentKey(comp
->ComponentId
);
2940 uirow
= MSI_CreateRecord(3);
2941 MSI_RecordSetStringW(uirow
,1,package
->ProductCode
);
2942 MSI_RecordSetStringW(uirow
,2,comp
->ComponentId
);
2943 MSI_RecordSetStringW(uirow
,3,comp
->FullKeypath
);
2944 ui_actiondata(package
,szProcessComponents
,uirow
);
2945 msiobj_release( &uirow
->hdr
);
2959 static BOOL CALLBACK
Typelib_EnumResNameProc( HMODULE hModule
, LPCWSTR lpszType
,
2960 LPWSTR lpszName
, LONG_PTR lParam
)
2963 typelib_struct
*tl_struct
= (typelib_struct
*) lParam
;
2964 static const WCHAR fmt
[] = {'%','s','\\','%','i',0};
2968 if (!IS_INTRESOURCE(lpszName
))
2970 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName
));
2974 sz
= strlenW(tl_struct
->source
)+4;
2975 sz
*= sizeof(WCHAR
);
2977 if ((INT_PTR
)lpszName
== 1)
2978 tl_struct
->path
= strdupW(tl_struct
->source
);
2981 tl_struct
->path
= msi_alloc(sz
);
2982 sprintfW(tl_struct
->path
,fmt
,tl_struct
->source
, lpszName
);
2985 TRACE("trying %s\n", debugstr_w(tl_struct
->path
));
2986 res
= LoadTypeLib(tl_struct
->path
,&tl_struct
->ptLib
);
2987 if (!SUCCEEDED(res
))
2989 msi_free(tl_struct
->path
);
2990 tl_struct
->path
= NULL
;
2995 ITypeLib_GetLibAttr(tl_struct
->ptLib
, &attr
);
2996 if (IsEqualGUID(&(tl_struct
->clsid
),&(attr
->guid
)))
2998 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
3002 msi_free(tl_struct
->path
);
3003 tl_struct
->path
= NULL
;
3005 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
3006 ITypeLib_Release(tl_struct
->ptLib
);
3011 static UINT
ITERATE_RegisterTypeLibraries(MSIRECORD
*row
, LPVOID param
)
3013 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
3017 typelib_struct tl_struct
;
3019 static const WCHAR szTYPELIB
[] = {'T','Y','P','E','L','I','B',0};
3021 component
= MSI_RecordGetString(row
,3);
3022 comp
= get_loaded_component(package
,component
);
3024 return ERROR_SUCCESS
;
3026 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3028 TRACE("Skipping typelib reg due to disabled component\n");
3030 comp
->Action
= comp
->Installed
;
3032 return ERROR_SUCCESS
;
3035 comp
->Action
= INSTALLSTATE_LOCAL
;
3037 file
= get_loaded_file( package
, comp
->KeyPath
);
3039 return ERROR_SUCCESS
;
3041 module
= LoadLibraryExW( file
->TargetPath
, NULL
, LOAD_LIBRARY_AS_DATAFILE
);
3045 guid
= MSI_RecordGetString(row
,1);
3046 CLSIDFromString((LPWSTR
)guid
, &tl_struct
.clsid
);
3047 tl_struct
.source
= strdupW( file
->TargetPath
);
3048 tl_struct
.path
= NULL
;
3050 EnumResourceNamesW(module
, szTYPELIB
, Typelib_EnumResNameProc
,
3051 (LONG_PTR
)&tl_struct
);
3059 helpid
= MSI_RecordGetString(row
,6);
3062 help
= resolve_folder(package
,helpid
,FALSE
,FALSE
,TRUE
,NULL
);
3063 res
= RegisterTypeLib(tl_struct
.ptLib
,tl_struct
.path
,help
);
3066 if (!SUCCEEDED(res
))
3067 ERR("Failed to register type library %s\n",
3068 debugstr_w(tl_struct
.path
));
3071 ui_actiondata(package
,szRegisterTypeLibraries
,row
);
3073 TRACE("Registered %s\n", debugstr_w(tl_struct
.path
));
3076 ITypeLib_Release(tl_struct
.ptLib
);
3077 msi_free(tl_struct
.path
);
3080 ERR("Failed to load type library %s\n",
3081 debugstr_w(tl_struct
.source
));
3083 FreeLibrary(module
);
3084 msi_free(tl_struct
.source
);
3087 ERR("Could not load file! %s\n", debugstr_w(file
->TargetPath
));
3089 return ERROR_SUCCESS
;
3092 static UINT
ACTION_RegisterTypeLibraries(MSIPACKAGE
*package
)
3095 * OK this is a bit confusing.. I am given a _Component key and I believe
3096 * that the file that is being registered as a type library is the "key file
3097 * of that component" which I interpret to mean "The file in the KeyPath of
3102 static const WCHAR Query
[] =
3103 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3104 '`','T','y','p','e','L','i','b','`',0};
3106 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3107 if (rc
!= ERROR_SUCCESS
)
3108 return ERROR_SUCCESS
;
3110 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_RegisterTypeLibraries
, package
);
3111 msiobj_release(&view
->hdr
);
3115 static UINT
ITERATE_CreateShortcuts(MSIRECORD
*row
, LPVOID param
)
3117 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3118 LPWSTR target_file
, target_folder
, filename
;
3119 LPCWSTR buffer
, extension
;
3121 static const WCHAR szlnk
[]={'.','l','n','k',0};
3122 IShellLinkW
*sl
= NULL
;
3123 IPersistFile
*pf
= NULL
;
3126 buffer
= MSI_RecordGetString(row
,4);
3127 comp
= get_loaded_component(package
,buffer
);
3129 return ERROR_SUCCESS
;
3131 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3133 TRACE("Skipping shortcut creation due to disabled component\n");
3135 comp
->Action
= comp
->Installed
;
3137 return ERROR_SUCCESS
;
3140 comp
->Action
= INSTALLSTATE_LOCAL
;
3142 ui_actiondata(package
,szCreateShortcuts
,row
);
3144 res
= CoCreateInstance( &CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
,
3145 &IID_IShellLinkW
, (LPVOID
*) &sl
);
3149 ERR("CLSID_ShellLink not available\n");
3153 res
= IShellLinkW_QueryInterface( sl
, &IID_IPersistFile
,(LPVOID
*) &pf
);
3156 ERR("QueryInterface(IID_IPersistFile) failed\n");
3160 buffer
= MSI_RecordGetString(row
,2);
3161 target_folder
= resolve_folder(package
, buffer
,FALSE
,FALSE
,TRUE
,NULL
);
3163 /* may be needed because of a bug somehwere else */
3164 create_full_pathW(target_folder
);
3166 filename
= msi_dup_record_field( row
, 3 );
3167 reduce_to_longfilename(filename
);
3169 extension
= strchrW(filename
,'.');
3170 if (!extension
|| strcmpiW(extension
,szlnk
))
3172 int len
= strlenW(filename
);
3173 filename
= msi_realloc(filename
, len
* sizeof(WCHAR
) + sizeof(szlnk
));
3174 memcpy(filename
+ len
, szlnk
, sizeof(szlnk
));
3176 target_file
= build_directory_name(2, target_folder
, filename
);
3177 msi_free(target_folder
);
3180 buffer
= MSI_RecordGetString(row
,5);
3181 if (strchrW(buffer
,'['))
3184 deformat_string(package
,buffer
,&deformated
);
3185 IShellLinkW_SetPath(sl
,deformated
);
3186 msi_free(deformated
);
3190 FIXME("poorly handled shortcut format, advertised shortcut\n");
3191 IShellLinkW_SetPath(sl
,comp
->FullKeypath
);
3194 if (!MSI_RecordIsNull(row
,6))
3197 buffer
= MSI_RecordGetString(row
,6);
3198 deformat_string(package
,buffer
,&deformated
);
3199 IShellLinkW_SetArguments(sl
,deformated
);
3200 msi_free(deformated
);
3203 if (!MSI_RecordIsNull(row
,7))
3205 buffer
= MSI_RecordGetString(row
,7);
3206 IShellLinkW_SetDescription(sl
,buffer
);
3209 if (!MSI_RecordIsNull(row
,8))
3210 IShellLinkW_SetHotkey(sl
,MSI_RecordGetInteger(row
,8));
3212 if (!MSI_RecordIsNull(row
,9))
3217 buffer
= MSI_RecordGetString(row
,9);
3219 Path
= build_icon_path(package
,buffer
);
3220 index
= MSI_RecordGetInteger(row
,10);
3222 /* no value means 0 */
3223 if (index
== MSI_NULL_INTEGER
)
3226 IShellLinkW_SetIconLocation(sl
,Path
,index
);
3230 if (!MSI_RecordIsNull(row
,11))
3231 IShellLinkW_SetShowCmd(sl
,MSI_RecordGetInteger(row
,11));
3233 if (!MSI_RecordIsNull(row
,12))
3236 buffer
= MSI_RecordGetString(row
,12);
3237 Path
= resolve_folder(package
, buffer
, FALSE
, FALSE
, TRUE
, NULL
);
3239 IShellLinkW_SetWorkingDirectory(sl
,Path
);
3243 TRACE("Writing shortcut to %s\n",debugstr_w(target_file
));
3244 IPersistFile_Save(pf
,target_file
,FALSE
);
3246 msi_free(target_file
);
3250 IPersistFile_Release( pf
);
3252 IShellLinkW_Release( sl
);
3254 return ERROR_SUCCESS
;
3257 static UINT
ACTION_CreateShortcuts(MSIPACKAGE
*package
)
3262 static const WCHAR Query
[] =
3263 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3264 '`','S','h','o','r','t','c','u','t','`',0};
3266 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3267 if (rc
!= ERROR_SUCCESS
)
3268 return ERROR_SUCCESS
;
3270 res
= CoInitialize( NULL
);
3273 ERR("CoInitialize failed\n");
3274 return ERROR_FUNCTION_FAILED
;
3277 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateShortcuts
, package
);
3278 msiobj_release(&view
->hdr
);
3285 static UINT
ITERATE_PublishProduct(MSIRECORD
*row
, LPVOID param
)
3287 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
3296 FileName
= MSI_RecordGetString(row
,1);
3299 ERR("Unable to get FileName\n");
3300 return ERROR_SUCCESS
;
3303 FilePath
= build_icon_path(package
,FileName
);
3305 TRACE("Creating icon file at %s\n",debugstr_w(FilePath
));
3307 the_file
= CreateFileW(FilePath
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
,
3308 FILE_ATTRIBUTE_NORMAL
, NULL
);
3310 if (the_file
== INVALID_HANDLE_VALUE
)
3312 ERR("Unable to create file %s\n",debugstr_w(FilePath
));
3314 return ERROR_SUCCESS
;
3321 rc
= MSI_RecordReadStream(row
,2,buffer
,&sz
);
3322 if (rc
!= ERROR_SUCCESS
)
3324 ERR("Failed to get stream\n");
3325 CloseHandle(the_file
);
3326 DeleteFileW(FilePath
);
3329 WriteFile(the_file
,buffer
,sz
,&write
,NULL
);
3330 } while (sz
== 1024);
3334 CloseHandle(the_file
);
3336 uirow
= MSI_CreateRecord(1);
3337 MSI_RecordSetStringW(uirow
,1,FileName
);
3338 ui_actiondata(package
,szPublishProduct
,uirow
);
3339 msiobj_release( &uirow
->hdr
);
3341 return ERROR_SUCCESS
;
3344 static BOOL
msi_check_publish(MSIPACKAGE
*package
)
3346 MSIFEATURE
*feature
;
3348 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3350 if (feature
->ActionRequest
== INSTALLSTATE_LOCAL
)
3358 * 99% of the work done here is only done for
3359 * advertised installs. However this is where the
3360 * Icon table is processed and written out
3361 * so that is what I am going to do here.
3363 static UINT
ACTION_PublishProduct(MSIPACKAGE
*package
)
3367 MSISOURCELISTINFO
*info
;
3369 static const WCHAR Query
[]=
3370 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3371 '`','I','c','o','n','`',0};
3372 /* for registry stuff */
3375 HKEY hudkey
=0, props
=0;
3377 static const WCHAR szProductLanguage
[] =
3378 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3379 static const WCHAR szARPProductIcon
[] =
3380 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3381 static const WCHAR szProductVersion
[] =
3382 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3383 static const WCHAR szSourceList
[] =
3384 {'S','o','u','r','c','e','L','i','s','t',0};
3385 static const WCHAR szEmpty
[] = {0};
3389 MSIHANDLE hDb
, hSumInfo
;
3391 /* FIXME: also need to publish if the product is in advertise mode */
3392 if (!msi_check_publish(package
))
3393 return ERROR_SUCCESS
;
3395 /* write out icon files */
3397 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3398 if (rc
== ERROR_SUCCESS
)
3400 MSI_IterateRecords(view
, NULL
, ITERATE_PublishProduct
, package
);
3401 msiobj_release(&view
->hdr
);
3404 /* ok there is a lot more done here but i need to figure out what */
3406 rc
= MSIREG_OpenProductsKey(package
->ProductCode
,&hkey
,TRUE
);
3407 if (rc
!= ERROR_SUCCESS
)
3410 rc
= MSIREG_OpenUserProductsKey(package
->ProductCode
,&hukey
,TRUE
);
3411 if (rc
!= ERROR_SUCCESS
)
3414 rc
= RegCreateKeyW(hukey
, szSourceList
, &source
);
3415 if (rc
!= ERROR_SUCCESS
)
3418 RegCloseKey(source
);
3420 rc
= MSIREG_OpenUserDataProductKey(package
->ProductCode
,&hudkey
,TRUE
);
3421 if (rc
!= ERROR_SUCCESS
)
3424 rc
= MSIREG_OpenInstallPropertiesKey(package
->ProductCode
,&props
,TRUE
);
3425 if (rc
!= ERROR_SUCCESS
)
3428 buffer
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTNAMEW
);
3429 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PRODUCTNAMEW
, buffer
);
3432 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
3433 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
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( hkey
, 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 buffer
= msi_dup_property( package
, cszSourceDir
);
3473 rc
= MsiSourceListSetInfoW( package
->ProductCode
, NULL
,
3474 MSIINSTALLCONTEXT_USERUNMANAGED
,
3475 MSICODE_PRODUCT
| MSISOURCETYPE_NETWORK
,
3476 INSTALLPROPERTY_LASTUSEDSOURCEW
, buffer
);
3478 if (rc
!= ERROR_SUCCESS
)
3481 /* FIXME: Need to write more keys to the user registry */
3483 hDb
= alloc_msihandle( &package
->db
->hdr
);
3485 rc
= ERROR_NOT_ENOUGH_MEMORY
;
3488 rc
= MsiGetSummaryInformationW(hDb
, NULL
, 0, &hSumInfo
);
3489 MsiCloseHandle(hDb
);
3490 if (rc
== ERROR_SUCCESS
)
3492 WCHAR guidbuffer
[0x200];
3494 rc
= MsiSummaryInfoGetPropertyW(hSumInfo
, 9, NULL
, NULL
, NULL
,
3496 if (rc
== ERROR_SUCCESS
)
3498 WCHAR squashed
[GUID_SIZE
];
3499 /* for now we only care about the first guid */
3500 LPWSTR ptr
= strchrW(guidbuffer
,';');
3502 squash_guid(guidbuffer
,squashed
);
3503 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PACKAGECODEW
, squashed
);
3507 ERR("Unable to query Revision_Number...\n");
3510 MsiCloseHandle(hSumInfo
);
3514 ERR("Unable to open Summary Information\n");
3518 /* publish the SourceList info */
3519 LIST_FOR_EACH_ENTRY(info
, &package
->sourcelist_info
, MSISOURCELISTINFO
, entry
)
3521 MsiSourceListSetInfoW(package
->ProductCode
, NULL
,
3522 info
->context
, info
->options
,
3523 info
->property
, info
->value
);
3526 LIST_FOR_EACH_ENTRY(disk
, &package
->sourcelist_media
, MSIMEDIADISK
, entry
)
3528 MsiSourceListAddMediaDiskW(package
->ProductCode
, NULL
,
3529 disk
->context
, disk
->options
,
3530 disk
->disk_id
, disk
->volume_label
, disk
->disk_prompt
);
3536 RegCloseKey(hudkey
);
3542 static UINT
ITERATE_WriteIniValues(MSIRECORD
*row
, LPVOID param
)
3544 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3545 LPCWSTR component
,section
,key
,value
,identifier
,filename
,dirproperty
;
3546 LPWSTR deformated_section
, deformated_key
, deformated_value
;
3547 LPWSTR folder
, fullname
= NULL
;
3551 static const WCHAR szWindowsFolder
[] =
3552 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3554 component
= MSI_RecordGetString(row
, 8);
3555 comp
= get_loaded_component(package
,component
);
3557 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3559 TRACE("Skipping ini file due to disabled component %s\n",
3560 debugstr_w(component
));
3562 comp
->Action
= comp
->Installed
;
3564 return ERROR_SUCCESS
;
3567 comp
->Action
= INSTALLSTATE_LOCAL
;
3569 identifier
= MSI_RecordGetString(row
,1);
3570 filename
= MSI_RecordGetString(row
,2);
3571 dirproperty
= MSI_RecordGetString(row
,3);
3572 section
= MSI_RecordGetString(row
,4);
3573 key
= MSI_RecordGetString(row
,5);
3574 value
= MSI_RecordGetString(row
,6);
3575 action
= MSI_RecordGetInteger(row
,7);
3577 deformat_string(package
,section
,&deformated_section
);
3578 deformat_string(package
,key
,&deformated_key
);
3579 deformat_string(package
,value
,&deformated_value
);
3583 folder
= resolve_folder(package
, dirproperty
, FALSE
, FALSE
, TRUE
, NULL
);
3585 folder
= msi_dup_property( package
, dirproperty
);
3588 folder
= msi_dup_property( package
, szWindowsFolder
);
3592 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty
));
3596 fullname
= build_directory_name(2, folder
, filename
);
3600 TRACE("Adding value %s to section %s in %s\n",
3601 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3602 debugstr_w(fullname
));
3603 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3604 deformated_value
, fullname
);
3606 else if (action
== 1)
3609 GetPrivateProfileStringW(deformated_section
, deformated_key
, NULL
,
3610 returned
, 10, fullname
);
3611 if (returned
[0] == 0)
3613 TRACE("Adding value %s to section %s in %s\n",
3614 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3615 debugstr_w(fullname
));
3617 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3618 deformated_value
, fullname
);
3621 else if (action
== 3)
3622 FIXME("Append to existing section not yet implemented\n");
3624 uirow
= MSI_CreateRecord(4);
3625 MSI_RecordSetStringW(uirow
,1,identifier
);
3626 MSI_RecordSetStringW(uirow
,2,deformated_section
);
3627 MSI_RecordSetStringW(uirow
,3,deformated_key
);
3628 MSI_RecordSetStringW(uirow
,4,deformated_value
);
3629 ui_actiondata(package
,szWriteIniValues
,uirow
);
3630 msiobj_release( &uirow
->hdr
);
3634 msi_free(deformated_key
);
3635 msi_free(deformated_value
);
3636 msi_free(deformated_section
);
3637 return ERROR_SUCCESS
;
3640 static UINT
ACTION_WriteIniValues(MSIPACKAGE
*package
)
3644 static const WCHAR ExecSeqQuery
[] =
3645 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3646 '`','I','n','i','F','i','l','e','`',0};
3648 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3649 if (rc
!= ERROR_SUCCESS
)
3651 TRACE("no IniFile table\n");
3652 return ERROR_SUCCESS
;
3655 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteIniValues
, package
);
3656 msiobj_release(&view
->hdr
);
3660 static UINT
ITERATE_SelfRegModules(MSIRECORD
*row
, LPVOID param
)
3662 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3667 static const WCHAR ExeStr
[] =
3668 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3669 static const WCHAR close
[] = {'\"',0};
3671 PROCESS_INFORMATION info
;
3676 memset(&si
,0,sizeof(STARTUPINFOW
));
3678 filename
= MSI_RecordGetString(row
,1);
3679 file
= get_loaded_file( package
, filename
);
3683 ERR("Unable to find file id %s\n",debugstr_w(filename
));
3684 return ERROR_SUCCESS
;
3687 len
= strlenW(ExeStr
) + strlenW( file
->TargetPath
) + 2;
3689 FullName
= msi_alloc(len
*sizeof(WCHAR
));
3690 strcpyW(FullName
,ExeStr
);
3691 strcatW( FullName
, file
->TargetPath
);
3692 strcatW(FullName
,close
);
3694 TRACE("Registering %s\n",debugstr_w(FullName
));
3695 brc
= CreateProcessW(NULL
, FullName
, NULL
, NULL
, FALSE
, 0, NULL
, c_colon
,
3699 msi_dialog_check_messages(info
.hProcess
);
3704 uirow
= MSI_CreateRecord( 2 );
3705 uipath
= strdupW( file
->TargetPath
);
3706 p
= strrchrW(uipath
,'\\');
3709 MSI_RecordSetStringW( uirow
, 1, &p
[1] );
3710 MSI_RecordSetStringW( uirow
, 2, uipath
);
3711 ui_actiondata( package
, szSelfRegModules
, uirow
);
3712 msiobj_release( &uirow
->hdr
);
3714 /* FIXME: call ui_progress? */
3716 return ERROR_SUCCESS
;
3719 static UINT
ACTION_SelfRegModules(MSIPACKAGE
*package
)
3723 static const WCHAR ExecSeqQuery
[] =
3724 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3725 '`','S','e','l','f','R','e','g','`',0};
3727 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3728 if (rc
!= ERROR_SUCCESS
)
3730 TRACE("no SelfReg table\n");
3731 return ERROR_SUCCESS
;
3734 MSI_IterateRecords(view
, NULL
, ITERATE_SelfRegModules
, package
);
3735 msiobj_release(&view
->hdr
);
3737 return ERROR_SUCCESS
;
3740 static UINT
ACTION_PublishFeatures(MSIPACKAGE
*package
)
3742 MSIFEATURE
*feature
;
3748 if (!msi_check_publish(package
))
3749 return ERROR_SUCCESS
;
3751 rc
= MSIREG_OpenFeaturesKey(package
->ProductCode
,&hkey
,TRUE
);
3752 if (rc
!= ERROR_SUCCESS
)
3755 rc
= MSIREG_OpenUserFeaturesKey(package
->ProductCode
,&hukey
,TRUE
);
3756 if (rc
!= ERROR_SUCCESS
)
3759 rc
= MSIREG_OpenUserDataFeaturesKey(package
->ProductCode
, &userdata
, TRUE
);
3760 if (rc
!= ERROR_SUCCESS
)
3763 /* here the guids are base 85 encoded */
3764 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
3770 BOOL absent
= FALSE
;
3773 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
) &&
3774 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_SOURCE
) &&
3775 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ADVERTISED
))
3779 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3783 if (feature
->Feature_Parent
)
3784 size
+= strlenW( feature
->Feature_Parent
)+2;
3786 data
= msi_alloc(size
* sizeof(WCHAR
));
3789 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3791 MSICOMPONENT
* component
= cl
->component
;
3795 if (component
->ComponentId
)
3797 TRACE("From %s\n",debugstr_w(component
->ComponentId
));
3798 CLSIDFromString(component
->ComponentId
, &clsid
);
3799 encode_base85_guid(&clsid
,buf
);
3800 TRACE("to %s\n",debugstr_w(buf
));
3805 if (feature
->Feature_Parent
)
3807 static const WCHAR sep
[] = {'\2',0};
3809 strcatW(data
,feature
->Feature_Parent
);
3812 msi_reg_set_val_str( hkey
, feature
->Feature
, data
);
3813 msi_reg_set_val_str( userdata
, feature
->Feature
, data
);
3817 if (feature
->Feature_Parent
)
3818 size
= strlenW(feature
->Feature_Parent
)*sizeof(WCHAR
);
3821 RegSetValueExW(hukey
,feature
->Feature
,0,REG_SZ
,
3822 (LPBYTE
)feature
->Feature_Parent
,size
);
3826 size
+= 2*sizeof(WCHAR
);
3827 data
= msi_alloc(size
);
3830 if (feature
->Feature_Parent
)
3831 strcpyW( &data
[1], feature
->Feature_Parent
);
3832 RegSetValueExW(hukey
,feature
->Feature
,0,REG_SZ
,
3838 uirow
= MSI_CreateRecord( 1 );
3839 MSI_RecordSetStringW( uirow
, 1, feature
->Feature
);
3840 ui_actiondata( package
, szPublishFeatures
, uirow
);
3841 msiobj_release( &uirow
->hdr
);
3842 /* FIXME: call ui_progress? */
3851 static UINT
msi_unpublish_feature(MSIPACKAGE
*package
, MSIFEATURE
*feature
)
3856 TRACE("unpublishing feature %s\n", debugstr_w(feature
->Feature
));
3858 r
= MSIREG_OpenUserFeaturesKey(package
->ProductCode
, &hkey
, FALSE
);
3859 if (r
== ERROR_SUCCESS
)
3861 RegDeleteValueW(hkey
, feature
->Feature
);
3865 r
= MSIREG_OpenUserDataFeaturesKey(package
->ProductCode
, &hkey
, FALSE
);
3866 if (r
== ERROR_SUCCESS
)
3868 RegDeleteValueW(hkey
, feature
->Feature
);
3872 return ERROR_SUCCESS
;
3875 static BOOL
msi_check_unpublish(MSIPACKAGE
*package
)
3877 MSIFEATURE
*feature
;
3879 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3881 if (feature
->ActionRequest
!= INSTALLSTATE_ABSENT
)
3888 static UINT
ACTION_UnpublishFeatures(MSIPACKAGE
*package
)
3890 MSIFEATURE
*feature
;
3892 if (!msi_check_unpublish(package
))
3893 return ERROR_SUCCESS
;
3895 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3897 msi_unpublish_feature(package
, feature
);
3900 return ERROR_SUCCESS
;
3903 static UINT
msi_get_local_package_name( LPWSTR path
)
3905 static const WCHAR szInstaller
[] = {
3906 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3907 static const WCHAR fmt
[] = { '%','x','.','m','s','i',0};
3911 time
= GetTickCount();
3912 GetWindowsDirectoryW( path
, MAX_PATH
);
3913 lstrcatW( path
, szInstaller
);
3914 CreateDirectoryW( path
, NULL
);
3916 len
= lstrlenW(path
);
3917 for (i
=0; i
<0x10000; i
++)
3919 snprintfW( &path
[len
], MAX_PATH
- len
, fmt
, (time
+i
)&0xffff );
3920 handle
= CreateFileW( path
, GENERIC_WRITE
, 0, NULL
,
3921 CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, 0 );
3922 if (handle
!= INVALID_HANDLE_VALUE
)
3924 CloseHandle(handle
);
3927 if (GetLastError() != ERROR_FILE_EXISTS
&&
3928 GetLastError() != ERROR_SHARING_VIOLATION
)
3929 return ERROR_FUNCTION_FAILED
;
3932 return ERROR_SUCCESS
;
3935 static UINT
msi_make_package_local( MSIPACKAGE
*package
, HKEY hkey
)
3937 WCHAR packagefile
[MAX_PATH
];
3941 r
= msi_get_local_package_name( packagefile
);
3942 if (r
!= ERROR_SUCCESS
)
3945 TRACE("Copying to local package %s\n",debugstr_w(packagefile
));
3947 r
= CopyFileW( package
->db
->path
, packagefile
, FALSE
);
3951 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3952 debugstr_w(package
->db
->path
), debugstr_w(packagefile
), GetLastError());
3953 return ERROR_FUNCTION_FAILED
;
3956 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_LOCALPACKAGEW
, packagefile
);
3958 r
= MSIREG_OpenInstallPropertiesKey(package
->ProductCode
, &props
, TRUE
);
3959 if (r
!= ERROR_SUCCESS
)
3962 msi_reg_set_val_str(props
, INSTALLPROPERTY_LOCALPACKAGEW
, packagefile
);
3964 return ERROR_SUCCESS
;
3967 static UINT
msi_write_uninstall_property_vals( MSIPACKAGE
*package
, HKEY hkey
)
3969 LPWSTR prop
, val
, key
;
3970 static const LPCSTR propval
[] = {
3971 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3972 "ARPCONTACT", "Contact",
3973 "ARPCOMMENTS", "Comments",
3974 "ProductName", "DisplayName",
3975 "ProductVersion", "DisplayVersion",
3976 "ARPHELPLINK", "HelpLink",
3977 "ARPHELPTELEPHONE", "HelpTelephone",
3978 "ARPINSTALLLOCATION", "InstallLocation",
3979 "SourceDir", "InstallSource",
3980 "Manufacturer", "Publisher",
3981 "ARPREADME", "Readme",
3983 "ARPURLINFOABOUT", "URLInfoAbout",
3984 "ARPURLUPDATEINFO", "URLUpdateInfo",
3987 const LPCSTR
*p
= propval
;
3991 prop
= strdupAtoW( *p
++ );
3992 key
= strdupAtoW( *p
++ );
3993 val
= msi_dup_property( package
, prop
);
3994 msi_reg_set_val_str( hkey
, key
, val
);
3999 return ERROR_SUCCESS
;
4002 static UINT
ACTION_RegisterProduct(MSIPACKAGE
*package
)
4005 HKEY hudkey
=0, props
=0;
4006 LPWSTR buffer
= NULL
;
4009 static const WCHAR szWindowsInstaller
[] =
4010 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4011 static const WCHAR szUpgradeCode
[] =
4012 {'U','p','g','r','a','d','e','C','o','d','e',0};
4013 static const WCHAR modpath_fmt
[] =
4014 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4015 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4016 static const WCHAR szModifyPath
[] =
4017 {'M','o','d','i','f','y','P','a','t','h',0};
4018 static const WCHAR szUninstallString
[] =
4019 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4020 static const WCHAR szEstimatedSize
[] =
4021 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4022 static const WCHAR szProductLanguage
[] =
4023 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4024 static const WCHAR szProductVersion
[] =
4025 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',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 /* dump all the info i can grab */
4041 /* FIXME: Flesh out more information */
4043 msi_write_uninstall_property_vals( package
, hkey
);
4045 msi_reg_set_val_dword( hkey
, szWindowsInstaller
, 1 );
4047 msi_make_package_local( package
, hkey
);
4049 /* do ModifyPath and UninstallString */
4050 size
= deformat_string(package
,modpath_fmt
,&buffer
);
4051 RegSetValueExW(hkey
,szModifyPath
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
4052 RegSetValueExW(hkey
,szUninstallString
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
4055 /* FIXME: Write real Estimated Size when we have it */
4056 msi_reg_set_val_dword( hkey
, szEstimatedSize
, 0 );
4058 GetLocalTime(&systime
);
4059 sprintfW(szDate
,date_fmt
,systime
.wYear
,systime
.wMonth
,systime
.wDay
);
4060 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_INSTALLDATEW
, szDate
);
4062 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
4063 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
4065 buffer
= msi_dup_property( package
, szProductVersion
);
4068 DWORD verdword
= msi_version_str_to_dword(buffer
);
4070 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
4071 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMAJORW
, verdword
>>24 );
4072 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMINORW
, (verdword
>>16)&0x00FF );
4076 /* Handle Upgrade Codes */
4077 upgrade_code
= msi_dup_property( package
, szUpgradeCode
);
4082 MSIREG_OpenUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
4083 squash_guid(package
->ProductCode
,squashed
);
4084 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
4086 MSIREG_OpenUserUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
4087 squash_guid(package
->ProductCode
,squashed
);
4088 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
4091 msi_free(upgrade_code
);
4096 rc
= MSIREG_OpenUserDataProductKey(package
->ProductCode
, &hudkey
, TRUE
);
4097 if (rc
!= ERROR_SUCCESS
)
4100 RegCloseKey(hudkey
);
4102 rc
= MSIREG_OpenInstallPropertiesKey(package
->ProductCode
, &props
, TRUE
);
4103 if (rc
!= ERROR_SUCCESS
)
4106 msi_reg_set_val_dword( props
, szWindowsInstaller
, 1 );
4109 return ERROR_SUCCESS
;
4112 static UINT
ACTION_InstallExecute(MSIPACKAGE
*package
)
4114 return execute_script(package
,INSTALL_SCRIPT
);
4117 static UINT
msi_unpublish_product(MSIPACKAGE
*package
)
4119 LPWSTR remove
= NULL
;
4120 LPWSTR
*features
= NULL
;
4121 BOOL full_uninstall
= TRUE
;
4122 MSIFEATURE
*feature
;
4124 static const WCHAR szRemove
[] = {'R','E','M','O','V','E',0};
4125 static const WCHAR szAll
[] = {'A','L','L',0};
4127 remove
= msi_dup_property(package
, szRemove
);
4129 return ERROR_SUCCESS
;
4131 features
= msi_split_string(remove
, ',');
4135 ERR("REMOVE feature list is empty!\n");
4136 return ERROR_FUNCTION_FAILED
;
4139 if (!lstrcmpW(features
[0], szAll
))
4140 full_uninstall
= TRUE
;
4143 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
4145 if (feature
->Action
!= INSTALLSTATE_ABSENT
)
4146 full_uninstall
= FALSE
;
4150 if (!full_uninstall
)
4153 MSIREG_DeleteProductKey(package
->ProductCode
);
4154 MSIREG_DeleteUserProductKey(package
->ProductCode
);
4155 MSIREG_DeleteUserDataProductKey(package
->ProductCode
);
4156 MSIREG_DeleteUserFeaturesKey(package
->ProductCode
);
4157 MSIREG_DeleteUninstallKey(package
->ProductCode
);
4162 return ERROR_SUCCESS
;
4165 static UINT
ACTION_InstallFinalize(MSIPACKAGE
*package
)
4169 rc
= msi_unpublish_product(package
);
4170 if (rc
!= ERROR_SUCCESS
)
4173 /* turn off scheduling */
4174 package
->script
->CurrentlyScripting
= FALSE
;
4176 /* first do the same as an InstallExecute */
4177 rc
= ACTION_InstallExecute(package
);
4178 if (rc
!= ERROR_SUCCESS
)
4181 /* then handle Commit Actions */
4182 rc
= execute_script(package
,COMMIT_SCRIPT
);
4187 UINT
ACTION_ForceReboot(MSIPACKAGE
*package
)
4189 static const WCHAR RunOnce
[] = {
4190 'S','o','f','t','w','a','r','e','\\',
4191 'M','i','c','r','o','s','o','f','t','\\',
4192 'W','i','n','d','o','w','s','\\',
4193 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4194 'R','u','n','O','n','c','e',0};
4195 static const WCHAR InstallRunOnce
[] = {
4196 'S','o','f','t','w','a','r','e','\\',
4197 'M','i','c','r','o','s','o','f','t','\\',
4198 'W','i','n','d','o','w','s','\\',
4199 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4200 'I','n','s','t','a','l','l','e','r','\\',
4201 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4203 static const WCHAR msiexec_fmt
[] = {
4205 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4206 '\"','%','s','\"',0};
4207 static const WCHAR install_fmt
[] = {
4208 '/','I',' ','\"','%','s','\"',' ',
4209 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4210 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4211 WCHAR buffer
[256], sysdir
[MAX_PATH
];
4213 WCHAR squished_pc
[100];
4215 squash_guid(package
->ProductCode
,squished_pc
);
4217 GetSystemDirectoryW(sysdir
, sizeof(sysdir
)/sizeof(sysdir
[0]));
4218 RegCreateKeyW(HKEY_LOCAL_MACHINE
,RunOnce
,&hkey
);
4219 snprintfW(buffer
,sizeof(buffer
)/sizeof(buffer
[0]),msiexec_fmt
,sysdir
,
4222 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
4225 TRACE("Reboot command %s\n",debugstr_w(buffer
));
4227 RegCreateKeyW(HKEY_LOCAL_MACHINE
,InstallRunOnce
,&hkey
);
4228 sprintfW(buffer
,install_fmt
,package
->ProductCode
,squished_pc
);
4230 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
4233 return ERROR_INSTALL_SUSPEND
;
4236 static UINT
ACTION_ResolveSource(MSIPACKAGE
* package
)
4242 * We are currently doing what should be done here in the top level Install
4243 * however for Administrative and uninstalls this step will be needed
4245 if (!package
->PackagePath
)
4246 return ERROR_SUCCESS
;
4248 msi_set_sourcedir_props(package
, TRUE
);
4250 attrib
= GetFileAttributesW(package
->db
->path
);
4251 if (attrib
== INVALID_FILE_ATTRIBUTES
)
4257 rc
= MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
4258 MSIINSTALLCONTEXT_USERUNMANAGED
, MSICODE_PRODUCT
,
4259 INSTALLPROPERTY_DISKPROMPTW
,NULL
,&size
);
4260 if (rc
== ERROR_MORE_DATA
)
4262 prompt
= msi_alloc(size
* sizeof(WCHAR
));
4263 MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
4264 MSIINSTALLCONTEXT_USERUNMANAGED
, MSICODE_PRODUCT
,
4265 INSTALLPROPERTY_DISKPROMPTW
,prompt
,&size
);
4268 prompt
= strdupW(package
->db
->path
);
4270 msg
= generate_error_string(package
,1302,1,prompt
);
4271 while(attrib
== INVALID_FILE_ATTRIBUTES
)
4273 rc
= MessageBoxW(NULL
,msg
,NULL
,MB_OKCANCEL
);
4276 rc
= ERROR_INSTALL_USEREXIT
;
4279 attrib
= GetFileAttributesW(package
->db
->path
);
4285 return ERROR_SUCCESS
;
4290 static UINT
ACTION_RegisterUser(MSIPACKAGE
*package
)
4297 static const WCHAR szPropKeys
[][80] =
4299 {'P','r','o','d','u','c','t','I','D',0},
4300 {'U','S','E','R','N','A','M','E',0},
4301 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4305 static const WCHAR szRegKeys
[][80] =
4307 {'P','r','o','d','u','c','t','I','D',0},
4308 {'R','e','g','O','w','n','e','r',0},
4309 {'R','e','g','C','o','m','p','a','n','y',0},
4313 productid
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTIDW
);
4315 return ERROR_SUCCESS
;
4317 rc
= MSIREG_OpenUninstallKey(package
->ProductCode
,&hkey
,TRUE
);
4318 if (rc
!= ERROR_SUCCESS
)
4321 for( i
= 0; szPropKeys
[i
][0]; i
++ )
4323 buffer
= msi_dup_property( package
, szPropKeys
[i
] );
4324 msi_reg_set_val_str( hkey
, szRegKeys
[i
], buffer
);
4329 msi_free(productid
);
4332 /* FIXME: call ui_actiondata */
4334 return ERROR_SUCCESS
;
4338 static UINT
ACTION_ExecuteAction(MSIPACKAGE
*package
)
4342 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
4343 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
4348 static UINT
ITERATE_PublishComponent(MSIRECORD
*rec
, LPVOID param
)
4350 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4351 LPCWSTR compgroupid
=NULL
;
4352 LPCWSTR feature
=NULL
;
4353 LPCWSTR text
= NULL
;
4354 LPCWSTR qualifier
= NULL
;
4355 LPCWSTR component
= NULL
;
4356 LPWSTR advertise
= NULL
;
4357 LPWSTR output
= NULL
;
4359 UINT rc
= ERROR_SUCCESS
;
4364 component
= MSI_RecordGetString(rec
,3);
4365 comp
= get_loaded_component(package
,component
);
4367 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
) &&
4368 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_SOURCE
) &&
4369 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ADVERTISED
))
4371 TRACE("Skipping: Component %s not scheduled for install\n",
4372 debugstr_w(component
));
4374 return ERROR_SUCCESS
;
4377 compgroupid
= MSI_RecordGetString(rec
,1);
4378 qualifier
= MSI_RecordGetString(rec
,2);
4380 rc
= MSIREG_OpenUserComponentsKey(compgroupid
, &hkey
, TRUE
);
4381 if (rc
!= ERROR_SUCCESS
)
4384 text
= MSI_RecordGetString(rec
,4);
4385 feature
= MSI_RecordGetString(rec
,5);
4387 advertise
= create_component_advertise_string(package
, comp
, feature
);
4389 sz
= strlenW(advertise
);
4392 sz
+= lstrlenW(text
);
4395 sz
*= sizeof(WCHAR
);
4397 output
= msi_alloc_zero(sz
);
4398 strcpyW(output
,advertise
);
4399 msi_free(advertise
);
4402 strcatW(output
,text
);
4404 msi_reg_set_val_multi_str( hkey
, qualifier
, output
);
4411 uirow
= MSI_CreateRecord( 2 );
4412 MSI_RecordSetStringW( uirow
, 1, compgroupid
);
4413 MSI_RecordSetStringW( uirow
, 2, qualifier
);
4414 ui_actiondata( package
, szPublishComponents
, uirow
);
4415 msiobj_release( &uirow
->hdr
);
4416 /* FIXME: call ui_progress? */
4422 * At present I am ignorning the advertised components part of this and only
4423 * focusing on the qualified component sets
4425 static UINT
ACTION_PublishComponents(MSIPACKAGE
*package
)
4429 static const WCHAR ExecSeqQuery
[] =
4430 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4431 '`','P','u','b','l','i','s','h',
4432 'C','o','m','p','o','n','e','n','t','`',0};
4434 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4435 if (rc
!= ERROR_SUCCESS
)
4436 return ERROR_SUCCESS
;
4438 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_PublishComponent
, package
);
4439 msiobj_release(&view
->hdr
);
4444 static UINT
ITERATE_InstallService(MSIRECORD
*rec
, LPVOID param
)
4446 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4449 SC_HANDLE hscm
, service
= NULL
;
4450 LPCWSTR name
, disp
, comp
, depends
, pass
;
4451 LPCWSTR load_order
, serv_name
, key
;
4452 DWORD serv_type
, start_type
;
4455 static const WCHAR query
[] =
4456 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4457 '`','C','o','m','p','o','n','e','n','t','`',' ',
4458 'W','H','E','R','E',' ',
4459 '`','C','o','m','p','o','n','e','n','t','`',' ',
4460 '=','\'','%','s','\'',0};
4462 hscm
= OpenSCManagerW(NULL
, SERVICES_ACTIVE_DATABASEW
, GENERIC_WRITE
);
4465 ERR("Failed to open the SC Manager!\n");
4469 start_type
= MSI_RecordGetInteger(rec
, 5);
4470 if (start_type
== SERVICE_BOOT_START
|| start_type
== SERVICE_SYSTEM_START
)
4473 depends
= MSI_RecordGetString(rec
, 8);
4474 if (depends
&& *depends
)
4475 FIXME("Dependency list unhandled!\n");
4477 name
= MSI_RecordGetString(rec
, 2);
4478 disp
= MSI_RecordGetString(rec
, 3);
4479 serv_type
= MSI_RecordGetInteger(rec
, 4);
4480 err_control
= MSI_RecordGetInteger(rec
, 6);
4481 load_order
= MSI_RecordGetString(rec
, 7);
4482 serv_name
= MSI_RecordGetString(rec
, 9);
4483 pass
= MSI_RecordGetString(rec
, 10);
4484 comp
= MSI_RecordGetString(rec
, 12);
4486 /* fetch the service path */
4487 row
= MSI_QueryGetRecord(package
->db
, query
, comp
);
4490 ERR("Control query failed!\n");
4494 key
= MSI_RecordGetString(row
, 6);
4496 file
= get_loaded_file(package
, key
);
4497 msiobj_release(&row
->hdr
);
4500 ERR("Failed to load the service file\n");
4504 service
= CreateServiceW(hscm
, name
, disp
, GENERIC_ALL
, serv_type
,
4505 start_type
, err_control
, file
->TargetPath
,
4506 load_order
, NULL
, NULL
, serv_name
, pass
);
4509 if (GetLastError() != ERROR_SERVICE_EXISTS
)
4510 ERR("Failed to create service %s: %d\n", debugstr_w(name
), GetLastError());
4514 CloseServiceHandle(service
);
4515 CloseServiceHandle(hscm
);
4517 return ERROR_SUCCESS
;
4520 static UINT
ACTION_InstallServices( MSIPACKAGE
*package
)
4524 static const WCHAR ExecSeqQuery
[] =
4525 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4526 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4528 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4529 if (rc
!= ERROR_SUCCESS
)
4530 return ERROR_SUCCESS
;
4532 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallService
, package
);
4533 msiobj_release(&view
->hdr
);
4538 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4539 static LPCWSTR
*msi_service_args_to_vector(LPWSTR args
, DWORD
*numargs
)
4541 LPCWSTR
*vector
, *temp_vector
;
4545 static const WCHAR separator
[] = {'[','~',']',0};
4548 sep_len
= sizeof(separator
) / sizeof(WCHAR
) - 1;
4553 vector
= msi_alloc(sizeof(LPWSTR
));
4561 vector
[*numargs
- 1] = p
;
4563 if ((q
= strstrW(p
, separator
)))
4567 temp_vector
= msi_realloc(vector
, (*numargs
+ 1) * sizeof(LPWSTR
));
4573 vector
= temp_vector
;
4582 static UINT
ITERATE_StartService(MSIRECORD
*rec
, LPVOID param
)
4584 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4586 SC_HANDLE scm
, service
= NULL
;
4587 LPCWSTR name
, *vector
= NULL
;
4589 DWORD event
, numargs
;
4590 UINT r
= ERROR_FUNCTION_FAILED
;
4592 comp
= get_loaded_component(package
, MSI_RecordGetString(rec
, 6));
4593 if (!comp
|| comp
->Action
== INSTALLSTATE_UNKNOWN
|| comp
->Action
== INSTALLSTATE_ABSENT
)
4594 return ERROR_SUCCESS
;
4596 name
= MSI_RecordGetString(rec
, 2);
4597 event
= MSI_RecordGetInteger(rec
, 3);
4598 args
= strdupW(MSI_RecordGetString(rec
, 4));
4600 if (!(event
& msidbServiceControlEventStart
))
4601 return ERROR_SUCCESS
;
4603 scm
= OpenSCManagerW(NULL
, NULL
, SC_MANAGER_CONNECT
);
4606 ERR("Failed to open the service control manager\n");
4610 service
= OpenServiceW(scm
, name
, SERVICE_START
);
4613 ERR("Failed to open service %s\n", debugstr_w(name
));
4617 vector
= msi_service_args_to_vector(args
, &numargs
);
4619 if (!StartServiceW(service
, numargs
, vector
))
4621 ERR("Failed to start service %s\n", debugstr_w(name
));
4628 CloseServiceHandle(service
);
4629 CloseServiceHandle(scm
);
4636 static UINT
ACTION_StartServices( MSIPACKAGE
*package
)
4641 static const WCHAR query
[] = {
4642 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4643 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4645 rc
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
4646 if (rc
!= ERROR_SUCCESS
)
4647 return ERROR_SUCCESS
;
4649 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_StartService
, package
);
4650 msiobj_release(&view
->hdr
);
4655 static MSIFILE
*msi_find_file( MSIPACKAGE
*package
, LPCWSTR filename
)
4659 LIST_FOR_EACH_ENTRY(file
, &package
->files
, MSIFILE
, entry
)
4661 if (!lstrcmpW(file
->File
, filename
))
4668 static UINT
ITERATE_InstallODBCDriver( MSIRECORD
*rec
, LPVOID param
)
4670 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4671 LPWSTR driver
, driver_path
, ptr
;
4672 WCHAR outpath
[MAX_PATH
];
4673 MSIFILE
*driver_file
, *setup_file
;
4676 UINT r
= ERROR_SUCCESS
;
4678 static const WCHAR driver_fmt
[] = {
4679 'D','r','i','v','e','r','=','%','s',0};
4680 static const WCHAR setup_fmt
[] = {
4681 'S','e','t','u','p','=','%','s',0};
4682 static const WCHAR usage_fmt
[] = {
4683 'F','i','l','e','U','s','a','g','e','=','1',0};
4685 desc
= MSI_RecordGetString(rec
, 3);
4687 driver_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 4));
4688 setup_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 5));
4690 if (!driver_file
|| !setup_file
)
4692 ERR("ODBC Driver entry not found!\n");
4693 return ERROR_FUNCTION_FAILED
;
4696 len
= lstrlenW(desc
) + lstrlenW(driver_fmt
) + lstrlenW(driver_file
->FileName
) +
4697 lstrlenW(setup_fmt
) + lstrlenW(setup_file
->FileName
) +
4698 lstrlenW(usage_fmt
) + 1;
4699 driver
= msi_alloc(len
* sizeof(WCHAR
));
4701 return ERROR_OUTOFMEMORY
;
4704 lstrcpyW(ptr
, desc
);
4705 ptr
+= lstrlenW(ptr
) + 1;
4707 sprintfW(ptr
, driver_fmt
, driver_file
->FileName
);
4708 ptr
+= lstrlenW(ptr
) + 1;
4710 sprintfW(ptr
, setup_fmt
, setup_file
->FileName
);
4711 ptr
+= lstrlenW(ptr
) + 1;
4713 lstrcpyW(ptr
, usage_fmt
);
4714 ptr
+= lstrlenW(ptr
) + 1;
4717 driver_path
= strdupW(driver_file
->TargetPath
);
4718 ptr
= strrchrW(driver_path
, '\\');
4719 if (ptr
) *ptr
= '\0';
4721 if (!SQLInstallDriverExW(driver
, driver_path
, outpath
, MAX_PATH
,
4722 NULL
, ODBC_INSTALL_COMPLETE
, &usage
))
4724 ERR("Failed to install SQL driver!\n");
4725 r
= ERROR_FUNCTION_FAILED
;
4729 msi_free(driver_path
);
4734 static UINT
ITERATE_InstallODBCTranslator( MSIRECORD
*rec
, LPVOID param
)
4736 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4737 LPWSTR translator
, translator_path
, ptr
;
4738 WCHAR outpath
[MAX_PATH
];
4739 MSIFILE
*translator_file
, *setup_file
;
4742 UINT r
= ERROR_SUCCESS
;
4744 static const WCHAR translator_fmt
[] = {
4745 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4746 static const WCHAR setup_fmt
[] = {
4747 'S','e','t','u','p','=','%','s',0};
4749 desc
= MSI_RecordGetString(rec
, 3);
4751 translator_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 4));
4752 setup_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 5));
4754 if (!translator_file
|| !setup_file
)
4756 ERR("ODBC Translator entry not found!\n");
4757 return ERROR_FUNCTION_FAILED
;
4760 len
= lstrlenW(desc
) + lstrlenW(translator_fmt
) + lstrlenW(translator_file
->FileName
) +
4761 lstrlenW(setup_fmt
) + lstrlenW(setup_file
->FileName
) + 1;
4762 translator
= msi_alloc(len
* sizeof(WCHAR
));
4764 return ERROR_OUTOFMEMORY
;
4767 lstrcpyW(ptr
, desc
);
4768 ptr
+= lstrlenW(ptr
) + 1;
4770 sprintfW(ptr
, translator_fmt
, translator_file
->FileName
);
4771 ptr
+= lstrlenW(ptr
) + 1;
4773 sprintfW(ptr
, setup_fmt
, setup_file
->FileName
);
4774 ptr
+= lstrlenW(ptr
) + 1;
4777 translator_path
= strdupW(translator_file
->TargetPath
);
4778 ptr
= strrchrW(translator_path
, '\\');
4779 if (ptr
) *ptr
= '\0';
4781 if (!SQLInstallTranslatorExW(translator
, translator_path
, outpath
, MAX_PATH
,
4782 NULL
, ODBC_INSTALL_COMPLETE
, &usage
))
4784 ERR("Failed to install SQL translator!\n");
4785 r
= ERROR_FUNCTION_FAILED
;
4788 msi_free(translator
);
4789 msi_free(translator_path
);
4794 static UINT
ITERATE_InstallODBCDataSource( MSIRECORD
*rec
, LPVOID param
)
4797 LPCWSTR desc
, driver
;
4798 WORD request
= ODBC_ADD_SYS_DSN
;
4801 UINT r
= ERROR_SUCCESS
;
4803 static const WCHAR attrs_fmt
[] = {
4804 'D','S','N','=','%','s',0 };
4806 desc
= MSI_RecordGetString(rec
, 3);
4807 driver
= MSI_RecordGetString(rec
, 4);
4808 registration
= MSI_RecordGetInteger(rec
, 5);
4810 if (registration
== msidbODBCDataSourceRegistrationPerMachine
) request
= ODBC_ADD_SYS_DSN
;
4811 else if (registration
== msidbODBCDataSourceRegistrationPerUser
) request
= ODBC_ADD_DSN
;
4813 len
= lstrlenW(attrs_fmt
) + lstrlenW(desc
) + 1 + 1;
4814 attrs
= msi_alloc(len
* sizeof(WCHAR
));
4816 return ERROR_OUTOFMEMORY
;
4818 sprintfW(attrs
, attrs_fmt
, desc
);
4819 attrs
[len
- 1] = '\0';
4821 if (!SQLConfigDataSourceW(NULL
, request
, driver
, attrs
))
4823 ERR("Failed to install SQL data source!\n");
4824 r
= ERROR_FUNCTION_FAILED
;
4832 static UINT
ACTION_InstallODBC( MSIPACKAGE
*package
)
4837 static const WCHAR driver_query
[] = {
4838 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4839 'O','D','B','C','D','r','i','v','e','r',0 };
4841 static const WCHAR translator_query
[] = {
4842 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4843 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4845 static const WCHAR source_query
[] = {
4846 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4847 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4849 rc
= MSI_DatabaseOpenViewW(package
->db
, driver_query
, &view
);
4850 if (rc
!= ERROR_SUCCESS
)
4851 return ERROR_SUCCESS
;
4853 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCDriver
, package
);
4854 msiobj_release(&view
->hdr
);
4856 rc
= MSI_DatabaseOpenViewW(package
->db
, translator_query
, &view
);
4857 if (rc
!= ERROR_SUCCESS
)
4858 return ERROR_SUCCESS
;
4860 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCTranslator
, package
);
4861 msiobj_release(&view
->hdr
);
4863 rc
= MSI_DatabaseOpenViewW(package
->db
, source_query
, &view
);
4864 if (rc
!= ERROR_SUCCESS
)
4865 return ERROR_SUCCESS
;
4867 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCDataSource
, package
);
4868 msiobj_release(&view
->hdr
);
4873 #define ENV_ACT_SETALWAYS 0x1
4874 #define ENV_ACT_SETABSENT 0x2
4875 #define ENV_ACT_REMOVE 0x4
4876 #define ENV_ACT_REMOVEMATCH 0x8
4878 #define ENV_MOD_MACHINE 0x20000000
4879 #define ENV_MOD_APPEND 0x40000000
4880 #define ENV_MOD_PREFIX 0x80000000
4881 #define ENV_MOD_MASK 0xC0000000
4883 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4885 static LONG
env_set_flags( LPCWSTR
*name
, LPCWSTR
*value
, DWORD
*flags
)
4887 LPCWSTR cptr
= *name
;
4888 LPCWSTR ptr
= *value
;
4890 static const WCHAR prefix
[] = {'[','~',']',0};
4891 static const int prefix_len
= 3;
4897 *flags
|= ENV_ACT_SETALWAYS
;
4898 else if (*cptr
== '+')
4899 *flags
|= ENV_ACT_SETABSENT
;
4900 else if (*cptr
== '-')
4901 *flags
|= ENV_ACT_REMOVE
;
4902 else if (*cptr
== '!')
4903 *flags
|= ENV_ACT_REMOVEMATCH
;
4904 else if (*cptr
== '*')
4905 *flags
|= ENV_MOD_MACHINE
;
4915 ERR("Missing environment variable\n");
4916 return ERROR_FUNCTION_FAILED
;
4919 if (!strncmpW(ptr
, prefix
, prefix_len
))
4921 *flags
|= ENV_MOD_APPEND
;
4922 *value
+= lstrlenW(prefix
);
4924 else if (lstrlenW(*value
) >= prefix_len
)
4926 ptr
+= lstrlenW(ptr
) - prefix_len
;
4927 if (!lstrcmpW(ptr
, prefix
))
4929 *flags
|= ENV_MOD_PREFIX
;
4930 /* the "[~]" will be removed by deformat_string */;
4935 check_flag_combo(*flags
, ENV_ACT_SETALWAYS
| ENV_ACT_SETABSENT
) ||
4936 check_flag_combo(*flags
, ENV_ACT_REMOVEMATCH
| ENV_ACT_SETABSENT
) ||
4937 check_flag_combo(*flags
, ENV_ACT_REMOVEMATCH
| ENV_ACT_SETALWAYS
) ||
4938 check_flag_combo(*flags
, ENV_ACT_SETABSENT
| ENV_MOD_MASK
))
4940 ERR("Invalid flags: %08x\n", *flags
);
4941 return ERROR_FUNCTION_FAILED
;
4944 return ERROR_SUCCESS
;
4947 static UINT
ITERATE_WriteEnvironmentString( MSIRECORD
*rec
, LPVOID param
)
4949 MSIPACKAGE
*package
= param
;
4950 LPCWSTR name
, value
, comp
;
4951 LPWSTR data
= NULL
, newval
= NULL
;
4952 LPWSTR deformatted
= NULL
, ptr
;
4953 DWORD flags
, type
, size
;
4955 HKEY env
= NULL
, root
;
4956 LPCWSTR environment
;
4958 static const WCHAR user_env
[] =
4959 {'E','n','v','i','r','o','n','m','e','n','t',0};
4960 static const WCHAR machine_env
[] =
4961 {'S','y','s','t','e','m','\\',
4962 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4963 'C','o','n','t','r','o','l','\\',
4964 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4965 'E','n','v','i','r','o','n','m','e','n','t',0};
4966 static const WCHAR semicolon
[] = {';',0};
4968 name
= MSI_RecordGetString(rec
, 2);
4969 value
= MSI_RecordGetString(rec
, 3);
4970 comp
= MSI_RecordGetString(rec
, 4);
4972 res
= env_set_flags(&name
, &value
, &flags
);
4973 if (res
!= ERROR_SUCCESS
)
4976 deformat_string(package
, value
, &deformatted
);
4979 res
= ERROR_OUTOFMEMORY
;
4983 value
= deformatted
;
4985 if (flags
& ENV_MOD_MACHINE
)
4987 environment
= machine_env
;
4988 root
= HKEY_LOCAL_MACHINE
;
4992 environment
= user_env
;
4993 root
= HKEY_CURRENT_USER
;
4996 res
= RegCreateKeyExW(root
, environment
, 0, NULL
, 0,
4997 KEY_ALL_ACCESS
, NULL
, &env
, NULL
);
4998 if (res
!= ERROR_SUCCESS
)
5001 if (flags
& ENV_ACT_REMOVE
)
5002 FIXME("Not removing environment variable on uninstall!\n");
5005 res
= RegQueryValueExW(env
, name
, NULL
, &type
, NULL
, &size
);
5006 if ((res
!= ERROR_SUCCESS
&& res
!= ERROR_FILE_NOT_FOUND
) ||
5007 (res
== ERROR_SUCCESS
&& type
!= REG_SZ
&& type
!= REG_EXPAND_SZ
))
5010 if (res
!= ERROR_FILE_NOT_FOUND
)
5012 if (flags
& ENV_ACT_SETABSENT
)
5014 res
= ERROR_SUCCESS
;
5018 data
= msi_alloc(size
);
5022 return ERROR_OUTOFMEMORY
;
5025 res
= RegQueryValueExW(env
, name
, NULL
, &type
, (LPVOID
)data
, &size
);
5026 if (res
!= ERROR_SUCCESS
)
5029 if (flags
& ENV_ACT_REMOVEMATCH
&& (!value
|| !lstrcmpW(data
, value
)))
5031 res
= RegDeleteKeyW(env
, name
);
5035 size
= (lstrlenW(value
) + 1 + size
) * sizeof(WCHAR
);
5036 newval
= msi_alloc(size
);
5040 res
= ERROR_OUTOFMEMORY
;
5044 if (!(flags
& ENV_MOD_MASK
))
5045 lstrcpyW(newval
, value
);
5048 if (flags
& ENV_MOD_PREFIX
)
5050 lstrcpyW(newval
, value
);
5051 lstrcatW(newval
, semicolon
);
5052 ptr
= newval
+ lstrlenW(value
) + 1;
5055 lstrcpyW(ptr
, data
);
5057 if (flags
& ENV_MOD_APPEND
)
5059 lstrcatW(newval
, semicolon
);
5060 lstrcatW(newval
, value
);
5066 size
= (lstrlenW(value
) + 1) * sizeof(WCHAR
);
5067 newval
= msi_alloc(size
);
5070 res
= ERROR_OUTOFMEMORY
;
5074 lstrcpyW(newval
, value
);
5077 TRACE("setting %s to %s\n", debugstr_w(name
), debugstr_w(newval
));
5078 res
= RegSetValueExW(env
, name
, 0, type
, (LPVOID
)newval
, size
);
5081 if (env
) RegCloseKey(env
);
5082 msi_free(deformatted
);
5088 static UINT
ACTION_WriteEnvironmentStrings( MSIPACKAGE
*package
)
5092 static const WCHAR ExecSeqQuery
[] =
5093 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5094 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5095 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
5096 if (rc
!= ERROR_SUCCESS
)
5097 return ERROR_SUCCESS
;
5099 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteEnvironmentString
, package
);
5100 msiobj_release(&view
->hdr
);
5105 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5116 static BOOL
msi_move_file(LPCWSTR source
, LPCWSTR dest
, int options
)
5120 if (GetFileAttributesW(source
) == FILE_ATTRIBUTE_DIRECTORY
||
5121 GetFileAttributesW(dest
) == FILE_ATTRIBUTE_DIRECTORY
)
5123 WARN("Source or dest is directory, not moving\n");
5127 if (options
== msidbMoveFileOptionsMove
)
5129 TRACE("moving %s -> %s\n", debugstr_w(source
), debugstr_w(dest
));
5130 ret
= MoveFileExW(source
, dest
, MOVEFILE_REPLACE_EXISTING
);
5133 WARN("MoveFile failed: %d\n", GetLastError());
5139 TRACE("copying %s -> %s\n", debugstr_w(source
), debugstr_w(dest
));
5140 ret
= CopyFileW(source
, dest
, FALSE
);
5143 WARN("CopyFile failed: %d\n", GetLastError());
5151 static LPWSTR
wildcard_to_file(LPWSTR wildcard
, LPWSTR filename
)
5154 DWORD dirlen
, pathlen
;
5156 ptr
= strrchrW(wildcard
, '\\');
5157 dirlen
= ptr
- wildcard
+ 1;
5159 pathlen
= dirlen
+ lstrlenW(filename
) + 1;
5160 path
= msi_alloc(pathlen
* sizeof(WCHAR
));
5162 lstrcpynW(path
, wildcard
, dirlen
+ 1);
5163 lstrcatW(path
, filename
);
5168 static void free_file_entry(FILE_LIST
*file
)
5170 msi_free(file
->source
);
5171 msi_free(file
->dest
);
5175 static void free_list(FILE_LIST
*list
)
5177 while (!list_empty(&list
->entry
))
5179 FILE_LIST
*file
= LIST_ENTRY(list_head(&list
->entry
), FILE_LIST
, entry
);
5181 list_remove(&file
->entry
);
5182 free_file_entry(file
);
5186 static BOOL
add_wildcard(FILE_LIST
*files
, LPWSTR source
, LPWSTR dest
)
5188 FILE_LIST
*new, *file
;
5189 LPWSTR ptr
, filename
;
5192 new = msi_alloc_zero(sizeof(FILE_LIST
));
5196 new->source
= strdupW(source
);
5197 ptr
= strrchrW(dest
, '\\') + 1;
5198 filename
= strrchrW(new->source
, '\\') + 1;
5200 new->sourcename
= filename
;
5203 new->destname
= ptr
;
5205 new->destname
= new->sourcename
;
5207 size
= (ptr
- dest
) + lstrlenW(filename
) + 1;
5208 new->dest
= msi_alloc(size
* sizeof(WCHAR
));
5211 free_file_entry(new);
5215 lstrcpynW(new->dest
, dest
, ptr
- dest
+ 1);
5216 lstrcatW(new->dest
, filename
);
5218 if (list_empty(&files
->entry
))
5220 list_add_head(&files
->entry
, &new->entry
);
5224 LIST_FOR_EACH_ENTRY(file
, &files
->entry
, FILE_LIST
, entry
)
5226 if (lstrcmpW(source
, file
->source
) < 0)
5228 list_add_before(&file
->entry
, &new->entry
);
5233 list_add_after(&file
->entry
, &new->entry
);
5237 BOOL
move_files_wildcard(LPWSTR source
, LPWSTR dest
, int options
)
5239 WIN32_FIND_DATAW wfd
;
5243 FILE_LIST files
, *file
;
5246 hfile
= FindFirstFileW(source
, &wfd
);
5247 if (hfile
== INVALID_HANDLE_VALUE
) return FALSE
;
5249 list_init(&files
.entry
);
5251 for (res
= TRUE
; res
; res
= FindNextFileW(hfile
, &wfd
))
5253 if (is_dot_dir(wfd
.cFileName
)) continue;
5255 path
= wildcard_to_file(source
, wfd
.cFileName
);
5262 add_wildcard(&files
, path
, dest
);
5266 /* only the first wildcard match gets renamed to dest */
5267 file
= LIST_ENTRY(list_head(&files
.entry
), FILE_LIST
, entry
);
5268 size
= (strrchrW(file
->dest
, '\\') - file
->dest
) + lstrlenW(file
->destname
) + 2;
5269 file
->dest
= msi_realloc(file
->dest
, size
* sizeof(WCHAR
));
5276 lstrcpyW(strrchrW(file
->dest
, '\\') + 1, file
->destname
);
5278 while (!list_empty(&files
.entry
))
5280 file
= LIST_ENTRY(list_head(&files
.entry
), FILE_LIST
, entry
);
5282 msi_move_file((LPCWSTR
)file
->source
, (LPCWSTR
)file
->dest
, options
);
5284 list_remove(&file
->entry
);
5285 free_file_entry(file
);
5296 static UINT
ITERATE_MoveFiles( MSIRECORD
*rec
, LPVOID param
)
5298 MSIPACKAGE
*package
= param
;
5300 LPCWSTR sourcename
, destname
;
5301 LPWSTR sourcedir
= NULL
, destdir
= NULL
;
5302 LPWSTR source
= NULL
, dest
= NULL
;
5305 BOOL ret
, wildcards
;
5307 static const WCHAR backslash
[] = {'\\',0};
5309 comp
= get_loaded_component(package
, MSI_RecordGetString(rec
, 2));
5310 if (!comp
|| !comp
->Enabled
||
5311 !(comp
->Action
& (INSTALLSTATE_LOCAL
| INSTALLSTATE_SOURCE
)))
5313 TRACE("Component not set for install, not moving file\n");
5314 return ERROR_SUCCESS
;
5317 sourcename
= MSI_RecordGetString(rec
, 3);
5318 destname
= MSI_RecordGetString(rec
, 4);
5319 options
= MSI_RecordGetInteger(rec
, 7);
5321 sourcedir
= msi_dup_property(package
, MSI_RecordGetString(rec
, 5));
5325 destdir
= msi_dup_property(package
, MSI_RecordGetString(rec
, 6));
5331 if (GetFileAttributesW(sourcedir
) == INVALID_FILE_ATTRIBUTES
)
5334 source
= strdupW(sourcedir
);
5340 size
= lstrlenW(sourcedir
) + lstrlenW(sourcename
) + 2;
5341 source
= msi_alloc(size
* sizeof(WCHAR
));
5345 lstrcpyW(source
, sourcedir
);
5346 if (source
[lstrlenW(source
) - 1] != '\\')
5347 lstrcatW(source
, backslash
);
5348 lstrcatW(source
, sourcename
);
5351 wildcards
= strchrW(source
, '*') || strchrW(source
, '?');
5353 if (!destname
&& !wildcards
)
5355 destname
= strdupW(sourcename
);
5362 size
= lstrlenW(destname
);
5364 size
+= lstrlenW(destdir
) + 2;
5365 dest
= msi_alloc(size
* sizeof(WCHAR
));
5369 lstrcpyW(dest
, destdir
);
5370 if (dest
[lstrlenW(dest
) - 1] != '\\')
5371 lstrcatW(dest
, backslash
);
5374 lstrcatW(dest
, destname
);
5376 if (GetFileAttributesW(destdir
) == INVALID_FILE_ATTRIBUTES
)
5378 ret
= CreateDirectoryW(destdir
, NULL
);
5381 WARN("CreateDirectory failed: %d\n", GetLastError());
5382 return ERROR_SUCCESS
;
5387 msi_move_file(source
, dest
, options
);
5389 move_files_wildcard(source
, dest
, options
);
5392 msi_free(sourcedir
);
5397 return ERROR_SUCCESS
;
5400 static UINT
ACTION_MoveFiles( MSIPACKAGE
*package
)
5405 static const WCHAR ExecSeqQuery
[] =
5406 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5407 '`','M','o','v','e','F','i','l','e','`',0};
5409 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
5410 if (rc
!= ERROR_SUCCESS
)
5411 return ERROR_SUCCESS
;
5413 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_MoveFiles
, package
);
5414 msiobj_release(&view
->hdr
);
5419 static UINT
msi_unimplemented_action_stub( MSIPACKAGE
*package
,
5420 LPCSTR action
, LPCWSTR table
)
5422 static const WCHAR query
[] = {
5423 'S','E','L','E','C','T',' ','*',' ',
5424 'F','R','O','M',' ','`','%','s','`',0 };
5425 MSIQUERY
*view
= NULL
;
5429 r
= MSI_OpenQuery( package
->db
, &view
, query
, table
);
5430 if (r
== ERROR_SUCCESS
)
5432 r
= MSI_IterateRecords(view
, &count
, NULL
, package
);
5433 msiobj_release(&view
->hdr
);
5437 FIXME("%s -> %u ignored %s table values\n",
5438 action
, count
, debugstr_w(table
));
5440 return ERROR_SUCCESS
;
5443 static UINT
ACTION_AllocateRegistrySpace( MSIPACKAGE
*package
)
5445 TRACE("%p\n", package
);
5446 return ERROR_SUCCESS
;
5449 static UINT
ACTION_RemoveIniValues( MSIPACKAGE
*package
)
5451 static const WCHAR table
[] =
5452 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5453 return msi_unimplemented_action_stub( package
, "RemoveIniValues", table
);
5456 static UINT
ACTION_PatchFiles( MSIPACKAGE
*package
)
5458 static const WCHAR table
[] = { 'P','a','t','c','h',0 };
5459 return msi_unimplemented_action_stub( package
, "PatchFiles", table
);
5462 static UINT
ACTION_BindImage( MSIPACKAGE
*package
)
5464 static const WCHAR table
[] = { 'B','i','n','d','I','m','a','g','e',0 };
5465 return msi_unimplemented_action_stub( package
, "BindImage", table
);
5468 static UINT
ACTION_IsolateComponents( MSIPACKAGE
*package
)
5470 static const WCHAR table
[] = {
5471 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5472 return msi_unimplemented_action_stub( package
, "IsolateComponents", table
);
5475 static UINT
ACTION_MigrateFeatureStates( MSIPACKAGE
*package
)
5477 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
5478 return msi_unimplemented_action_stub( package
, "MigrateFeatureStates", table
);
5481 static UINT
ACTION_SelfUnregModules( MSIPACKAGE
*package
)
5483 static const WCHAR table
[] = { 'S','e','l','f','R','e','g',0 };
5484 return msi_unimplemented_action_stub( package
, "SelfUnregModules", table
);
5487 static UINT
ACTION_StopServices( MSIPACKAGE
*package
)
5489 static const WCHAR table
[] = {
5490 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5491 return msi_unimplemented_action_stub( package
, "StopServices", table
);
5494 static UINT
ACTION_DeleteServices( MSIPACKAGE
*package
)
5496 static const WCHAR table
[] = {
5497 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5498 return msi_unimplemented_action_stub( package
, "DeleteServices", table
);
5500 static UINT
ACTION_ValidateProductID( MSIPACKAGE
*package
)
5502 static const WCHAR table
[] = {
5503 'P','r','o','d','u','c','t','I','D',0 };
5504 return msi_unimplemented_action_stub( package
, "ValidateProductID", table
);
5507 static UINT
ACTION_RemoveEnvironmentStrings( MSIPACKAGE
*package
)
5509 static const WCHAR table
[] = {
5510 'E','n','v','i','r','o','n','m','e','n','t',0 };
5511 return msi_unimplemented_action_stub( package
, "RemoveEnvironmentStrings", table
);
5514 static UINT
ACTION_MsiPublishAssemblies( MSIPACKAGE
*package
)
5516 static const WCHAR table
[] = {
5517 'M','s','i','A','s','s','e','m','b','l','y',0 };
5518 return msi_unimplemented_action_stub( package
, "MsiPublishAssemblies", table
);
5521 static UINT
ACTION_MsiUnpublishAssemblies( MSIPACKAGE
*package
)
5523 static const WCHAR table
[] = {
5524 'M','s','i','A','s','s','e','m','b','l','y',0 };
5525 return msi_unimplemented_action_stub( package
, "MsiUnpublishAssemblies", table
);
5528 static UINT
ACTION_UnregisterFonts( MSIPACKAGE
*package
)
5530 static const WCHAR table
[] = { 'F','o','n','t',0 };
5531 return msi_unimplemented_action_stub( package
, "UnregisterFonts", table
);
5534 static UINT
ACTION_RMCCPSearch( MSIPACKAGE
*package
)
5536 static const WCHAR table
[] = { 'C','C','P','S','e','a','r','c','h',0 };
5537 return msi_unimplemented_action_stub( package
, "RMCCPSearch", table
);
5540 static UINT
ACTION_RegisterComPlus( MSIPACKAGE
*package
)
5542 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
5543 return msi_unimplemented_action_stub( package
, "RegisterComPlus", table
);
5546 static UINT
ACTION_UnregisterComPlus( MSIPACKAGE
*package
)
5548 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
5549 return msi_unimplemented_action_stub( package
, "UnregisterComPlus", table
);
5552 static UINT
ACTION_InstallSFPCatalogFile( MSIPACKAGE
*package
)
5554 static const WCHAR table
[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5555 return msi_unimplemented_action_stub( package
, "InstallSFPCatalogFile", table
);
5558 static UINT
ACTION_RemoveDuplicateFiles( MSIPACKAGE
*package
)
5560 static const WCHAR table
[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5561 return msi_unimplemented_action_stub( package
, "RemoveDuplicateFiles", table
);
5564 static UINT
ACTION_RemoveExistingProducts( MSIPACKAGE
*package
)
5566 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
5567 return msi_unimplemented_action_stub( package
, "RemoveExistingProducts", table
);
5570 static UINT
ACTION_RemoveFolders( MSIPACKAGE
*package
)
5572 static const WCHAR table
[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5573 return msi_unimplemented_action_stub( package
, "RemoveFolders", table
);
5576 static UINT
ACTION_RemoveODBC( MSIPACKAGE
*package
)
5578 static const WCHAR table
[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5579 return msi_unimplemented_action_stub( package
, "RemoveODBC", table
);
5582 static UINT
ACTION_RemoveRegistryValues( MSIPACKAGE
*package
)
5584 static const WCHAR table
[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5585 return msi_unimplemented_action_stub( package
, "RemoveRegistryValues", table
);
5588 static UINT
ACTION_RemoveShortcuts( MSIPACKAGE
*package
)
5590 static const WCHAR table
[] = { 'S','h','o','r','t','c','u','t',0 };
5591 return msi_unimplemented_action_stub( package
, "RemoveShortcuts", table
);
5594 static UINT
ACTION_UnpublishComponents( MSIPACKAGE
*package
)
5596 static const WCHAR table
[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5597 return msi_unimplemented_action_stub( package
, "UnpublishComponents", table
);
5600 static UINT
ACTION_UnregisterClassInfo( MSIPACKAGE
*package
)
5602 static const WCHAR table
[] = { 'A','p','p','I','d',0 };
5603 return msi_unimplemented_action_stub( package
, "UnregisterClassInfo", table
);
5606 static UINT
ACTION_UnregisterExtensionInfo( MSIPACKAGE
*package
)
5608 static const WCHAR table
[] = { 'E','x','t','e','n','s','i','o','n',0 };
5609 return msi_unimplemented_action_stub( package
, "UnregisterExtensionInfo", table
);
5612 static UINT
ACTION_UnregisterMIMEInfo( MSIPACKAGE
*package
)
5614 static const WCHAR table
[] = { 'M','I','M','E',0 };
5615 return msi_unimplemented_action_stub( package
, "UnregisterMIMEInfo", table
);
5618 static UINT
ACTION_UnregisterProgIdInfo( MSIPACKAGE
*package
)
5620 static const WCHAR table
[] = { 'P','r','o','g','I','d',0 };
5621 return msi_unimplemented_action_stub( package
, "UnregisterProgIdInfo", table
);
5624 static UINT
ACTION_UnregisterTypeLibraries( MSIPACKAGE
*package
)
5626 static const WCHAR table
[] = { 'T','y','p','e','L','i','b',0 };
5627 return msi_unimplemented_action_stub( package
, "UnregisterTypeLibraries", table
);
5630 static const struct _actions StandardActions
[] = {
5631 { szAllocateRegistrySpace
, ACTION_AllocateRegistrySpace
},
5632 { szAppSearch
, ACTION_AppSearch
},
5633 { szBindImage
, ACTION_BindImage
},
5634 { szCCPSearch
, ACTION_CCPSearch
},
5635 { szCostFinalize
, ACTION_CostFinalize
},
5636 { szCostInitialize
, ACTION_CostInitialize
},
5637 { szCreateFolders
, ACTION_CreateFolders
},
5638 { szCreateShortcuts
, ACTION_CreateShortcuts
},
5639 { szDeleteServices
, ACTION_DeleteServices
},
5640 { szDisableRollback
, NULL
},
5641 { szDuplicateFiles
, ACTION_DuplicateFiles
},
5642 { szExecuteAction
, ACTION_ExecuteAction
},
5643 { szFileCost
, ACTION_FileCost
},
5644 { szFindRelatedProducts
, ACTION_FindRelatedProducts
},
5645 { szForceReboot
, ACTION_ForceReboot
},
5646 { szInstallAdminPackage
, NULL
},
5647 { szInstallExecute
, ACTION_InstallExecute
},
5648 { szInstallExecuteAgain
, ACTION_InstallExecute
},
5649 { szInstallFiles
, ACTION_InstallFiles
},
5650 { szInstallFinalize
, ACTION_InstallFinalize
},
5651 { szInstallInitialize
, ACTION_InstallInitialize
},
5652 { szInstallSFPCatalogFile
, ACTION_InstallSFPCatalogFile
},
5653 { szInstallValidate
, ACTION_InstallValidate
},
5654 { szIsolateComponents
, ACTION_IsolateComponents
},
5655 { szLaunchConditions
, ACTION_LaunchConditions
},
5656 { szMigrateFeatureStates
, ACTION_MigrateFeatureStates
},
5657 { szMoveFiles
, ACTION_MoveFiles
},
5658 { szMsiPublishAssemblies
, ACTION_MsiPublishAssemblies
},
5659 { szMsiUnpublishAssemblies
, ACTION_MsiUnpublishAssemblies
},
5660 { szInstallODBC
, ACTION_InstallODBC
},
5661 { szInstallServices
, ACTION_InstallServices
},
5662 { szPatchFiles
, ACTION_PatchFiles
},
5663 { szProcessComponents
, ACTION_ProcessComponents
},
5664 { szPublishComponents
, ACTION_PublishComponents
},
5665 { szPublishFeatures
, ACTION_PublishFeatures
},
5666 { szPublishProduct
, ACTION_PublishProduct
},
5667 { szRegisterClassInfo
, ACTION_RegisterClassInfo
},
5668 { szRegisterComPlus
, ACTION_RegisterComPlus
},
5669 { szRegisterExtensionInfo
, ACTION_RegisterExtensionInfo
},
5670 { szRegisterFonts
, ACTION_RegisterFonts
},
5671 { szRegisterMIMEInfo
, ACTION_RegisterMIMEInfo
},
5672 { szRegisterProduct
, ACTION_RegisterProduct
},
5673 { szRegisterProgIdInfo
, ACTION_RegisterProgIdInfo
},
5674 { szRegisterTypeLibraries
, ACTION_RegisterTypeLibraries
},
5675 { szRegisterUser
, ACTION_RegisterUser
},
5676 { szRemoveDuplicateFiles
, ACTION_RemoveDuplicateFiles
},
5677 { szRemoveEnvironmentStrings
, ACTION_RemoveEnvironmentStrings
},
5678 { szRemoveExistingProducts
, ACTION_RemoveExistingProducts
},
5679 { szRemoveFiles
, ACTION_RemoveFiles
},
5680 { szRemoveFolders
, ACTION_RemoveFolders
},
5681 { szRemoveIniValues
, ACTION_RemoveIniValues
},
5682 { szRemoveODBC
, ACTION_RemoveODBC
},
5683 { szRemoveRegistryValues
, ACTION_RemoveRegistryValues
},
5684 { szRemoveShortcuts
, ACTION_RemoveShortcuts
},
5685 { szResolveSource
, ACTION_ResolveSource
},
5686 { szRMCCPSearch
, ACTION_RMCCPSearch
},
5687 { szScheduleReboot
, NULL
},
5688 { szSelfRegModules
, ACTION_SelfRegModules
},
5689 { szSelfUnregModules
, ACTION_SelfUnregModules
},
5690 { szSetODBCFolders
, NULL
},
5691 { szStartServices
, ACTION_StartServices
},
5692 { szStopServices
, ACTION_StopServices
},
5693 { szUnpublishComponents
, ACTION_UnpublishComponents
},
5694 { szUnpublishFeatures
, ACTION_UnpublishFeatures
},
5695 { szUnregisterClassInfo
, ACTION_UnregisterClassInfo
},
5696 { szUnregisterComPlus
, ACTION_UnregisterComPlus
},
5697 { szUnregisterExtensionInfo
, ACTION_UnregisterExtensionInfo
},
5698 { szUnregisterFonts
, ACTION_UnregisterFonts
},
5699 { szUnregisterMIMEInfo
, ACTION_UnregisterMIMEInfo
},
5700 { szUnregisterProgIdInfo
, ACTION_UnregisterProgIdInfo
},
5701 { szUnregisterTypeLibraries
, ACTION_UnregisterTypeLibraries
},
5702 { szValidateProductID
, ACTION_ValidateProductID
},
5703 { szWriteEnvironmentStrings
, ACTION_WriteEnvironmentStrings
},
5704 { szWriteIniValues
, ACTION_WriteIniValues
},
5705 { szWriteRegistryValues
, ACTION_WriteRegistryValues
},