2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
26 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
39 #include "wine/debug.h"
44 #include "wine/unicode.h"
47 #define REG_PROGRESS_VALUE 13200
48 #define COMPONENT_PROGRESS_VALUE 24000
50 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
55 static UINT
ACTION_ProcessExecSequence(MSIPACKAGE
*package
, BOOL UIran
);
56 static UINT
ACTION_ProcessUISequence(MSIPACKAGE
*package
);
57 static UINT
ACTION_PerformActionSequence(MSIPACKAGE
*package
, UINT seq
, BOOL UI
);
60 * consts and values used
62 static const WCHAR c_colon
[] = {'C',':','\\',0};
64 static const WCHAR szCreateFolders
[] =
65 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
66 static const WCHAR szCostFinalize
[] =
67 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
68 const WCHAR szInstallFiles
[] =
69 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
70 const WCHAR szDuplicateFiles
[] =
71 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
72 static const WCHAR szWriteRegistryValues
[] =
73 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
74 'V','a','l','u','e','s',0};
75 static const WCHAR szCostInitialize
[] =
76 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
77 static const WCHAR szFileCost
[] =
78 {'F','i','l','e','C','o','s','t',0};
79 static const WCHAR szInstallInitialize
[] =
80 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
81 static const WCHAR szInstallValidate
[] =
82 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
83 static const WCHAR szLaunchConditions
[] =
84 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
85 static const WCHAR szProcessComponents
[] =
86 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
87 static const WCHAR szRegisterTypeLibraries
[] =
88 {'R','e','g','i','s','t','e','r','T','y','p','e',
89 'L','i','b','r','a','r','i','e','s',0};
90 const WCHAR szRegisterClassInfo
[] =
91 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
92 const WCHAR szRegisterProgIdInfo
[] =
93 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
94 static const WCHAR szCreateShortcuts
[] =
95 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
96 static const WCHAR szPublishProduct
[] =
97 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
98 static const WCHAR szWriteIniValues
[] =
99 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
100 static const WCHAR szSelfRegModules
[] =
101 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
102 static const WCHAR szPublishFeatures
[] =
103 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
104 static const WCHAR szRegisterProduct
[] =
105 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
106 static const WCHAR szInstallExecute
[] =
107 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
108 static const WCHAR szInstallExecuteAgain
[] =
109 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
110 'A','g','a','i','n',0};
111 static const WCHAR szInstallFinalize
[] =
112 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
113 static const WCHAR szForceReboot
[] =
114 {'F','o','r','c','e','R','e','b','o','o','t',0};
115 static const WCHAR szResolveSource
[] =
116 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
117 static const WCHAR szAppSearch
[] =
118 {'A','p','p','S','e','a','r','c','h',0};
119 static const WCHAR szAllocateRegistrySpace
[] =
120 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
121 'S','p','a','c','e',0};
122 static const WCHAR szBindImage
[] =
123 {'B','i','n','d','I','m','a','g','e',0};
124 static const WCHAR szCCPSearch
[] =
125 {'C','C','P','S','e','a','r','c','h',0};
126 static const WCHAR szDeleteServices
[] =
127 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
128 static const WCHAR szDisableRollback
[] =
129 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
130 static const WCHAR szExecuteAction
[] =
131 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
132 const WCHAR szFindRelatedProducts
[] =
133 {'F','i','n','d','R','e','l','a','t','e','d',
134 'P','r','o','d','u','c','t','s',0};
135 static const WCHAR szInstallAdminPackage
[] =
136 {'I','n','s','t','a','l','l','A','d','m','i','n',
137 'P','a','c','k','a','g','e',0};
138 static const WCHAR szInstallSFPCatalogFile
[] =
139 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
141 static const WCHAR szIsolateComponents
[] =
142 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
143 const WCHAR szMigrateFeatureStates
[] =
144 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
145 'S','t','a','t','e','s',0};
146 const WCHAR szMoveFiles
[] =
147 {'M','o','v','e','F','i','l','e','s',0};
148 static const WCHAR szMsiPublishAssemblies
[] =
149 {'M','s','i','P','u','b','l','i','s','h',
150 'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szMsiUnpublishAssemblies
[] =
152 {'M','s','i','U','n','p','u','b','l','i','s','h',
153 'A','s','s','e','m','b','l','i','e','s',0};
154 static const WCHAR szInstallODBC
[] =
155 {'I','n','s','t','a','l','l','O','D','B','C',0};
156 static const WCHAR szInstallServices
[] =
157 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
158 const WCHAR szPatchFiles
[] =
159 {'P','a','t','c','h','F','i','l','e','s',0};
160 static const WCHAR szPublishComponents
[] =
161 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
162 static const WCHAR szRegisterComPlus
[] =
163 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
164 const WCHAR szRegisterExtensionInfo
[] =
165 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
167 static const WCHAR szRegisterFonts
[] =
168 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
169 const WCHAR szRegisterMIMEInfo
[] =
170 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
171 static const WCHAR szRegisterUser
[] =
172 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
173 const WCHAR szRemoveDuplicateFiles
[] =
174 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
175 'F','i','l','e','s',0};
176 static const WCHAR szRemoveEnvironmentStrings
[] =
177 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
178 'S','t','r','i','n','g','s',0};
179 const WCHAR szRemoveExistingProducts
[] =
180 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
181 'P','r','o','d','u','c','t','s',0};
182 const WCHAR szRemoveFiles
[] =
183 {'R','e','m','o','v','e','F','i','l','e','s',0};
184 static const WCHAR szRemoveFolders
[] =
185 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
186 static const WCHAR szRemoveIniValues
[] =
187 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
188 static const WCHAR szRemoveODBC
[] =
189 {'R','e','m','o','v','e','O','D','B','C',0};
190 static const WCHAR szRemoveRegistryValues
[] =
191 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
192 'V','a','l','u','e','s',0};
193 static const WCHAR szRemoveShortcuts
[] =
194 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
195 static const WCHAR szRMCCPSearch
[] =
196 {'R','M','C','C','P','S','e','a','r','c','h',0};
197 static const WCHAR szScheduleReboot
[] =
198 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
199 static const WCHAR szSelfUnregModules
[] =
200 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
201 static const WCHAR szSetODBCFolders
[] =
202 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
203 static const WCHAR szStartServices
[] =
204 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
205 static const WCHAR szStopServices
[] =
206 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
207 static const WCHAR szUnpublishComponents
[] =
208 {'U','n','p','u','b','l','i','s','h',
209 'C','o','m','p','o','n','e','n','t','s',0};
210 static const WCHAR szUnpublishFeatures
[] =
211 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
212 const WCHAR szUnregisterClassInfo
[] =
213 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
215 static const WCHAR szUnregisterComPlus
[] =
216 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
217 const WCHAR szUnregisterExtensionInfo
[] =
218 {'U','n','r','e','g','i','s','t','e','r',
219 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
220 static const WCHAR szUnregisterFonts
[] =
221 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
222 const WCHAR szUnregisterMIMEInfo
[] =
223 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
224 const WCHAR szUnregisterProgIdInfo
[] =
225 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
227 static const WCHAR szUnregisterTypeLibraries
[] =
228 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
229 'L','i','b','r','a','r','i','e','s',0};
230 static const WCHAR szValidateProductID
[] =
231 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
232 static const WCHAR szWriteEnvironmentStrings
[] =
233 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
234 'S','t','r','i','n','g','s',0};
236 /* action handlers */
237 typedef UINT (*STANDARDACTIONHANDLER
)(MSIPACKAGE
*);
241 STANDARDACTIONHANDLER handler
;
244 static const struct _actions StandardActions
[];
247 /********************************************************
249 ********************************************************/
251 static void ui_actionstart(MSIPACKAGE
*package
, LPCWSTR action
)
253 static const WCHAR Query_t
[] =
254 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
255 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
256 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
257 ' ','\'','%','s','\'',0};
260 row
= MSI_QueryGetRecord( package
->db
, Query_t
, action
);
263 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONSTART
, row
);
264 msiobj_release(&row
->hdr
);
267 static void ui_actioninfo(MSIPACKAGE
*package
, LPCWSTR action
, BOOL start
,
271 static const WCHAR template_s
[]=
272 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
274 static const WCHAR template_e
[]=
275 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
276 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
278 static const WCHAR format
[] =
279 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
283 GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, NULL
, format
, timet
, 0x100);
285 sprintfW(message
,template_s
,timet
,action
);
287 sprintfW(message
,template_e
,timet
,action
,rc
);
289 row
= MSI_CreateRecord(1);
290 MSI_RecordSetStringW(row
,1,message
);
292 MSI_ProcessMessage(package
, INSTALLMESSAGE_INFO
, row
);
293 msiobj_release(&row
->hdr
);
296 UINT
msi_parse_command_line( MSIPACKAGE
*package
, LPCWSTR szCommandLine
)
301 LPWSTR prop
= NULL
, val
= NULL
;
304 return ERROR_SUCCESS
;
316 TRACE("Looking at %s\n",debugstr_w(ptr
));
318 ptr2
= strchrW(ptr
,'=');
321 ERR("command line contains unknown string : %s\n", debugstr_w(ptr
));
328 prop
= msi_alloc((len
+1)*sizeof(WCHAR
));
329 memcpy(prop
,ptr
,len
*sizeof(WCHAR
));
335 while (*ptr
&& (quote
|| (!quote
&& *ptr
!=' ')))
348 val
= msi_alloc((len
+1)*sizeof(WCHAR
));
349 memcpy(val
,ptr2
,len
*sizeof(WCHAR
));
352 if (lstrlenW(prop
) > 0)
354 TRACE("Found commandline property (%s) = (%s)\n",
355 debugstr_w(prop
), debugstr_w(val
));
356 MSI_SetPropertyW(package
,prop
,val
);
362 return ERROR_SUCCESS
;
366 static LPWSTR
* msi_split_string( LPCWSTR str
, WCHAR sep
)
369 LPWSTR p
, *ret
= NULL
;
375 /* count the number of substrings */
376 for ( pc
= str
, count
= 0; pc
; count
++ )
378 pc
= strchrW( pc
, sep
);
383 /* allocate space for an array of substring pointers and the substrings */
384 ret
= msi_alloc( (count
+1) * sizeof (LPWSTR
) +
385 (lstrlenW(str
)+1) * sizeof(WCHAR
) );
389 /* copy the string and set the pointers */
390 p
= (LPWSTR
) &ret
[count
+1];
392 for( count
= 0; (ret
[count
] = p
); count
++ )
394 p
= strchrW( p
, sep
);
402 static UINT
msi_check_transform_applicable( MSIPACKAGE
*package
, IStorage
*patch
)
404 WCHAR szProductCode
[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
405 LPWSTR prod_code
, patch_product
;
408 prod_code
= msi_dup_property( package
, szProductCode
);
409 patch_product
= msi_get_suminfo_product( patch
);
411 TRACE("db = %s patch = %s\n", debugstr_w(prod_code
), debugstr_w(patch_product
));
413 if ( strstrW( patch_product
, prod_code
) )
416 ret
= ERROR_FUNCTION_FAILED
;
418 msi_free( patch_product
);
419 msi_free( prod_code
);
424 static UINT
msi_apply_substorage_transform( MSIPACKAGE
*package
,
425 MSIDATABASE
*patch_db
, LPCWSTR name
)
427 UINT ret
= ERROR_FUNCTION_FAILED
;
428 IStorage
*stg
= NULL
;
431 TRACE("%p %s\n", package
, debugstr_w(name
) );
435 ERR("expected a colon in %s\n", debugstr_w(name
));
436 return ERROR_FUNCTION_FAILED
;
439 r
= IStorage_OpenStorage( patch_db
->storage
, name
, NULL
, STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
442 ret
= msi_check_transform_applicable( package
, stg
);
443 if (ret
== ERROR_SUCCESS
)
444 msi_table_apply_transform( package
->db
, stg
);
446 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name
));
447 IStorage_Release( stg
);
450 ERR("failed to open substorage %s\n", debugstr_w(name
));
452 return ERROR_SUCCESS
;
455 static UINT
msi_check_patch_applicable( MSIPACKAGE
*package
, MSISUMMARYINFO
*si
)
457 static const WCHAR szProdCode
[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
458 LPWSTR guid_list
, *guids
, product_code
;
459 UINT i
, ret
= ERROR_FUNCTION_FAILED
;
461 product_code
= msi_dup_property( package
, szProdCode
);
464 /* FIXME: the property ProductCode should be written into the DB somewhere */
465 ERR("no product code to check\n");
466 return ERROR_SUCCESS
;
469 guid_list
= msi_suminfo_dup_string( si
, PID_TEMPLATE
);
470 guids
= msi_split_string( guid_list
, ';' );
471 for ( i
= 0; guids
[i
] && ret
!= ERROR_SUCCESS
; i
++ )
473 if (!lstrcmpW( guids
[i
], product_code
))
477 msi_free( guid_list
);
478 msi_free( product_code
);
483 static UINT
msi_parse_patch_summary( MSIPACKAGE
*package
, MSIDATABASE
*patch_db
)
486 LPWSTR str
, *substorage
;
487 UINT i
, r
= ERROR_SUCCESS
;
489 si
= MSI_GetSummaryInformationW( patch_db
->storage
, 0 );
491 return ERROR_FUNCTION_FAILED
;
493 msi_check_patch_applicable( package
, si
);
495 /* enumerate the substorage */
496 str
= msi_suminfo_dup_string( si
, PID_LASTAUTHOR
);
497 substorage
= msi_split_string( str
, ';' );
498 for ( i
= 0; substorage
&& substorage
[i
] && r
== ERROR_SUCCESS
; i
++ )
499 r
= msi_apply_substorage_transform( package
, patch_db
, substorage
[i
] );
500 msi_free( substorage
);
503 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
505 msiobj_release( &si
->hdr
);
510 static UINT
msi_apply_patch_package( MSIPACKAGE
*package
, LPCWSTR file
)
512 MSIDATABASE
*patch_db
= NULL
;
515 TRACE("%p %s\n", package
, debugstr_w( file
) );
518 * We probably want to make sure we only open a patch collection here.
519 * Patch collections (.msp) and databases (.msi) have different GUIDs
520 * but currently MSI_OpenDatabaseW will accept both.
522 r
= MSI_OpenDatabaseW( file
, MSIDBOPEN_READONLY
, &patch_db
);
523 if ( r
!= ERROR_SUCCESS
)
525 ERR("failed to open patch collection %s\n", debugstr_w( file
) );
529 msi_parse_patch_summary( package
, patch_db
);
532 * There might be a CAB file in the patch package,
533 * so append it to the list of storage to search for streams.
535 append_storage_to_db( package
->db
, patch_db
->storage
);
537 msiobj_release( &patch_db
->hdr
);
539 return ERROR_SUCCESS
;
542 /* get the PATCH property, and apply all the patches it specifies */
543 static UINT
msi_apply_patches( MSIPACKAGE
*package
)
545 static const WCHAR szPatch
[] = { 'P','A','T','C','H',0 };
546 LPWSTR patch_list
, *patches
;
547 UINT i
, r
= ERROR_SUCCESS
;
549 patch_list
= msi_dup_property( package
, szPatch
);
551 TRACE("patches to be applied: %s\n", debugstr_w( patch_list
) );
553 patches
= msi_split_string( patch_list
, ';' );
554 for( i
=0; patches
&& patches
[i
] && r
== ERROR_SUCCESS
; i
++ )
555 r
= msi_apply_patch_package( package
, patches
[i
] );
558 msi_free( patch_list
);
563 static UINT
msi_apply_transforms( MSIPACKAGE
*package
)
565 static const WCHAR szTransforms
[] = {
566 'T','R','A','N','S','F','O','R','M','S',0 };
567 LPWSTR xform_list
, *xforms
;
568 UINT i
, r
= ERROR_SUCCESS
;
570 xform_list
= msi_dup_property( package
, szTransforms
);
571 xforms
= msi_split_string( xform_list
, ';' );
573 for( i
=0; xforms
&& xforms
[i
] && r
== ERROR_SUCCESS
; i
++ )
575 if (xforms
[i
][0] == ':')
576 r
= msi_apply_substorage_transform( package
, package
->db
, &xforms
[i
][1] );
578 r
= MSI_DatabaseApplyTransformW( package
->db
, xforms
[i
], 0 );
582 msi_free( xform_list
);
587 BOOL
ui_sequence_exists( MSIPACKAGE
*package
)
592 static const WCHAR ExecSeqQuery
[] =
593 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
594 '`','I','n','s','t','a','l','l',
595 'U','I','S','e','q','u','e','n','c','e','`',
596 ' ','W','H','E','R','E',' ',
597 '`','S','e','q','u','e','n','c','e','`',' ',
598 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
599 '`','S','e','q','u','e','n','c','e','`',0};
601 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
602 if (rc
== ERROR_SUCCESS
)
604 msiobj_release(&view
->hdr
);
611 /****************************************************
612 * TOP level entry points
613 *****************************************************/
615 UINT
MSI_InstallPackage( MSIPACKAGE
*package
, LPCWSTR szPackagePath
,
616 LPCWSTR szCommandLine
)
619 BOOL ui
= FALSE
, ui_exists
;
620 static const WCHAR szUILevel
[] = {'U','I','L','e','v','e','l',0};
621 static const WCHAR szAction
[] = {'A','C','T','I','O','N',0};
622 static const WCHAR szInstall
[] = {'I','N','S','T','A','L','L',0};
624 MSI_SetPropertyW(package
, szAction
, szInstall
);
626 package
->script
= msi_alloc_zero(sizeof(MSISCRIPT
));
628 package
->script
->InWhatSequence
= SEQUENCE_INSTALL
;
632 LPWSTR p
, check
, dir
;
635 dir
= strdupW(szPackagePath
);
636 p
= strrchrW(dir
, '\\');
640 file
= szPackagePath
+ (p
- dir
);
645 dir
= msi_alloc(MAX_PATH
*sizeof(WCHAR
));
646 GetCurrentDirectoryW(MAX_PATH
, dir
);
647 lstrcatW(dir
, cszbs
);
648 file
= szPackagePath
;
651 msi_free( package
->PackagePath
);
652 package
->PackagePath
= msi_alloc((lstrlenW(dir
) + lstrlenW(file
) + 1) * sizeof(WCHAR
));
653 if (!package
->PackagePath
)
656 return ERROR_OUTOFMEMORY
;
659 lstrcpyW(package
->PackagePath
, dir
);
660 lstrcatW(package
->PackagePath
, file
);
662 check
= msi_dup_property( package
, cszSourceDir
);
664 MSI_SetPropertyW(package
, cszSourceDir
, dir
);
667 check
= msi_dup_property( package
, cszSOURCEDIR
);
669 MSI_SetPropertyW(package
, cszSOURCEDIR
, dir
);
675 msi_parse_command_line( package
, szCommandLine
);
677 msi_apply_transforms( package
);
678 msi_apply_patches( package
);
680 /* properties may have been added by a transform */
681 msi_clone_properties( package
);
683 if ( (msi_get_property_int(package
, szUILevel
, 0) & INSTALLUILEVEL_MASK
) >= INSTALLUILEVEL_REDUCED
)
685 package
->script
->InWhatSequence
|= SEQUENCE_UI
;
686 rc
= ACTION_ProcessUISequence(package
);
688 ui_exists
= ui_sequence_exists(package
);
689 if (rc
== ERROR_SUCCESS
|| !ui_exists
)
691 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
692 rc
= ACTION_ProcessExecSequence(package
,ui_exists
);
696 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
700 /* install was halted but should be considered a success */
704 package
->script
->CurrentlyScripting
= FALSE
;
706 /* process the ending type action */
707 if (rc
== ERROR_SUCCESS
)
708 ACTION_PerformActionSequence(package
,-1,ui
);
709 else if (rc
== ERROR_INSTALL_USEREXIT
)
710 ACTION_PerformActionSequence(package
,-2,ui
);
711 else if (rc
== ERROR_INSTALL_SUSPEND
)
712 ACTION_PerformActionSequence(package
,-4,ui
);
714 ACTION_PerformActionSequence(package
,-3,ui
);
716 /* finish up running custom actions */
717 ACTION_FinishCustomActions(package
);
722 static UINT
ACTION_PerformActionSequence(MSIPACKAGE
*package
, UINT seq
, BOOL UI
)
724 UINT rc
= ERROR_SUCCESS
;
726 static const WCHAR ExecSeqQuery
[] =
727 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
728 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
729 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
730 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
732 static const WCHAR UISeqQuery
[] =
733 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
734 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
735 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
736 ' ', '=',' ','%','i',0};
739 row
= MSI_QueryGetRecord(package
->db
, UISeqQuery
, seq
);
741 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
, seq
);
745 LPCWSTR action
, cond
;
747 TRACE("Running the actions\n");
749 /* check conditions */
750 cond
= MSI_RecordGetString(row
,2);
752 /* this is a hack to skip errors in the condition code */
753 if (MSI_EvaluateConditionW(package
, cond
) == MSICONDITION_FALSE
)
756 action
= MSI_RecordGetString(row
,1);
759 ERR("failed to fetch action\n");
760 rc
= ERROR_FUNCTION_FAILED
;
765 rc
= ACTION_PerformUIAction(package
,action
,-1);
767 rc
= ACTION_PerformAction(package
,action
,-1,FALSE
);
769 msiobj_release(&row
->hdr
);
780 } iterate_action_param
;
782 static UINT
ITERATE_Actions(MSIRECORD
*row
, LPVOID param
)
784 iterate_action_param
*iap
= (iterate_action_param
*)param
;
786 LPCWSTR cond
, action
;
788 action
= MSI_RecordGetString(row
,1);
791 ERR("Error is retrieving action name\n");
792 return ERROR_FUNCTION_FAILED
;
795 /* check conditions */
796 cond
= MSI_RecordGetString(row
,2);
798 /* this is a hack to skip errors in the condition code */
799 if (MSI_EvaluateConditionW(iap
->package
, cond
) == MSICONDITION_FALSE
)
801 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action
));
802 return ERROR_SUCCESS
;
806 rc
= ACTION_PerformUIAction(iap
->package
,action
,-1);
808 rc
= ACTION_PerformAction(iap
->package
,action
,-1,FALSE
);
810 msi_dialog_check_messages( NULL
);
812 if (iap
->package
->CurrentInstallState
!= ERROR_SUCCESS
)
813 rc
= iap
->package
->CurrentInstallState
;
815 if (rc
== ERROR_FUNCTION_NOT_CALLED
)
818 if (rc
!= ERROR_SUCCESS
)
819 ERR("Execution halted, action %s returned %i\n", debugstr_w(action
), rc
);
824 UINT
MSI_Sequence( MSIPACKAGE
*package
, LPCWSTR szTable
, INT iSequenceMode
)
828 static const WCHAR query
[] =
829 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
831 ' ','W','H','E','R','E',' ',
832 '`','S','e','q','u','e','n','c','e','`',' ',
833 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
834 '`','S','e','q','u','e','n','c','e','`',0};
835 iterate_action_param iap
;
838 * FIXME: probably should be checking UILevel in the
839 * ACTION_PerformUIAction/ACTION_PerformAction
840 * rather than saving the UI level here. Those
841 * two functions can be merged too.
843 iap
.package
= package
;
846 TRACE("%p %s %i\n", package
, debugstr_w(szTable
), iSequenceMode
);
848 r
= MSI_OpenQuery( package
->db
, &view
, query
, szTable
);
849 if (r
== ERROR_SUCCESS
)
851 r
= MSI_IterateRecords( view
, NULL
, ITERATE_Actions
, &iap
);
852 msiobj_release(&view
->hdr
);
858 static UINT
ACTION_ProcessExecSequence(MSIPACKAGE
*package
, BOOL UIran
)
862 static const WCHAR ExecSeqQuery
[] =
863 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
864 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
865 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
866 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
867 'O','R','D','E','R',' ', 'B','Y',' ',
868 '`','S','e','q','u','e','n','c','e','`',0 };
870 static const WCHAR IVQuery
[] =
871 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
872 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
873 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
874 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
875 ' ','\'', 'I','n','s','t','a','l','l',
876 'V','a','l','i','d','a','t','e','\'', 0};
878 iterate_action_param iap
;
880 iap
.package
= package
;
883 if (package
->script
->ExecuteSequenceRun
)
885 TRACE("Execute Sequence already Run\n");
886 return ERROR_SUCCESS
;
889 package
->script
->ExecuteSequenceRun
= TRUE
;
891 /* get the sequence number */
894 row
= MSI_QueryGetRecord(package
->db
, IVQuery
);
896 return ERROR_FUNCTION_FAILED
;
897 seq
= MSI_RecordGetInteger(row
,1);
898 msiobj_release(&row
->hdr
);
901 rc
= MSI_OpenQuery(package
->db
, &view
, ExecSeqQuery
, seq
);
902 if (rc
== ERROR_SUCCESS
)
904 TRACE("Running the actions\n");
906 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, &iap
);
907 msiobj_release(&view
->hdr
);
913 static UINT
ACTION_ProcessUISequence(MSIPACKAGE
*package
)
917 static const WCHAR ExecSeqQuery
[] =
918 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
919 '`','I','n','s','t','a','l','l',
920 'U','I','S','e','q','u','e','n','c','e','`',
921 ' ','W','H','E','R','E',' ',
922 '`','S','e','q','u','e','n','c','e','`',' ',
923 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
924 '`','S','e','q','u','e','n','c','e','`',0};
925 iterate_action_param iap
;
927 iap
.package
= package
;
930 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
932 if (rc
== ERROR_SUCCESS
)
934 TRACE("Running the actions\n");
936 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, &iap
);
937 msiobj_release(&view
->hdr
);
943 /********************************************************
944 * ACTION helper functions and functions that perform the actions
945 *******************************************************/
946 static BOOL
ACTION_HandleStandardAction(MSIPACKAGE
*package
, LPCWSTR action
,
947 UINT
* rc
, BOOL force
)
953 if (!run
&& !package
->script
->CurrentlyScripting
)
958 if (strcmpW(action
,szInstallFinalize
) == 0 ||
959 strcmpW(action
,szInstallExecute
) == 0 ||
960 strcmpW(action
,szInstallExecuteAgain
) == 0)
965 while (StandardActions
[i
].action
!= NULL
)
967 if (strcmpW(StandardActions
[i
].action
, action
)==0)
971 ui_actioninfo(package
, action
, TRUE
, 0);
972 *rc
= schedule_action(package
,INSTALL_SCRIPT
,action
);
973 ui_actioninfo(package
, action
, FALSE
, *rc
);
977 ui_actionstart(package
, action
);
978 if (StandardActions
[i
].handler
)
980 *rc
= StandardActions
[i
].handler(package
);
984 FIXME("unhandled standard action %s\n",debugstr_w(action
));
996 static BOOL
ACTION_HandleCustomAction( MSIPACKAGE
* package
, LPCWSTR action
,
997 UINT
* rc
, UINT script
, BOOL force
)
1002 arc
= ACTION_CustomAction(package
, action
, script
, force
);
1004 if (arc
!= ERROR_CALL_NOT_IMPLEMENTED
)
1013 * A lot of actions are really important even if they don't do anything
1014 * explicit... Lots of properties are set at the beginning of the installation
1015 * CostFinalize does a bunch of work to translate the directories and such
1017 * But until I get write access to the database that is hard, so I am going to
1018 * hack it to see if I can get something to run.
1020 UINT
ACTION_PerformAction(MSIPACKAGE
*package
, const WCHAR
*action
, UINT script
, BOOL force
)
1022 UINT rc
= ERROR_SUCCESS
;
1025 TRACE("Performing action (%s)\n",debugstr_w(action
));
1027 handled
= ACTION_HandleStandardAction(package
, action
, &rc
, force
);
1030 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, script
, force
);
1034 FIXME("unhandled msi action %s\n",debugstr_w(action
));
1035 rc
= ERROR_FUNCTION_NOT_CALLED
;
1041 UINT
ACTION_PerformUIAction(MSIPACKAGE
*package
, const WCHAR
*action
, UINT script
)
1043 UINT rc
= ERROR_SUCCESS
;
1044 BOOL handled
= FALSE
;
1046 TRACE("Performing action (%s)\n",debugstr_w(action
));
1048 handled
= ACTION_HandleStandardAction(package
, action
, &rc
,TRUE
);
1051 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, script
, FALSE
);
1053 if( !handled
&& ACTION_DialogBox(package
,action
) == ERROR_SUCCESS
)
1058 FIXME("unhandled msi action %s\n",debugstr_w(action
));
1059 rc
= ERROR_FUNCTION_NOT_CALLED
;
1067 * Actual Action Handlers
1070 static UINT
ITERATE_CreateFolders(MSIRECORD
*row
, LPVOID param
)
1072 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1078 dir
= MSI_RecordGetString(row
,1);
1081 ERR("Unable to get folder id\n");
1082 return ERROR_SUCCESS
;
1085 full_path
= resolve_folder(package
,dir
,FALSE
,FALSE
,TRUE
,&folder
);
1088 ERR("Unable to resolve folder id %s\n",debugstr_w(dir
));
1089 return ERROR_SUCCESS
;
1092 TRACE("Folder is %s\n",debugstr_w(full_path
));
1095 uirow
= MSI_CreateRecord(1);
1096 MSI_RecordSetStringW(uirow
,1,full_path
);
1097 ui_actiondata(package
,szCreateFolders
,uirow
);
1098 msiobj_release( &uirow
->hdr
);
1100 if (folder
->State
== 0)
1101 create_full_pathW(full_path
);
1105 msi_free(full_path
);
1106 return ERROR_SUCCESS
;
1109 /* FIXME: probably should merge this with the above function */
1110 static UINT
msi_create_directory( MSIPACKAGE
* package
, LPCWSTR dir
)
1112 UINT rc
= ERROR_SUCCESS
;
1114 LPWSTR install_path
;
1116 install_path
= resolve_folder(package
, dir
, FALSE
, FALSE
, TRUE
, &folder
);
1118 return ERROR_FUNCTION_FAILED
;
1120 /* create the path */
1121 if (folder
->State
== 0)
1123 create_full_pathW(install_path
);
1126 msi_free(install_path
);
1131 UINT
msi_create_component_directories( MSIPACKAGE
*package
)
1135 /* create all the folders required by the components are going to install */
1136 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1138 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
1140 msi_create_directory( package
, comp
->Directory
);
1143 return ERROR_SUCCESS
;
1147 * Also we cannot enable/disable components either, so for now I am just going
1148 * to do all the directories for all the components.
1150 static UINT
ACTION_CreateFolders(MSIPACKAGE
*package
)
1152 static const WCHAR ExecSeqQuery
[] =
1153 {'S','E','L','E','C','T',' ',
1154 '`','D','i','r','e','c','t','o','r','y','_','`',
1155 ' ','F','R','O','M',' ',
1156 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1160 /* create all the empty folders specified in the CreateFolder table */
1161 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
1162 if (rc
!= ERROR_SUCCESS
)
1163 return ERROR_SUCCESS
;
1165 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateFolders
, package
);
1166 msiobj_release(&view
->hdr
);
1168 msi_create_component_directories( package
);
1173 static UINT
load_component( MSIRECORD
*row
, LPVOID param
)
1175 MSIPACKAGE
*package
= param
;
1178 comp
= msi_alloc_zero( sizeof(MSICOMPONENT
) );
1180 return ERROR_FUNCTION_FAILED
;
1182 list_add_tail( &package
->components
, &comp
->entry
);
1184 /* fill in the data */
1185 comp
->Component
= msi_dup_record_field( row
, 1 );
1187 TRACE("Loading Component %s\n", debugstr_w(comp
->Component
));
1189 comp
->ComponentId
= msi_dup_record_field( row
, 2 );
1190 comp
->Directory
= msi_dup_record_field( row
, 3 );
1191 comp
->Attributes
= MSI_RecordGetInteger(row
,4);
1192 comp
->Condition
= msi_dup_record_field( row
, 5 );
1193 comp
->KeyPath
= msi_dup_record_field( row
, 6 );
1195 comp
->Installed
= INSTALLSTATE_UNKNOWN
;
1196 msi_component_set_state( comp
, INSTALLSTATE_UNKNOWN
);
1198 return ERROR_SUCCESS
;
1201 static UINT
load_all_components( MSIPACKAGE
*package
)
1203 static const WCHAR query
[] = {
1204 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1205 '`','C','o','m','p','o','n','e','n','t','`',0 };
1209 if (!list_empty(&package
->components
))
1210 return ERROR_SUCCESS
;
1212 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1213 if (r
!= ERROR_SUCCESS
)
1216 r
= MSI_IterateRecords(view
, NULL
, load_component
, package
);
1217 msiobj_release(&view
->hdr
);
1222 MSIPACKAGE
*package
;
1223 MSIFEATURE
*feature
;
1226 static UINT
add_feature_component( MSIFEATURE
*feature
, MSICOMPONENT
*comp
)
1230 cl
= msi_alloc( sizeof (*cl
) );
1232 return ERROR_NOT_ENOUGH_MEMORY
;
1233 cl
->component
= comp
;
1234 list_add_tail( &feature
->Components
, &cl
->entry
);
1236 return ERROR_SUCCESS
;
1239 static UINT
add_feature_child( MSIFEATURE
*parent
, MSIFEATURE
*child
)
1243 fl
= msi_alloc( sizeof(*fl
) );
1245 return ERROR_NOT_ENOUGH_MEMORY
;
1246 fl
->feature
= child
;
1247 list_add_tail( &parent
->Children
, &fl
->entry
);
1249 return ERROR_SUCCESS
;
1252 static UINT
iterate_load_featurecomponents(MSIRECORD
*row
, LPVOID param
)
1254 _ilfs
* ilfs
= (_ilfs
*)param
;
1258 component
= MSI_RecordGetString(row
,1);
1260 /* check to see if the component is already loaded */
1261 comp
= get_loaded_component( ilfs
->package
, component
);
1264 ERR("unknown component %s\n", debugstr_w(component
));
1265 return ERROR_FUNCTION_FAILED
;
1268 add_feature_component( ilfs
->feature
, comp
);
1269 comp
->Enabled
= TRUE
;
1271 return ERROR_SUCCESS
;
1274 static MSIFEATURE
*find_feature_by_name( MSIPACKAGE
*package
, LPCWSTR name
)
1276 MSIFEATURE
*feature
;
1278 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1280 if ( !lstrcmpW( feature
->Feature
, name
) )
1287 static UINT
load_feature(MSIRECORD
* row
, LPVOID param
)
1289 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1290 MSIFEATURE
* feature
;
1291 static const WCHAR Query1
[] =
1292 {'S','E','L','E','C','T',' ',
1293 '`','C','o','m','p','o','n','e','n','t','_','`',
1294 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1295 'C','o','m','p','o','n','e','n','t','s','`',' ',
1296 'W','H','E','R','E',' ',
1297 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1302 /* fill in the data */
1304 feature
= msi_alloc_zero( sizeof (MSIFEATURE
) );
1306 return ERROR_NOT_ENOUGH_MEMORY
;
1308 list_init( &feature
->Children
);
1309 list_init( &feature
->Components
);
1311 feature
->Feature
= msi_dup_record_field( row
, 1 );
1313 TRACE("Loading feature %s\n",debugstr_w(feature
->Feature
));
1315 feature
->Feature_Parent
= msi_dup_record_field( row
, 2 );
1316 feature
->Title
= msi_dup_record_field( row
, 3 );
1317 feature
->Description
= msi_dup_record_field( row
, 4 );
1319 if (!MSI_RecordIsNull(row
,5))
1320 feature
->Display
= MSI_RecordGetInteger(row
,5);
1322 feature
->Level
= MSI_RecordGetInteger(row
,6);
1323 feature
->Directory
= msi_dup_record_field( row
, 7 );
1324 feature
->Attributes
= MSI_RecordGetInteger(row
,8);
1326 feature
->Installed
= INSTALLSTATE_UNKNOWN
;
1327 msi_feature_set_state( feature
, INSTALLSTATE_UNKNOWN
);
1329 list_add_tail( &package
->features
, &feature
->entry
);
1331 /* load feature components */
1333 rc
= MSI_OpenQuery( package
->db
, &view
, Query1
, feature
->Feature
);
1334 if (rc
!= ERROR_SUCCESS
)
1335 return ERROR_SUCCESS
;
1337 ilfs
.package
= package
;
1338 ilfs
.feature
= feature
;
1340 MSI_IterateRecords(view
, NULL
, iterate_load_featurecomponents
, &ilfs
);
1341 msiobj_release(&view
->hdr
);
1343 return ERROR_SUCCESS
;
1346 static UINT
find_feature_children(MSIRECORD
* row
, LPVOID param
)
1348 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1349 MSIFEATURE
*parent
, *child
;
1351 child
= find_feature_by_name( package
, MSI_RecordGetString( row
, 1 ) );
1353 return ERROR_FUNCTION_FAILED
;
1355 if (!child
->Feature_Parent
)
1356 return ERROR_SUCCESS
;
1358 parent
= find_feature_by_name( package
, child
->Feature_Parent
);
1360 return ERROR_FUNCTION_FAILED
;
1362 add_feature_child( parent
, child
);
1363 return ERROR_SUCCESS
;
1366 static UINT
load_all_features( MSIPACKAGE
*package
)
1368 static const WCHAR query
[] = {
1369 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1370 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1371 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1375 if (!list_empty(&package
->features
))
1376 return ERROR_SUCCESS
;
1378 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1379 if (r
!= ERROR_SUCCESS
)
1382 r
= MSI_IterateRecords( view
, NULL
, load_feature
, package
);
1383 if (r
!= ERROR_SUCCESS
)
1386 r
= MSI_IterateRecords( view
, NULL
, find_feature_children
, package
);
1387 msiobj_release( &view
->hdr
);
1392 static LPWSTR
folder_split_path(LPWSTR p
, WCHAR ch
)
1403 static UINT
load_file(MSIRECORD
*row
, LPVOID param
)
1405 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1409 /* fill in the data */
1411 file
= msi_alloc_zero( sizeof (MSIFILE
) );
1413 return ERROR_NOT_ENOUGH_MEMORY
;
1415 file
->File
= msi_dup_record_field( row
, 1 );
1417 component
= MSI_RecordGetString( row
, 2 );
1418 file
->Component
= get_loaded_component( package
, component
);
1420 if (!file
->Component
)
1421 ERR("Unfound Component %s\n",debugstr_w(component
));
1423 file
->FileName
= msi_dup_record_field( row
, 3 );
1424 reduce_to_longfilename( file
->FileName
);
1426 file
->ShortName
= msi_dup_record_field( row
, 3 );
1427 file
->LongName
= strdupW( folder_split_path(file
->ShortName
, '|'));
1429 file
->FileSize
= MSI_RecordGetInteger( row
, 4 );
1430 file
->Version
= msi_dup_record_field( row
, 5 );
1431 file
->Language
= msi_dup_record_field( row
, 6 );
1432 file
->Attributes
= MSI_RecordGetInteger( row
, 7 );
1433 file
->Sequence
= MSI_RecordGetInteger( row
, 8 );
1435 file
->state
= msifs_invalid
;
1437 /* if the compressed bits are not set in the file attributes,
1438 * then read the information from the package word count property
1440 if (file
->Attributes
& msidbFileAttributesCompressed
)
1442 file
->IsCompressed
= TRUE
;
1444 else if (file
->Attributes
& msidbFileAttributesNoncompressed
)
1446 file
->IsCompressed
= FALSE
;
1450 file
->IsCompressed
= package
->WordCount
& MSIWORDCOUNT_COMPRESSED
;
1453 TRACE("File Loaded (%s)\n",debugstr_w(file
->File
));
1455 list_add_tail( &package
->files
, &file
->entry
);
1457 return ERROR_SUCCESS
;
1460 static UINT
load_all_files(MSIPACKAGE
*package
)
1464 static const WCHAR Query
[] =
1465 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1466 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1467 '`','S','e','q','u','e','n','c','e','`', 0};
1469 if (!list_empty(&package
->files
))
1470 return ERROR_SUCCESS
;
1472 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
1473 if (rc
!= ERROR_SUCCESS
)
1474 return ERROR_SUCCESS
;
1476 rc
= MSI_IterateRecords(view
, NULL
, load_file
, package
);
1477 msiobj_release(&view
->hdr
);
1479 return ERROR_SUCCESS
;
1482 static UINT
load_folder( MSIRECORD
*row
, LPVOID param
)
1484 MSIPACKAGE
*package
= param
;
1485 static const WCHAR szDot
[] = { '.',0 };
1486 static WCHAR szEmpty
[] = { 0 };
1487 LPWSTR p
, tgt_short
, tgt_long
, src_short
, src_long
;
1490 folder
= msi_alloc_zero( sizeof (MSIFOLDER
) );
1492 return ERROR_NOT_ENOUGH_MEMORY
;
1494 folder
->Directory
= msi_dup_record_field( row
, 1 );
1496 TRACE("%s\n", debugstr_w(folder
->Directory
));
1498 p
= msi_dup_record_field(row
, 3);
1500 /* split src and target dir */
1502 src_short
= folder_split_path( p
, ':' );
1504 /* split the long and short paths */
1505 tgt_long
= folder_split_path( tgt_short
, '|' );
1506 src_long
= folder_split_path( src_short
, '|' );
1508 /* check for no-op dirs */
1509 if (!lstrcmpW(szDot
, tgt_short
))
1510 tgt_short
= szEmpty
;
1511 if (!lstrcmpW(szDot
, src_short
))
1512 src_short
= szEmpty
;
1515 tgt_long
= tgt_short
;
1518 src_short
= tgt_short
;
1519 src_long
= tgt_long
;
1523 src_long
= src_short
;
1525 /* FIXME: use the target short path too */
1526 folder
->TargetDefault
= strdupW(tgt_long
);
1527 folder
->SourceShortPath
= strdupW(src_short
);
1528 folder
->SourceLongPath
= strdupW(src_long
);
1531 TRACE("TargetDefault = %s\n",debugstr_w( folder
->TargetDefault
));
1532 TRACE("SourceLong = %s\n", debugstr_w( folder
->SourceLongPath
));
1533 TRACE("SourceShort = %s\n", debugstr_w( folder
->SourceShortPath
));
1535 folder
->Parent
= msi_dup_record_field( row
, 2 );
1537 folder
->Property
= msi_dup_property( package
, folder
->Directory
);
1539 list_add_tail( &package
->folders
, &folder
->entry
);
1541 TRACE("returning %p\n", folder
);
1543 return ERROR_SUCCESS
;
1546 static UINT
load_all_folders( MSIPACKAGE
*package
)
1548 static const WCHAR query
[] = {
1549 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1550 '`','D','i','r','e','c','t','o','r','y','`',0 };
1554 if (!list_empty(&package
->folders
))
1555 return ERROR_SUCCESS
;
1557 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1558 if (r
!= ERROR_SUCCESS
)
1561 r
= MSI_IterateRecords(view
, NULL
, load_folder
, package
);
1562 msiobj_release(&view
->hdr
);
1567 * I am not doing any of the costing functionality yet.
1568 * Mostly looking at doing the Component and Feature loading
1570 * The native MSI does A LOT of modification to tables here. Mostly adding
1571 * a lot of temporary columns to the Feature and Component tables.
1573 * note: Native msi also tracks the short filename. But I am only going to
1574 * track the long ones. Also looking at this directory table
1575 * it appears that the directory table does not get the parents
1576 * resolved base on property only based on their entries in the
1579 static UINT
ACTION_CostInitialize(MSIPACKAGE
*package
)
1581 static const WCHAR szCosting
[] =
1582 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1583 static const WCHAR szZero
[] = { '0', 0 };
1585 MSI_SetPropertyW(package
, szCosting
, szZero
);
1586 MSI_SetPropertyW(package
, cszRootDrive
, c_colon
);
1588 load_all_components( package
);
1589 load_all_features( package
);
1590 load_all_files( package
);
1591 load_all_folders( package
);
1593 return ERROR_SUCCESS
;
1596 static UINT
execute_script(MSIPACKAGE
*package
, UINT script
)
1599 UINT rc
= ERROR_SUCCESS
;
1601 TRACE("Executing Script %i\n",script
);
1603 if (!package
->script
)
1605 ERR("no script!\n");
1606 return ERROR_FUNCTION_FAILED
;
1609 for (i
= 0; i
< package
->script
->ActionCount
[script
]; i
++)
1612 action
= package
->script
->Actions
[script
][i
];
1613 ui_actionstart(package
, action
);
1614 TRACE("Executing Action (%s)\n",debugstr_w(action
));
1615 rc
= ACTION_PerformAction(package
, action
, script
, TRUE
);
1616 if (rc
!= ERROR_SUCCESS
)
1619 msi_free_action_script(package
, script
);
1623 static UINT
ACTION_FileCost(MSIPACKAGE
*package
)
1625 return ERROR_SUCCESS
;
1628 static void ACTION_GetComponentInstallStates(MSIPACKAGE
*package
)
1632 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1636 if (!comp
->ComponentId
)
1639 res
= MsiGetComponentPathW( package
->ProductCode
,
1640 comp
->ComponentId
, NULL
, NULL
);
1642 res
= INSTALLSTATE_ABSENT
;
1643 comp
->Installed
= res
;
1647 /* scan for and update current install states */
1648 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE
*package
)
1651 MSIFEATURE
*feature
;
1653 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1656 INSTALLSTATE res
= INSTALLSTATE_ABSENT
;
1658 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1660 comp
= cl
->component
;
1662 if (!comp
->ComponentId
)
1664 res
= INSTALLSTATE_ABSENT
;
1668 if (res
== INSTALLSTATE_ABSENT
)
1669 res
= comp
->Installed
;
1672 if (res
== comp
->Installed
)
1675 if (res
!= INSTALLSTATE_DEFAULT
&& res
!= INSTALLSTATE_LOCAL
&&
1676 res
!= INSTALLSTATE_SOURCE
)
1678 res
= INSTALLSTATE_INCOMPLETE
;
1682 feature
->Installed
= res
;
1686 static BOOL
process_state_property (MSIPACKAGE
* package
, LPCWSTR property
,
1689 static const WCHAR all
[]={'A','L','L',0};
1691 MSIFEATURE
*feature
;
1693 override
= msi_dup_property( package
, property
);
1697 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1699 if (strcmpiW(override
,all
)==0)
1700 msi_feature_set_state( feature
, state
);
1703 LPWSTR ptr
= override
;
1704 LPWSTR ptr2
= strchrW(override
,',');
1708 if ((ptr2
&& strncmpW(ptr
,feature
->Feature
, ptr2
-ptr
)==0)
1709 || (!ptr2
&& strcmpW(ptr
,feature
->Feature
)==0))
1711 msi_feature_set_state( feature
, state
);
1717 ptr2
= strchrW(ptr
,',');
1729 UINT
MSI_SetFeatureStates(MSIPACKAGE
*package
)
1732 static const WCHAR szlevel
[] =
1733 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1734 static const WCHAR szAddLocal
[] =
1735 {'A','D','D','L','O','C','A','L',0};
1736 static const WCHAR szRemove
[] =
1737 {'R','E','M','O','V','E',0};
1738 static const WCHAR szReinstall
[] =
1739 {'R','E','I','N','S','T','A','L','L',0};
1740 BOOL override
= FALSE
;
1741 MSICOMPONENT
* component
;
1742 MSIFEATURE
*feature
;
1745 /* I do not know if this is where it should happen.. but */
1747 TRACE("Checking Install Level\n");
1749 install_level
= msi_get_property_int( package
, szlevel
, 1 );
1751 /* ok here is the _real_ rub
1752 * all these activation/deactivation things happen in order and things
1753 * later on the list override things earlier on the list.
1754 * 1) INSTALLLEVEL processing
1764 * 11) FILEADDDEFAULT
1765 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1766 * ignored for all the features. seems strange, especially since it is not
1767 * documented anywhere, but it is how it works.
1769 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1770 * REMOVE are the big ones, since we don't handle administrative installs
1773 override
|= process_state_property(package
,szAddLocal
,INSTALLSTATE_LOCAL
);
1774 override
|= process_state_property(package
,szRemove
,INSTALLSTATE_ABSENT
);
1775 override
|= process_state_property(package
,szReinstall
,INSTALLSTATE_LOCAL
);
1779 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1781 BOOL feature_state
= ((feature
->Level
> 0) &&
1782 (feature
->Level
<= install_level
));
1784 if ((feature_state
) && (feature
->Action
== INSTALLSTATE_UNKNOWN
))
1786 if (feature
->Attributes
& msidbFeatureAttributesFavorSource
)
1787 msi_feature_set_state( feature
, INSTALLSTATE_SOURCE
);
1788 else if (feature
->Attributes
& msidbFeatureAttributesFavorAdvertise
)
1789 msi_feature_set_state( feature
, INSTALLSTATE_ADVERTISED
);
1791 msi_feature_set_state( feature
, INSTALLSTATE_LOCAL
);
1795 /* disable child features of unselected parent features */
1796 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1800 if (feature
->Level
> 0 && feature
->Level
<= install_level
)
1803 LIST_FOR_EACH_ENTRY( fl
, &feature
->Children
, FeatureList
, entry
)
1804 msi_feature_set_state( fl
->feature
, INSTALLSTATE_UNKNOWN
);
1809 /* set the Preselected Property */
1810 static const WCHAR szPreselected
[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1811 static const WCHAR szOne
[] = { '1', 0 };
1813 MSI_SetPropertyW(package
,szPreselected
,szOne
);
1817 * now we want to enable or disable components base on feature
1820 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1824 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1825 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
);
1827 /* features with components that have compressed files are made local */
1828 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1830 if (cl
->component
->Enabled
&&
1831 cl
->component
->ForceLocalState
&&
1832 feature
->Action
== INSTALLSTATE_SOURCE
)
1834 msi_feature_set_state( feature
, INSTALLSTATE_LOCAL
);
1839 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1841 component
= cl
->component
;
1843 if (!component
->Enabled
)
1846 switch (feature
->Action
)
1848 case INSTALLSTATE_ABSENT
:
1849 component
->anyAbsent
= 1;
1851 case INSTALLSTATE_ADVERTISED
:
1852 component
->hasAdvertiseFeature
= 1;
1854 case INSTALLSTATE_SOURCE
:
1855 component
->hasSourceFeature
= 1;
1857 case INSTALLSTATE_LOCAL
:
1858 component
->hasLocalFeature
= 1;
1860 case INSTALLSTATE_DEFAULT
:
1861 if (feature
->Attributes
& msidbFeatureAttributesFavorAdvertise
)
1862 component
->hasAdvertiseFeature
= 1;
1863 else if (feature
->Attributes
& msidbFeatureAttributesFavorSource
)
1864 component
->hasSourceFeature
= 1;
1866 component
->hasLocalFeature
= 1;
1874 LIST_FOR_EACH_ENTRY( component
, &package
->components
, MSICOMPONENT
, entry
)
1876 /* if the component isn't enabled, leave it alone */
1877 if (!component
->Enabled
)
1880 /* check if it's local or source */
1881 if (!(component
->Attributes
& msidbComponentAttributesOptional
) &&
1882 (component
->hasLocalFeature
|| component
->hasSourceFeature
))
1884 if ((component
->Attributes
& msidbComponentAttributesSourceOnly
) &&
1885 !component
->ForceLocalState
)
1886 msi_component_set_state( component
, INSTALLSTATE_SOURCE
);
1888 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1892 /* if any feature is local, the component must be local too */
1893 if (component
->hasLocalFeature
)
1895 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1899 if (component
->hasSourceFeature
)
1901 msi_component_set_state( component
, INSTALLSTATE_SOURCE
);
1905 if (component
->hasAdvertiseFeature
)
1907 msi_component_set_state( component
, INSTALLSTATE_ADVERTISED
);
1911 TRACE("nobody wants component %s\n", debugstr_w(component
->Component
));
1912 if (component
->anyAbsent
)
1913 msi_component_set_state(component
, INSTALLSTATE_ABSENT
);
1916 LIST_FOR_EACH_ENTRY( component
, &package
->components
, MSICOMPONENT
, entry
)
1918 if (component
->Action
== INSTALLSTATE_DEFAULT
)
1920 TRACE("%s was default, setting to local\n", debugstr_w(component
->Component
));
1921 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1924 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1925 debugstr_w(component
->Component
), component
->Installed
, component
->Action
);
1929 return ERROR_SUCCESS
;
1932 static UINT
ITERATE_CostFinalizeDirectories(MSIRECORD
*row
, LPVOID param
)
1934 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1939 name
= MSI_RecordGetString(row
,1);
1941 f
= get_loaded_folder(package
, name
);
1942 if (!f
) return ERROR_SUCCESS
;
1944 /* reset the ResolvedTarget */
1945 msi_free(f
->ResolvedTarget
);
1946 f
->ResolvedTarget
= NULL
;
1948 /* This helper function now does ALL the work */
1949 TRACE("Dir %s ...\n",debugstr_w(name
));
1950 path
= resolve_folder(package
,name
,FALSE
,TRUE
,TRUE
,NULL
);
1951 TRACE("resolves to %s\n",debugstr_w(path
));
1954 return ERROR_SUCCESS
;
1957 static UINT
ITERATE_CostFinalizeConditions(MSIRECORD
*row
, LPVOID param
)
1959 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1961 MSIFEATURE
*feature
;
1963 name
= MSI_RecordGetString( row
, 1 );
1965 feature
= get_loaded_feature( package
, name
);
1967 ERR("FAILED to find loaded feature %s\n",debugstr_w(name
));
1971 Condition
= MSI_RecordGetString(row
,3);
1973 if (MSI_EvaluateConditionW(package
,Condition
) == MSICONDITION_TRUE
)
1975 int level
= MSI_RecordGetInteger(row
,2);
1976 TRACE("Reseting feature %s to level %i\n", debugstr_w(name
), level
);
1977 feature
->Level
= level
;
1980 return ERROR_SUCCESS
;
1983 static LPWSTR
msi_get_disk_file_version( LPCWSTR filename
)
1985 static const WCHAR name_fmt
[] =
1986 {'%','u','.','%','u','.','%','u','.','%','u',0};
1987 static WCHAR name
[] = {'\\',0};
1988 VS_FIXEDFILEINFO
*lpVer
;
1989 WCHAR filever
[0x100];
1995 TRACE("%s\n", debugstr_w(filename
));
1997 versize
= GetFileVersionInfoSizeW( filename
, &handle
);
2001 version
= msi_alloc( versize
);
2002 GetFileVersionInfoW( filename
, 0, versize
, version
);
2004 if (!VerQueryValueW( version
, name
, (LPVOID
*)&lpVer
, &sz
))
2006 msi_free( version
);
2010 sprintfW( filever
, name_fmt
,
2011 HIWORD(lpVer
->dwFileVersionMS
),
2012 LOWORD(lpVer
->dwFileVersionMS
),
2013 HIWORD(lpVer
->dwFileVersionLS
),
2014 LOWORD(lpVer
->dwFileVersionLS
));
2016 msi_free( version
);
2018 return strdupW( filever
);
2021 static UINT
msi_check_file_install_states( MSIPACKAGE
*package
)
2023 LPWSTR file_version
;
2026 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2028 MSICOMPONENT
* comp
= file
->Component
;
2034 if (file
->IsCompressed
)
2035 comp
->ForceLocalState
= TRUE
;
2037 /* calculate target */
2038 p
= resolve_folder(package
, comp
->Directory
, FALSE
, FALSE
, TRUE
, NULL
);
2040 msi_free(file
->TargetPath
);
2042 TRACE("file %s is named %s\n",
2043 debugstr_w(file
->File
), debugstr_w(file
->FileName
));
2045 file
->TargetPath
= build_directory_name(2, p
, file
->FileName
);
2049 TRACE("file %s resolves to %s\n",
2050 debugstr_w(file
->File
), debugstr_w(file
->TargetPath
));
2052 /* don't check files of components that aren't installed */
2053 if (comp
->Installed
== INSTALLSTATE_UNKNOWN
||
2054 comp
->Installed
== INSTALLSTATE_ABSENT
)
2056 file
->state
= msifs_missing
; /* assume files are missing */
2060 if (GetFileAttributesW(file
->TargetPath
) == INVALID_FILE_ATTRIBUTES
)
2062 file
->state
= msifs_missing
;
2063 comp
->Cost
+= file
->FileSize
;
2064 comp
->Installed
= INSTALLSTATE_INCOMPLETE
;
2068 if (file
->Version
&&
2069 (file_version
= msi_get_disk_file_version( file
->TargetPath
)))
2071 TRACE("new %s old %s\n", debugstr_w(file
->Version
),
2072 debugstr_w(file_version
));
2073 /* FIXME: seems like a bad way to compare version numbers */
2074 if (lstrcmpiW(file_version
, file
->Version
)<0)
2076 file
->state
= msifs_overwrite
;
2077 comp
->Cost
+= file
->FileSize
;
2078 comp
->Installed
= INSTALLSTATE_INCOMPLETE
;
2081 file
->state
= msifs_present
;
2082 msi_free( file_version
);
2085 file
->state
= msifs_present
;
2088 return ERROR_SUCCESS
;
2092 * A lot is done in this function aside from just the costing.
2093 * The costing needs to be implemented at some point but for now I am going
2094 * to focus on the directory building
2097 static UINT
ACTION_CostFinalize(MSIPACKAGE
*package
)
2099 static const WCHAR ExecSeqQuery
[] =
2100 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2101 '`','D','i','r','e','c','t','o','r','y','`',0};
2102 static const WCHAR ConditionQuery
[] =
2103 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2104 '`','C','o','n','d','i','t','i','o','n','`',0};
2105 static const WCHAR szCosting
[] =
2106 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2107 static const WCHAR szlevel
[] =
2108 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2109 static const WCHAR szOne
[] = { '1', 0 };
2115 if ( 1 == msi_get_property_int( package
, szCosting
, 0 ) )
2116 return ERROR_SUCCESS
;
2118 TRACE("Building Directory properties\n");
2120 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2121 if (rc
== ERROR_SUCCESS
)
2123 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CostFinalizeDirectories
,
2125 msiobj_release(&view
->hdr
);
2128 /* read components states from the registry */
2129 ACTION_GetComponentInstallStates(package
);
2131 TRACE("File calculations\n");
2132 msi_check_file_install_states( package
);
2134 TRACE("Evaluating Condition Table\n");
2136 rc
= MSI_DatabaseOpenViewW(package
->db
, ConditionQuery
, &view
);
2137 if (rc
== ERROR_SUCCESS
)
2139 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CostFinalizeConditions
,
2141 msiobj_release(&view
->hdr
);
2144 TRACE("Enabling or Disabling Components\n");
2145 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2147 if (MSI_EvaluateConditionW(package
, comp
->Condition
) == MSICONDITION_FALSE
)
2149 TRACE("Disabling component %s\n", debugstr_w(comp
->Component
));
2150 comp
->Enabled
= FALSE
;
2154 MSI_SetPropertyW(package
,szCosting
,szOne
);
2155 /* set default run level if not set */
2156 level
= msi_dup_property( package
, szlevel
);
2158 MSI_SetPropertyW(package
,szlevel
, szOne
);
2161 ACTION_UpdateFeatureInstallStates(package
);
2163 return MSI_SetFeatureStates(package
);
2166 /* OK this value is "interpreted" and then formatted based on the
2167 first few characters */
2168 static LPSTR
parse_value(MSIPACKAGE
*package
, LPCWSTR value
, DWORD
*type
,
2172 if (value
[0]=='#' && value
[1]!='#' && value
[1]!='%')
2178 LPWSTR deformated
= NULL
;
2181 deformat_string(package
, &value
[2], &deformated
);
2183 /* binary value type */
2187 *size
= (strlenW(ptr
)/2)+1;
2189 *size
= strlenW(ptr
)/2;
2191 data
= msi_alloc(*size
);
2197 /* if uneven pad with a zero in front */
2203 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2205 TRACE("Uneven byte count\n");
2213 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2216 msi_free(deformated
);
2218 TRACE("Data %i bytes(%i)\n",*size
,count
);
2225 deformat_string(package
, &value
[1], &deformated
);
2228 *size
= sizeof(DWORD
);
2229 data
= msi_alloc(*size
);
2235 if ( (*p
< '0') || (*p
> '9') )
2241 if (deformated
[0] == '-')
2244 TRACE("DWORD %i\n",*(LPDWORD
)data
);
2246 msi_free(deformated
);
2251 static const WCHAR szMulti
[] = {'[','~',']',0};
2260 *type
=REG_EXPAND_SZ
;
2268 if (strstrW(value
,szMulti
))
2269 *type
= REG_MULTI_SZ
;
2271 *size
= deformat_string(package
, ptr
,(LPWSTR
*)&data
);
2276 static UINT
ITERATE_WriteRegistryValues(MSIRECORD
*row
, LPVOID param
)
2278 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
2279 static const WCHAR szHCR
[] =
2280 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2281 'R','O','O','T','\\',0};
2282 static const WCHAR szHCU
[] =
2283 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2284 'U','S','E','R','\\',0};
2285 static const WCHAR szHLM
[] =
2286 {'H','K','E','Y','_','L','O','C','A','L','_',
2287 'M','A','C','H','I','N','E','\\',0};
2288 static const WCHAR szHU
[] =
2289 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2291 LPSTR value_data
= NULL
;
2292 HKEY root_key
, hkey
;
2295 LPCWSTR szRoot
, component
, name
, key
, value
;
2300 BOOL check_first
= FALSE
;
2303 ui_progress(package
,2,0,0,0);
2310 component
= MSI_RecordGetString(row
, 6);
2311 comp
= get_loaded_component(package
,component
);
2313 return ERROR_SUCCESS
;
2315 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2317 TRACE("Skipping write due to disabled component %s\n",
2318 debugstr_w(component
));
2320 comp
->Action
= comp
->Installed
;
2322 return ERROR_SUCCESS
;
2325 comp
->Action
= INSTALLSTATE_LOCAL
;
2327 name
= MSI_RecordGetString(row
, 4);
2328 if( MSI_RecordIsNull(row
,5) && name
)
2330 /* null values can have special meanings */
2331 if (name
[0]=='-' && name
[1] == 0)
2332 return ERROR_SUCCESS
;
2333 else if ((name
[0]=='+' && name
[1] == 0) ||
2334 (name
[0] == '*' && name
[1] == 0))
2339 root
= MSI_RecordGetInteger(row
,2);
2340 key
= MSI_RecordGetString(row
, 3);
2342 /* get the root key */
2347 static const WCHAR szALLUSER
[] = {'A','L','L','U','S','E','R','S',0};
2348 LPWSTR all_users
= msi_dup_property( package
, szALLUSER
);
2349 if (all_users
&& all_users
[0] == '1')
2351 root_key
= HKEY_LOCAL_MACHINE
;
2356 root_key
= HKEY_CURRENT_USER
;
2359 msi_free(all_users
);
2362 case 0: root_key
= HKEY_CLASSES_ROOT
;
2365 case 1: root_key
= HKEY_CURRENT_USER
;
2368 case 2: root_key
= HKEY_LOCAL_MACHINE
;
2371 case 3: root_key
= HKEY_USERS
;
2375 ERR("Unknown root %i\n",root
);
2381 return ERROR_SUCCESS
;
2383 deformat_string(package
, key
, &deformated
);
2384 size
= strlenW(deformated
) + strlenW(szRoot
) + 1;
2385 uikey
= msi_alloc(size
*sizeof(WCHAR
));
2386 strcpyW(uikey
,szRoot
);
2387 strcatW(uikey
,deformated
);
2389 if (RegCreateKeyW( root_key
, deformated
, &hkey
))
2391 ERR("Could not create key %s\n",debugstr_w(deformated
));
2392 msi_free(deformated
);
2394 return ERROR_SUCCESS
;
2396 msi_free(deformated
);
2398 value
= MSI_RecordGetString(row
,5);
2400 value_data
= parse_value(package
, value
, &type
, &size
);
2403 static const WCHAR szEmpty
[] = {0};
2404 value_data
= (LPSTR
)strdupW(szEmpty
);
2409 deformat_string(package
, name
, &deformated
);
2411 /* get the double nulls to terminate SZ_MULTI */
2412 if (type
== REG_MULTI_SZ
)
2413 size
+=sizeof(WCHAR
);
2417 TRACE("Setting value %s of %s\n",debugstr_w(deformated
),
2419 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
)value_data
, size
);
2424 rc
= RegQueryValueExW(hkey
, deformated
, NULL
, NULL
, NULL
, &sz
);
2425 if (rc
== ERROR_SUCCESS
|| rc
== ERROR_MORE_DATA
)
2427 TRACE("value %s of %s checked already exists\n",
2428 debugstr_w(deformated
), debugstr_w(uikey
));
2432 TRACE("Checked and setting value %s of %s\n",
2433 debugstr_w(deformated
), debugstr_w(uikey
));
2434 if (deformated
|| size
)
2435 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
) value_data
, size
);
2440 uirow
= MSI_CreateRecord(3);
2441 MSI_RecordSetStringW(uirow
,2,deformated
);
2442 MSI_RecordSetStringW(uirow
,1,uikey
);
2445 MSI_RecordSetStringW(uirow
,3,(LPWSTR
)value_data
);
2447 MSI_RecordSetStringW(uirow
,3,value
);
2449 ui_actiondata(package
,szWriteRegistryValues
,uirow
);
2450 msiobj_release( &uirow
->hdr
);
2452 msi_free(value_data
);
2453 msi_free(deformated
);
2456 return ERROR_SUCCESS
;
2459 static UINT
ACTION_WriteRegistryValues(MSIPACKAGE
*package
)
2463 static const WCHAR ExecSeqQuery
[] =
2464 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2465 '`','R','e','g','i','s','t','r','y','`',0 };
2467 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2468 if (rc
!= ERROR_SUCCESS
)
2469 return ERROR_SUCCESS
;
2471 /* increment progress bar each time action data is sent */
2472 ui_progress(package
,1,REG_PROGRESS_VALUE
,1,0);
2474 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteRegistryValues
, package
);
2476 msiobj_release(&view
->hdr
);
2480 static UINT
ACTION_InstallInitialize(MSIPACKAGE
*package
)
2482 package
->script
->CurrentlyScripting
= TRUE
;
2484 return ERROR_SUCCESS
;
2488 static UINT
ACTION_InstallValidate(MSIPACKAGE
*package
)
2493 static const WCHAR q1
[]=
2494 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2495 '`','R','e','g','i','s','t','r','y','`',0};
2498 MSIFEATURE
*feature
;
2501 TRACE("InstallValidate\n");
2503 rc
= MSI_DatabaseOpenViewW(package
->db
, q1
, &view
);
2504 if (rc
== ERROR_SUCCESS
)
2506 MSI_IterateRecords( view
, &progress
, NULL
, package
);
2507 msiobj_release( &view
->hdr
);
2508 total
+= progress
* REG_PROGRESS_VALUE
;
2511 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2512 total
+= COMPONENT_PROGRESS_VALUE
;
2514 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2515 total
+= file
->FileSize
;
2517 ui_progress(package
,0,total
,0,0);
2519 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2521 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2522 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
,
2523 feature
->ActionRequest
);
2526 return ERROR_SUCCESS
;
2529 static UINT
ITERATE_LaunchConditions(MSIRECORD
*row
, LPVOID param
)
2531 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
2532 LPCWSTR cond
= NULL
;
2533 LPCWSTR message
= NULL
;
2536 static const WCHAR title
[]=
2537 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2539 cond
= MSI_RecordGetString(row
,1);
2541 r
= MSI_EvaluateConditionW(package
,cond
);
2542 if (r
== MSICONDITION_FALSE
)
2544 if ((gUILevel
& INSTALLUILEVEL_MASK
) != INSTALLUILEVEL_NONE
)
2547 message
= MSI_RecordGetString(row
,2);
2548 deformat_string(package
,message
,&deformated
);
2549 MessageBoxW(NULL
,deformated
,title
,MB_OK
);
2550 msi_free(deformated
);
2553 return ERROR_INSTALL_FAILURE
;
2556 return ERROR_SUCCESS
;
2559 static UINT
ACTION_LaunchConditions(MSIPACKAGE
*package
)
2562 MSIQUERY
* view
= NULL
;
2563 static const WCHAR ExecSeqQuery
[] =
2564 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2565 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2567 TRACE("Checking launch conditions\n");
2569 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2570 if (rc
!= ERROR_SUCCESS
)
2571 return ERROR_SUCCESS
;
2573 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_LaunchConditions
, package
);
2574 msiobj_release(&view
->hdr
);
2579 static LPWSTR
resolve_keypath( MSIPACKAGE
* package
, MSICOMPONENT
*cmp
)
2583 return resolve_folder(package
,cmp
->Directory
,FALSE
,FALSE
,TRUE
,NULL
);
2585 if (cmp
->Attributes
& msidbComponentAttributesRegistryKeyPath
)
2587 MSIRECORD
* row
= 0;
2589 LPWSTR deformated
,buffer
,deformated_name
;
2591 static const WCHAR ExecSeqQuery
[] =
2592 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2593 '`','R','e','g','i','s','t','r','y','`',' ',
2594 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2595 ' ','=',' ' ,'\'','%','s','\'',0 };
2596 static const WCHAR fmt
[]={'%','0','2','i',':','\\','%','s','\\',0};
2597 static const WCHAR fmt2
[]=
2598 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2600 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
,cmp
->KeyPath
);
2604 root
= MSI_RecordGetInteger(row
,2);
2605 key
= MSI_RecordGetString(row
, 3);
2606 name
= MSI_RecordGetString(row
, 4);
2607 deformat_string(package
, key
, &deformated
);
2608 deformat_string(package
, name
, &deformated_name
);
2610 len
= strlenW(deformated
) + 6;
2611 if (deformated_name
)
2612 len
+=strlenW(deformated_name
);
2614 buffer
= msi_alloc( len
*sizeof(WCHAR
));
2616 if (deformated_name
)
2617 sprintfW(buffer
,fmt2
,root
,deformated
,deformated_name
);
2619 sprintfW(buffer
,fmt
,root
,deformated
);
2621 msi_free(deformated
);
2622 msi_free(deformated_name
);
2623 msiobj_release(&row
->hdr
);
2627 else if (cmp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2629 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2634 MSIFILE
*file
= get_loaded_file( package
, cmp
->KeyPath
);
2637 return strdupW( file
->TargetPath
);
2642 static HKEY
openSharedDLLsKey(void)
2645 static const WCHAR path
[] =
2646 {'S','o','f','t','w','a','r','e','\\',
2647 'M','i','c','r','o','s','o','f','t','\\',
2648 'W','i','n','d','o','w','s','\\',
2649 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2650 'S','h','a','r','e','d','D','L','L','s',0};
2652 RegCreateKeyW(HKEY_LOCAL_MACHINE
,path
,&hkey
);
2656 static UINT
ACTION_GetSharedDLLsCount(LPCWSTR dll
)
2661 DWORD sz
= sizeof(count
);
2664 hkey
= openSharedDLLsKey();
2665 rc
= RegQueryValueExW(hkey
, dll
, NULL
, &type
, (LPBYTE
)&count
, &sz
);
2666 if (rc
!= ERROR_SUCCESS
)
2672 static UINT
ACTION_WriteSharedDLLsCount(LPCWSTR path
, UINT count
)
2676 hkey
= openSharedDLLsKey();
2678 msi_reg_set_val_dword( hkey
, path
, count
);
2680 RegDeleteValueW(hkey
,path
);
2686 * Return TRUE if the count should be written out and FALSE if not
2688 static void ACTION_RefCountComponent( MSIPACKAGE
* package
, MSICOMPONENT
*comp
)
2690 MSIFEATURE
*feature
;
2694 /* only refcount DLLs */
2695 if (comp
->KeyPath
== NULL
||
2696 comp
->Attributes
& msidbComponentAttributesRegistryKeyPath
||
2697 comp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2701 count
= ACTION_GetSharedDLLsCount( comp
->FullKeypath
);
2702 write
= (count
> 0);
2704 if (comp
->Attributes
& msidbComponentAttributesSharedDllRefCount
)
2708 /* increment counts */
2709 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2713 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
))
2716 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2718 if ( cl
->component
== comp
)
2723 /* decrement counts */
2724 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2728 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ABSENT
))
2731 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2733 if ( cl
->component
== comp
)
2738 /* ref count all the files in the component */
2743 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2745 if (file
->Component
== comp
)
2746 ACTION_WriteSharedDLLsCount( file
->TargetPath
, count
);
2750 /* add a count for permenent */
2751 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2754 comp
->RefCount
= count
;
2757 ACTION_WriteSharedDLLsCount( comp
->FullKeypath
, comp
->RefCount
);
2761 * Ok further analysis makes me think that this work is
2762 * actually done in the PublishComponents and PublishFeatures
2763 * step, and not here. It appears like the keypath and all that is
2764 * resolved in this step, however actually written in the Publish steps.
2765 * But we will leave it here for now because it is unclear
2767 static UINT
ACTION_ProcessComponents(MSIPACKAGE
*package
)
2769 WCHAR squished_pc
[GUID_SIZE
];
2770 WCHAR squished_cc
[GUID_SIZE
];
2773 HKEY hkey
=0,hkey2
=0;
2777 /* writes the Component and Features values to the registry */
2779 rc
= MSIREG_OpenComponents(&hkey
);
2780 if (rc
!= ERROR_SUCCESS
)
2783 squash_guid(package
->ProductCode
,squished_pc
);
2784 ui_progress(package
,1,COMPONENT_PROGRESS_VALUE
,1,0);
2786 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2790 ui_progress(package
,2,0,0,0);
2791 if (!comp
->ComponentId
)
2794 squash_guid(comp
->ComponentId
,squished_cc
);
2796 msi_free(comp
->FullKeypath
);
2797 comp
->FullKeypath
= resolve_keypath( package
, comp
);
2799 /* do the refcounting */
2800 ACTION_RefCountComponent( package
, comp
);
2802 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2803 debugstr_w(comp
->Component
),
2804 debugstr_w(squished_cc
),
2805 debugstr_w(comp
->FullKeypath
),
2808 * Write the keypath out if the component is to be registered
2809 * and delete the key if the component is to be deregistered
2811 if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2813 rc
= RegCreateKeyW(hkey
,squished_cc
,&hkey2
);
2814 if (rc
!= ERROR_SUCCESS
)
2817 if (!comp
->FullKeypath
)
2820 msi_reg_set_val_str( hkey2
, squished_pc
, comp
->FullKeypath
);
2822 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2824 static const WCHAR szPermKey
[] =
2825 { '0','0','0','0','0','0','0','0','0','0','0','0',
2826 '0','0','0','0','0','0','0','0','0','0','0','0',
2827 '0','0','0','0','0','0','0','0',0 };
2829 msi_reg_set_val_str( hkey2
, szPermKey
, comp
->FullKeypath
);
2834 rc
= MSIREG_OpenUserDataComponentKey(comp
->ComponentId
, &hkey2
, TRUE
);
2835 if (rc
!= ERROR_SUCCESS
)
2838 msi_reg_set_val_str(hkey2
, squished_pc
, comp
->FullKeypath
);
2841 else if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ABSENT
))
2845 rc
= RegOpenKeyW(hkey
,squished_cc
,&hkey2
);
2846 if (rc
!= ERROR_SUCCESS
)
2849 RegDeleteValueW(hkey2
,squished_pc
);
2851 /* if the key is empty delete it */
2852 res
= RegEnumKeyExW(hkey2
,0,NULL
,0,0,NULL
,0,NULL
);
2854 if (res
== ERROR_NO_MORE_ITEMS
)
2855 RegDeleteKeyW(hkey
,squished_cc
);
2857 MSIREG_DeleteUserDataComponentKey(comp
->ComponentId
);
2861 uirow
= MSI_CreateRecord(3);
2862 MSI_RecordSetStringW(uirow
,1,package
->ProductCode
);
2863 MSI_RecordSetStringW(uirow
,2,comp
->ComponentId
);
2864 MSI_RecordSetStringW(uirow
,3,comp
->FullKeypath
);
2865 ui_actiondata(package
,szProcessComponents
,uirow
);
2866 msiobj_release( &uirow
->hdr
);
2880 static BOOL CALLBACK
Typelib_EnumResNameProc( HMODULE hModule
, LPCWSTR lpszType
,
2881 LPWSTR lpszName
, LONG_PTR lParam
)
2884 typelib_struct
*tl_struct
= (typelib_struct
*) lParam
;
2885 static const WCHAR fmt
[] = {'%','s','\\','%','i',0};
2889 if (!IS_INTRESOURCE(lpszName
))
2891 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName
));
2895 sz
= strlenW(tl_struct
->source
)+4;
2896 sz
*= sizeof(WCHAR
);
2898 if ((INT_PTR
)lpszName
== 1)
2899 tl_struct
->path
= strdupW(tl_struct
->source
);
2902 tl_struct
->path
= msi_alloc(sz
);
2903 sprintfW(tl_struct
->path
,fmt
,tl_struct
->source
, lpszName
);
2906 TRACE("trying %s\n", debugstr_w(tl_struct
->path
));
2907 res
= LoadTypeLib(tl_struct
->path
,&tl_struct
->ptLib
);
2908 if (!SUCCEEDED(res
))
2910 msi_free(tl_struct
->path
);
2911 tl_struct
->path
= NULL
;
2916 ITypeLib_GetLibAttr(tl_struct
->ptLib
, &attr
);
2917 if (IsEqualGUID(&(tl_struct
->clsid
),&(attr
->guid
)))
2919 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2923 msi_free(tl_struct
->path
);
2924 tl_struct
->path
= NULL
;
2926 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2927 ITypeLib_Release(tl_struct
->ptLib
);
2932 static UINT
ITERATE_RegisterTypeLibraries(MSIRECORD
*row
, LPVOID param
)
2934 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
2938 typelib_struct tl_struct
;
2940 static const WCHAR szTYPELIB
[] = {'T','Y','P','E','L','I','B',0};
2942 component
= MSI_RecordGetString(row
,3);
2943 comp
= get_loaded_component(package
,component
);
2945 return ERROR_SUCCESS
;
2947 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2949 TRACE("Skipping typelib reg due to disabled component\n");
2951 comp
->Action
= comp
->Installed
;
2953 return ERROR_SUCCESS
;
2956 comp
->Action
= INSTALLSTATE_LOCAL
;
2958 file
= get_loaded_file( package
, comp
->KeyPath
);
2960 return ERROR_SUCCESS
;
2962 module
= LoadLibraryExW( file
->TargetPath
, NULL
, LOAD_LIBRARY_AS_DATAFILE
);
2966 guid
= MSI_RecordGetString(row
,1);
2967 CLSIDFromString((LPWSTR
)guid
, &tl_struct
.clsid
);
2968 tl_struct
.source
= strdupW( file
->TargetPath
);
2969 tl_struct
.path
= NULL
;
2971 EnumResourceNamesW(module
, szTYPELIB
, Typelib_EnumResNameProc
,
2972 (LONG_PTR
)&tl_struct
);
2980 helpid
= MSI_RecordGetString(row
,6);
2983 help
= resolve_folder(package
,helpid
,FALSE
,FALSE
,TRUE
,NULL
);
2984 res
= RegisterTypeLib(tl_struct
.ptLib
,tl_struct
.path
,help
);
2987 if (!SUCCEEDED(res
))
2988 ERR("Failed to register type library %s\n",
2989 debugstr_w(tl_struct
.path
));
2992 ui_actiondata(package
,szRegisterTypeLibraries
,row
);
2994 TRACE("Registered %s\n", debugstr_w(tl_struct
.path
));
2997 ITypeLib_Release(tl_struct
.ptLib
);
2998 msi_free(tl_struct
.path
);
3001 ERR("Failed to load type library %s\n",
3002 debugstr_w(tl_struct
.source
));
3004 FreeLibrary(module
);
3005 msi_free(tl_struct
.source
);
3008 ERR("Could not load file! %s\n", debugstr_w(file
->TargetPath
));
3010 return ERROR_SUCCESS
;
3013 static UINT
ACTION_RegisterTypeLibraries(MSIPACKAGE
*package
)
3016 * OK this is a bit confusing.. I am given a _Component key and I believe
3017 * that the file that is being registered as a type library is the "key file
3018 * of that component" which I interpret to mean "The file in the KeyPath of
3023 static const WCHAR Query
[] =
3024 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3025 '`','T','y','p','e','L','i','b','`',0};
3027 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3028 if (rc
!= ERROR_SUCCESS
)
3029 return ERROR_SUCCESS
;
3031 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_RegisterTypeLibraries
, package
);
3032 msiobj_release(&view
->hdr
);
3036 static UINT
ITERATE_CreateShortcuts(MSIRECORD
*row
, LPVOID param
)
3038 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3039 LPWSTR target_file
, target_folder
, filename
;
3040 LPCWSTR buffer
, extension
;
3042 static const WCHAR szlnk
[]={'.','l','n','k',0};
3043 IShellLinkW
*sl
= NULL
;
3044 IPersistFile
*pf
= NULL
;
3047 buffer
= MSI_RecordGetString(row
,4);
3048 comp
= get_loaded_component(package
,buffer
);
3050 return ERROR_SUCCESS
;
3052 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3054 TRACE("Skipping shortcut creation due to disabled component\n");
3056 comp
->Action
= comp
->Installed
;
3058 return ERROR_SUCCESS
;
3061 comp
->Action
= INSTALLSTATE_LOCAL
;
3063 ui_actiondata(package
,szCreateShortcuts
,row
);
3065 res
= CoCreateInstance( &CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
,
3066 &IID_IShellLinkW
, (LPVOID
*) &sl
);
3070 ERR("CLSID_ShellLink not available\n");
3074 res
= IShellLinkW_QueryInterface( sl
, &IID_IPersistFile
,(LPVOID
*) &pf
);
3077 ERR("QueryInterface(IID_IPersistFile) failed\n");
3081 buffer
= MSI_RecordGetString(row
,2);
3082 target_folder
= resolve_folder(package
, buffer
,FALSE
,FALSE
,TRUE
,NULL
);
3084 /* may be needed because of a bug somehwere else */
3085 create_full_pathW(target_folder
);
3087 filename
= msi_dup_record_field( row
, 3 );
3088 reduce_to_longfilename(filename
);
3090 extension
= strchrW(filename
,'.');
3091 if (!extension
|| strcmpiW(extension
,szlnk
))
3093 int len
= strlenW(filename
);
3094 filename
= msi_realloc(filename
, len
* sizeof(WCHAR
) + sizeof(szlnk
));
3095 memcpy(filename
+ len
, szlnk
, sizeof(szlnk
));
3097 target_file
= build_directory_name(2, target_folder
, filename
);
3098 msi_free(target_folder
);
3101 buffer
= MSI_RecordGetString(row
,5);
3102 if (strchrW(buffer
,'['))
3105 deformat_string(package
,buffer
,&deformated
);
3106 IShellLinkW_SetPath(sl
,deformated
);
3107 msi_free(deformated
);
3111 FIXME("poorly handled shortcut format, advertised shortcut\n");
3112 IShellLinkW_SetPath(sl
,comp
->FullKeypath
);
3115 if (!MSI_RecordIsNull(row
,6))
3118 buffer
= MSI_RecordGetString(row
,6);
3119 deformat_string(package
,buffer
,&deformated
);
3120 IShellLinkW_SetArguments(sl
,deformated
);
3121 msi_free(deformated
);
3124 if (!MSI_RecordIsNull(row
,7))
3126 buffer
= MSI_RecordGetString(row
,7);
3127 IShellLinkW_SetDescription(sl
,buffer
);
3130 if (!MSI_RecordIsNull(row
,8))
3131 IShellLinkW_SetHotkey(sl
,MSI_RecordGetInteger(row
,8));
3133 if (!MSI_RecordIsNull(row
,9))
3138 buffer
= MSI_RecordGetString(row
,9);
3140 Path
= build_icon_path(package
,buffer
);
3141 index
= MSI_RecordGetInteger(row
,10);
3143 /* no value means 0 */
3144 if (index
== MSI_NULL_INTEGER
)
3147 IShellLinkW_SetIconLocation(sl
,Path
,index
);
3151 if (!MSI_RecordIsNull(row
,11))
3152 IShellLinkW_SetShowCmd(sl
,MSI_RecordGetInteger(row
,11));
3154 if (!MSI_RecordIsNull(row
,12))
3157 buffer
= MSI_RecordGetString(row
,12);
3158 Path
= resolve_folder(package
, buffer
, FALSE
, FALSE
, TRUE
, NULL
);
3160 IShellLinkW_SetWorkingDirectory(sl
,Path
);
3164 TRACE("Writing shortcut to %s\n",debugstr_w(target_file
));
3165 IPersistFile_Save(pf
,target_file
,FALSE
);
3167 msi_free(target_file
);
3171 IPersistFile_Release( pf
);
3173 IShellLinkW_Release( sl
);
3175 return ERROR_SUCCESS
;
3178 static UINT
ACTION_CreateShortcuts(MSIPACKAGE
*package
)
3183 static const WCHAR Query
[] =
3184 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3185 '`','S','h','o','r','t','c','u','t','`',0};
3187 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3188 if (rc
!= ERROR_SUCCESS
)
3189 return ERROR_SUCCESS
;
3191 res
= CoInitialize( NULL
);
3194 ERR("CoInitialize failed\n");
3195 return ERROR_FUNCTION_FAILED
;
3198 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateShortcuts
, package
);
3199 msiobj_release(&view
->hdr
);
3206 static UINT
ITERATE_PublishProduct(MSIRECORD
*row
, LPVOID param
)
3208 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
3217 FileName
= MSI_RecordGetString(row
,1);
3220 ERR("Unable to get FileName\n");
3221 return ERROR_SUCCESS
;
3224 FilePath
= build_icon_path(package
,FileName
);
3226 TRACE("Creating icon file at %s\n",debugstr_w(FilePath
));
3228 the_file
= CreateFileW(FilePath
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
,
3229 FILE_ATTRIBUTE_NORMAL
, NULL
);
3231 if (the_file
== INVALID_HANDLE_VALUE
)
3233 ERR("Unable to create file %s\n",debugstr_w(FilePath
));
3235 return ERROR_SUCCESS
;
3242 rc
= MSI_RecordReadStream(row
,2,buffer
,&sz
);
3243 if (rc
!= ERROR_SUCCESS
)
3245 ERR("Failed to get stream\n");
3246 CloseHandle(the_file
);
3247 DeleteFileW(FilePath
);
3250 WriteFile(the_file
,buffer
,sz
,&write
,NULL
);
3251 } while (sz
== 1024);
3255 CloseHandle(the_file
);
3257 uirow
= MSI_CreateRecord(1);
3258 MSI_RecordSetStringW(uirow
,1,FileName
);
3259 ui_actiondata(package
,szPublishProduct
,uirow
);
3260 msiobj_release( &uirow
->hdr
);
3262 return ERROR_SUCCESS
;
3265 static BOOL
msi_check_publish(MSIPACKAGE
*package
)
3267 MSIFEATURE
*feature
;
3269 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3271 if (feature
->ActionRequest
== INSTALLSTATE_LOCAL
)
3279 * 99% of the work done here is only done for
3280 * advertised installs. However this is where the
3281 * Icon table is processed and written out
3282 * so that is what I am going to do here.
3284 static UINT
ACTION_PublishProduct(MSIPACKAGE
*package
)
3288 MSISOURCELISTINFO
*info
;
3290 static const WCHAR Query
[]=
3291 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3292 '`','I','c','o','n','`',0};
3293 /* for registry stuff */
3296 HKEY hudkey
=0, props
=0;
3297 static const WCHAR szProductLanguage
[] =
3298 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3299 static const WCHAR szARPProductIcon
[] =
3300 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3301 static const WCHAR szProductVersion
[] =
3302 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3306 MSIHANDLE hDb
, hSumInfo
;
3308 /* FIXME: also need to publish if the product is in advertise mode */
3309 if (!msi_check_publish(package
))
3310 return ERROR_SUCCESS
;
3312 /* write out icon files */
3314 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3315 if (rc
== ERROR_SUCCESS
)
3317 MSI_IterateRecords(view
, NULL
, ITERATE_PublishProduct
, package
);
3318 msiobj_release(&view
->hdr
);
3321 /* ok there is a lot more done here but i need to figure out what */
3323 rc
= MSIREG_OpenProductsKey(package
->ProductCode
,&hkey
,TRUE
);
3324 if (rc
!= ERROR_SUCCESS
)
3327 rc
= MSIREG_OpenUserProductsKey(package
->ProductCode
,&hukey
,TRUE
);
3328 if (rc
!= ERROR_SUCCESS
)
3331 rc
= MSIREG_OpenUserDataProductKey(package
->ProductCode
,&hudkey
,TRUE
);
3332 if (rc
!= ERROR_SUCCESS
)
3335 rc
= MSIREG_OpenInstallPropertiesKey(package
->ProductCode
,&props
,TRUE
);
3336 if (rc
!= ERROR_SUCCESS
)
3339 buffer
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTNAMEW
);
3340 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PRODUCTNAMEW
, buffer
);
3343 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
3344 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
3346 buffer
= msi_dup_property( package
, szARPProductIcon
);
3349 LPWSTR path
= build_icon_path(package
,buffer
);
3350 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PRODUCTICONW
, path
);
3355 buffer
= msi_dup_property( package
, szProductVersion
);
3358 DWORD verdword
= msi_version_str_to_dword(buffer
);
3359 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
3363 /* FIXME: Need to write more keys to the user registry */
3365 hDb
= alloc_msihandle( &package
->db
->hdr
);
3367 rc
= ERROR_NOT_ENOUGH_MEMORY
;
3370 rc
= MsiGetSummaryInformationW(hDb
, NULL
, 0, &hSumInfo
);
3371 MsiCloseHandle(hDb
);
3372 if (rc
== ERROR_SUCCESS
)
3374 WCHAR guidbuffer
[0x200];
3376 rc
= MsiSummaryInfoGetPropertyW(hSumInfo
, 9, NULL
, NULL
, NULL
,
3378 if (rc
== ERROR_SUCCESS
)
3380 WCHAR squashed
[GUID_SIZE
];
3381 /* for now we only care about the first guid */
3382 LPWSTR ptr
= strchrW(guidbuffer
,';');
3384 squash_guid(guidbuffer
,squashed
);
3385 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PACKAGECODEW
, squashed
);
3389 ERR("Unable to query Revision_Number...\n");
3392 MsiCloseHandle(hSumInfo
);
3396 ERR("Unable to open Summary Information\n");
3400 /* publish the SourceList info */
3401 LIST_FOR_EACH_ENTRY(info
, &package
->sourcelist_info
, MSISOURCELISTINFO
, entry
)
3403 MsiSourceListSetInfoW(package
->ProductCode
, NULL
,
3404 info
->context
, info
->options
,
3405 info
->property
, info
->value
);
3408 LIST_FOR_EACH_ENTRY(disk
, &package
->sourcelist_media
, MSIMEDIADISK
, entry
)
3410 MsiSourceListAddMediaDiskW(package
->ProductCode
, NULL
,
3411 disk
->context
, disk
->options
,
3412 disk
->disk_id
, disk
->volume_label
, disk
->disk_prompt
);
3418 RegCloseKey(hudkey
);
3424 static UINT
ITERATE_WriteIniValues(MSIRECORD
*row
, LPVOID param
)
3426 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3427 LPCWSTR component
,section
,key
,value
,identifier
,filename
,dirproperty
;
3428 LPWSTR deformated_section
, deformated_key
, deformated_value
;
3429 LPWSTR folder
, fullname
= NULL
;
3433 static const WCHAR szWindowsFolder
[] =
3434 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3436 component
= MSI_RecordGetString(row
, 8);
3437 comp
= get_loaded_component(package
,component
);
3439 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3441 TRACE("Skipping ini file due to disabled component %s\n",
3442 debugstr_w(component
));
3444 comp
->Action
= comp
->Installed
;
3446 return ERROR_SUCCESS
;
3449 comp
->Action
= INSTALLSTATE_LOCAL
;
3451 identifier
= MSI_RecordGetString(row
,1);
3452 filename
= MSI_RecordGetString(row
,2);
3453 dirproperty
= MSI_RecordGetString(row
,3);
3454 section
= MSI_RecordGetString(row
,4);
3455 key
= MSI_RecordGetString(row
,5);
3456 value
= MSI_RecordGetString(row
,6);
3457 action
= MSI_RecordGetInteger(row
,7);
3459 deformat_string(package
,section
,&deformated_section
);
3460 deformat_string(package
,key
,&deformated_key
);
3461 deformat_string(package
,value
,&deformated_value
);
3465 folder
= resolve_folder(package
, dirproperty
, FALSE
, FALSE
, TRUE
, NULL
);
3467 folder
= msi_dup_property( package
, dirproperty
);
3470 folder
= msi_dup_property( package
, szWindowsFolder
);
3474 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty
));
3478 fullname
= build_directory_name(2, folder
, filename
);
3482 TRACE("Adding value %s to section %s in %s\n",
3483 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3484 debugstr_w(fullname
));
3485 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3486 deformated_value
, fullname
);
3488 else if (action
== 1)
3491 GetPrivateProfileStringW(deformated_section
, deformated_key
, NULL
,
3492 returned
, 10, fullname
);
3493 if (returned
[0] == 0)
3495 TRACE("Adding value %s to section %s in %s\n",
3496 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3497 debugstr_w(fullname
));
3499 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3500 deformated_value
, fullname
);
3503 else if (action
== 3)
3504 FIXME("Append to existing section not yet implemented\n");
3506 uirow
= MSI_CreateRecord(4);
3507 MSI_RecordSetStringW(uirow
,1,identifier
);
3508 MSI_RecordSetStringW(uirow
,2,deformated_section
);
3509 MSI_RecordSetStringW(uirow
,3,deformated_key
);
3510 MSI_RecordSetStringW(uirow
,4,deformated_value
);
3511 ui_actiondata(package
,szWriteIniValues
,uirow
);
3512 msiobj_release( &uirow
->hdr
);
3516 msi_free(deformated_key
);
3517 msi_free(deformated_value
);
3518 msi_free(deformated_section
);
3519 return ERROR_SUCCESS
;
3522 static UINT
ACTION_WriteIniValues(MSIPACKAGE
*package
)
3526 static const WCHAR ExecSeqQuery
[] =
3527 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3528 '`','I','n','i','F','i','l','e','`',0};
3530 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3531 if (rc
!= ERROR_SUCCESS
)
3533 TRACE("no IniFile table\n");
3534 return ERROR_SUCCESS
;
3537 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteIniValues
, package
);
3538 msiobj_release(&view
->hdr
);
3542 static UINT
ITERATE_SelfRegModules(MSIRECORD
*row
, LPVOID param
)
3544 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3549 static const WCHAR ExeStr
[] =
3550 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3551 static const WCHAR close
[] = {'\"',0};
3553 PROCESS_INFORMATION info
;
3558 memset(&si
,0,sizeof(STARTUPINFOW
));
3560 filename
= MSI_RecordGetString(row
,1);
3561 file
= get_loaded_file( package
, filename
);
3565 ERR("Unable to find file id %s\n",debugstr_w(filename
));
3566 return ERROR_SUCCESS
;
3569 len
= strlenW(ExeStr
) + strlenW( file
->TargetPath
) + 2;
3571 FullName
= msi_alloc(len
*sizeof(WCHAR
));
3572 strcpyW(FullName
,ExeStr
);
3573 strcatW( FullName
, file
->TargetPath
);
3574 strcatW(FullName
,close
);
3576 TRACE("Registering %s\n",debugstr_w(FullName
));
3577 brc
= CreateProcessW(NULL
, FullName
, NULL
, NULL
, FALSE
, 0, NULL
, c_colon
,
3581 msi_dialog_check_messages(info
.hProcess
);
3586 uirow
= MSI_CreateRecord( 2 );
3587 uipath
= strdupW( file
->TargetPath
);
3588 p
= strrchrW(uipath
,'\\');
3591 MSI_RecordSetStringW( uirow
, 1, &p
[1] );
3592 MSI_RecordSetStringW( uirow
, 2, uipath
);
3593 ui_actiondata( package
, szSelfRegModules
, uirow
);
3594 msiobj_release( &uirow
->hdr
);
3596 /* FIXME: call ui_progress? */
3598 return ERROR_SUCCESS
;
3601 static UINT
ACTION_SelfRegModules(MSIPACKAGE
*package
)
3605 static const WCHAR ExecSeqQuery
[] =
3606 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3607 '`','S','e','l','f','R','e','g','`',0};
3609 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3610 if (rc
!= ERROR_SUCCESS
)
3612 TRACE("no SelfReg table\n");
3613 return ERROR_SUCCESS
;
3616 MSI_IterateRecords(view
, NULL
, ITERATE_SelfRegModules
, package
);
3617 msiobj_release(&view
->hdr
);
3619 return ERROR_SUCCESS
;
3622 static UINT
ACTION_PublishFeatures(MSIPACKAGE
*package
)
3624 MSIFEATURE
*feature
;
3630 if (!msi_check_publish(package
))
3631 return ERROR_SUCCESS
;
3633 rc
= MSIREG_OpenFeaturesKey(package
->ProductCode
,&hkey
,TRUE
);
3634 if (rc
!= ERROR_SUCCESS
)
3637 rc
= MSIREG_OpenUserFeaturesKey(package
->ProductCode
,&hukey
,TRUE
);
3638 if (rc
!= ERROR_SUCCESS
)
3641 rc
= MSIREG_OpenUserDataFeaturesKey(package
->ProductCode
, &userdata
, TRUE
);
3642 if (rc
!= ERROR_SUCCESS
)
3645 /* here the guids are base 85 encoded */
3646 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
3652 BOOL absent
= FALSE
;
3655 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
) &&
3656 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_SOURCE
) &&
3657 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ADVERTISED
))
3661 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3665 if (feature
->Feature_Parent
)
3666 size
+= strlenW( feature
->Feature_Parent
)+2;
3668 data
= msi_alloc(size
* sizeof(WCHAR
));
3671 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3673 MSICOMPONENT
* component
= cl
->component
;
3677 if (component
->ComponentId
)
3679 TRACE("From %s\n",debugstr_w(component
->ComponentId
));
3680 CLSIDFromString(component
->ComponentId
, &clsid
);
3681 encode_base85_guid(&clsid
,buf
);
3682 TRACE("to %s\n",debugstr_w(buf
));
3687 if (feature
->Feature_Parent
)
3689 static const WCHAR sep
[] = {'\2',0};
3691 strcatW(data
,feature
->Feature_Parent
);
3694 msi_reg_set_val_str( hkey
, feature
->Feature
, data
);
3695 msi_reg_set_val_str( userdata
, feature
->Feature
, data
);
3699 if (feature
->Feature_Parent
)
3700 size
= strlenW(feature
->Feature_Parent
)*sizeof(WCHAR
);
3703 RegSetValueExW(hukey
,feature
->Feature
,0,REG_SZ
,
3704 (LPBYTE
)feature
->Feature_Parent
,size
);
3708 size
+= 2*sizeof(WCHAR
);
3709 data
= msi_alloc(size
);
3712 if (feature
->Feature_Parent
)
3713 strcpyW( &data
[1], feature
->Feature_Parent
);
3714 RegSetValueExW(hukey
,feature
->Feature
,0,REG_SZ
,
3720 uirow
= MSI_CreateRecord( 1 );
3721 MSI_RecordSetStringW( uirow
, 1, feature
->Feature
);
3722 ui_actiondata( package
, szPublishFeatures
, uirow
);
3723 msiobj_release( &uirow
->hdr
);
3724 /* FIXME: call ui_progress? */
3733 static UINT
msi_unpublish_feature(MSIPACKAGE
*package
, MSIFEATURE
*feature
)
3738 TRACE("unpublishing feature %s\n", debugstr_w(feature
->Feature
));
3740 r
= MSIREG_OpenUserFeaturesKey(package
->ProductCode
, &hkey
, FALSE
);
3741 if (r
== ERROR_SUCCESS
)
3743 RegDeleteValueW(hkey
, feature
->Feature
);
3747 r
= MSIREG_OpenUserDataFeaturesKey(package
->ProductCode
, &hkey
, FALSE
);
3748 if (r
== ERROR_SUCCESS
)
3750 RegDeleteValueW(hkey
, feature
->Feature
);
3754 return ERROR_SUCCESS
;
3757 static UINT
ACTION_UnpublishFeatures(MSIPACKAGE
*package
)
3759 MSIFEATURE
*feature
;
3761 if (msi_check_publish(package
))
3762 return ERROR_SUCCESS
;
3764 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3766 msi_unpublish_feature(package
, feature
);
3769 return ERROR_SUCCESS
;
3772 static UINT
msi_get_local_package_name( LPWSTR path
)
3774 static const WCHAR szInstaller
[] = {
3775 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3776 static const WCHAR fmt
[] = { '%','x','.','m','s','i',0};
3780 time
= GetTickCount();
3781 GetWindowsDirectoryW( path
, MAX_PATH
);
3782 lstrcatW( path
, szInstaller
);
3783 CreateDirectoryW( path
, NULL
);
3785 len
= lstrlenW(path
);
3786 for (i
=0; i
<0x10000; i
++)
3788 snprintfW( &path
[len
], MAX_PATH
- len
, fmt
, (time
+i
)&0xffff );
3789 handle
= CreateFileW( path
, GENERIC_WRITE
, 0, NULL
,
3790 CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, 0 );
3791 if (handle
!= INVALID_HANDLE_VALUE
)
3793 CloseHandle(handle
);
3796 if (GetLastError() != ERROR_FILE_EXISTS
&&
3797 GetLastError() != ERROR_SHARING_VIOLATION
)
3798 return ERROR_FUNCTION_FAILED
;
3801 return ERROR_SUCCESS
;
3804 static UINT
msi_make_package_local( MSIPACKAGE
*package
, HKEY hkey
)
3806 static const WCHAR szOriginalDatabase
[] =
3807 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3808 WCHAR packagefile
[MAX_PATH
];
3813 r
= msi_get_local_package_name( packagefile
);
3814 if (r
!= ERROR_SUCCESS
)
3817 TRACE("Copying to local package %s\n",debugstr_w(packagefile
));
3819 msiFilePath
= msi_dup_property( package
, szOriginalDatabase
);
3820 r
= CopyFileW( msiFilePath
, packagefile
, FALSE
);
3824 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3825 debugstr_w(msiFilePath
), debugstr_w(packagefile
), GetLastError());
3826 msi_free( msiFilePath
);
3827 return ERROR_FUNCTION_FAILED
;
3829 msi_free( msiFilePath
);
3831 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_LOCALPACKAGEW
, packagefile
);
3833 r
= MSIREG_OpenInstallPropertiesKey(package
->ProductCode
, &props
, TRUE
);
3834 if (r
!= ERROR_SUCCESS
)
3837 msi_reg_set_val_str(props
, INSTALLPROPERTY_LOCALPACKAGEW
, packagefile
);
3839 return ERROR_SUCCESS
;
3842 static UINT
msi_write_uninstall_property_vals( MSIPACKAGE
*package
, HKEY hkey
)
3844 LPWSTR prop
, val
, key
;
3845 static const LPCSTR propval
[] = {
3846 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3847 "ARPCONTACT", "Contact",
3848 "ARPCOMMENTS", "Comments",
3849 "ProductName", "DisplayName",
3850 "ProductVersion", "DisplayVersion",
3851 "ARPHELPLINK", "HelpLink",
3852 "ARPHELPTELEPHONE", "HelpTelephone",
3853 "ARPINSTALLLOCATION", "InstallLocation",
3854 "SourceDir", "InstallSource",
3855 "Manufacturer", "Publisher",
3856 "ARPREADME", "Readme",
3858 "ARPURLINFOABOUT", "URLInfoAbout",
3859 "ARPURLUPDATEINFO", "URLUpdateInfo",
3862 const LPCSTR
*p
= propval
;
3866 prop
= strdupAtoW( *p
++ );
3867 key
= strdupAtoW( *p
++ );
3868 val
= msi_dup_property( package
, prop
);
3869 msi_reg_set_val_str( hkey
, key
, val
);
3874 return ERROR_SUCCESS
;
3877 static UINT
ACTION_RegisterProduct(MSIPACKAGE
*package
)
3880 HKEY hudkey
=0, props
=0;
3881 LPWSTR buffer
= NULL
;
3884 static const WCHAR szWindowsInstaller
[] =
3885 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3886 static const WCHAR szUpgradeCode
[] =
3887 {'U','p','g','r','a','d','e','C','o','d','e',0};
3888 static const WCHAR modpath_fmt
[] =
3889 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3890 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3891 static const WCHAR szModifyPath
[] =
3892 {'M','o','d','i','f','y','P','a','t','h',0};
3893 static const WCHAR szUninstallString
[] =
3894 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3895 static const WCHAR szEstimatedSize
[] =
3896 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3897 static const WCHAR szProductLanguage
[] =
3898 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3899 static const WCHAR szProductVersion
[] =
3900 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3903 static const WCHAR date_fmt
[] = {'%','i','%','i','%','i',0};
3904 LPWSTR upgrade_code
;
3907 /* FIXME: also need to publish if the product is in advertise mode */
3908 if (!msi_check_publish(package
))
3909 return ERROR_SUCCESS
;
3911 rc
= MSIREG_OpenUninstallKey(package
->ProductCode
,&hkey
,TRUE
);
3912 if (rc
!= ERROR_SUCCESS
)
3915 /* dump all the info i can grab */
3916 /* FIXME: Flesh out more information */
3918 msi_write_uninstall_property_vals( package
, hkey
);
3920 msi_reg_set_val_dword( hkey
, szWindowsInstaller
, 1 );
3922 msi_make_package_local( package
, hkey
);
3924 /* do ModifyPath and UninstallString */
3925 size
= deformat_string(package
,modpath_fmt
,&buffer
);
3926 RegSetValueExW(hkey
,szModifyPath
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
3927 RegSetValueExW(hkey
,szUninstallString
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
3930 /* FIXME: Write real Estimated Size when we have it */
3931 msi_reg_set_val_dword( hkey
, szEstimatedSize
, 0 );
3933 GetLocalTime(&systime
);
3934 sprintfW(szDate
,date_fmt
,systime
.wYear
,systime
.wMonth
,systime
.wDay
);
3935 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_INSTALLDATEW
, szDate
);
3937 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
3938 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
3940 buffer
= msi_dup_property( package
, szProductVersion
);
3943 DWORD verdword
= msi_version_str_to_dword(buffer
);
3945 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
3946 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMAJORW
, verdword
>>24 );
3947 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMINORW
, (verdword
>>16)&0x00FF );
3951 /* Handle Upgrade Codes */
3952 upgrade_code
= msi_dup_property( package
, szUpgradeCode
);
3957 MSIREG_OpenUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
3958 squash_guid(package
->ProductCode
,squashed
);
3959 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
3961 MSIREG_OpenUserUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
3962 squash_guid(package
->ProductCode
,squashed
);
3963 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
3966 msi_free(upgrade_code
);
3971 rc
= MSIREG_OpenUserDataProductKey(package
->ProductCode
, &hudkey
, TRUE
);
3972 if (rc
!= ERROR_SUCCESS
)
3975 RegCloseKey(hudkey
);
3977 rc
= MSIREG_OpenInstallPropertiesKey(package
->ProductCode
, &props
, TRUE
);
3978 if (rc
!= ERROR_SUCCESS
)
3981 msi_reg_set_val_dword( props
, szWindowsInstaller
, 1 );
3984 return ERROR_SUCCESS
;
3987 static UINT
ACTION_InstallExecute(MSIPACKAGE
*package
)
3989 return execute_script(package
,INSTALL_SCRIPT
);
3992 static UINT
msi_unpublish_product(MSIPACKAGE
*package
)
3994 LPWSTR remove
= NULL
;
3995 LPWSTR
*features
= NULL
;
3996 BOOL full_uninstall
= TRUE
;
3997 MSIFEATURE
*feature
;
3999 static const WCHAR szRemove
[] = {'R','E','M','O','V','E',0};
4000 static const WCHAR szAll
[] = {'A','L','L',0};
4002 remove
= msi_dup_property(package
, szRemove
);
4004 return ERROR_SUCCESS
;
4006 features
= msi_split_string(remove
, ',');
4010 ERR("REMOVE feature list is empty!\n");
4011 return ERROR_FUNCTION_FAILED
;
4014 if (!lstrcmpW(features
[0], szAll
))
4015 full_uninstall
= TRUE
;
4018 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
4020 if (feature
->Action
!= INSTALLSTATE_ABSENT
)
4021 full_uninstall
= FALSE
;
4025 if (!full_uninstall
)
4028 MSIREG_DeleteProductKey(package
->ProductCode
);
4029 MSIREG_DeleteUserProductKey(package
->ProductCode
);
4030 MSIREG_DeleteUserDataProductKey(package
->ProductCode
);
4031 MSIREG_DeleteUserFeaturesKey(package
->ProductCode
);
4036 return ERROR_SUCCESS
;
4039 static UINT
ACTION_InstallFinalize(MSIPACKAGE
*package
)
4043 rc
= msi_unpublish_product(package
);
4044 if (rc
!= ERROR_SUCCESS
)
4047 /* turn off scheduling */
4048 package
->script
->CurrentlyScripting
= FALSE
;
4050 /* first do the same as an InstallExecute */
4051 rc
= ACTION_InstallExecute(package
);
4052 if (rc
!= ERROR_SUCCESS
)
4055 /* then handle Commit Actions */
4056 rc
= execute_script(package
,COMMIT_SCRIPT
);
4061 UINT
ACTION_ForceReboot(MSIPACKAGE
*package
)
4063 static const WCHAR RunOnce
[] = {
4064 'S','o','f','t','w','a','r','e','\\',
4065 'M','i','c','r','o','s','o','f','t','\\',
4066 'W','i','n','d','o','w','s','\\',
4067 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4068 'R','u','n','O','n','c','e',0};
4069 static const WCHAR InstallRunOnce
[] = {
4070 'S','o','f','t','w','a','r','e','\\',
4071 'M','i','c','r','o','s','o','f','t','\\',
4072 'W','i','n','d','o','w','s','\\',
4073 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4074 'I','n','s','t','a','l','l','e','r','\\',
4075 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4077 static const WCHAR msiexec_fmt
[] = {
4079 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4080 '\"','%','s','\"',0};
4081 static const WCHAR install_fmt
[] = {
4082 '/','I',' ','\"','%','s','\"',' ',
4083 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4084 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4085 WCHAR buffer
[256], sysdir
[MAX_PATH
];
4087 WCHAR squished_pc
[100];
4089 squash_guid(package
->ProductCode
,squished_pc
);
4091 GetSystemDirectoryW(sysdir
, sizeof(sysdir
)/sizeof(sysdir
[0]));
4092 RegCreateKeyW(HKEY_LOCAL_MACHINE
,RunOnce
,&hkey
);
4093 snprintfW(buffer
,sizeof(buffer
)/sizeof(buffer
[0]),msiexec_fmt
,sysdir
,
4096 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
4099 TRACE("Reboot command %s\n",debugstr_w(buffer
));
4101 RegCreateKeyW(HKEY_LOCAL_MACHINE
,InstallRunOnce
,&hkey
);
4102 sprintfW(buffer
,install_fmt
,package
->ProductCode
,squished_pc
);
4104 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
4107 return ERROR_INSTALL_SUSPEND
;
4110 static UINT
msi_set_sourcedir_props(MSIPACKAGE
*package
)
4115 p
= strrchrW( package
->PackagePath
, '\\' );
4117 return ERROR_SUCCESS
;
4119 len
= p
- package
->PackagePath
+ 2;
4120 source
= msi_alloc( len
* sizeof(WCHAR
) );
4121 lstrcpynW( source
, package
->PackagePath
, len
);
4123 MSI_SetPropertyW( package
, cszSourceDir
, source
);
4124 MSI_SetPropertyW( package
, cszSOURCEDIR
, source
);
4128 return ERROR_SUCCESS
;
4131 static UINT
ACTION_ResolveSource(MSIPACKAGE
* package
)
4137 * We are currently doing what should be done here in the top level Install
4138 * however for Administrative and uninstalls this step will be needed
4140 if (!package
->PackagePath
)
4141 return ERROR_SUCCESS
;
4143 msi_set_sourcedir_props(package
);
4145 attrib
= GetFileAttributesW(package
->PackagePath
);
4146 if (attrib
== INVALID_FILE_ATTRIBUTES
)
4152 rc
= MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
4153 MSIINSTALLCONTEXT_USERMANAGED
, MSICODE_PRODUCT
,
4154 INSTALLPROPERTY_DISKPROMPTW
,NULL
,&size
);
4155 if (rc
== ERROR_MORE_DATA
)
4157 prompt
= msi_alloc(size
* sizeof(WCHAR
));
4158 MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
4159 MSIINSTALLCONTEXT_USERMANAGED
, MSICODE_PRODUCT
,
4160 INSTALLPROPERTY_DISKPROMPTW
,prompt
,&size
);
4163 prompt
= strdupW(package
->PackagePath
);
4165 msg
= generate_error_string(package
,1302,1,prompt
);
4166 while(attrib
== INVALID_FILE_ATTRIBUTES
)
4168 rc
= MessageBoxW(NULL
,msg
,NULL
,MB_OKCANCEL
);
4171 rc
= ERROR_INSTALL_USEREXIT
;
4174 attrib
= GetFileAttributesW(package
->PackagePath
);
4180 return ERROR_SUCCESS
;
4185 static UINT
ACTION_RegisterUser(MSIPACKAGE
*package
)
4192 static const WCHAR szPropKeys
[][80] =
4194 {'P','r','o','d','u','c','t','I','D',0},
4195 {'U','S','E','R','N','A','M','E',0},
4196 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4200 static const WCHAR szRegKeys
[][80] =
4202 {'P','r','o','d','u','c','t','I','D',0},
4203 {'R','e','g','O','w','n','e','r',0},
4204 {'R','e','g','C','o','m','p','a','n','y',0},
4208 productid
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTIDW
);
4210 return ERROR_SUCCESS
;
4212 rc
= MSIREG_OpenUninstallKey(package
->ProductCode
,&hkey
,TRUE
);
4213 if (rc
!= ERROR_SUCCESS
)
4216 for( i
= 0; szPropKeys
[i
][0]; i
++ )
4218 buffer
= msi_dup_property( package
, szPropKeys
[i
] );
4219 msi_reg_set_val_str( hkey
, szRegKeys
[i
], buffer
);
4224 msi_free(productid
);
4227 /* FIXME: call ui_actiondata */
4229 return ERROR_SUCCESS
;
4233 static UINT
ACTION_ExecuteAction(MSIPACKAGE
*package
)
4237 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
4238 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
4243 static UINT
ITERATE_PublishComponent(MSIRECORD
*rec
, LPVOID param
)
4245 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4246 LPCWSTR compgroupid
=NULL
;
4247 LPCWSTR feature
=NULL
;
4248 LPCWSTR text
= NULL
;
4249 LPCWSTR qualifier
= NULL
;
4250 LPCWSTR component
= NULL
;
4251 LPWSTR advertise
= NULL
;
4252 LPWSTR output
= NULL
;
4254 UINT rc
= ERROR_SUCCESS
;
4259 component
= MSI_RecordGetString(rec
,3);
4260 comp
= get_loaded_component(package
,component
);
4262 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
) &&
4263 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_SOURCE
) &&
4264 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ADVERTISED
))
4266 TRACE("Skipping: Component %s not scheduled for install\n",
4267 debugstr_w(component
));
4269 return ERROR_SUCCESS
;
4272 compgroupid
= MSI_RecordGetString(rec
,1);
4273 qualifier
= MSI_RecordGetString(rec
,2);
4275 rc
= MSIREG_OpenUserComponentsKey(compgroupid
, &hkey
, TRUE
);
4276 if (rc
!= ERROR_SUCCESS
)
4279 text
= MSI_RecordGetString(rec
,4);
4280 feature
= MSI_RecordGetString(rec
,5);
4282 advertise
= create_component_advertise_string(package
, comp
, feature
);
4284 sz
= strlenW(advertise
);
4287 sz
+= lstrlenW(text
);
4290 sz
*= sizeof(WCHAR
);
4292 output
= msi_alloc_zero(sz
);
4293 strcpyW(output
,advertise
);
4294 msi_free(advertise
);
4297 strcatW(output
,text
);
4299 msi_reg_set_val_multi_str( hkey
, qualifier
, output
);
4306 uirow
= MSI_CreateRecord( 2 );
4307 MSI_RecordSetStringW( uirow
, 1, compgroupid
);
4308 MSI_RecordSetStringW( uirow
, 2, qualifier
);
4309 ui_actiondata( package
, szPublishComponents
, uirow
);
4310 msiobj_release( &uirow
->hdr
);
4311 /* FIXME: call ui_progress? */
4317 * At present I am ignorning the advertised components part of this and only
4318 * focusing on the qualified component sets
4320 static UINT
ACTION_PublishComponents(MSIPACKAGE
*package
)
4324 static const WCHAR ExecSeqQuery
[] =
4325 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4326 '`','P','u','b','l','i','s','h',
4327 'C','o','m','p','o','n','e','n','t','`',0};
4329 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4330 if (rc
!= ERROR_SUCCESS
)
4331 return ERROR_SUCCESS
;
4333 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_PublishComponent
, package
);
4334 msiobj_release(&view
->hdr
);
4339 static UINT
ITERATE_InstallService(MSIRECORD
*rec
, LPVOID param
)
4341 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4344 SC_HANDLE hscm
, service
= NULL
;
4345 LPCWSTR name
, disp
, comp
, depends
, pass
;
4346 LPCWSTR load_order
, serv_name
, key
;
4347 DWORD serv_type
, start_type
;
4350 static const WCHAR query
[] =
4351 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4352 '`','C','o','m','p','o','n','e','n','t','`',' ',
4353 'W','H','E','R','E',' ',
4354 '`','C','o','m','p','o','n','e','n','t','`',' ',
4355 '=','\'','%','s','\'',0};
4357 hscm
= OpenSCManagerW(NULL
, SERVICES_ACTIVE_DATABASEW
, GENERIC_WRITE
);
4360 ERR("Failed to open the SC Manager!\n");
4364 start_type
= MSI_RecordGetInteger(rec
, 5);
4365 if (start_type
== SERVICE_BOOT_START
|| start_type
== SERVICE_SYSTEM_START
)
4368 depends
= MSI_RecordGetString(rec
, 8);
4369 if (depends
&& *depends
)
4370 FIXME("Dependency list unhandled!\n");
4372 name
= MSI_RecordGetString(rec
, 2);
4373 disp
= MSI_RecordGetString(rec
, 3);
4374 serv_type
= MSI_RecordGetInteger(rec
, 4);
4375 err_control
= MSI_RecordGetInteger(rec
, 6);
4376 load_order
= MSI_RecordGetString(rec
, 7);
4377 serv_name
= MSI_RecordGetString(rec
, 9);
4378 pass
= MSI_RecordGetString(rec
, 10);
4379 comp
= MSI_RecordGetString(rec
, 12);
4381 /* fetch the service path */
4382 row
= MSI_QueryGetRecord(package
->db
, query
, comp
);
4385 ERR("Control query failed!\n");
4389 key
= MSI_RecordGetString(row
, 6);
4391 file
= get_loaded_file(package
, key
);
4392 msiobj_release(&row
->hdr
);
4395 ERR("Failed to load the service file\n");
4399 service
= CreateServiceW(hscm
, name
, disp
, GENERIC_ALL
, serv_type
,
4400 start_type
, err_control
, file
->TargetPath
,
4401 load_order
, NULL
, NULL
, serv_name
, pass
);
4404 if (GetLastError() != ERROR_SERVICE_EXISTS
)
4405 ERR("Failed to create service %s: %d\n", debugstr_w(name
), GetLastError());
4409 CloseServiceHandle(service
);
4410 CloseServiceHandle(hscm
);
4412 return ERROR_SUCCESS
;
4415 static UINT
ACTION_InstallServices( MSIPACKAGE
*package
)
4419 static const WCHAR ExecSeqQuery
[] =
4420 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4421 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4423 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4424 if (rc
!= ERROR_SUCCESS
)
4425 return ERROR_SUCCESS
;
4427 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallService
, package
);
4428 msiobj_release(&view
->hdr
);
4433 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4434 static LPCWSTR
*msi_service_args_to_vector(LPCWSTR name
, LPWSTR args
, DWORD
*numargs
)
4440 static const WCHAR separator
[] = {'[','~',']',0};
4443 sep_len
= sizeof(separator
) / sizeof(WCHAR
) - 1;
4448 vector
= msi_alloc(sizeof(LPWSTR
));
4456 vector
[*numargs
- 1] = p
;
4458 if ((q
= strstrW(p
, separator
)))
4462 vector
= msi_realloc(vector
, (*numargs
+ 1) * sizeof(LPWSTR
));
4473 static UINT
ITERATE_StartService(MSIRECORD
*rec
, LPVOID param
)
4475 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4477 SC_HANDLE scm
, service
= NULL
;
4478 LPCWSTR name
, *vector
= NULL
;
4480 DWORD event
, numargs
;
4481 UINT r
= ERROR_FUNCTION_FAILED
;
4483 comp
= get_loaded_component(package
, MSI_RecordGetString(rec
, 6));
4484 if (!comp
|| comp
->Action
== INSTALLSTATE_UNKNOWN
|| comp
->Action
== INSTALLSTATE_ABSENT
)
4485 return ERROR_SUCCESS
;
4487 name
= MSI_RecordGetString(rec
, 2);
4488 event
= MSI_RecordGetInteger(rec
, 3);
4489 args
= strdupW(MSI_RecordGetString(rec
, 4));
4491 if (!(event
& msidbServiceControlEventStart
))
4492 return ERROR_SUCCESS
;
4494 scm
= OpenSCManagerW(NULL
, NULL
, SC_MANAGER_CONNECT
);
4497 ERR("Failed to open the service control manager\n");
4501 service
= OpenServiceW(scm
, name
, SERVICE_START
);
4504 ERR("Failed to open service %s\n", debugstr_w(name
));
4508 vector
= msi_service_args_to_vector(name
, args
, &numargs
);
4510 if (!StartServiceW(service
, numargs
, vector
))
4512 ERR("Failed to start service %s\n", debugstr_w(name
));
4519 CloseServiceHandle(service
);
4520 CloseServiceHandle(scm
);
4527 static UINT
ACTION_StartServices( MSIPACKAGE
*package
)
4532 static const WCHAR query
[] = {
4533 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4534 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4536 rc
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
4537 if (rc
!= ERROR_SUCCESS
)
4538 return ERROR_SUCCESS
;
4540 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_StartService
, package
);
4541 msiobj_release(&view
->hdr
);
4546 static MSIFILE
*msi_find_file( MSIPACKAGE
*package
, LPCWSTR filename
)
4550 LIST_FOR_EACH_ENTRY(file
, &package
->files
, MSIFILE
, entry
)
4552 if (!lstrcmpW(file
->File
, filename
))
4559 static UINT
ITERATE_InstallODBCDriver( MSIRECORD
*rec
, LPVOID param
)
4561 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4562 LPWSTR driver
, driver_path
, ptr
;
4563 WCHAR outpath
[MAX_PATH
];
4564 MSIFILE
*driver_file
, *setup_file
;
4567 UINT r
= ERROR_SUCCESS
;
4569 static const WCHAR driver_fmt
[] = {
4570 'D','r','i','v','e','r','=','%','s',0};
4571 static const WCHAR setup_fmt
[] = {
4572 'S','e','t','u','p','=','%','s',0};
4573 static const WCHAR usage_fmt
[] = {
4574 'F','i','l','e','U','s','a','g','e','=','1',0};
4576 desc
= MSI_RecordGetString(rec
, 3);
4578 driver_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 4));
4579 setup_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 5));
4581 if (!driver_file
|| !setup_file
)
4583 ERR("ODBC Driver entry not found!\n");
4584 return ERROR_FUNCTION_FAILED
;
4587 len
= lstrlenW(desc
) + lstrlenW(driver_fmt
) + lstrlenW(driver_file
->FileName
) +
4588 lstrlenW(setup_fmt
) + lstrlenW(setup_file
->FileName
) +
4589 lstrlenW(usage_fmt
) + 1;
4590 driver
= msi_alloc(len
* sizeof(WCHAR
));
4592 return ERROR_OUTOFMEMORY
;
4595 lstrcpyW(ptr
, desc
);
4596 ptr
+= lstrlenW(ptr
) + 1;
4598 sprintfW(ptr
, driver_fmt
, driver_file
->FileName
);
4599 ptr
+= lstrlenW(ptr
) + 1;
4601 sprintfW(ptr
, setup_fmt
, setup_file
->FileName
);
4602 ptr
+= lstrlenW(ptr
) + 1;
4604 lstrcpyW(ptr
, usage_fmt
);
4605 ptr
+= lstrlenW(ptr
) + 1;
4608 driver_path
= strdupW(driver_file
->TargetPath
);
4609 ptr
= strrchrW(driver_path
, '\\');
4610 if (ptr
) *ptr
= '\0';
4612 if (!SQLInstallDriverExW(driver
, driver_path
, outpath
, MAX_PATH
,
4613 NULL
, ODBC_INSTALL_COMPLETE
, &usage
))
4615 ERR("Failed to install SQL driver!\n");
4616 r
= ERROR_FUNCTION_FAILED
;
4620 msi_free(driver_path
);
4625 static UINT
ITERATE_InstallODBCTranslator( MSIRECORD
*rec
, LPVOID param
)
4627 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4628 LPWSTR translator
, translator_path
, ptr
;
4629 WCHAR outpath
[MAX_PATH
];
4630 MSIFILE
*translator_file
, *setup_file
;
4633 UINT r
= ERROR_SUCCESS
;
4635 static const WCHAR translator_fmt
[] = {
4636 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4637 static const WCHAR setup_fmt
[] = {
4638 'S','e','t','u','p','=','%','s',0};
4640 desc
= MSI_RecordGetString(rec
, 3);
4642 translator_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 4));
4643 setup_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 5));
4645 if (!translator_file
|| !setup_file
)
4647 ERR("ODBC Translator entry not found!\n");
4648 return ERROR_FUNCTION_FAILED
;
4651 len
= lstrlenW(desc
) + lstrlenW(translator_fmt
) + lstrlenW(translator_file
->FileName
) +
4652 lstrlenW(setup_fmt
) + lstrlenW(setup_file
->FileName
) + 1;
4653 translator
= msi_alloc(len
* sizeof(WCHAR
));
4655 return ERROR_OUTOFMEMORY
;
4658 lstrcpyW(ptr
, desc
);
4659 ptr
+= lstrlenW(ptr
) + 1;
4661 sprintfW(ptr
, translator_fmt
, translator_file
->FileName
);
4662 ptr
+= lstrlenW(ptr
) + 1;
4664 sprintfW(ptr
, setup_fmt
, setup_file
->FileName
);
4665 ptr
+= lstrlenW(ptr
) + 1;
4668 translator_path
= strdupW(translator_file
->TargetPath
);
4669 ptr
= strrchrW(translator_path
, '\\');
4670 if (ptr
) *ptr
= '\0';
4672 if (!SQLInstallTranslatorExW(translator
, translator_path
, outpath
, MAX_PATH
,
4673 NULL
, ODBC_INSTALL_COMPLETE
, &usage
))
4675 ERR("Failed to install SQL translator!\n");
4676 r
= ERROR_FUNCTION_FAILED
;
4679 msi_free(translator
);
4680 msi_free(translator_path
);
4685 static UINT
ITERATE_InstallODBCDataSource( MSIRECORD
*rec
, LPVOID param
)
4688 LPCWSTR desc
, driver
;
4689 WORD request
= ODBC_ADD_SYS_DSN
;
4692 UINT r
= ERROR_SUCCESS
;
4694 static const WCHAR attrs_fmt
[] = {
4695 'D','S','N','=','%','s',0 };
4697 desc
= MSI_RecordGetString(rec
, 3);
4698 driver
= MSI_RecordGetString(rec
, 4);
4699 registration
= MSI_RecordGetInteger(rec
, 5);
4701 if (registration
== msidbODBCDataSourceRegistrationPerMachine
) request
= ODBC_ADD_SYS_DSN
;
4702 else if (registration
== msidbODBCDataSourceRegistrationPerUser
) request
= ODBC_ADD_DSN
;
4704 len
= lstrlenW(attrs_fmt
) + lstrlenW(desc
) + 1 + 1;
4705 attrs
= msi_alloc(len
* sizeof(WCHAR
));
4707 return ERROR_OUTOFMEMORY
;
4709 sprintfW(attrs
, attrs_fmt
, desc
);
4710 attrs
[len
- 1] = '\0';
4712 if (!SQLConfigDataSourceW(NULL
, request
, driver
, attrs
))
4714 ERR("Failed to install SQL data source!\n");
4715 r
= ERROR_FUNCTION_FAILED
;
4723 static UINT
ACTION_InstallODBC( MSIPACKAGE
*package
)
4728 static const WCHAR driver_query
[] = {
4729 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4730 'O','D','B','C','D','r','i','v','e','r',0 };
4732 static const WCHAR translator_query
[] = {
4733 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4734 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4736 static const WCHAR source_query
[] = {
4737 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4738 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4740 rc
= MSI_DatabaseOpenViewW(package
->db
, driver_query
, &view
);
4741 if (rc
!= ERROR_SUCCESS
)
4742 return ERROR_SUCCESS
;
4744 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCDriver
, package
);
4745 msiobj_release(&view
->hdr
);
4747 rc
= MSI_DatabaseOpenViewW(package
->db
, translator_query
, &view
);
4748 if (rc
!= ERROR_SUCCESS
)
4749 return ERROR_SUCCESS
;
4751 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCTranslator
, package
);
4752 msiobj_release(&view
->hdr
);
4754 rc
= MSI_DatabaseOpenViewW(package
->db
, source_query
, &view
);
4755 if (rc
!= ERROR_SUCCESS
)
4756 return ERROR_SUCCESS
;
4758 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCDataSource
, package
);
4759 msiobj_release(&view
->hdr
);
4764 #define ENV_ACT_SETALWAYS 0x1
4765 #define ENV_ACT_SETABSENT 0x2
4766 #define ENV_ACT_REMOVE 0x4
4767 #define ENV_ACT_REMOVEMATCH 0x8
4769 #define ENV_MOD_MACHINE 0x20000000
4770 #define ENV_MOD_APPEND 0x40000000
4771 #define ENV_MOD_PREFIX 0x80000000
4772 #define ENV_MOD_MASK 0xC0000000
4774 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4776 static LONG
env_set_flags( LPCWSTR
*name
, LPCWSTR
*value
, DWORD
*flags
)
4778 LPCWSTR cptr
= *name
;
4779 LPCWSTR ptr
= *value
;
4781 static const WCHAR prefix
[] = {'[','~',']',0};
4782 static const int prefix_len
= 3;
4788 *flags
|= ENV_ACT_SETALWAYS
;
4789 else if (*cptr
== '+')
4790 *flags
|= ENV_ACT_SETABSENT
;
4791 else if (*cptr
== '-')
4792 *flags
|= ENV_ACT_REMOVE
;
4793 else if (*cptr
== '!')
4794 *flags
|= ENV_ACT_REMOVEMATCH
;
4795 else if (*cptr
== '*')
4796 *flags
|= ENV_MOD_MACHINE
;
4806 ERR("Missing environment variable\n");
4807 return ERROR_FUNCTION_FAILED
;
4810 if (!strncmpW(ptr
, prefix
, prefix_len
))
4812 *flags
|= ENV_MOD_APPEND
;
4813 *value
+= lstrlenW(prefix
);
4815 else if (lstrlenW(*value
) >= prefix_len
)
4817 ptr
+= lstrlenW(ptr
) - prefix_len
;
4818 if (!lstrcmpW(ptr
, prefix
))
4820 *flags
|= ENV_MOD_PREFIX
;
4821 /* the "[~]" will be removed by deformat_string */;
4826 check_flag_combo(*flags
, ENV_ACT_SETALWAYS
| ENV_ACT_SETABSENT
) ||
4827 check_flag_combo(*flags
, ENV_ACT_REMOVEMATCH
| ENV_ACT_SETABSENT
) ||
4828 check_flag_combo(*flags
, ENV_ACT_REMOVEMATCH
| ENV_ACT_SETALWAYS
) ||
4829 check_flag_combo(*flags
, ENV_ACT_SETABSENT
| ENV_MOD_MASK
))
4831 ERR("Invalid flags: %08x\n", *flags
);
4832 return ERROR_FUNCTION_FAILED
;
4835 return ERROR_SUCCESS
;
4838 static UINT
ITERATE_WriteEnvironmentString( MSIRECORD
*rec
, LPVOID param
)
4840 MSIPACKAGE
*package
= param
;
4841 LPCWSTR name
, value
, comp
;
4842 LPWSTR data
= NULL
, newval
= NULL
;
4843 LPWSTR deformatted
= NULL
, ptr
;
4844 DWORD flags
, type
, size
;
4846 HKEY env
= NULL
, root
= HKEY_CURRENT_USER
;
4848 static const WCHAR environment
[] =
4849 {'S','y','s','t','e','m','\\',
4850 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4851 'C','o','n','t','r','o','l','\\',
4852 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4853 'E','n','v','i','r','o','n','m','e','n','t',0};
4854 static const WCHAR semicolon
[] = {';',0};
4856 name
= MSI_RecordGetString(rec
, 2);
4857 value
= MSI_RecordGetString(rec
, 3);
4858 comp
= MSI_RecordGetString(rec
, 4);
4860 res
= env_set_flags(&name
, &value
, &flags
);
4861 if (res
!= ERROR_SUCCESS
)
4864 deformat_string(package
, value
, &deformatted
);
4867 res
= ERROR_OUTOFMEMORY
;
4871 value
= deformatted
;
4873 if (flags
& ENV_MOD_MACHINE
)
4874 root
= HKEY_LOCAL_MACHINE
;
4876 res
= RegOpenKeyExW(root
, environment
, 0, KEY_ALL_ACCESS
, &env
);
4877 if (res
!= ERROR_SUCCESS
)
4880 if (flags
& ENV_ACT_REMOVE
)
4881 FIXME("Not removing environment variable on uninstall!\n");
4884 res
= RegQueryValueExW(env
, name
, NULL
, &type
, NULL
, &size
);
4885 if ((res
!= ERROR_SUCCESS
&& res
!= ERROR_FILE_NOT_FOUND
) ||
4886 (res
== ERROR_SUCCESS
&& type
!= REG_SZ
))
4889 if (res
!= ERROR_FILE_NOT_FOUND
)
4891 if (flags
& ENV_ACT_SETABSENT
)
4893 res
= ERROR_SUCCESS
;
4897 data
= msi_alloc(size
);
4901 return ERROR_OUTOFMEMORY
;
4904 res
= RegQueryValueExW(env
, name
, NULL
, &type
, (LPVOID
)data
, &size
);
4905 if (res
!= ERROR_SUCCESS
)
4908 if (flags
& ENV_ACT_REMOVEMATCH
&& (!value
|| !lstrcmpW(data
, value
)))
4910 res
= RegDeleteKeyW(env
, name
);
4914 size
= (lstrlenW(value
) + 1 + size
) * sizeof(WCHAR
);
4915 newval
= msi_alloc(size
);
4919 res
= ERROR_OUTOFMEMORY
;
4923 if (!(flags
& ENV_MOD_MASK
))
4924 lstrcpyW(newval
, value
);
4927 if (flags
& ENV_MOD_PREFIX
)
4929 lstrcpyW(newval
, value
);
4930 lstrcatW(newval
, semicolon
);
4931 ptr
= newval
+ lstrlenW(value
) + 1;
4934 lstrcpyW(ptr
, data
);
4936 if (flags
& ENV_MOD_APPEND
)
4938 lstrcatW(newval
, semicolon
);
4939 lstrcatW(newval
, value
);
4945 size
= (lstrlenW(value
) + 1) * sizeof(WCHAR
);
4946 newval
= msi_alloc(size
);
4949 res
= ERROR_OUTOFMEMORY
;
4953 lstrcpyW(newval
, value
);
4956 TRACE("setting %s to %s\n", debugstr_w(name
), debugstr_w(newval
));
4957 res
= RegSetValueExW(env
, name
, 0, type
, (LPVOID
)newval
, size
);
4960 if (env
) RegCloseKey(env
);
4961 msi_free(deformatted
);
4967 static UINT
ACTION_WriteEnvironmentStrings( MSIPACKAGE
*package
)
4971 static const WCHAR ExecSeqQuery
[] =
4972 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4973 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
4974 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4975 if (rc
!= ERROR_SUCCESS
)
4976 return ERROR_SUCCESS
;
4978 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteEnvironmentString
, package
);
4979 msiobj_release(&view
->hdr
);
4984 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
4995 static BOOL
msi_move_file(LPCWSTR source
, LPCWSTR dest
, int options
)
4999 if (GetFileAttributesW(source
) == FILE_ATTRIBUTE_DIRECTORY
||
5000 GetFileAttributesW(dest
) == FILE_ATTRIBUTE_DIRECTORY
)
5002 WARN("Source or dest is directory, not moving\n");
5006 if (options
== msidbMoveFileOptionsMove
)
5008 TRACE("moving %s -> %s\n", debugstr_w(source
), debugstr_w(dest
));
5009 ret
= MoveFileExW(source
, dest
, MOVEFILE_REPLACE_EXISTING
);
5012 WARN("MoveFile failed: %d\n", GetLastError());
5018 TRACE("copying %s -> %s\n", debugstr_w(source
), debugstr_w(dest
));
5019 ret
= CopyFileW(source
, dest
, FALSE
);
5022 WARN("CopyFile failed: %d\n", GetLastError());
5030 static LPWSTR
wildcard_to_file(LPWSTR wildcard
, LPWSTR filename
)
5033 DWORD dirlen
, pathlen
;
5035 ptr
= strrchrW(wildcard
, '\\');
5036 dirlen
= ptr
- wildcard
+ 1;
5038 pathlen
= dirlen
+ lstrlenW(filename
) + 1;
5039 path
= msi_alloc(pathlen
* sizeof(WCHAR
));
5041 lstrcpynW(path
, wildcard
, dirlen
+ 1);
5042 lstrcatW(path
, filename
);
5047 static void free_file_entry(FILE_LIST
*file
)
5049 msi_free(file
->source
);
5050 msi_free(file
->dest
);
5054 static void free_list(FILE_LIST
*list
)
5056 while (!list_empty(&list
->entry
))
5058 FILE_LIST
*file
= LIST_ENTRY(list_head(&list
->entry
), FILE_LIST
, entry
);
5060 list_remove(&file
->entry
);
5061 free_file_entry(file
);
5065 static BOOL
add_wildcard(FILE_LIST
*files
, LPWSTR source
, LPWSTR dest
)
5067 FILE_LIST
*new, *file
;
5068 LPWSTR ptr
, filename
;
5071 new = msi_alloc_zero(sizeof(FILE_LIST
));
5075 new->source
= strdupW(source
);
5076 ptr
= strrchrW(dest
, '\\') + 1;
5077 filename
= strrchrW(new->source
, '\\') + 1;
5079 new->sourcename
= filename
;
5082 new->destname
= ptr
;
5084 new->destname
= new->sourcename
;
5086 size
= (ptr
- dest
) + lstrlenW(filename
) + 1;
5087 new->dest
= msi_alloc(size
* sizeof(WCHAR
));
5090 free_file_entry(new);
5094 lstrcpynW(new->dest
, dest
, ptr
- dest
+ 1);
5095 lstrcatW(new->dest
, filename
);
5097 if (list_empty(&files
->entry
))
5099 list_add_head(&files
->entry
, &new->entry
);
5103 LIST_FOR_EACH_ENTRY(file
, &files
->entry
, FILE_LIST
, entry
)
5105 if (lstrcmpW(source
, file
->source
) < 0)
5107 list_add_before(&file
->entry
, &new->entry
);
5112 list_add_after(&file
->entry
, &new->entry
);
5116 BOOL
move_files_wildcard(LPWSTR source
, LPWSTR dest
, int options
)
5118 WIN32_FIND_DATAW wfd
;
5122 FILE_LIST files
, *file
;
5125 hfile
= FindFirstFileW(source
, &wfd
);
5126 if (hfile
== INVALID_HANDLE_VALUE
) return FALSE
;
5128 list_init(&files
.entry
);
5130 for (res
= TRUE
; res
; res
= FindNextFileW(hfile
, &wfd
))
5132 if (is_dot_dir(wfd
.cFileName
)) continue;
5134 path
= wildcard_to_file(source
, wfd
.cFileName
);
5141 add_wildcard(&files
, path
, dest
);
5145 /* only the first wildcard match gets renamed to dest */
5146 file
= LIST_ENTRY(list_head(&files
.entry
), FILE_LIST
, entry
);
5147 size
= (strrchrW(file
->dest
, '\\') - file
->dest
) + lstrlenW(file
->destname
) + 2;
5148 file
->dest
= msi_realloc(file
->dest
, size
* sizeof(WCHAR
));
5155 lstrcpyW(strrchrW(file
->dest
, '\\') + 1, file
->destname
);
5157 while (!list_empty(&files
.entry
))
5159 file
= LIST_ENTRY(list_head(&files
.entry
), FILE_LIST
, entry
);
5161 msi_move_file((LPCWSTR
)file
->source
, (LPCWSTR
)file
->dest
, options
);
5163 list_remove(&file
->entry
);
5164 free_file_entry(file
);
5170 static UINT
ITERATE_MoveFiles( MSIRECORD
*rec
, LPVOID param
)
5172 MSIPACKAGE
*package
= param
;
5174 LPCWSTR sourcename
, destname
;
5175 LPWSTR sourcedir
= NULL
, destdir
= NULL
;
5176 LPWSTR source
= NULL
, dest
= NULL
;
5179 BOOL ret
, wildcards
;
5181 static const WCHAR backslash
[] = {'\\',0};
5183 comp
= get_loaded_component(package
, MSI_RecordGetString(rec
, 2));
5184 if (!comp
|| !comp
->Enabled
||
5185 !(comp
->Action
& (INSTALLSTATE_LOCAL
| INSTALLSTATE_SOURCE
)))
5187 TRACE("Component not set for install, not moving file\n");
5188 return ERROR_SUCCESS
;
5191 sourcename
= MSI_RecordGetString(rec
, 3);
5192 destname
= MSI_RecordGetString(rec
, 4);
5193 options
= MSI_RecordGetInteger(rec
, 7);
5195 sourcedir
= msi_dup_property(package
, MSI_RecordGetString(rec
, 5));
5199 destdir
= msi_dup_property(package
, MSI_RecordGetString(rec
, 6));
5205 if (GetFileAttributesW(sourcedir
) == INVALID_FILE_ATTRIBUTES
)
5208 source
= strdupW(sourcedir
);
5214 size
= lstrlenW(sourcedir
) + lstrlenW(sourcename
) + 2;
5215 source
= msi_alloc(size
* sizeof(WCHAR
));
5219 lstrcpyW(source
, sourcedir
);
5220 if (source
[lstrlenW(source
) - 1] != '\\')
5221 lstrcatW(source
, backslash
);
5222 lstrcatW(source
, sourcename
);
5225 wildcards
= strchrW(source
, '*') || strchrW(source
, '?');
5227 if (!destname
&& !wildcards
)
5229 destname
= strdupW(sourcename
);
5236 size
= lstrlenW(destname
);
5238 size
+= lstrlenW(destdir
) + 2;
5239 dest
= msi_alloc(size
* sizeof(WCHAR
));
5243 lstrcpyW(dest
, destdir
);
5244 if (dest
[lstrlenW(dest
) - 1] != '\\')
5245 lstrcatW(dest
, backslash
);
5248 lstrcatW(dest
, destname
);
5250 if (GetFileAttributesW(destdir
) == INVALID_FILE_ATTRIBUTES
)
5252 ret
= CreateDirectoryW(destdir
, NULL
);
5255 WARN("CreateDirectory failed: %d\n", GetLastError());
5256 return ERROR_SUCCESS
;
5261 msi_move_file(source
, dest
, options
);
5263 move_files_wildcard(source
, dest
, options
);
5266 msi_free(sourcedir
);
5271 return ERROR_SUCCESS
;
5274 static UINT
ACTION_MoveFiles( MSIPACKAGE
*package
)
5279 static const WCHAR ExecSeqQuery
[] =
5280 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5281 '`','M','o','v','e','F','i','l','e','`',0};
5283 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
5284 if (rc
!= ERROR_SUCCESS
)
5285 return ERROR_SUCCESS
;
5287 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_MoveFiles
, package
);
5288 msiobj_release(&view
->hdr
);
5293 static UINT
msi_unimplemented_action_stub( MSIPACKAGE
*package
,
5294 LPCSTR action
, LPCWSTR table
)
5296 static const WCHAR query
[] = {
5297 'S','E','L','E','C','T',' ','*',' ',
5298 'F','R','O','M',' ','`','%','s','`',0 };
5299 MSIQUERY
*view
= NULL
;
5303 r
= MSI_OpenQuery( package
->db
, &view
, query
, table
);
5304 if (r
== ERROR_SUCCESS
)
5306 r
= MSI_IterateRecords(view
, &count
, NULL
, package
);
5307 msiobj_release(&view
->hdr
);
5311 FIXME("%s -> %u ignored %s table values\n",
5312 action
, count
, debugstr_w(table
));
5314 return ERROR_SUCCESS
;
5317 static UINT
ACTION_AllocateRegistrySpace( MSIPACKAGE
*package
)
5319 TRACE("%p\n", package
);
5320 return ERROR_SUCCESS
;
5323 static UINT
ACTION_RemoveIniValues( MSIPACKAGE
*package
)
5325 static const WCHAR table
[] =
5326 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5327 return msi_unimplemented_action_stub( package
, "RemoveIniValues", table
);
5330 static UINT
ACTION_PatchFiles( MSIPACKAGE
*package
)
5332 static const WCHAR table
[] = { 'P','a','t','c','h',0 };
5333 return msi_unimplemented_action_stub( package
, "PatchFiles", table
);
5336 static UINT
ACTION_BindImage( MSIPACKAGE
*package
)
5338 static const WCHAR table
[] = { 'B','i','n','d','I','m','a','g','e',0 };
5339 return msi_unimplemented_action_stub( package
, "BindImage", table
);
5342 static UINT
ACTION_IsolateComponents( MSIPACKAGE
*package
)
5344 static const WCHAR table
[] = {
5345 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5346 return msi_unimplemented_action_stub( package
, "IsolateComponents", table
);
5349 static UINT
ACTION_MigrateFeatureStates( MSIPACKAGE
*package
)
5351 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
5352 return msi_unimplemented_action_stub( package
, "MigrateFeatureStates", table
);
5355 static UINT
ACTION_SelfUnregModules( MSIPACKAGE
*package
)
5357 static const WCHAR table
[] = { 'S','e','l','f','R','e','g',0 };
5358 return msi_unimplemented_action_stub( package
, "SelfUnregModules", table
);
5361 static UINT
ACTION_StopServices( MSIPACKAGE
*package
)
5363 static const WCHAR table
[] = {
5364 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5365 return msi_unimplemented_action_stub( package
, "StopServices", table
);
5368 static UINT
ACTION_DeleteServices( MSIPACKAGE
*package
)
5370 static const WCHAR table
[] = {
5371 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5372 return msi_unimplemented_action_stub( package
, "DeleteServices", table
);
5374 static UINT
ACTION_ValidateProductID( MSIPACKAGE
*package
)
5376 static const WCHAR table
[] = {
5377 'P','r','o','d','u','c','t','I','D',0 };
5378 return msi_unimplemented_action_stub( package
, "ValidateProductID", table
);
5381 static UINT
ACTION_RemoveEnvironmentStrings( MSIPACKAGE
*package
)
5383 static const WCHAR table
[] = {
5384 'E','n','v','i','r','o','n','m','e','n','t',0 };
5385 return msi_unimplemented_action_stub( package
, "RemoveEnvironmentStrings", table
);
5388 static UINT
ACTION_MsiPublishAssemblies( MSIPACKAGE
*package
)
5390 static const WCHAR table
[] = {
5391 'M','s','i','A','s','s','e','m','b','l','y',0 };
5392 return msi_unimplemented_action_stub( package
, "MsiPublishAssemblies", table
);
5395 static UINT
ACTION_MsiUnpublishAssemblies( MSIPACKAGE
*package
)
5397 static const WCHAR table
[] = {
5398 'M','s','i','A','s','s','e','m','b','l','y',0 };
5399 return msi_unimplemented_action_stub( package
, "MsiUnpublishAssemblies", table
);
5402 static UINT
ACTION_UnregisterFonts( MSIPACKAGE
*package
)
5404 static const WCHAR table
[] = { 'F','o','n','t',0 };
5405 return msi_unimplemented_action_stub( package
, "UnregisterFonts", table
);
5408 static UINT
ACTION_CCPSearch( MSIPACKAGE
*package
)
5410 static const WCHAR table
[] = { 'C','C','P','S','e','a','r','c','h',0 };
5411 return msi_unimplemented_action_stub( package
, "CCPSearch", table
);
5414 static UINT
ACTION_RMCCPSearch( MSIPACKAGE
*package
)
5416 static const WCHAR table
[] = { 'C','C','P','S','e','a','r','c','h',0 };
5417 return msi_unimplemented_action_stub( package
, "RMCCPSearch", table
);
5420 static UINT
ACTION_RegisterComPlus( MSIPACKAGE
*package
)
5422 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
5423 return msi_unimplemented_action_stub( package
, "RegisterComPlus", table
);
5426 static UINT
ACTION_UnregisterComPlus( MSIPACKAGE
*package
)
5428 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
5429 return msi_unimplemented_action_stub( package
, "UnregisterComPlus", table
);
5432 static UINT
ACTION_InstallSFPCatalogFile( MSIPACKAGE
*package
)
5434 static const WCHAR table
[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5435 return msi_unimplemented_action_stub( package
, "InstallSFPCatalogFile", table
);
5438 static UINT
ACTION_RemoveDuplicateFiles( MSIPACKAGE
*package
)
5440 static const WCHAR table
[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5441 return msi_unimplemented_action_stub( package
, "RemoveDuplicateFiles", table
);
5444 static UINT
ACTION_RemoveExistingProducts( MSIPACKAGE
*package
)
5446 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
5447 return msi_unimplemented_action_stub( package
, "RemoveExistingProducts", table
);
5450 static UINT
ACTION_RemoveFolders( MSIPACKAGE
*package
)
5452 static const WCHAR table
[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5453 return msi_unimplemented_action_stub( package
, "RemoveFolders", table
);
5456 static UINT
ACTION_RemoveODBC( MSIPACKAGE
*package
)
5458 static const WCHAR table
[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5459 return msi_unimplemented_action_stub( package
, "RemoveODBC", table
);
5462 static UINT
ACTION_RemoveRegistryValues( MSIPACKAGE
*package
)
5464 static const WCHAR table
[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5465 return msi_unimplemented_action_stub( package
, "RemoveRegistryValues", table
);
5468 static UINT
ACTION_RemoveShortcuts( MSIPACKAGE
*package
)
5470 static const WCHAR table
[] = { 'S','h','o','r','t','c','u','t',0 };
5471 return msi_unimplemented_action_stub( package
, "RemoveShortcuts", table
);
5474 static UINT
ACTION_UnpublishComponents( MSIPACKAGE
*package
)
5476 static const WCHAR table
[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5477 return msi_unimplemented_action_stub( package
, "UnpublishComponents", table
);
5480 static UINT
ACTION_UnregisterClassInfo( MSIPACKAGE
*package
)
5482 static const WCHAR table
[] = { 'A','p','p','I','d',0 };
5483 return msi_unimplemented_action_stub( package
, "UnregisterClassInfo", table
);
5486 static UINT
ACTION_UnregisterExtensionInfo( MSIPACKAGE
*package
)
5488 static const WCHAR table
[] = { 'E','x','t','e','n','s','i','o','n',0 };
5489 return msi_unimplemented_action_stub( package
, "UnregisterExtensionInfo", table
);
5492 static UINT
ACTION_UnregisterMIMEInfo( MSIPACKAGE
*package
)
5494 static const WCHAR table
[] = { 'M','I','M','E',0 };
5495 return msi_unimplemented_action_stub( package
, "UnregisterMIMEInfo", table
);
5498 static UINT
ACTION_UnregisterProgIdInfo( MSIPACKAGE
*package
)
5500 static const WCHAR table
[] = { 'P','r','o','g','I','d',0 };
5501 return msi_unimplemented_action_stub( package
, "UnregisterProgIdInfo", table
);
5504 static UINT
ACTION_UnregisterTypeLibraries( MSIPACKAGE
*package
)
5506 static const WCHAR table
[] = { 'T','y','p','e','L','i','b',0 };
5507 return msi_unimplemented_action_stub( package
, "UnregisterTypeLibraries", table
);
5510 static const struct _actions StandardActions
[] = {
5511 { szAllocateRegistrySpace
, ACTION_AllocateRegistrySpace
},
5512 { szAppSearch
, ACTION_AppSearch
},
5513 { szBindImage
, ACTION_BindImage
},
5514 { szCCPSearch
, ACTION_CCPSearch
},
5515 { szCostFinalize
, ACTION_CostFinalize
},
5516 { szCostInitialize
, ACTION_CostInitialize
},
5517 { szCreateFolders
, ACTION_CreateFolders
},
5518 { szCreateShortcuts
, ACTION_CreateShortcuts
},
5519 { szDeleteServices
, ACTION_DeleteServices
},
5520 { szDisableRollback
, NULL
},
5521 { szDuplicateFiles
, ACTION_DuplicateFiles
},
5522 { szExecuteAction
, ACTION_ExecuteAction
},
5523 { szFileCost
, ACTION_FileCost
},
5524 { szFindRelatedProducts
, ACTION_FindRelatedProducts
},
5525 { szForceReboot
, ACTION_ForceReboot
},
5526 { szInstallAdminPackage
, NULL
},
5527 { szInstallExecute
, ACTION_InstallExecute
},
5528 { szInstallExecuteAgain
, ACTION_InstallExecute
},
5529 { szInstallFiles
, ACTION_InstallFiles
},
5530 { szInstallFinalize
, ACTION_InstallFinalize
},
5531 { szInstallInitialize
, ACTION_InstallInitialize
},
5532 { szInstallSFPCatalogFile
, ACTION_InstallSFPCatalogFile
},
5533 { szInstallValidate
, ACTION_InstallValidate
},
5534 { szIsolateComponents
, ACTION_IsolateComponents
},
5535 { szLaunchConditions
, ACTION_LaunchConditions
},
5536 { szMigrateFeatureStates
, ACTION_MigrateFeatureStates
},
5537 { szMoveFiles
, ACTION_MoveFiles
},
5538 { szMsiPublishAssemblies
, ACTION_MsiPublishAssemblies
},
5539 { szMsiUnpublishAssemblies
, ACTION_MsiUnpublishAssemblies
},
5540 { szInstallODBC
, ACTION_InstallODBC
},
5541 { szInstallServices
, ACTION_InstallServices
},
5542 { szPatchFiles
, ACTION_PatchFiles
},
5543 { szProcessComponents
, ACTION_ProcessComponents
},
5544 { szPublishComponents
, ACTION_PublishComponents
},
5545 { szPublishFeatures
, ACTION_PublishFeatures
},
5546 { szPublishProduct
, ACTION_PublishProduct
},
5547 { szRegisterClassInfo
, ACTION_RegisterClassInfo
},
5548 { szRegisterComPlus
, ACTION_RegisterComPlus
},
5549 { szRegisterExtensionInfo
, ACTION_RegisterExtensionInfo
},
5550 { szRegisterFonts
, ACTION_RegisterFonts
},
5551 { szRegisterMIMEInfo
, ACTION_RegisterMIMEInfo
},
5552 { szRegisterProduct
, ACTION_RegisterProduct
},
5553 { szRegisterProgIdInfo
, ACTION_RegisterProgIdInfo
},
5554 { szRegisterTypeLibraries
, ACTION_RegisterTypeLibraries
},
5555 { szRegisterUser
, ACTION_RegisterUser
},
5556 { szRemoveDuplicateFiles
, ACTION_RemoveDuplicateFiles
},
5557 { szRemoveEnvironmentStrings
, ACTION_RemoveEnvironmentStrings
},
5558 { szRemoveExistingProducts
, ACTION_RemoveExistingProducts
},
5559 { szRemoveFiles
, ACTION_RemoveFiles
},
5560 { szRemoveFolders
, ACTION_RemoveFolders
},
5561 { szRemoveIniValues
, ACTION_RemoveIniValues
},
5562 { szRemoveODBC
, ACTION_RemoveODBC
},
5563 { szRemoveRegistryValues
, ACTION_RemoveRegistryValues
},
5564 { szRemoveShortcuts
, ACTION_RemoveShortcuts
},
5565 { szResolveSource
, ACTION_ResolveSource
},
5566 { szRMCCPSearch
, ACTION_RMCCPSearch
},
5567 { szScheduleReboot
, NULL
},
5568 { szSelfRegModules
, ACTION_SelfRegModules
},
5569 { szSelfUnregModules
, ACTION_SelfUnregModules
},
5570 { szSetODBCFolders
, NULL
},
5571 { szStartServices
, ACTION_StartServices
},
5572 { szStopServices
, ACTION_StopServices
},
5573 { szUnpublishComponents
, ACTION_UnpublishComponents
},
5574 { szUnpublishFeatures
, ACTION_UnpublishFeatures
},
5575 { szUnregisterClassInfo
, ACTION_UnregisterClassInfo
},
5576 { szUnregisterComPlus
, ACTION_UnregisterComPlus
},
5577 { szUnregisterExtensionInfo
, ACTION_UnregisterExtensionInfo
},
5578 { szUnregisterFonts
, ACTION_UnregisterFonts
},
5579 { szUnregisterMIMEInfo
, ACTION_UnregisterMIMEInfo
},
5580 { szUnregisterProgIdInfo
, ACTION_UnregisterProgIdInfo
},
5581 { szUnregisterTypeLibraries
, ACTION_UnregisterTypeLibraries
},
5582 { szValidateProductID
, ACTION_ValidateProductID
},
5583 { szWriteEnvironmentStrings
, ACTION_WriteEnvironmentStrings
},
5584 { szWriteIniValues
, ACTION_WriteIniValues
},
5585 { szWriteRegistryValues
, ACTION_WriteRegistryValues
},