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
,
2243 if (value
[0]=='#' && value
[1]!='#' && value
[1]!='%')
2249 LPWSTR deformated
= NULL
;
2252 deformat_string(package
, &value
[2], &deformated
);
2254 /* binary value type */
2258 *size
= (strlenW(ptr
)/2)+1;
2260 *size
= strlenW(ptr
)/2;
2262 data
= msi_alloc(*size
);
2268 /* if uneven pad with a zero in front */
2274 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2276 TRACE("Uneven byte count\n");
2284 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2287 msi_free(deformated
);
2289 TRACE("Data %i bytes(%i)\n",*size
,count
);
2296 deformat_string(package
, &value
[1], &deformated
);
2299 *size
= sizeof(DWORD
);
2300 data
= msi_alloc(*size
);
2306 if ( (*p
< '0') || (*p
> '9') )
2312 if (deformated
[0] == '-')
2315 TRACE("DWORD %i\n",*(LPDWORD
)data
);
2317 msi_free(deformated
);
2322 static const WCHAR szMulti
[] = {'[','~',']',0};
2331 *type
=REG_EXPAND_SZ
;
2339 if (strstrW(value
,szMulti
))
2340 *type
= REG_MULTI_SZ
;
2342 *size
= deformat_string(package
, ptr
,(LPWSTR
*)&data
);
2347 static UINT
ITERATE_WriteRegistryValues(MSIRECORD
*row
, LPVOID param
)
2349 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
2350 static const WCHAR szHCR
[] =
2351 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2352 'R','O','O','T','\\',0};
2353 static const WCHAR szHCU
[] =
2354 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2355 'U','S','E','R','\\',0};
2356 static const WCHAR szHLM
[] =
2357 {'H','K','E','Y','_','L','O','C','A','L','_',
2358 'M','A','C','H','I','N','E','\\',0};
2359 static const WCHAR szHU
[] =
2360 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2362 LPSTR value_data
= NULL
;
2363 HKEY root_key
, hkey
;
2366 LPCWSTR szRoot
, component
, name
, key
, value
;
2371 BOOL check_first
= FALSE
;
2374 ui_progress(package
,2,0,0,0);
2381 component
= MSI_RecordGetString(row
, 6);
2382 comp
= get_loaded_component(package
,component
);
2384 return ERROR_SUCCESS
;
2386 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2388 TRACE("Skipping write due to disabled component %s\n",
2389 debugstr_w(component
));
2391 comp
->Action
= comp
->Installed
;
2393 return ERROR_SUCCESS
;
2396 comp
->Action
= INSTALLSTATE_LOCAL
;
2398 name
= MSI_RecordGetString(row
, 4);
2399 if( MSI_RecordIsNull(row
,5) && name
)
2401 /* null values can have special meanings */
2402 if (name
[0]=='-' && name
[1] == 0)
2403 return ERROR_SUCCESS
;
2404 else if ((name
[0]=='+' && name
[1] == 0) ||
2405 (name
[0] == '*' && name
[1] == 0))
2410 root
= MSI_RecordGetInteger(row
,2);
2411 key
= MSI_RecordGetString(row
, 3);
2413 /* get the root key */
2418 static const WCHAR szALLUSER
[] = {'A','L','L','U','S','E','R','S',0};
2419 LPWSTR all_users
= msi_dup_property( package
, szALLUSER
);
2420 if (all_users
&& all_users
[0] == '1')
2422 root_key
= HKEY_LOCAL_MACHINE
;
2427 root_key
= HKEY_CURRENT_USER
;
2430 msi_free(all_users
);
2433 case 0: root_key
= HKEY_CLASSES_ROOT
;
2436 case 1: root_key
= HKEY_CURRENT_USER
;
2439 case 2: root_key
= HKEY_LOCAL_MACHINE
;
2442 case 3: root_key
= HKEY_USERS
;
2446 ERR("Unknown root %i\n",root
);
2452 return ERROR_SUCCESS
;
2454 deformat_string(package
, key
, &deformated
);
2455 size
= strlenW(deformated
) + strlenW(szRoot
) + 1;
2456 uikey
= msi_alloc(size
*sizeof(WCHAR
));
2457 strcpyW(uikey
,szRoot
);
2458 strcatW(uikey
,deformated
);
2460 if (RegCreateKeyW( root_key
, deformated
, &hkey
))
2462 ERR("Could not create key %s\n",debugstr_w(deformated
));
2463 msi_free(deformated
);
2465 return ERROR_SUCCESS
;
2467 msi_free(deformated
);
2469 value
= MSI_RecordGetString(row
,5);
2471 value_data
= parse_value(package
, value
, &type
, &size
);
2474 static const WCHAR szEmpty
[] = {0};
2475 value_data
= (LPSTR
)strdupW(szEmpty
);
2480 deformat_string(package
, name
, &deformated
);
2482 /* get the double nulls to terminate SZ_MULTI */
2483 if (type
== REG_MULTI_SZ
)
2484 size
+=sizeof(WCHAR
);
2488 TRACE("Setting value %s of %s\n",debugstr_w(deformated
),
2490 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
)value_data
, size
);
2495 rc
= RegQueryValueExW(hkey
, deformated
, NULL
, NULL
, NULL
, &sz
);
2496 if (rc
== ERROR_SUCCESS
|| rc
== ERROR_MORE_DATA
)
2498 TRACE("value %s of %s checked already exists\n",
2499 debugstr_w(deformated
), debugstr_w(uikey
));
2503 TRACE("Checked and setting value %s of %s\n",
2504 debugstr_w(deformated
), debugstr_w(uikey
));
2505 if (deformated
|| size
)
2506 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
) value_data
, size
);
2511 uirow
= MSI_CreateRecord(3);
2512 MSI_RecordSetStringW(uirow
,2,deformated
);
2513 MSI_RecordSetStringW(uirow
,1,uikey
);
2516 MSI_RecordSetStringW(uirow
,3,(LPWSTR
)value_data
);
2518 MSI_RecordSetStringW(uirow
,3,value
);
2520 ui_actiondata(package
,szWriteRegistryValues
,uirow
);
2521 msiobj_release( &uirow
->hdr
);
2523 msi_free(value_data
);
2524 msi_free(deformated
);
2527 return ERROR_SUCCESS
;
2530 static UINT
ACTION_WriteRegistryValues(MSIPACKAGE
*package
)
2534 static const WCHAR ExecSeqQuery
[] =
2535 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2536 '`','R','e','g','i','s','t','r','y','`',0 };
2538 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2539 if (rc
!= ERROR_SUCCESS
)
2540 return ERROR_SUCCESS
;
2542 /* increment progress bar each time action data is sent */
2543 ui_progress(package
,1,REG_PROGRESS_VALUE
,1,0);
2545 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteRegistryValues
, package
);
2547 msiobj_release(&view
->hdr
);
2551 static UINT
ACTION_InstallInitialize(MSIPACKAGE
*package
)
2553 package
->script
->CurrentlyScripting
= TRUE
;
2555 return ERROR_SUCCESS
;
2559 static UINT
ACTION_InstallValidate(MSIPACKAGE
*package
)
2564 static const WCHAR q1
[]=
2565 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2566 '`','R','e','g','i','s','t','r','y','`',0};
2569 MSIFEATURE
*feature
;
2572 TRACE("InstallValidate\n");
2574 rc
= MSI_DatabaseOpenViewW(package
->db
, q1
, &view
);
2575 if (rc
== ERROR_SUCCESS
)
2577 MSI_IterateRecords( view
, &progress
, NULL
, package
);
2578 msiobj_release( &view
->hdr
);
2579 total
+= progress
* REG_PROGRESS_VALUE
;
2582 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2583 total
+= COMPONENT_PROGRESS_VALUE
;
2585 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2586 total
+= file
->FileSize
;
2588 ui_progress(package
,0,total
,0,0);
2590 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2592 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2593 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
,
2594 feature
->ActionRequest
);
2597 return ERROR_SUCCESS
;
2600 static UINT
ITERATE_LaunchConditions(MSIRECORD
*row
, LPVOID param
)
2602 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
2603 LPCWSTR cond
= NULL
;
2604 LPCWSTR message
= NULL
;
2607 static const WCHAR title
[]=
2608 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2610 cond
= MSI_RecordGetString(row
,1);
2612 r
= MSI_EvaluateConditionW(package
,cond
);
2613 if (r
== MSICONDITION_FALSE
)
2615 if ((gUILevel
& INSTALLUILEVEL_MASK
) != INSTALLUILEVEL_NONE
)
2618 message
= MSI_RecordGetString(row
,2);
2619 deformat_string(package
,message
,&deformated
);
2620 MessageBoxW(NULL
,deformated
,title
,MB_OK
);
2621 msi_free(deformated
);
2624 return ERROR_INSTALL_FAILURE
;
2627 return ERROR_SUCCESS
;
2630 static UINT
ACTION_LaunchConditions(MSIPACKAGE
*package
)
2633 MSIQUERY
* view
= NULL
;
2634 static const WCHAR ExecSeqQuery
[] =
2635 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2636 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2638 TRACE("Checking launch conditions\n");
2640 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2641 if (rc
!= ERROR_SUCCESS
)
2642 return ERROR_SUCCESS
;
2644 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_LaunchConditions
, package
);
2645 msiobj_release(&view
->hdr
);
2650 static LPWSTR
resolve_keypath( MSIPACKAGE
* package
, MSICOMPONENT
*cmp
)
2654 return resolve_folder(package
,cmp
->Directory
,FALSE
,FALSE
,TRUE
,NULL
);
2656 if (cmp
->Attributes
& msidbComponentAttributesRegistryKeyPath
)
2658 MSIRECORD
* row
= 0;
2660 LPWSTR deformated
,buffer
,deformated_name
;
2662 static const WCHAR ExecSeqQuery
[] =
2663 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2664 '`','R','e','g','i','s','t','r','y','`',' ',
2665 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2666 ' ','=',' ' ,'\'','%','s','\'',0 };
2667 static const WCHAR fmt
[]={'%','0','2','i',':','\\','%','s','\\',0};
2668 static const WCHAR fmt2
[]=
2669 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2671 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
,cmp
->KeyPath
);
2675 root
= MSI_RecordGetInteger(row
,2);
2676 key
= MSI_RecordGetString(row
, 3);
2677 name
= MSI_RecordGetString(row
, 4);
2678 deformat_string(package
, key
, &deformated
);
2679 deformat_string(package
, name
, &deformated_name
);
2681 len
= strlenW(deformated
) + 6;
2682 if (deformated_name
)
2683 len
+=strlenW(deformated_name
);
2685 buffer
= msi_alloc( len
*sizeof(WCHAR
));
2687 if (deformated_name
)
2688 sprintfW(buffer
,fmt2
,root
,deformated
,deformated_name
);
2690 sprintfW(buffer
,fmt
,root
,deformated
);
2692 msi_free(deformated
);
2693 msi_free(deformated_name
);
2694 msiobj_release(&row
->hdr
);
2698 else if (cmp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2700 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2705 MSIFILE
*file
= get_loaded_file( package
, cmp
->KeyPath
);
2708 return strdupW( file
->TargetPath
);
2713 static HKEY
openSharedDLLsKey(void)
2716 static const WCHAR path
[] =
2717 {'S','o','f','t','w','a','r','e','\\',
2718 'M','i','c','r','o','s','o','f','t','\\',
2719 'W','i','n','d','o','w','s','\\',
2720 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2721 'S','h','a','r','e','d','D','L','L','s',0};
2723 RegCreateKeyW(HKEY_LOCAL_MACHINE
,path
,&hkey
);
2727 static UINT
ACTION_GetSharedDLLsCount(LPCWSTR dll
)
2732 DWORD sz
= sizeof(count
);
2735 hkey
= openSharedDLLsKey();
2736 rc
= RegQueryValueExW(hkey
, dll
, NULL
, &type
, (LPBYTE
)&count
, &sz
);
2737 if (rc
!= ERROR_SUCCESS
)
2743 static UINT
ACTION_WriteSharedDLLsCount(LPCWSTR path
, UINT count
)
2747 hkey
= openSharedDLLsKey();
2749 msi_reg_set_val_dword( hkey
, path
, count
);
2751 RegDeleteValueW(hkey
,path
);
2757 * Return TRUE if the count should be written out and FALSE if not
2759 static void ACTION_RefCountComponent( MSIPACKAGE
* package
, MSICOMPONENT
*comp
)
2761 MSIFEATURE
*feature
;
2765 /* only refcount DLLs */
2766 if (comp
->KeyPath
== NULL
||
2767 comp
->Attributes
& msidbComponentAttributesRegistryKeyPath
||
2768 comp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2772 count
= ACTION_GetSharedDLLsCount( comp
->FullKeypath
);
2773 write
= (count
> 0);
2775 if (comp
->Attributes
& msidbComponentAttributesSharedDllRefCount
)
2779 /* increment counts */
2780 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2784 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
))
2787 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2789 if ( cl
->component
== comp
)
2794 /* decrement counts */
2795 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2799 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ABSENT
))
2802 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2804 if ( cl
->component
== comp
)
2809 /* ref count all the files in the component */
2814 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2816 if (file
->Component
== comp
)
2817 ACTION_WriteSharedDLLsCount( file
->TargetPath
, count
);
2821 /* add a count for permenent */
2822 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2825 comp
->RefCount
= count
;
2828 ACTION_WriteSharedDLLsCount( comp
->FullKeypath
, comp
->RefCount
);
2832 * Ok further analysis makes me think that this work is
2833 * actually done in the PublishComponents and PublishFeatures
2834 * step, and not here. It appears like the keypath and all that is
2835 * resolved in this step, however actually written in the Publish steps.
2836 * But we will leave it here for now because it is unclear
2838 static UINT
ACTION_ProcessComponents(MSIPACKAGE
*package
)
2840 WCHAR squished_pc
[GUID_SIZE
];
2841 WCHAR squished_cc
[GUID_SIZE
];
2844 HKEY hkey
=0,hkey2
=0;
2848 /* writes the Component and Features values to the registry */
2850 rc
= MSIREG_OpenComponents(&hkey
);
2851 if (rc
!= ERROR_SUCCESS
)
2854 squash_guid(package
->ProductCode
,squished_pc
);
2855 ui_progress(package
,1,COMPONENT_PROGRESS_VALUE
,1,0);
2857 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2861 ui_progress(package
,2,0,0,0);
2862 if (!comp
->ComponentId
)
2865 squash_guid(comp
->ComponentId
,squished_cc
);
2867 msi_free(comp
->FullKeypath
);
2868 comp
->FullKeypath
= resolve_keypath( package
, comp
);
2870 /* do the refcounting */
2871 ACTION_RefCountComponent( package
, comp
);
2873 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2874 debugstr_w(comp
->Component
),
2875 debugstr_w(squished_cc
),
2876 debugstr_w(comp
->FullKeypath
),
2879 * Write the keypath out if the component is to be registered
2880 * and delete the key if the component is to be deregistered
2882 if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2884 rc
= RegCreateKeyW(hkey
,squished_cc
,&hkey2
);
2885 if (rc
!= ERROR_SUCCESS
)
2888 if (!comp
->FullKeypath
)
2891 msi_reg_set_val_str( hkey2
, squished_pc
, comp
->FullKeypath
);
2893 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2895 static const WCHAR szPermKey
[] =
2896 { '0','0','0','0','0','0','0','0','0','0','0','0',
2897 '0','0','0','0','0','0','0','0','0','0','0','0',
2898 '0','0','0','0','0','0','0','0',0 };
2900 msi_reg_set_val_str( hkey2
, szPermKey
, comp
->FullKeypath
);
2905 rc
= MSIREG_OpenUserDataComponentKey(comp
->ComponentId
, &hkey2
, TRUE
);
2906 if (rc
!= ERROR_SUCCESS
)
2909 msi_reg_set_val_str(hkey2
, squished_pc
, comp
->FullKeypath
);
2912 else if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ABSENT
))
2916 rc
= RegOpenKeyW(hkey
,squished_cc
,&hkey2
);
2917 if (rc
!= ERROR_SUCCESS
)
2920 RegDeleteValueW(hkey2
,squished_pc
);
2922 /* if the key is empty delete it */
2923 res
= RegEnumKeyExW(hkey2
,0,NULL
,0,0,NULL
,0,NULL
);
2925 if (res
== ERROR_NO_MORE_ITEMS
)
2926 RegDeleteKeyW(hkey
,squished_cc
);
2928 MSIREG_DeleteUserDataComponentKey(comp
->ComponentId
);
2932 uirow
= MSI_CreateRecord(3);
2933 MSI_RecordSetStringW(uirow
,1,package
->ProductCode
);
2934 MSI_RecordSetStringW(uirow
,2,comp
->ComponentId
);
2935 MSI_RecordSetStringW(uirow
,3,comp
->FullKeypath
);
2936 ui_actiondata(package
,szProcessComponents
,uirow
);
2937 msiobj_release( &uirow
->hdr
);
2951 static BOOL CALLBACK
Typelib_EnumResNameProc( HMODULE hModule
, LPCWSTR lpszType
,
2952 LPWSTR lpszName
, LONG_PTR lParam
)
2955 typelib_struct
*tl_struct
= (typelib_struct
*) lParam
;
2956 static const WCHAR fmt
[] = {'%','s','\\','%','i',0};
2960 if (!IS_INTRESOURCE(lpszName
))
2962 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName
));
2966 sz
= strlenW(tl_struct
->source
)+4;
2967 sz
*= sizeof(WCHAR
);
2969 if ((INT_PTR
)lpszName
== 1)
2970 tl_struct
->path
= strdupW(tl_struct
->source
);
2973 tl_struct
->path
= msi_alloc(sz
);
2974 sprintfW(tl_struct
->path
,fmt
,tl_struct
->source
, lpszName
);
2977 TRACE("trying %s\n", debugstr_w(tl_struct
->path
));
2978 res
= LoadTypeLib(tl_struct
->path
,&tl_struct
->ptLib
);
2979 if (!SUCCEEDED(res
))
2981 msi_free(tl_struct
->path
);
2982 tl_struct
->path
= NULL
;
2987 ITypeLib_GetLibAttr(tl_struct
->ptLib
, &attr
);
2988 if (IsEqualGUID(&(tl_struct
->clsid
),&(attr
->guid
)))
2990 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2994 msi_free(tl_struct
->path
);
2995 tl_struct
->path
= NULL
;
2997 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2998 ITypeLib_Release(tl_struct
->ptLib
);
3003 static UINT
ITERATE_RegisterTypeLibraries(MSIRECORD
*row
, LPVOID param
)
3005 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
3009 typelib_struct tl_struct
;
3011 static const WCHAR szTYPELIB
[] = {'T','Y','P','E','L','I','B',0};
3013 component
= MSI_RecordGetString(row
,3);
3014 comp
= get_loaded_component(package
,component
);
3016 return ERROR_SUCCESS
;
3018 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3020 TRACE("Skipping typelib reg due to disabled component\n");
3022 comp
->Action
= comp
->Installed
;
3024 return ERROR_SUCCESS
;
3027 comp
->Action
= INSTALLSTATE_LOCAL
;
3029 file
= get_loaded_file( package
, comp
->KeyPath
);
3031 return ERROR_SUCCESS
;
3033 module
= LoadLibraryExW( file
->TargetPath
, NULL
, LOAD_LIBRARY_AS_DATAFILE
);
3037 guid
= MSI_RecordGetString(row
,1);
3038 CLSIDFromString((LPWSTR
)guid
, &tl_struct
.clsid
);
3039 tl_struct
.source
= strdupW( file
->TargetPath
);
3040 tl_struct
.path
= NULL
;
3042 EnumResourceNamesW(module
, szTYPELIB
, Typelib_EnumResNameProc
,
3043 (LONG_PTR
)&tl_struct
);
3051 helpid
= MSI_RecordGetString(row
,6);
3054 help
= resolve_folder(package
,helpid
,FALSE
,FALSE
,TRUE
,NULL
);
3055 res
= RegisterTypeLib(tl_struct
.ptLib
,tl_struct
.path
,help
);
3058 if (!SUCCEEDED(res
))
3059 ERR("Failed to register type library %s\n",
3060 debugstr_w(tl_struct
.path
));
3063 ui_actiondata(package
,szRegisterTypeLibraries
,row
);
3065 TRACE("Registered %s\n", debugstr_w(tl_struct
.path
));
3068 ITypeLib_Release(tl_struct
.ptLib
);
3069 msi_free(tl_struct
.path
);
3072 ERR("Failed to load type library %s\n",
3073 debugstr_w(tl_struct
.source
));
3075 FreeLibrary(module
);
3076 msi_free(tl_struct
.source
);
3079 ERR("Could not load file! %s\n", debugstr_w(file
->TargetPath
));
3081 return ERROR_SUCCESS
;
3084 static UINT
ACTION_RegisterTypeLibraries(MSIPACKAGE
*package
)
3087 * OK this is a bit confusing.. I am given a _Component key and I believe
3088 * that the file that is being registered as a type library is the "key file
3089 * of that component" which I interpret to mean "The file in the KeyPath of
3094 static const WCHAR Query
[] =
3095 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3096 '`','T','y','p','e','L','i','b','`',0};
3098 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3099 if (rc
!= ERROR_SUCCESS
)
3100 return ERROR_SUCCESS
;
3102 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_RegisterTypeLibraries
, package
);
3103 msiobj_release(&view
->hdr
);
3107 static UINT
ITERATE_CreateShortcuts(MSIRECORD
*row
, LPVOID param
)
3109 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3110 LPWSTR target_file
, target_folder
, filename
;
3111 LPCWSTR buffer
, extension
;
3113 static const WCHAR szlnk
[]={'.','l','n','k',0};
3114 IShellLinkW
*sl
= NULL
;
3115 IPersistFile
*pf
= NULL
;
3118 buffer
= MSI_RecordGetString(row
,4);
3119 comp
= get_loaded_component(package
,buffer
);
3121 return ERROR_SUCCESS
;
3123 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3125 TRACE("Skipping shortcut creation due to disabled component\n");
3127 comp
->Action
= comp
->Installed
;
3129 return ERROR_SUCCESS
;
3132 comp
->Action
= INSTALLSTATE_LOCAL
;
3134 ui_actiondata(package
,szCreateShortcuts
,row
);
3136 res
= CoCreateInstance( &CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
,
3137 &IID_IShellLinkW
, (LPVOID
*) &sl
);
3141 ERR("CLSID_ShellLink not available\n");
3145 res
= IShellLinkW_QueryInterface( sl
, &IID_IPersistFile
,(LPVOID
*) &pf
);
3148 ERR("QueryInterface(IID_IPersistFile) failed\n");
3152 buffer
= MSI_RecordGetString(row
,2);
3153 target_folder
= resolve_folder(package
, buffer
,FALSE
,FALSE
,TRUE
,NULL
);
3155 /* may be needed because of a bug somehwere else */
3156 create_full_pathW(target_folder
);
3158 filename
= msi_dup_record_field( row
, 3 );
3159 reduce_to_longfilename(filename
);
3161 extension
= strchrW(filename
,'.');
3162 if (!extension
|| strcmpiW(extension
,szlnk
))
3164 int len
= strlenW(filename
);
3165 filename
= msi_realloc(filename
, len
* sizeof(WCHAR
) + sizeof(szlnk
));
3166 memcpy(filename
+ len
, szlnk
, sizeof(szlnk
));
3168 target_file
= build_directory_name(2, target_folder
, filename
);
3169 msi_free(target_folder
);
3172 buffer
= MSI_RecordGetString(row
,5);
3173 if (strchrW(buffer
,'['))
3176 deformat_string(package
,buffer
,&deformated
);
3177 IShellLinkW_SetPath(sl
,deformated
);
3178 msi_free(deformated
);
3182 FIXME("poorly handled shortcut format, advertised shortcut\n");
3183 IShellLinkW_SetPath(sl
,comp
->FullKeypath
);
3186 if (!MSI_RecordIsNull(row
,6))
3189 buffer
= MSI_RecordGetString(row
,6);
3190 deformat_string(package
,buffer
,&deformated
);
3191 IShellLinkW_SetArguments(sl
,deformated
);
3192 msi_free(deformated
);
3195 if (!MSI_RecordIsNull(row
,7))
3197 buffer
= MSI_RecordGetString(row
,7);
3198 IShellLinkW_SetDescription(sl
,buffer
);
3201 if (!MSI_RecordIsNull(row
,8))
3202 IShellLinkW_SetHotkey(sl
,MSI_RecordGetInteger(row
,8));
3204 if (!MSI_RecordIsNull(row
,9))
3209 buffer
= MSI_RecordGetString(row
,9);
3211 Path
= build_icon_path(package
,buffer
);
3212 index
= MSI_RecordGetInteger(row
,10);
3214 /* no value means 0 */
3215 if (index
== MSI_NULL_INTEGER
)
3218 IShellLinkW_SetIconLocation(sl
,Path
,index
);
3222 if (!MSI_RecordIsNull(row
,11))
3223 IShellLinkW_SetShowCmd(sl
,MSI_RecordGetInteger(row
,11));
3225 if (!MSI_RecordIsNull(row
,12))
3228 buffer
= MSI_RecordGetString(row
,12);
3229 Path
= resolve_folder(package
, buffer
, FALSE
, FALSE
, TRUE
, NULL
);
3231 IShellLinkW_SetWorkingDirectory(sl
,Path
);
3235 TRACE("Writing shortcut to %s\n",debugstr_w(target_file
));
3236 IPersistFile_Save(pf
,target_file
,FALSE
);
3238 msi_free(target_file
);
3242 IPersistFile_Release( pf
);
3244 IShellLinkW_Release( sl
);
3246 return ERROR_SUCCESS
;
3249 static UINT
ACTION_CreateShortcuts(MSIPACKAGE
*package
)
3254 static const WCHAR Query
[] =
3255 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3256 '`','S','h','o','r','t','c','u','t','`',0};
3258 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3259 if (rc
!= ERROR_SUCCESS
)
3260 return ERROR_SUCCESS
;
3262 res
= CoInitialize( NULL
);
3265 ERR("CoInitialize failed\n");
3266 return ERROR_FUNCTION_FAILED
;
3269 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateShortcuts
, package
);
3270 msiobj_release(&view
->hdr
);
3277 static UINT
ITERATE_PublishProduct(MSIRECORD
*row
, LPVOID param
)
3279 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
3288 FileName
= MSI_RecordGetString(row
,1);
3291 ERR("Unable to get FileName\n");
3292 return ERROR_SUCCESS
;
3295 FilePath
= build_icon_path(package
,FileName
);
3297 TRACE("Creating icon file at %s\n",debugstr_w(FilePath
));
3299 the_file
= CreateFileW(FilePath
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
,
3300 FILE_ATTRIBUTE_NORMAL
, NULL
);
3302 if (the_file
== INVALID_HANDLE_VALUE
)
3304 ERR("Unable to create file %s\n",debugstr_w(FilePath
));
3306 return ERROR_SUCCESS
;
3313 rc
= MSI_RecordReadStream(row
,2,buffer
,&sz
);
3314 if (rc
!= ERROR_SUCCESS
)
3316 ERR("Failed to get stream\n");
3317 CloseHandle(the_file
);
3318 DeleteFileW(FilePath
);
3321 WriteFile(the_file
,buffer
,sz
,&write
,NULL
);
3322 } while (sz
== 1024);
3326 CloseHandle(the_file
);
3328 uirow
= MSI_CreateRecord(1);
3329 MSI_RecordSetStringW(uirow
,1,FileName
);
3330 ui_actiondata(package
,szPublishProduct
,uirow
);
3331 msiobj_release( &uirow
->hdr
);
3333 return ERROR_SUCCESS
;
3336 static BOOL
msi_check_publish(MSIPACKAGE
*package
)
3338 MSIFEATURE
*feature
;
3340 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3342 if (feature
->ActionRequest
== INSTALLSTATE_LOCAL
)
3350 * 99% of the work done here is only done for
3351 * advertised installs. However this is where the
3352 * Icon table is processed and written out
3353 * so that is what I am going to do here.
3355 static UINT
ACTION_PublishProduct(MSIPACKAGE
*package
)
3359 MSISOURCELISTINFO
*info
;
3361 static const WCHAR Query
[]=
3362 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3363 '`','I','c','o','n','`',0};
3364 /* for registry stuff */
3367 HKEY hudkey
=0, props
=0;
3368 static const WCHAR szProductLanguage
[] =
3369 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3370 static const WCHAR szARPProductIcon
[] =
3371 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3372 static const WCHAR szProductVersion
[] =
3373 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3377 MSIHANDLE hDb
, hSumInfo
;
3379 /* FIXME: also need to publish if the product is in advertise mode */
3380 if (!msi_check_publish(package
))
3381 return ERROR_SUCCESS
;
3383 /* write out icon files */
3385 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3386 if (rc
== ERROR_SUCCESS
)
3388 MSI_IterateRecords(view
, NULL
, ITERATE_PublishProduct
, package
);
3389 msiobj_release(&view
->hdr
);
3392 /* ok there is a lot more done here but i need to figure out what */
3394 rc
= MSIREG_OpenProductsKey(package
->ProductCode
,&hkey
,TRUE
);
3395 if (rc
!= ERROR_SUCCESS
)
3398 rc
= MSIREG_OpenUserProductsKey(package
->ProductCode
,&hukey
,TRUE
);
3399 if (rc
!= ERROR_SUCCESS
)
3402 rc
= MSIREG_OpenUserDataProductKey(package
->ProductCode
,&hudkey
,TRUE
);
3403 if (rc
!= ERROR_SUCCESS
)
3406 rc
= MSIREG_OpenInstallPropertiesKey(package
->ProductCode
,&props
,TRUE
);
3407 if (rc
!= ERROR_SUCCESS
)
3410 buffer
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTNAMEW
);
3411 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PRODUCTNAMEW
, buffer
);
3414 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
3415 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
3417 buffer
= msi_dup_property( package
, szARPProductIcon
);
3420 LPWSTR path
= build_icon_path(package
,buffer
);
3421 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PRODUCTICONW
, path
);
3426 buffer
= msi_dup_property( package
, szProductVersion
);
3429 DWORD verdword
= msi_version_str_to_dword(buffer
);
3430 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
3434 /* FIXME: Need to write more keys to the user registry */
3436 hDb
= alloc_msihandle( &package
->db
->hdr
);
3438 rc
= ERROR_NOT_ENOUGH_MEMORY
;
3441 rc
= MsiGetSummaryInformationW(hDb
, NULL
, 0, &hSumInfo
);
3442 MsiCloseHandle(hDb
);
3443 if (rc
== ERROR_SUCCESS
)
3445 WCHAR guidbuffer
[0x200];
3447 rc
= MsiSummaryInfoGetPropertyW(hSumInfo
, 9, NULL
, NULL
, NULL
,
3449 if (rc
== ERROR_SUCCESS
)
3451 WCHAR squashed
[GUID_SIZE
];
3452 /* for now we only care about the first guid */
3453 LPWSTR ptr
= strchrW(guidbuffer
,';');
3455 squash_guid(guidbuffer
,squashed
);
3456 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PACKAGECODEW
, squashed
);
3460 ERR("Unable to query Revision_Number...\n");
3463 MsiCloseHandle(hSumInfo
);
3467 ERR("Unable to open Summary Information\n");
3471 /* publish the SourceList info */
3472 LIST_FOR_EACH_ENTRY(info
, &package
->sourcelist_info
, MSISOURCELISTINFO
, entry
)
3474 MsiSourceListSetInfoW(package
->ProductCode
, NULL
,
3475 info
->context
, info
->options
,
3476 info
->property
, info
->value
);
3479 LIST_FOR_EACH_ENTRY(disk
, &package
->sourcelist_media
, MSIMEDIADISK
, entry
)
3481 MsiSourceListAddMediaDiskW(package
->ProductCode
, NULL
,
3482 disk
->context
, disk
->options
,
3483 disk
->disk_id
, disk
->volume_label
, disk
->disk_prompt
);
3489 RegCloseKey(hudkey
);
3495 static UINT
ITERATE_WriteIniValues(MSIRECORD
*row
, LPVOID param
)
3497 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3498 LPCWSTR component
,section
,key
,value
,identifier
,filename
,dirproperty
;
3499 LPWSTR deformated_section
, deformated_key
, deformated_value
;
3500 LPWSTR folder
, fullname
= NULL
;
3504 static const WCHAR szWindowsFolder
[] =
3505 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3507 component
= MSI_RecordGetString(row
, 8);
3508 comp
= get_loaded_component(package
,component
);
3510 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3512 TRACE("Skipping ini file due to disabled component %s\n",
3513 debugstr_w(component
));
3515 comp
->Action
= comp
->Installed
;
3517 return ERROR_SUCCESS
;
3520 comp
->Action
= INSTALLSTATE_LOCAL
;
3522 identifier
= MSI_RecordGetString(row
,1);
3523 filename
= MSI_RecordGetString(row
,2);
3524 dirproperty
= MSI_RecordGetString(row
,3);
3525 section
= MSI_RecordGetString(row
,4);
3526 key
= MSI_RecordGetString(row
,5);
3527 value
= MSI_RecordGetString(row
,6);
3528 action
= MSI_RecordGetInteger(row
,7);
3530 deformat_string(package
,section
,&deformated_section
);
3531 deformat_string(package
,key
,&deformated_key
);
3532 deformat_string(package
,value
,&deformated_value
);
3536 folder
= resolve_folder(package
, dirproperty
, FALSE
, FALSE
, TRUE
, NULL
);
3538 folder
= msi_dup_property( package
, dirproperty
);
3541 folder
= msi_dup_property( package
, szWindowsFolder
);
3545 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty
));
3549 fullname
= build_directory_name(2, folder
, filename
);
3553 TRACE("Adding value %s to section %s in %s\n",
3554 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3555 debugstr_w(fullname
));
3556 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3557 deformated_value
, fullname
);
3559 else if (action
== 1)
3562 GetPrivateProfileStringW(deformated_section
, deformated_key
, NULL
,
3563 returned
, 10, fullname
);
3564 if (returned
[0] == 0)
3566 TRACE("Adding value %s to section %s in %s\n",
3567 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3568 debugstr_w(fullname
));
3570 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3571 deformated_value
, fullname
);
3574 else if (action
== 3)
3575 FIXME("Append to existing section not yet implemented\n");
3577 uirow
= MSI_CreateRecord(4);
3578 MSI_RecordSetStringW(uirow
,1,identifier
);
3579 MSI_RecordSetStringW(uirow
,2,deformated_section
);
3580 MSI_RecordSetStringW(uirow
,3,deformated_key
);
3581 MSI_RecordSetStringW(uirow
,4,deformated_value
);
3582 ui_actiondata(package
,szWriteIniValues
,uirow
);
3583 msiobj_release( &uirow
->hdr
);
3587 msi_free(deformated_key
);
3588 msi_free(deformated_value
);
3589 msi_free(deformated_section
);
3590 return ERROR_SUCCESS
;
3593 static UINT
ACTION_WriteIniValues(MSIPACKAGE
*package
)
3597 static const WCHAR ExecSeqQuery
[] =
3598 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3599 '`','I','n','i','F','i','l','e','`',0};
3601 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3602 if (rc
!= ERROR_SUCCESS
)
3604 TRACE("no IniFile table\n");
3605 return ERROR_SUCCESS
;
3608 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteIniValues
, package
);
3609 msiobj_release(&view
->hdr
);
3613 static UINT
ITERATE_SelfRegModules(MSIRECORD
*row
, LPVOID param
)
3615 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3620 static const WCHAR ExeStr
[] =
3621 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3622 static const WCHAR close
[] = {'\"',0};
3624 PROCESS_INFORMATION info
;
3629 memset(&si
,0,sizeof(STARTUPINFOW
));
3631 filename
= MSI_RecordGetString(row
,1);
3632 file
= get_loaded_file( package
, filename
);
3636 ERR("Unable to find file id %s\n",debugstr_w(filename
));
3637 return ERROR_SUCCESS
;
3640 len
= strlenW(ExeStr
) + strlenW( file
->TargetPath
) + 2;
3642 FullName
= msi_alloc(len
*sizeof(WCHAR
));
3643 strcpyW(FullName
,ExeStr
);
3644 strcatW( FullName
, file
->TargetPath
);
3645 strcatW(FullName
,close
);
3647 TRACE("Registering %s\n",debugstr_w(FullName
));
3648 brc
= CreateProcessW(NULL
, FullName
, NULL
, NULL
, FALSE
, 0, NULL
, c_colon
,
3652 msi_dialog_check_messages(info
.hProcess
);
3657 uirow
= MSI_CreateRecord( 2 );
3658 uipath
= strdupW( file
->TargetPath
);
3659 p
= strrchrW(uipath
,'\\');
3662 MSI_RecordSetStringW( uirow
, 1, &p
[1] );
3663 MSI_RecordSetStringW( uirow
, 2, uipath
);
3664 ui_actiondata( package
, szSelfRegModules
, uirow
);
3665 msiobj_release( &uirow
->hdr
);
3667 /* FIXME: call ui_progress? */
3669 return ERROR_SUCCESS
;
3672 static UINT
ACTION_SelfRegModules(MSIPACKAGE
*package
)
3676 static const WCHAR ExecSeqQuery
[] =
3677 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3678 '`','S','e','l','f','R','e','g','`',0};
3680 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3681 if (rc
!= ERROR_SUCCESS
)
3683 TRACE("no SelfReg table\n");
3684 return ERROR_SUCCESS
;
3687 MSI_IterateRecords(view
, NULL
, ITERATE_SelfRegModules
, package
);
3688 msiobj_release(&view
->hdr
);
3690 return ERROR_SUCCESS
;
3693 static UINT
ACTION_PublishFeatures(MSIPACKAGE
*package
)
3695 MSIFEATURE
*feature
;
3701 if (!msi_check_publish(package
))
3702 return ERROR_SUCCESS
;
3704 rc
= MSIREG_OpenFeaturesKey(package
->ProductCode
,&hkey
,TRUE
);
3705 if (rc
!= ERROR_SUCCESS
)
3708 rc
= MSIREG_OpenUserFeaturesKey(package
->ProductCode
,&hukey
,TRUE
);
3709 if (rc
!= ERROR_SUCCESS
)
3712 rc
= MSIREG_OpenUserDataFeaturesKey(package
->ProductCode
, &userdata
, TRUE
);
3713 if (rc
!= ERROR_SUCCESS
)
3716 /* here the guids are base 85 encoded */
3717 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
3723 BOOL absent
= FALSE
;
3726 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
) &&
3727 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_SOURCE
) &&
3728 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ADVERTISED
))
3732 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3736 if (feature
->Feature_Parent
)
3737 size
+= strlenW( feature
->Feature_Parent
)+2;
3739 data
= msi_alloc(size
* sizeof(WCHAR
));
3742 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3744 MSICOMPONENT
* component
= cl
->component
;
3748 if (component
->ComponentId
)
3750 TRACE("From %s\n",debugstr_w(component
->ComponentId
));
3751 CLSIDFromString(component
->ComponentId
, &clsid
);
3752 encode_base85_guid(&clsid
,buf
);
3753 TRACE("to %s\n",debugstr_w(buf
));
3758 if (feature
->Feature_Parent
)
3760 static const WCHAR sep
[] = {'\2',0};
3762 strcatW(data
,feature
->Feature_Parent
);
3765 msi_reg_set_val_str( hkey
, feature
->Feature
, data
);
3766 msi_reg_set_val_str( userdata
, feature
->Feature
, data
);
3770 if (feature
->Feature_Parent
)
3771 size
= strlenW(feature
->Feature_Parent
)*sizeof(WCHAR
);
3774 RegSetValueExW(hukey
,feature
->Feature
,0,REG_SZ
,
3775 (LPBYTE
)feature
->Feature_Parent
,size
);
3779 size
+= 2*sizeof(WCHAR
);
3780 data
= msi_alloc(size
);
3783 if (feature
->Feature_Parent
)
3784 strcpyW( &data
[1], feature
->Feature_Parent
);
3785 RegSetValueExW(hukey
,feature
->Feature
,0,REG_SZ
,
3791 uirow
= MSI_CreateRecord( 1 );
3792 MSI_RecordSetStringW( uirow
, 1, feature
->Feature
);
3793 ui_actiondata( package
, szPublishFeatures
, uirow
);
3794 msiobj_release( &uirow
->hdr
);
3795 /* FIXME: call ui_progress? */
3804 static UINT
msi_unpublish_feature(MSIPACKAGE
*package
, MSIFEATURE
*feature
)
3809 TRACE("unpublishing feature %s\n", debugstr_w(feature
->Feature
));
3811 r
= MSIREG_OpenUserFeaturesKey(package
->ProductCode
, &hkey
, FALSE
);
3812 if (r
== ERROR_SUCCESS
)
3814 RegDeleteValueW(hkey
, feature
->Feature
);
3818 r
= MSIREG_OpenUserDataFeaturesKey(package
->ProductCode
, &hkey
, FALSE
);
3819 if (r
== ERROR_SUCCESS
)
3821 RegDeleteValueW(hkey
, feature
->Feature
);
3825 return ERROR_SUCCESS
;
3828 static BOOL
msi_check_unpublish(MSIPACKAGE
*package
)
3830 MSIFEATURE
*feature
;
3832 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3834 if (feature
->ActionRequest
!= INSTALLSTATE_ABSENT
)
3841 static UINT
ACTION_UnpublishFeatures(MSIPACKAGE
*package
)
3843 MSIFEATURE
*feature
;
3845 if (!msi_check_unpublish(package
))
3846 return ERROR_SUCCESS
;
3848 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3850 msi_unpublish_feature(package
, feature
);
3853 return ERROR_SUCCESS
;
3856 static UINT
msi_get_local_package_name( LPWSTR path
)
3858 static const WCHAR szInstaller
[] = {
3859 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3860 static const WCHAR fmt
[] = { '%','x','.','m','s','i',0};
3864 time
= GetTickCount();
3865 GetWindowsDirectoryW( path
, MAX_PATH
);
3866 lstrcatW( path
, szInstaller
);
3867 CreateDirectoryW( path
, NULL
);
3869 len
= lstrlenW(path
);
3870 for (i
=0; i
<0x10000; i
++)
3872 snprintfW( &path
[len
], MAX_PATH
- len
, fmt
, (time
+i
)&0xffff );
3873 handle
= CreateFileW( path
, GENERIC_WRITE
, 0, NULL
,
3874 CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, 0 );
3875 if (handle
!= INVALID_HANDLE_VALUE
)
3877 CloseHandle(handle
);
3880 if (GetLastError() != ERROR_FILE_EXISTS
&&
3881 GetLastError() != ERROR_SHARING_VIOLATION
)
3882 return ERROR_FUNCTION_FAILED
;
3885 return ERROR_SUCCESS
;
3888 static UINT
msi_make_package_local( MSIPACKAGE
*package
, HKEY hkey
)
3890 WCHAR packagefile
[MAX_PATH
];
3894 r
= msi_get_local_package_name( packagefile
);
3895 if (r
!= ERROR_SUCCESS
)
3898 TRACE("Copying to local package %s\n",debugstr_w(packagefile
));
3900 r
= CopyFileW( package
->db
->path
, packagefile
, FALSE
);
3904 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3905 debugstr_w(package
->db
->path
), debugstr_w(packagefile
), GetLastError());
3906 return ERROR_FUNCTION_FAILED
;
3909 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_LOCALPACKAGEW
, packagefile
);
3911 r
= MSIREG_OpenInstallPropertiesKey(package
->ProductCode
, &props
, TRUE
);
3912 if (r
!= ERROR_SUCCESS
)
3915 msi_reg_set_val_str(props
, INSTALLPROPERTY_LOCALPACKAGEW
, packagefile
);
3917 return ERROR_SUCCESS
;
3920 static UINT
msi_write_uninstall_property_vals( MSIPACKAGE
*package
, HKEY hkey
)
3922 LPWSTR prop
, val
, key
;
3923 static const LPCSTR propval
[] = {
3924 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3925 "ARPCONTACT", "Contact",
3926 "ARPCOMMENTS", "Comments",
3927 "ProductName", "DisplayName",
3928 "ProductVersion", "DisplayVersion",
3929 "ARPHELPLINK", "HelpLink",
3930 "ARPHELPTELEPHONE", "HelpTelephone",
3931 "ARPINSTALLLOCATION", "InstallLocation",
3932 "SourceDir", "InstallSource",
3933 "Manufacturer", "Publisher",
3934 "ARPREADME", "Readme",
3936 "ARPURLINFOABOUT", "URLInfoAbout",
3937 "ARPURLUPDATEINFO", "URLUpdateInfo",
3940 const LPCSTR
*p
= propval
;
3944 prop
= strdupAtoW( *p
++ );
3945 key
= strdupAtoW( *p
++ );
3946 val
= msi_dup_property( package
, prop
);
3947 msi_reg_set_val_str( hkey
, key
, val
);
3952 return ERROR_SUCCESS
;
3955 static UINT
ACTION_RegisterProduct(MSIPACKAGE
*package
)
3958 HKEY hudkey
=0, props
=0;
3959 LPWSTR buffer
= NULL
;
3962 static const WCHAR szWindowsInstaller
[] =
3963 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3964 static const WCHAR szUpgradeCode
[] =
3965 {'U','p','g','r','a','d','e','C','o','d','e',0};
3966 static const WCHAR modpath_fmt
[] =
3967 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3968 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3969 static const WCHAR szModifyPath
[] =
3970 {'M','o','d','i','f','y','P','a','t','h',0};
3971 static const WCHAR szUninstallString
[] =
3972 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3973 static const WCHAR szEstimatedSize
[] =
3974 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3975 static const WCHAR szProductLanguage
[] =
3976 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3977 static const WCHAR szProductVersion
[] =
3978 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3981 static const WCHAR date_fmt
[] = {'%','i','%','i','%','i',0};
3982 LPWSTR upgrade_code
;
3985 /* FIXME: also need to publish if the product is in advertise mode */
3986 if (!msi_check_publish(package
))
3987 return ERROR_SUCCESS
;
3989 rc
= MSIREG_OpenUninstallKey(package
->ProductCode
,&hkey
,TRUE
);
3990 if (rc
!= ERROR_SUCCESS
)
3993 /* dump all the info i can grab */
3994 /* FIXME: Flesh out more information */
3996 msi_write_uninstall_property_vals( package
, hkey
);
3998 msi_reg_set_val_dword( hkey
, szWindowsInstaller
, 1 );
4000 msi_make_package_local( package
, hkey
);
4002 /* do ModifyPath and UninstallString */
4003 size
= deformat_string(package
,modpath_fmt
,&buffer
);
4004 RegSetValueExW(hkey
,szModifyPath
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
4005 RegSetValueExW(hkey
,szUninstallString
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
4008 /* FIXME: Write real Estimated Size when we have it */
4009 msi_reg_set_val_dword( hkey
, szEstimatedSize
, 0 );
4011 GetLocalTime(&systime
);
4012 sprintfW(szDate
,date_fmt
,systime
.wYear
,systime
.wMonth
,systime
.wDay
);
4013 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_INSTALLDATEW
, szDate
);
4015 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
4016 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
4018 buffer
= msi_dup_property( package
, szProductVersion
);
4021 DWORD verdword
= msi_version_str_to_dword(buffer
);
4023 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
4024 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMAJORW
, verdword
>>24 );
4025 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMINORW
, (verdword
>>16)&0x00FF );
4029 /* Handle Upgrade Codes */
4030 upgrade_code
= msi_dup_property( package
, szUpgradeCode
);
4035 MSIREG_OpenUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
4036 squash_guid(package
->ProductCode
,squashed
);
4037 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
4039 MSIREG_OpenUserUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
4040 squash_guid(package
->ProductCode
,squashed
);
4041 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
4044 msi_free(upgrade_code
);
4049 rc
= MSIREG_OpenUserDataProductKey(package
->ProductCode
, &hudkey
, TRUE
);
4050 if (rc
!= ERROR_SUCCESS
)
4053 RegCloseKey(hudkey
);
4055 rc
= MSIREG_OpenInstallPropertiesKey(package
->ProductCode
, &props
, TRUE
);
4056 if (rc
!= ERROR_SUCCESS
)
4059 msi_reg_set_val_dword( props
, szWindowsInstaller
, 1 );
4062 return ERROR_SUCCESS
;
4065 static UINT
ACTION_InstallExecute(MSIPACKAGE
*package
)
4067 return execute_script(package
,INSTALL_SCRIPT
);
4070 static UINT
msi_unpublish_product(MSIPACKAGE
*package
)
4072 LPWSTR remove
= NULL
;
4073 LPWSTR
*features
= NULL
;
4074 BOOL full_uninstall
= TRUE
;
4075 MSIFEATURE
*feature
;
4077 static const WCHAR szRemove
[] = {'R','E','M','O','V','E',0};
4078 static const WCHAR szAll
[] = {'A','L','L',0};
4080 remove
= msi_dup_property(package
, szRemove
);
4082 return ERROR_SUCCESS
;
4084 features
= msi_split_string(remove
, ',');
4088 ERR("REMOVE feature list is empty!\n");
4089 return ERROR_FUNCTION_FAILED
;
4092 if (!lstrcmpW(features
[0], szAll
))
4093 full_uninstall
= TRUE
;
4096 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
4098 if (feature
->Action
!= INSTALLSTATE_ABSENT
)
4099 full_uninstall
= FALSE
;
4103 if (!full_uninstall
)
4106 MSIREG_DeleteProductKey(package
->ProductCode
);
4107 MSIREG_DeleteUserProductKey(package
->ProductCode
);
4108 MSIREG_DeleteUserDataProductKey(package
->ProductCode
);
4109 MSIREG_DeleteUserFeaturesKey(package
->ProductCode
);
4110 MSIREG_DeleteUninstallKey(package
->ProductCode
);
4115 return ERROR_SUCCESS
;
4118 static UINT
ACTION_InstallFinalize(MSIPACKAGE
*package
)
4122 rc
= msi_unpublish_product(package
);
4123 if (rc
!= ERROR_SUCCESS
)
4126 /* turn off scheduling */
4127 package
->script
->CurrentlyScripting
= FALSE
;
4129 /* first do the same as an InstallExecute */
4130 rc
= ACTION_InstallExecute(package
);
4131 if (rc
!= ERROR_SUCCESS
)
4134 /* then handle Commit Actions */
4135 rc
= execute_script(package
,COMMIT_SCRIPT
);
4140 UINT
ACTION_ForceReboot(MSIPACKAGE
*package
)
4142 static const WCHAR RunOnce
[] = {
4143 'S','o','f','t','w','a','r','e','\\',
4144 'M','i','c','r','o','s','o','f','t','\\',
4145 'W','i','n','d','o','w','s','\\',
4146 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4147 'R','u','n','O','n','c','e',0};
4148 static const WCHAR InstallRunOnce
[] = {
4149 'S','o','f','t','w','a','r','e','\\',
4150 'M','i','c','r','o','s','o','f','t','\\',
4151 'W','i','n','d','o','w','s','\\',
4152 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4153 'I','n','s','t','a','l','l','e','r','\\',
4154 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4156 static const WCHAR msiexec_fmt
[] = {
4158 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4159 '\"','%','s','\"',0};
4160 static const WCHAR install_fmt
[] = {
4161 '/','I',' ','\"','%','s','\"',' ',
4162 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4163 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4164 WCHAR buffer
[256], sysdir
[MAX_PATH
];
4166 WCHAR squished_pc
[100];
4168 squash_guid(package
->ProductCode
,squished_pc
);
4170 GetSystemDirectoryW(sysdir
, sizeof(sysdir
)/sizeof(sysdir
[0]));
4171 RegCreateKeyW(HKEY_LOCAL_MACHINE
,RunOnce
,&hkey
);
4172 snprintfW(buffer
,sizeof(buffer
)/sizeof(buffer
[0]),msiexec_fmt
,sysdir
,
4175 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
4178 TRACE("Reboot command %s\n",debugstr_w(buffer
));
4180 RegCreateKeyW(HKEY_LOCAL_MACHINE
,InstallRunOnce
,&hkey
);
4181 sprintfW(buffer
,install_fmt
,package
->ProductCode
,squished_pc
);
4183 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
4186 return ERROR_INSTALL_SUSPEND
;
4189 static UINT
ACTION_ResolveSource(MSIPACKAGE
* package
)
4195 * We are currently doing what should be done here in the top level Install
4196 * however for Administrative and uninstalls this step will be needed
4198 if (!package
->PackagePath
)
4199 return ERROR_SUCCESS
;
4201 msi_set_sourcedir_props(package
, TRUE
);
4203 attrib
= GetFileAttributesW(package
->db
->path
);
4204 if (attrib
== INVALID_FILE_ATTRIBUTES
)
4210 rc
= MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
4211 MSIINSTALLCONTEXT_USERMANAGED
, MSICODE_PRODUCT
,
4212 INSTALLPROPERTY_DISKPROMPTW
,NULL
,&size
);
4213 if (rc
== ERROR_MORE_DATA
)
4215 prompt
= msi_alloc(size
* sizeof(WCHAR
));
4216 MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
4217 MSIINSTALLCONTEXT_USERMANAGED
, MSICODE_PRODUCT
,
4218 INSTALLPROPERTY_DISKPROMPTW
,prompt
,&size
);
4221 prompt
= strdupW(package
->db
->path
);
4223 msg
= generate_error_string(package
,1302,1,prompt
);
4224 while(attrib
== INVALID_FILE_ATTRIBUTES
)
4226 rc
= MessageBoxW(NULL
,msg
,NULL
,MB_OKCANCEL
);
4229 rc
= ERROR_INSTALL_USEREXIT
;
4232 attrib
= GetFileAttributesW(package
->db
->path
);
4238 return ERROR_SUCCESS
;
4243 static UINT
ACTION_RegisterUser(MSIPACKAGE
*package
)
4250 static const WCHAR szPropKeys
[][80] =
4252 {'P','r','o','d','u','c','t','I','D',0},
4253 {'U','S','E','R','N','A','M','E',0},
4254 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4258 static const WCHAR szRegKeys
[][80] =
4260 {'P','r','o','d','u','c','t','I','D',0},
4261 {'R','e','g','O','w','n','e','r',0},
4262 {'R','e','g','C','o','m','p','a','n','y',0},
4266 productid
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTIDW
);
4268 return ERROR_SUCCESS
;
4270 rc
= MSIREG_OpenUninstallKey(package
->ProductCode
,&hkey
,TRUE
);
4271 if (rc
!= ERROR_SUCCESS
)
4274 for( i
= 0; szPropKeys
[i
][0]; i
++ )
4276 buffer
= msi_dup_property( package
, szPropKeys
[i
] );
4277 msi_reg_set_val_str( hkey
, szRegKeys
[i
], buffer
);
4282 msi_free(productid
);
4285 /* FIXME: call ui_actiondata */
4287 return ERROR_SUCCESS
;
4291 static UINT
ACTION_ExecuteAction(MSIPACKAGE
*package
)
4295 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
4296 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
4301 static UINT
ITERATE_PublishComponent(MSIRECORD
*rec
, LPVOID param
)
4303 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4304 LPCWSTR compgroupid
=NULL
;
4305 LPCWSTR feature
=NULL
;
4306 LPCWSTR text
= NULL
;
4307 LPCWSTR qualifier
= NULL
;
4308 LPCWSTR component
= NULL
;
4309 LPWSTR advertise
= NULL
;
4310 LPWSTR output
= NULL
;
4312 UINT rc
= ERROR_SUCCESS
;
4317 component
= MSI_RecordGetString(rec
,3);
4318 comp
= get_loaded_component(package
,component
);
4320 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
) &&
4321 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_SOURCE
) &&
4322 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ADVERTISED
))
4324 TRACE("Skipping: Component %s not scheduled for install\n",
4325 debugstr_w(component
));
4327 return ERROR_SUCCESS
;
4330 compgroupid
= MSI_RecordGetString(rec
,1);
4331 qualifier
= MSI_RecordGetString(rec
,2);
4333 rc
= MSIREG_OpenUserComponentsKey(compgroupid
, &hkey
, TRUE
);
4334 if (rc
!= ERROR_SUCCESS
)
4337 text
= MSI_RecordGetString(rec
,4);
4338 feature
= MSI_RecordGetString(rec
,5);
4340 advertise
= create_component_advertise_string(package
, comp
, feature
);
4342 sz
= strlenW(advertise
);
4345 sz
+= lstrlenW(text
);
4348 sz
*= sizeof(WCHAR
);
4350 output
= msi_alloc_zero(sz
);
4351 strcpyW(output
,advertise
);
4352 msi_free(advertise
);
4355 strcatW(output
,text
);
4357 msi_reg_set_val_multi_str( hkey
, qualifier
, output
);
4364 uirow
= MSI_CreateRecord( 2 );
4365 MSI_RecordSetStringW( uirow
, 1, compgroupid
);
4366 MSI_RecordSetStringW( uirow
, 2, qualifier
);
4367 ui_actiondata( package
, szPublishComponents
, uirow
);
4368 msiobj_release( &uirow
->hdr
);
4369 /* FIXME: call ui_progress? */
4375 * At present I am ignorning the advertised components part of this and only
4376 * focusing on the qualified component sets
4378 static UINT
ACTION_PublishComponents(MSIPACKAGE
*package
)
4382 static const WCHAR ExecSeqQuery
[] =
4383 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4384 '`','P','u','b','l','i','s','h',
4385 'C','o','m','p','o','n','e','n','t','`',0};
4387 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4388 if (rc
!= ERROR_SUCCESS
)
4389 return ERROR_SUCCESS
;
4391 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_PublishComponent
, package
);
4392 msiobj_release(&view
->hdr
);
4397 static UINT
ITERATE_InstallService(MSIRECORD
*rec
, LPVOID param
)
4399 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4402 SC_HANDLE hscm
, service
= NULL
;
4403 LPCWSTR name
, disp
, comp
, depends
, pass
;
4404 LPCWSTR load_order
, serv_name
, key
;
4405 DWORD serv_type
, start_type
;
4408 static const WCHAR query
[] =
4409 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4410 '`','C','o','m','p','o','n','e','n','t','`',' ',
4411 'W','H','E','R','E',' ',
4412 '`','C','o','m','p','o','n','e','n','t','`',' ',
4413 '=','\'','%','s','\'',0};
4415 hscm
= OpenSCManagerW(NULL
, SERVICES_ACTIVE_DATABASEW
, GENERIC_WRITE
);
4418 ERR("Failed to open the SC Manager!\n");
4422 start_type
= MSI_RecordGetInteger(rec
, 5);
4423 if (start_type
== SERVICE_BOOT_START
|| start_type
== SERVICE_SYSTEM_START
)
4426 depends
= MSI_RecordGetString(rec
, 8);
4427 if (depends
&& *depends
)
4428 FIXME("Dependency list unhandled!\n");
4430 name
= MSI_RecordGetString(rec
, 2);
4431 disp
= MSI_RecordGetString(rec
, 3);
4432 serv_type
= MSI_RecordGetInteger(rec
, 4);
4433 err_control
= MSI_RecordGetInteger(rec
, 6);
4434 load_order
= MSI_RecordGetString(rec
, 7);
4435 serv_name
= MSI_RecordGetString(rec
, 9);
4436 pass
= MSI_RecordGetString(rec
, 10);
4437 comp
= MSI_RecordGetString(rec
, 12);
4439 /* fetch the service path */
4440 row
= MSI_QueryGetRecord(package
->db
, query
, comp
);
4443 ERR("Control query failed!\n");
4447 key
= MSI_RecordGetString(row
, 6);
4449 file
= get_loaded_file(package
, key
);
4450 msiobj_release(&row
->hdr
);
4453 ERR("Failed to load the service file\n");
4457 service
= CreateServiceW(hscm
, name
, disp
, GENERIC_ALL
, serv_type
,
4458 start_type
, err_control
, file
->TargetPath
,
4459 load_order
, NULL
, NULL
, serv_name
, pass
);
4462 if (GetLastError() != ERROR_SERVICE_EXISTS
)
4463 ERR("Failed to create service %s: %d\n", debugstr_w(name
), GetLastError());
4467 CloseServiceHandle(service
);
4468 CloseServiceHandle(hscm
);
4470 return ERROR_SUCCESS
;
4473 static UINT
ACTION_InstallServices( MSIPACKAGE
*package
)
4477 static const WCHAR ExecSeqQuery
[] =
4478 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4479 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4481 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4482 if (rc
!= ERROR_SUCCESS
)
4483 return ERROR_SUCCESS
;
4485 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallService
, package
);
4486 msiobj_release(&view
->hdr
);
4491 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4492 static LPCWSTR
*msi_service_args_to_vector(LPWSTR args
, DWORD
*numargs
)
4494 LPCWSTR
*vector
, *temp_vector
;
4498 static const WCHAR separator
[] = {'[','~',']',0};
4501 sep_len
= sizeof(separator
) / sizeof(WCHAR
) - 1;
4506 vector
= msi_alloc(sizeof(LPWSTR
));
4514 vector
[*numargs
- 1] = p
;
4516 if ((q
= strstrW(p
, separator
)))
4520 temp_vector
= msi_realloc(vector
, (*numargs
+ 1) * sizeof(LPWSTR
));
4526 vector
= temp_vector
;
4535 static UINT
ITERATE_StartService(MSIRECORD
*rec
, LPVOID param
)
4537 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4539 SC_HANDLE scm
, service
= NULL
;
4540 LPCWSTR name
, *vector
= NULL
;
4542 DWORD event
, numargs
;
4543 UINT r
= ERROR_FUNCTION_FAILED
;
4545 comp
= get_loaded_component(package
, MSI_RecordGetString(rec
, 6));
4546 if (!comp
|| comp
->Action
== INSTALLSTATE_UNKNOWN
|| comp
->Action
== INSTALLSTATE_ABSENT
)
4547 return ERROR_SUCCESS
;
4549 name
= MSI_RecordGetString(rec
, 2);
4550 event
= MSI_RecordGetInteger(rec
, 3);
4551 args
= strdupW(MSI_RecordGetString(rec
, 4));
4553 if (!(event
& msidbServiceControlEventStart
))
4554 return ERROR_SUCCESS
;
4556 scm
= OpenSCManagerW(NULL
, NULL
, SC_MANAGER_CONNECT
);
4559 ERR("Failed to open the service control manager\n");
4563 service
= OpenServiceW(scm
, name
, SERVICE_START
);
4566 ERR("Failed to open service %s\n", debugstr_w(name
));
4570 vector
= msi_service_args_to_vector(args
, &numargs
);
4572 if (!StartServiceW(service
, numargs
, vector
))
4574 ERR("Failed to start service %s\n", debugstr_w(name
));
4581 CloseServiceHandle(service
);
4582 CloseServiceHandle(scm
);
4589 static UINT
ACTION_StartServices( MSIPACKAGE
*package
)
4594 static const WCHAR query
[] = {
4595 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4596 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4598 rc
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
4599 if (rc
!= ERROR_SUCCESS
)
4600 return ERROR_SUCCESS
;
4602 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_StartService
, package
);
4603 msiobj_release(&view
->hdr
);
4608 static MSIFILE
*msi_find_file( MSIPACKAGE
*package
, LPCWSTR filename
)
4612 LIST_FOR_EACH_ENTRY(file
, &package
->files
, MSIFILE
, entry
)
4614 if (!lstrcmpW(file
->File
, filename
))
4621 static UINT
ITERATE_InstallODBCDriver( MSIRECORD
*rec
, LPVOID param
)
4623 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4624 LPWSTR driver
, driver_path
, ptr
;
4625 WCHAR outpath
[MAX_PATH
];
4626 MSIFILE
*driver_file
, *setup_file
;
4629 UINT r
= ERROR_SUCCESS
;
4631 static const WCHAR driver_fmt
[] = {
4632 'D','r','i','v','e','r','=','%','s',0};
4633 static const WCHAR setup_fmt
[] = {
4634 'S','e','t','u','p','=','%','s',0};
4635 static const WCHAR usage_fmt
[] = {
4636 'F','i','l','e','U','s','a','g','e','=','1',0};
4638 desc
= MSI_RecordGetString(rec
, 3);
4640 driver_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 4));
4641 setup_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 5));
4643 if (!driver_file
|| !setup_file
)
4645 ERR("ODBC Driver entry not found!\n");
4646 return ERROR_FUNCTION_FAILED
;
4649 len
= lstrlenW(desc
) + lstrlenW(driver_fmt
) + lstrlenW(driver_file
->FileName
) +
4650 lstrlenW(setup_fmt
) + lstrlenW(setup_file
->FileName
) +
4651 lstrlenW(usage_fmt
) + 1;
4652 driver
= msi_alloc(len
* sizeof(WCHAR
));
4654 return ERROR_OUTOFMEMORY
;
4657 lstrcpyW(ptr
, desc
);
4658 ptr
+= lstrlenW(ptr
) + 1;
4660 sprintfW(ptr
, driver_fmt
, driver_file
->FileName
);
4661 ptr
+= lstrlenW(ptr
) + 1;
4663 sprintfW(ptr
, setup_fmt
, setup_file
->FileName
);
4664 ptr
+= lstrlenW(ptr
) + 1;
4666 lstrcpyW(ptr
, usage_fmt
);
4667 ptr
+= lstrlenW(ptr
) + 1;
4670 driver_path
= strdupW(driver_file
->TargetPath
);
4671 ptr
= strrchrW(driver_path
, '\\');
4672 if (ptr
) *ptr
= '\0';
4674 if (!SQLInstallDriverExW(driver
, driver_path
, outpath
, MAX_PATH
,
4675 NULL
, ODBC_INSTALL_COMPLETE
, &usage
))
4677 ERR("Failed to install SQL driver!\n");
4678 r
= ERROR_FUNCTION_FAILED
;
4682 msi_free(driver_path
);
4687 static UINT
ITERATE_InstallODBCTranslator( MSIRECORD
*rec
, LPVOID param
)
4689 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4690 LPWSTR translator
, translator_path
, ptr
;
4691 WCHAR outpath
[MAX_PATH
];
4692 MSIFILE
*translator_file
, *setup_file
;
4695 UINT r
= ERROR_SUCCESS
;
4697 static const WCHAR translator_fmt
[] = {
4698 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4699 static const WCHAR setup_fmt
[] = {
4700 'S','e','t','u','p','=','%','s',0};
4702 desc
= MSI_RecordGetString(rec
, 3);
4704 translator_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 4));
4705 setup_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 5));
4707 if (!translator_file
|| !setup_file
)
4709 ERR("ODBC Translator entry not found!\n");
4710 return ERROR_FUNCTION_FAILED
;
4713 len
= lstrlenW(desc
) + lstrlenW(translator_fmt
) + lstrlenW(translator_file
->FileName
) +
4714 lstrlenW(setup_fmt
) + lstrlenW(setup_file
->FileName
) + 1;
4715 translator
= msi_alloc(len
* sizeof(WCHAR
));
4717 return ERROR_OUTOFMEMORY
;
4720 lstrcpyW(ptr
, desc
);
4721 ptr
+= lstrlenW(ptr
) + 1;
4723 sprintfW(ptr
, translator_fmt
, translator_file
->FileName
);
4724 ptr
+= lstrlenW(ptr
) + 1;
4726 sprintfW(ptr
, setup_fmt
, setup_file
->FileName
);
4727 ptr
+= lstrlenW(ptr
) + 1;
4730 translator_path
= strdupW(translator_file
->TargetPath
);
4731 ptr
= strrchrW(translator_path
, '\\');
4732 if (ptr
) *ptr
= '\0';
4734 if (!SQLInstallTranslatorExW(translator
, translator_path
, outpath
, MAX_PATH
,
4735 NULL
, ODBC_INSTALL_COMPLETE
, &usage
))
4737 ERR("Failed to install SQL translator!\n");
4738 r
= ERROR_FUNCTION_FAILED
;
4741 msi_free(translator
);
4742 msi_free(translator_path
);
4747 static UINT
ITERATE_InstallODBCDataSource( MSIRECORD
*rec
, LPVOID param
)
4750 LPCWSTR desc
, driver
;
4751 WORD request
= ODBC_ADD_SYS_DSN
;
4754 UINT r
= ERROR_SUCCESS
;
4756 static const WCHAR attrs_fmt
[] = {
4757 'D','S','N','=','%','s',0 };
4759 desc
= MSI_RecordGetString(rec
, 3);
4760 driver
= MSI_RecordGetString(rec
, 4);
4761 registration
= MSI_RecordGetInteger(rec
, 5);
4763 if (registration
== msidbODBCDataSourceRegistrationPerMachine
) request
= ODBC_ADD_SYS_DSN
;
4764 else if (registration
== msidbODBCDataSourceRegistrationPerUser
) request
= ODBC_ADD_DSN
;
4766 len
= lstrlenW(attrs_fmt
) + lstrlenW(desc
) + 1 + 1;
4767 attrs
= msi_alloc(len
* sizeof(WCHAR
));
4769 return ERROR_OUTOFMEMORY
;
4771 sprintfW(attrs
, attrs_fmt
, desc
);
4772 attrs
[len
- 1] = '\0';
4774 if (!SQLConfigDataSourceW(NULL
, request
, driver
, attrs
))
4776 ERR("Failed to install SQL data source!\n");
4777 r
= ERROR_FUNCTION_FAILED
;
4785 static UINT
ACTION_InstallODBC( MSIPACKAGE
*package
)
4790 static const WCHAR driver_query
[] = {
4791 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4792 'O','D','B','C','D','r','i','v','e','r',0 };
4794 static const WCHAR translator_query
[] = {
4795 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4796 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4798 static const WCHAR source_query
[] = {
4799 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4800 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4802 rc
= MSI_DatabaseOpenViewW(package
->db
, driver_query
, &view
);
4803 if (rc
!= ERROR_SUCCESS
)
4804 return ERROR_SUCCESS
;
4806 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCDriver
, package
);
4807 msiobj_release(&view
->hdr
);
4809 rc
= MSI_DatabaseOpenViewW(package
->db
, translator_query
, &view
);
4810 if (rc
!= ERROR_SUCCESS
)
4811 return ERROR_SUCCESS
;
4813 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCTranslator
, package
);
4814 msiobj_release(&view
->hdr
);
4816 rc
= MSI_DatabaseOpenViewW(package
->db
, source_query
, &view
);
4817 if (rc
!= ERROR_SUCCESS
)
4818 return ERROR_SUCCESS
;
4820 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCDataSource
, package
);
4821 msiobj_release(&view
->hdr
);
4826 #define ENV_ACT_SETALWAYS 0x1
4827 #define ENV_ACT_SETABSENT 0x2
4828 #define ENV_ACT_REMOVE 0x4
4829 #define ENV_ACT_REMOVEMATCH 0x8
4831 #define ENV_MOD_MACHINE 0x20000000
4832 #define ENV_MOD_APPEND 0x40000000
4833 #define ENV_MOD_PREFIX 0x80000000
4834 #define ENV_MOD_MASK 0xC0000000
4836 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4838 static LONG
env_set_flags( LPCWSTR
*name
, LPCWSTR
*value
, DWORD
*flags
)
4840 LPCWSTR cptr
= *name
;
4841 LPCWSTR ptr
= *value
;
4843 static const WCHAR prefix
[] = {'[','~',']',0};
4844 static const int prefix_len
= 3;
4850 *flags
|= ENV_ACT_SETALWAYS
;
4851 else if (*cptr
== '+')
4852 *flags
|= ENV_ACT_SETABSENT
;
4853 else if (*cptr
== '-')
4854 *flags
|= ENV_ACT_REMOVE
;
4855 else if (*cptr
== '!')
4856 *flags
|= ENV_ACT_REMOVEMATCH
;
4857 else if (*cptr
== '*')
4858 *flags
|= ENV_MOD_MACHINE
;
4868 ERR("Missing environment variable\n");
4869 return ERROR_FUNCTION_FAILED
;
4872 if (!strncmpW(ptr
, prefix
, prefix_len
))
4874 *flags
|= ENV_MOD_APPEND
;
4875 *value
+= lstrlenW(prefix
);
4877 else if (lstrlenW(*value
) >= prefix_len
)
4879 ptr
+= lstrlenW(ptr
) - prefix_len
;
4880 if (!lstrcmpW(ptr
, prefix
))
4882 *flags
|= ENV_MOD_PREFIX
;
4883 /* the "[~]" will be removed by deformat_string */;
4888 check_flag_combo(*flags
, ENV_ACT_SETALWAYS
| ENV_ACT_SETABSENT
) ||
4889 check_flag_combo(*flags
, ENV_ACT_REMOVEMATCH
| ENV_ACT_SETABSENT
) ||
4890 check_flag_combo(*flags
, ENV_ACT_REMOVEMATCH
| ENV_ACT_SETALWAYS
) ||
4891 check_flag_combo(*flags
, ENV_ACT_SETABSENT
| ENV_MOD_MASK
))
4893 ERR("Invalid flags: %08x\n", *flags
);
4894 return ERROR_FUNCTION_FAILED
;
4897 return ERROR_SUCCESS
;
4900 static UINT
ITERATE_WriteEnvironmentString( MSIRECORD
*rec
, LPVOID param
)
4902 MSIPACKAGE
*package
= param
;
4903 LPCWSTR name
, value
, comp
;
4904 LPWSTR data
= NULL
, newval
= NULL
;
4905 LPWSTR deformatted
= NULL
, ptr
;
4906 DWORD flags
, type
, size
;
4908 HKEY env
= NULL
, root
;
4909 LPCWSTR environment
;
4911 static const WCHAR user_env
[] =
4912 {'E','n','v','i','r','o','n','m','e','n','t',0};
4913 static const WCHAR machine_env
[] =
4914 {'S','y','s','t','e','m','\\',
4915 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4916 'C','o','n','t','r','o','l','\\',
4917 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4918 'E','n','v','i','r','o','n','m','e','n','t',0};
4919 static const WCHAR semicolon
[] = {';',0};
4921 name
= MSI_RecordGetString(rec
, 2);
4922 value
= MSI_RecordGetString(rec
, 3);
4923 comp
= MSI_RecordGetString(rec
, 4);
4925 res
= env_set_flags(&name
, &value
, &flags
);
4926 if (res
!= ERROR_SUCCESS
)
4929 deformat_string(package
, value
, &deformatted
);
4932 res
= ERROR_OUTOFMEMORY
;
4936 value
= deformatted
;
4938 if (flags
& ENV_MOD_MACHINE
)
4940 environment
= machine_env
;
4941 root
= HKEY_LOCAL_MACHINE
;
4945 environment
= user_env
;
4946 root
= HKEY_CURRENT_USER
;
4949 res
= RegCreateKeyExW(root
, environment
, 0, NULL
, 0,
4950 KEY_ALL_ACCESS
, NULL
, &env
, NULL
);
4951 if (res
!= ERROR_SUCCESS
)
4954 if (flags
& ENV_ACT_REMOVE
)
4955 FIXME("Not removing environment variable on uninstall!\n");
4958 res
= RegQueryValueExW(env
, name
, NULL
, &type
, NULL
, &size
);
4959 if ((res
!= ERROR_SUCCESS
&& res
!= ERROR_FILE_NOT_FOUND
) ||
4960 (res
== ERROR_SUCCESS
&& type
!= REG_SZ
&& type
!= REG_EXPAND_SZ
))
4963 if (res
!= ERROR_FILE_NOT_FOUND
)
4965 if (flags
& ENV_ACT_SETABSENT
)
4967 res
= ERROR_SUCCESS
;
4971 data
= msi_alloc(size
);
4975 return ERROR_OUTOFMEMORY
;
4978 res
= RegQueryValueExW(env
, name
, NULL
, &type
, (LPVOID
)data
, &size
);
4979 if (res
!= ERROR_SUCCESS
)
4982 if (flags
& ENV_ACT_REMOVEMATCH
&& (!value
|| !lstrcmpW(data
, value
)))
4984 res
= RegDeleteKeyW(env
, name
);
4988 size
= (lstrlenW(value
) + 1 + size
) * sizeof(WCHAR
);
4989 newval
= msi_alloc(size
);
4993 res
= ERROR_OUTOFMEMORY
;
4997 if (!(flags
& ENV_MOD_MASK
))
4998 lstrcpyW(newval
, value
);
5001 if (flags
& ENV_MOD_PREFIX
)
5003 lstrcpyW(newval
, value
);
5004 lstrcatW(newval
, semicolon
);
5005 ptr
= newval
+ lstrlenW(value
) + 1;
5008 lstrcpyW(ptr
, data
);
5010 if (flags
& ENV_MOD_APPEND
)
5012 lstrcatW(newval
, semicolon
);
5013 lstrcatW(newval
, value
);
5019 size
= (lstrlenW(value
) + 1) * sizeof(WCHAR
);
5020 newval
= msi_alloc(size
);
5023 res
= ERROR_OUTOFMEMORY
;
5027 lstrcpyW(newval
, value
);
5030 TRACE("setting %s to %s\n", debugstr_w(name
), debugstr_w(newval
));
5031 res
= RegSetValueExW(env
, name
, 0, type
, (LPVOID
)newval
, size
);
5034 if (env
) RegCloseKey(env
);
5035 msi_free(deformatted
);
5041 static UINT
ACTION_WriteEnvironmentStrings( MSIPACKAGE
*package
)
5045 static const WCHAR ExecSeqQuery
[] =
5046 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5047 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5048 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
5049 if (rc
!= ERROR_SUCCESS
)
5050 return ERROR_SUCCESS
;
5052 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteEnvironmentString
, package
);
5053 msiobj_release(&view
->hdr
);
5058 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5069 static BOOL
msi_move_file(LPCWSTR source
, LPCWSTR dest
, int options
)
5073 if (GetFileAttributesW(source
) == FILE_ATTRIBUTE_DIRECTORY
||
5074 GetFileAttributesW(dest
) == FILE_ATTRIBUTE_DIRECTORY
)
5076 WARN("Source or dest is directory, not moving\n");
5080 if (options
== msidbMoveFileOptionsMove
)
5082 TRACE("moving %s -> %s\n", debugstr_w(source
), debugstr_w(dest
));
5083 ret
= MoveFileExW(source
, dest
, MOVEFILE_REPLACE_EXISTING
);
5086 WARN("MoveFile failed: %d\n", GetLastError());
5092 TRACE("copying %s -> %s\n", debugstr_w(source
), debugstr_w(dest
));
5093 ret
= CopyFileW(source
, dest
, FALSE
);
5096 WARN("CopyFile failed: %d\n", GetLastError());
5104 static LPWSTR
wildcard_to_file(LPWSTR wildcard
, LPWSTR filename
)
5107 DWORD dirlen
, pathlen
;
5109 ptr
= strrchrW(wildcard
, '\\');
5110 dirlen
= ptr
- wildcard
+ 1;
5112 pathlen
= dirlen
+ lstrlenW(filename
) + 1;
5113 path
= msi_alloc(pathlen
* sizeof(WCHAR
));
5115 lstrcpynW(path
, wildcard
, dirlen
+ 1);
5116 lstrcatW(path
, filename
);
5121 static void free_file_entry(FILE_LIST
*file
)
5123 msi_free(file
->source
);
5124 msi_free(file
->dest
);
5128 static void free_list(FILE_LIST
*list
)
5130 while (!list_empty(&list
->entry
))
5132 FILE_LIST
*file
= LIST_ENTRY(list_head(&list
->entry
), FILE_LIST
, entry
);
5134 list_remove(&file
->entry
);
5135 free_file_entry(file
);
5139 static BOOL
add_wildcard(FILE_LIST
*files
, LPWSTR source
, LPWSTR dest
)
5141 FILE_LIST
*new, *file
;
5142 LPWSTR ptr
, filename
;
5145 new = msi_alloc_zero(sizeof(FILE_LIST
));
5149 new->source
= strdupW(source
);
5150 ptr
= strrchrW(dest
, '\\') + 1;
5151 filename
= strrchrW(new->source
, '\\') + 1;
5153 new->sourcename
= filename
;
5156 new->destname
= ptr
;
5158 new->destname
= new->sourcename
;
5160 size
= (ptr
- dest
) + lstrlenW(filename
) + 1;
5161 new->dest
= msi_alloc(size
* sizeof(WCHAR
));
5164 free_file_entry(new);
5168 lstrcpynW(new->dest
, dest
, ptr
- dest
+ 1);
5169 lstrcatW(new->dest
, filename
);
5171 if (list_empty(&files
->entry
))
5173 list_add_head(&files
->entry
, &new->entry
);
5177 LIST_FOR_EACH_ENTRY(file
, &files
->entry
, FILE_LIST
, entry
)
5179 if (lstrcmpW(source
, file
->source
) < 0)
5181 list_add_before(&file
->entry
, &new->entry
);
5186 list_add_after(&file
->entry
, &new->entry
);
5190 BOOL
move_files_wildcard(LPWSTR source
, LPWSTR dest
, int options
)
5192 WIN32_FIND_DATAW wfd
;
5196 FILE_LIST files
, *file
;
5199 hfile
= FindFirstFileW(source
, &wfd
);
5200 if (hfile
== INVALID_HANDLE_VALUE
) return FALSE
;
5202 list_init(&files
.entry
);
5204 for (res
= TRUE
; res
; res
= FindNextFileW(hfile
, &wfd
))
5206 if (is_dot_dir(wfd
.cFileName
)) continue;
5208 path
= wildcard_to_file(source
, wfd
.cFileName
);
5215 add_wildcard(&files
, path
, dest
);
5219 /* only the first wildcard match gets renamed to dest */
5220 file
= LIST_ENTRY(list_head(&files
.entry
), FILE_LIST
, entry
);
5221 size
= (strrchrW(file
->dest
, '\\') - file
->dest
) + lstrlenW(file
->destname
) + 2;
5222 file
->dest
= msi_realloc(file
->dest
, size
* sizeof(WCHAR
));
5229 lstrcpyW(strrchrW(file
->dest
, '\\') + 1, file
->destname
);
5231 while (!list_empty(&files
.entry
))
5233 file
= LIST_ENTRY(list_head(&files
.entry
), FILE_LIST
, entry
);
5235 msi_move_file((LPCWSTR
)file
->source
, (LPCWSTR
)file
->dest
, options
);
5237 list_remove(&file
->entry
);
5238 free_file_entry(file
);
5249 static UINT
ITERATE_MoveFiles( MSIRECORD
*rec
, LPVOID param
)
5251 MSIPACKAGE
*package
= param
;
5253 LPCWSTR sourcename
, destname
;
5254 LPWSTR sourcedir
= NULL
, destdir
= NULL
;
5255 LPWSTR source
= NULL
, dest
= NULL
;
5258 BOOL ret
, wildcards
;
5260 static const WCHAR backslash
[] = {'\\',0};
5262 comp
= get_loaded_component(package
, MSI_RecordGetString(rec
, 2));
5263 if (!comp
|| !comp
->Enabled
||
5264 !(comp
->Action
& (INSTALLSTATE_LOCAL
| INSTALLSTATE_SOURCE
)))
5266 TRACE("Component not set for install, not moving file\n");
5267 return ERROR_SUCCESS
;
5270 sourcename
= MSI_RecordGetString(rec
, 3);
5271 destname
= MSI_RecordGetString(rec
, 4);
5272 options
= MSI_RecordGetInteger(rec
, 7);
5274 sourcedir
= msi_dup_property(package
, MSI_RecordGetString(rec
, 5));
5278 destdir
= msi_dup_property(package
, MSI_RecordGetString(rec
, 6));
5284 if (GetFileAttributesW(sourcedir
) == INVALID_FILE_ATTRIBUTES
)
5287 source
= strdupW(sourcedir
);
5293 size
= lstrlenW(sourcedir
) + lstrlenW(sourcename
) + 2;
5294 source
= msi_alloc(size
* sizeof(WCHAR
));
5298 lstrcpyW(source
, sourcedir
);
5299 if (source
[lstrlenW(source
) - 1] != '\\')
5300 lstrcatW(source
, backslash
);
5301 lstrcatW(source
, sourcename
);
5304 wildcards
= strchrW(source
, '*') || strchrW(source
, '?');
5306 if (!destname
&& !wildcards
)
5308 destname
= strdupW(sourcename
);
5315 size
= lstrlenW(destname
);
5317 size
+= lstrlenW(destdir
) + 2;
5318 dest
= msi_alloc(size
* sizeof(WCHAR
));
5322 lstrcpyW(dest
, destdir
);
5323 if (dest
[lstrlenW(dest
) - 1] != '\\')
5324 lstrcatW(dest
, backslash
);
5327 lstrcatW(dest
, destname
);
5329 if (GetFileAttributesW(destdir
) == INVALID_FILE_ATTRIBUTES
)
5331 ret
= CreateDirectoryW(destdir
, NULL
);
5334 WARN("CreateDirectory failed: %d\n", GetLastError());
5335 return ERROR_SUCCESS
;
5340 msi_move_file(source
, dest
, options
);
5342 move_files_wildcard(source
, dest
, options
);
5345 msi_free(sourcedir
);
5350 return ERROR_SUCCESS
;
5353 static UINT
ACTION_MoveFiles( MSIPACKAGE
*package
)
5358 static const WCHAR ExecSeqQuery
[] =
5359 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5360 '`','M','o','v','e','F','i','l','e','`',0};
5362 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
5363 if (rc
!= ERROR_SUCCESS
)
5364 return ERROR_SUCCESS
;
5366 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_MoveFiles
, package
);
5367 msiobj_release(&view
->hdr
);
5372 static UINT
msi_unimplemented_action_stub( MSIPACKAGE
*package
,
5373 LPCSTR action
, LPCWSTR table
)
5375 static const WCHAR query
[] = {
5376 'S','E','L','E','C','T',' ','*',' ',
5377 'F','R','O','M',' ','`','%','s','`',0 };
5378 MSIQUERY
*view
= NULL
;
5382 r
= MSI_OpenQuery( package
->db
, &view
, query
, table
);
5383 if (r
== ERROR_SUCCESS
)
5385 r
= MSI_IterateRecords(view
, &count
, NULL
, package
);
5386 msiobj_release(&view
->hdr
);
5390 FIXME("%s -> %u ignored %s table values\n",
5391 action
, count
, debugstr_w(table
));
5393 return ERROR_SUCCESS
;
5396 static UINT
ACTION_AllocateRegistrySpace( MSIPACKAGE
*package
)
5398 TRACE("%p\n", package
);
5399 return ERROR_SUCCESS
;
5402 static UINT
ACTION_RemoveIniValues( MSIPACKAGE
*package
)
5404 static const WCHAR table
[] =
5405 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5406 return msi_unimplemented_action_stub( package
, "RemoveIniValues", table
);
5409 static UINT
ACTION_PatchFiles( MSIPACKAGE
*package
)
5411 static const WCHAR table
[] = { 'P','a','t','c','h',0 };
5412 return msi_unimplemented_action_stub( package
, "PatchFiles", table
);
5415 static UINT
ACTION_BindImage( MSIPACKAGE
*package
)
5417 static const WCHAR table
[] = { 'B','i','n','d','I','m','a','g','e',0 };
5418 return msi_unimplemented_action_stub( package
, "BindImage", table
);
5421 static UINT
ACTION_IsolateComponents( MSIPACKAGE
*package
)
5423 static const WCHAR table
[] = {
5424 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5425 return msi_unimplemented_action_stub( package
, "IsolateComponents", table
);
5428 static UINT
ACTION_MigrateFeatureStates( MSIPACKAGE
*package
)
5430 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
5431 return msi_unimplemented_action_stub( package
, "MigrateFeatureStates", table
);
5434 static UINT
ACTION_SelfUnregModules( MSIPACKAGE
*package
)
5436 static const WCHAR table
[] = { 'S','e','l','f','R','e','g',0 };
5437 return msi_unimplemented_action_stub( package
, "SelfUnregModules", table
);
5440 static UINT
ACTION_StopServices( MSIPACKAGE
*package
)
5442 static const WCHAR table
[] = {
5443 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5444 return msi_unimplemented_action_stub( package
, "StopServices", table
);
5447 static UINT
ACTION_DeleteServices( MSIPACKAGE
*package
)
5449 static const WCHAR table
[] = {
5450 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5451 return msi_unimplemented_action_stub( package
, "DeleteServices", table
);
5453 static UINT
ACTION_ValidateProductID( MSIPACKAGE
*package
)
5455 static const WCHAR table
[] = {
5456 'P','r','o','d','u','c','t','I','D',0 };
5457 return msi_unimplemented_action_stub( package
, "ValidateProductID", table
);
5460 static UINT
ACTION_RemoveEnvironmentStrings( MSIPACKAGE
*package
)
5462 static const WCHAR table
[] = {
5463 'E','n','v','i','r','o','n','m','e','n','t',0 };
5464 return msi_unimplemented_action_stub( package
, "RemoveEnvironmentStrings", table
);
5467 static UINT
ACTION_MsiPublishAssemblies( MSIPACKAGE
*package
)
5469 static const WCHAR table
[] = {
5470 'M','s','i','A','s','s','e','m','b','l','y',0 };
5471 return msi_unimplemented_action_stub( package
, "MsiPublishAssemblies", table
);
5474 static UINT
ACTION_MsiUnpublishAssemblies( MSIPACKAGE
*package
)
5476 static const WCHAR table
[] = {
5477 'M','s','i','A','s','s','e','m','b','l','y',0 };
5478 return msi_unimplemented_action_stub( package
, "MsiUnpublishAssemblies", table
);
5481 static UINT
ACTION_UnregisterFonts( MSIPACKAGE
*package
)
5483 static const WCHAR table
[] = { 'F','o','n','t',0 };
5484 return msi_unimplemented_action_stub( package
, "UnregisterFonts", table
);
5487 static UINT
ACTION_RMCCPSearch( MSIPACKAGE
*package
)
5489 static const WCHAR table
[] = { 'C','C','P','S','e','a','r','c','h',0 };
5490 return msi_unimplemented_action_stub( package
, "RMCCPSearch", table
);
5493 static UINT
ACTION_RegisterComPlus( MSIPACKAGE
*package
)
5495 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
5496 return msi_unimplemented_action_stub( package
, "RegisterComPlus", table
);
5499 static UINT
ACTION_UnregisterComPlus( MSIPACKAGE
*package
)
5501 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
5502 return msi_unimplemented_action_stub( package
, "UnregisterComPlus", table
);
5505 static UINT
ACTION_InstallSFPCatalogFile( MSIPACKAGE
*package
)
5507 static const WCHAR table
[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5508 return msi_unimplemented_action_stub( package
, "InstallSFPCatalogFile", table
);
5511 static UINT
ACTION_RemoveDuplicateFiles( MSIPACKAGE
*package
)
5513 static const WCHAR table
[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5514 return msi_unimplemented_action_stub( package
, "RemoveDuplicateFiles", table
);
5517 static UINT
ACTION_RemoveExistingProducts( MSIPACKAGE
*package
)
5519 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
5520 return msi_unimplemented_action_stub( package
, "RemoveExistingProducts", table
);
5523 static UINT
ACTION_RemoveFolders( MSIPACKAGE
*package
)
5525 static const WCHAR table
[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5526 return msi_unimplemented_action_stub( package
, "RemoveFolders", table
);
5529 static UINT
ACTION_RemoveODBC( MSIPACKAGE
*package
)
5531 static const WCHAR table
[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5532 return msi_unimplemented_action_stub( package
, "RemoveODBC", table
);
5535 static UINT
ACTION_RemoveRegistryValues( MSIPACKAGE
*package
)
5537 static const WCHAR table
[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5538 return msi_unimplemented_action_stub( package
, "RemoveRegistryValues", table
);
5541 static UINT
ACTION_RemoveShortcuts( MSIPACKAGE
*package
)
5543 static const WCHAR table
[] = { 'S','h','o','r','t','c','u','t',0 };
5544 return msi_unimplemented_action_stub( package
, "RemoveShortcuts", table
);
5547 static UINT
ACTION_UnpublishComponents( MSIPACKAGE
*package
)
5549 static const WCHAR table
[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5550 return msi_unimplemented_action_stub( package
, "UnpublishComponents", table
);
5553 static UINT
ACTION_UnregisterClassInfo( MSIPACKAGE
*package
)
5555 static const WCHAR table
[] = { 'A','p','p','I','d',0 };
5556 return msi_unimplemented_action_stub( package
, "UnregisterClassInfo", table
);
5559 static UINT
ACTION_UnregisterExtensionInfo( MSIPACKAGE
*package
)
5561 static const WCHAR table
[] = { 'E','x','t','e','n','s','i','o','n',0 };
5562 return msi_unimplemented_action_stub( package
, "UnregisterExtensionInfo", table
);
5565 static UINT
ACTION_UnregisterMIMEInfo( MSIPACKAGE
*package
)
5567 static const WCHAR table
[] = { 'M','I','M','E',0 };
5568 return msi_unimplemented_action_stub( package
, "UnregisterMIMEInfo", table
);
5571 static UINT
ACTION_UnregisterProgIdInfo( MSIPACKAGE
*package
)
5573 static const WCHAR table
[] = { 'P','r','o','g','I','d',0 };
5574 return msi_unimplemented_action_stub( package
, "UnregisterProgIdInfo", table
);
5577 static UINT
ACTION_UnregisterTypeLibraries( MSIPACKAGE
*package
)
5579 static const WCHAR table
[] = { 'T','y','p','e','L','i','b',0 };
5580 return msi_unimplemented_action_stub( package
, "UnregisterTypeLibraries", table
);
5583 static const struct _actions StandardActions
[] = {
5584 { szAllocateRegistrySpace
, ACTION_AllocateRegistrySpace
},
5585 { szAppSearch
, ACTION_AppSearch
},
5586 { szBindImage
, ACTION_BindImage
},
5587 { szCCPSearch
, ACTION_CCPSearch
},
5588 { szCostFinalize
, ACTION_CostFinalize
},
5589 { szCostInitialize
, ACTION_CostInitialize
},
5590 { szCreateFolders
, ACTION_CreateFolders
},
5591 { szCreateShortcuts
, ACTION_CreateShortcuts
},
5592 { szDeleteServices
, ACTION_DeleteServices
},
5593 { szDisableRollback
, NULL
},
5594 { szDuplicateFiles
, ACTION_DuplicateFiles
},
5595 { szExecuteAction
, ACTION_ExecuteAction
},
5596 { szFileCost
, ACTION_FileCost
},
5597 { szFindRelatedProducts
, ACTION_FindRelatedProducts
},
5598 { szForceReboot
, ACTION_ForceReboot
},
5599 { szInstallAdminPackage
, NULL
},
5600 { szInstallExecute
, ACTION_InstallExecute
},
5601 { szInstallExecuteAgain
, ACTION_InstallExecute
},
5602 { szInstallFiles
, ACTION_InstallFiles
},
5603 { szInstallFinalize
, ACTION_InstallFinalize
},
5604 { szInstallInitialize
, ACTION_InstallInitialize
},
5605 { szInstallSFPCatalogFile
, ACTION_InstallSFPCatalogFile
},
5606 { szInstallValidate
, ACTION_InstallValidate
},
5607 { szIsolateComponents
, ACTION_IsolateComponents
},
5608 { szLaunchConditions
, ACTION_LaunchConditions
},
5609 { szMigrateFeatureStates
, ACTION_MigrateFeatureStates
},
5610 { szMoveFiles
, ACTION_MoveFiles
},
5611 { szMsiPublishAssemblies
, ACTION_MsiPublishAssemblies
},
5612 { szMsiUnpublishAssemblies
, ACTION_MsiUnpublishAssemblies
},
5613 { szInstallODBC
, ACTION_InstallODBC
},
5614 { szInstallServices
, ACTION_InstallServices
},
5615 { szPatchFiles
, ACTION_PatchFiles
},
5616 { szProcessComponents
, ACTION_ProcessComponents
},
5617 { szPublishComponents
, ACTION_PublishComponents
},
5618 { szPublishFeatures
, ACTION_PublishFeatures
},
5619 { szPublishProduct
, ACTION_PublishProduct
},
5620 { szRegisterClassInfo
, ACTION_RegisterClassInfo
},
5621 { szRegisterComPlus
, ACTION_RegisterComPlus
},
5622 { szRegisterExtensionInfo
, ACTION_RegisterExtensionInfo
},
5623 { szRegisterFonts
, ACTION_RegisterFonts
},
5624 { szRegisterMIMEInfo
, ACTION_RegisterMIMEInfo
},
5625 { szRegisterProduct
, ACTION_RegisterProduct
},
5626 { szRegisterProgIdInfo
, ACTION_RegisterProgIdInfo
},
5627 { szRegisterTypeLibraries
, ACTION_RegisterTypeLibraries
},
5628 { szRegisterUser
, ACTION_RegisterUser
},
5629 { szRemoveDuplicateFiles
, ACTION_RemoveDuplicateFiles
},
5630 { szRemoveEnvironmentStrings
, ACTION_RemoveEnvironmentStrings
},
5631 { szRemoveExistingProducts
, ACTION_RemoveExistingProducts
},
5632 { szRemoveFiles
, ACTION_RemoveFiles
},
5633 { szRemoveFolders
, ACTION_RemoveFolders
},
5634 { szRemoveIniValues
, ACTION_RemoveIniValues
},
5635 { szRemoveODBC
, ACTION_RemoveODBC
},
5636 { szRemoveRegistryValues
, ACTION_RemoveRegistryValues
},
5637 { szRemoveShortcuts
, ACTION_RemoveShortcuts
},
5638 { szResolveSource
, ACTION_ResolveSource
},
5639 { szRMCCPSearch
, ACTION_RMCCPSearch
},
5640 { szScheduleReboot
, NULL
},
5641 { szSelfRegModules
, ACTION_SelfRegModules
},
5642 { szSelfUnregModules
, ACTION_SelfUnregModules
},
5643 { szSetODBCFolders
, NULL
},
5644 { szStartServices
, ACTION_StartServices
},
5645 { szStopServices
, ACTION_StopServices
},
5646 { szUnpublishComponents
, ACTION_UnpublishComponents
},
5647 { szUnpublishFeatures
, ACTION_UnpublishFeatures
},
5648 { szUnregisterClassInfo
, ACTION_UnregisterClassInfo
},
5649 { szUnregisterComPlus
, ACTION_UnregisterComPlus
},
5650 { szUnregisterExtensionInfo
, ACTION_UnregisterExtensionInfo
},
5651 { szUnregisterFonts
, ACTION_UnregisterFonts
},
5652 { szUnregisterMIMEInfo
, ACTION_UnregisterMIMEInfo
},
5653 { szUnregisterProgIdInfo
, ACTION_UnregisterProgIdInfo
},
5654 { szUnregisterTypeLibraries
, ACTION_UnregisterTypeLibraries
},
5655 { szValidateProductID
, ACTION_ValidateProductID
},
5656 { szWriteEnvironmentStrings
, ACTION_WriteEnvironmentStrings
},
5657 { szWriteIniValues
, ACTION_WriteIniValues
},
5658 { szWriteRegistryValues
, ACTION_WriteRegistryValues
},