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 BOOL
msi_check_unpublish(MSIPACKAGE
*package
)
3759 MSIFEATURE
*feature
;
3761 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3763 if (feature
->ActionRequest
!= INSTALLSTATE_ABSENT
)
3770 static UINT
ACTION_UnpublishFeatures(MSIPACKAGE
*package
)
3772 MSIFEATURE
*feature
;
3774 if (!msi_check_unpublish(package
))
3775 return ERROR_SUCCESS
;
3777 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3779 msi_unpublish_feature(package
, feature
);
3782 return ERROR_SUCCESS
;
3785 static UINT
msi_get_local_package_name( LPWSTR path
)
3787 static const WCHAR szInstaller
[] = {
3788 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3789 static const WCHAR fmt
[] = { '%','x','.','m','s','i',0};
3793 time
= GetTickCount();
3794 GetWindowsDirectoryW( path
, MAX_PATH
);
3795 lstrcatW( path
, szInstaller
);
3796 CreateDirectoryW( path
, NULL
);
3798 len
= lstrlenW(path
);
3799 for (i
=0; i
<0x10000; i
++)
3801 snprintfW( &path
[len
], MAX_PATH
- len
, fmt
, (time
+i
)&0xffff );
3802 handle
= CreateFileW( path
, GENERIC_WRITE
, 0, NULL
,
3803 CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, 0 );
3804 if (handle
!= INVALID_HANDLE_VALUE
)
3806 CloseHandle(handle
);
3809 if (GetLastError() != ERROR_FILE_EXISTS
&&
3810 GetLastError() != ERROR_SHARING_VIOLATION
)
3811 return ERROR_FUNCTION_FAILED
;
3814 return ERROR_SUCCESS
;
3817 static UINT
msi_make_package_local( MSIPACKAGE
*package
, HKEY hkey
)
3819 static const WCHAR szOriginalDatabase
[] =
3820 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3821 WCHAR packagefile
[MAX_PATH
];
3826 r
= msi_get_local_package_name( packagefile
);
3827 if (r
!= ERROR_SUCCESS
)
3830 TRACE("Copying to local package %s\n",debugstr_w(packagefile
));
3832 msiFilePath
= msi_dup_property( package
, szOriginalDatabase
);
3833 r
= CopyFileW( msiFilePath
, packagefile
, FALSE
);
3837 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3838 debugstr_w(msiFilePath
), debugstr_w(packagefile
), GetLastError());
3839 msi_free( msiFilePath
);
3840 return ERROR_FUNCTION_FAILED
;
3842 msi_free( msiFilePath
);
3844 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_LOCALPACKAGEW
, packagefile
);
3846 r
= MSIREG_OpenInstallPropertiesKey(package
->ProductCode
, &props
, TRUE
);
3847 if (r
!= ERROR_SUCCESS
)
3850 msi_reg_set_val_str(props
, INSTALLPROPERTY_LOCALPACKAGEW
, packagefile
);
3852 return ERROR_SUCCESS
;
3855 static UINT
msi_write_uninstall_property_vals( MSIPACKAGE
*package
, HKEY hkey
)
3857 LPWSTR prop
, val
, key
;
3858 static const LPCSTR propval
[] = {
3859 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3860 "ARPCONTACT", "Contact",
3861 "ARPCOMMENTS", "Comments",
3862 "ProductName", "DisplayName",
3863 "ProductVersion", "DisplayVersion",
3864 "ARPHELPLINK", "HelpLink",
3865 "ARPHELPTELEPHONE", "HelpTelephone",
3866 "ARPINSTALLLOCATION", "InstallLocation",
3867 "SourceDir", "InstallSource",
3868 "Manufacturer", "Publisher",
3869 "ARPREADME", "Readme",
3871 "ARPURLINFOABOUT", "URLInfoAbout",
3872 "ARPURLUPDATEINFO", "URLUpdateInfo",
3875 const LPCSTR
*p
= propval
;
3879 prop
= strdupAtoW( *p
++ );
3880 key
= strdupAtoW( *p
++ );
3881 val
= msi_dup_property( package
, prop
);
3882 msi_reg_set_val_str( hkey
, key
, val
);
3887 return ERROR_SUCCESS
;
3890 static UINT
ACTION_RegisterProduct(MSIPACKAGE
*package
)
3893 HKEY hudkey
=0, props
=0;
3894 LPWSTR buffer
= NULL
;
3897 static const WCHAR szWindowsInstaller
[] =
3898 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3899 static const WCHAR szUpgradeCode
[] =
3900 {'U','p','g','r','a','d','e','C','o','d','e',0};
3901 static const WCHAR modpath_fmt
[] =
3902 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3903 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3904 static const WCHAR szModifyPath
[] =
3905 {'M','o','d','i','f','y','P','a','t','h',0};
3906 static const WCHAR szUninstallString
[] =
3907 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3908 static const WCHAR szEstimatedSize
[] =
3909 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3910 static const WCHAR szProductLanguage
[] =
3911 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3912 static const WCHAR szProductVersion
[] =
3913 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3916 static const WCHAR date_fmt
[] = {'%','i','%','i','%','i',0};
3917 LPWSTR upgrade_code
;
3920 /* FIXME: also need to publish if the product is in advertise mode */
3921 if (!msi_check_publish(package
))
3922 return ERROR_SUCCESS
;
3924 rc
= MSIREG_OpenUninstallKey(package
->ProductCode
,&hkey
,TRUE
);
3925 if (rc
!= ERROR_SUCCESS
)
3928 /* dump all the info i can grab */
3929 /* FIXME: Flesh out more information */
3931 msi_write_uninstall_property_vals( package
, hkey
);
3933 msi_reg_set_val_dword( hkey
, szWindowsInstaller
, 1 );
3935 msi_make_package_local( package
, hkey
);
3937 /* do ModifyPath and UninstallString */
3938 size
= deformat_string(package
,modpath_fmt
,&buffer
);
3939 RegSetValueExW(hkey
,szModifyPath
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
3940 RegSetValueExW(hkey
,szUninstallString
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
3943 /* FIXME: Write real Estimated Size when we have it */
3944 msi_reg_set_val_dword( hkey
, szEstimatedSize
, 0 );
3946 GetLocalTime(&systime
);
3947 sprintfW(szDate
,date_fmt
,systime
.wYear
,systime
.wMonth
,systime
.wDay
);
3948 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_INSTALLDATEW
, szDate
);
3950 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
3951 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
3953 buffer
= msi_dup_property( package
, szProductVersion
);
3956 DWORD verdword
= msi_version_str_to_dword(buffer
);
3958 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
3959 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMAJORW
, verdword
>>24 );
3960 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMINORW
, (verdword
>>16)&0x00FF );
3964 /* Handle Upgrade Codes */
3965 upgrade_code
= msi_dup_property( package
, szUpgradeCode
);
3970 MSIREG_OpenUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
3971 squash_guid(package
->ProductCode
,squashed
);
3972 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
3974 MSIREG_OpenUserUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
3975 squash_guid(package
->ProductCode
,squashed
);
3976 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
3979 msi_free(upgrade_code
);
3984 rc
= MSIREG_OpenUserDataProductKey(package
->ProductCode
, &hudkey
, TRUE
);
3985 if (rc
!= ERROR_SUCCESS
)
3988 RegCloseKey(hudkey
);
3990 rc
= MSIREG_OpenInstallPropertiesKey(package
->ProductCode
, &props
, TRUE
);
3991 if (rc
!= ERROR_SUCCESS
)
3994 msi_reg_set_val_dword( props
, szWindowsInstaller
, 1 );
3997 return ERROR_SUCCESS
;
4000 static UINT
ACTION_InstallExecute(MSIPACKAGE
*package
)
4002 return execute_script(package
,INSTALL_SCRIPT
);
4005 static UINT
msi_unpublish_product(MSIPACKAGE
*package
)
4007 LPWSTR remove
= NULL
;
4008 LPWSTR
*features
= NULL
;
4009 BOOL full_uninstall
= TRUE
;
4010 MSIFEATURE
*feature
;
4012 static const WCHAR szRemove
[] = {'R','E','M','O','V','E',0};
4013 static const WCHAR szAll
[] = {'A','L','L',0};
4015 remove
= msi_dup_property(package
, szRemove
);
4017 return ERROR_SUCCESS
;
4019 features
= msi_split_string(remove
, ',');
4023 ERR("REMOVE feature list is empty!\n");
4024 return ERROR_FUNCTION_FAILED
;
4027 if (!lstrcmpW(features
[0], szAll
))
4028 full_uninstall
= TRUE
;
4031 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
4033 if (feature
->Action
!= INSTALLSTATE_ABSENT
)
4034 full_uninstall
= FALSE
;
4038 if (!full_uninstall
)
4041 MSIREG_DeleteProductKey(package
->ProductCode
);
4042 MSIREG_DeleteUserProductKey(package
->ProductCode
);
4043 MSIREG_DeleteUserDataProductKey(package
->ProductCode
);
4044 MSIREG_DeleteUserFeaturesKey(package
->ProductCode
);
4049 return ERROR_SUCCESS
;
4052 static UINT
ACTION_InstallFinalize(MSIPACKAGE
*package
)
4056 rc
= msi_unpublish_product(package
);
4057 if (rc
!= ERROR_SUCCESS
)
4060 /* turn off scheduling */
4061 package
->script
->CurrentlyScripting
= FALSE
;
4063 /* first do the same as an InstallExecute */
4064 rc
= ACTION_InstallExecute(package
);
4065 if (rc
!= ERROR_SUCCESS
)
4068 /* then handle Commit Actions */
4069 rc
= execute_script(package
,COMMIT_SCRIPT
);
4074 UINT
ACTION_ForceReboot(MSIPACKAGE
*package
)
4076 static const WCHAR RunOnce
[] = {
4077 'S','o','f','t','w','a','r','e','\\',
4078 'M','i','c','r','o','s','o','f','t','\\',
4079 'W','i','n','d','o','w','s','\\',
4080 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4081 'R','u','n','O','n','c','e',0};
4082 static const WCHAR InstallRunOnce
[] = {
4083 'S','o','f','t','w','a','r','e','\\',
4084 'M','i','c','r','o','s','o','f','t','\\',
4085 'W','i','n','d','o','w','s','\\',
4086 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4087 'I','n','s','t','a','l','l','e','r','\\',
4088 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4090 static const WCHAR msiexec_fmt
[] = {
4092 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4093 '\"','%','s','\"',0};
4094 static const WCHAR install_fmt
[] = {
4095 '/','I',' ','\"','%','s','\"',' ',
4096 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4097 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4098 WCHAR buffer
[256], sysdir
[MAX_PATH
];
4100 WCHAR squished_pc
[100];
4102 squash_guid(package
->ProductCode
,squished_pc
);
4104 GetSystemDirectoryW(sysdir
, sizeof(sysdir
)/sizeof(sysdir
[0]));
4105 RegCreateKeyW(HKEY_LOCAL_MACHINE
,RunOnce
,&hkey
);
4106 snprintfW(buffer
,sizeof(buffer
)/sizeof(buffer
[0]),msiexec_fmt
,sysdir
,
4109 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
4112 TRACE("Reboot command %s\n",debugstr_w(buffer
));
4114 RegCreateKeyW(HKEY_LOCAL_MACHINE
,InstallRunOnce
,&hkey
);
4115 sprintfW(buffer
,install_fmt
,package
->ProductCode
,squished_pc
);
4117 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
4120 return ERROR_INSTALL_SUSPEND
;
4123 static UINT
msi_set_sourcedir_props(MSIPACKAGE
*package
)
4128 p
= strrchrW( package
->PackagePath
, '\\' );
4130 return ERROR_SUCCESS
;
4132 len
= p
- package
->PackagePath
+ 2;
4133 source
= msi_alloc( len
* sizeof(WCHAR
) );
4134 lstrcpynW( source
, package
->PackagePath
, len
);
4136 MSI_SetPropertyW( package
, cszSourceDir
, source
);
4137 MSI_SetPropertyW( package
, cszSOURCEDIR
, source
);
4141 return ERROR_SUCCESS
;
4144 static UINT
ACTION_ResolveSource(MSIPACKAGE
* package
)
4150 * We are currently doing what should be done here in the top level Install
4151 * however for Administrative and uninstalls this step will be needed
4153 if (!package
->PackagePath
)
4154 return ERROR_SUCCESS
;
4156 msi_set_sourcedir_props(package
);
4158 attrib
= GetFileAttributesW(package
->PackagePath
);
4159 if (attrib
== INVALID_FILE_ATTRIBUTES
)
4165 rc
= MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
4166 MSIINSTALLCONTEXT_USERMANAGED
, MSICODE_PRODUCT
,
4167 INSTALLPROPERTY_DISKPROMPTW
,NULL
,&size
);
4168 if (rc
== ERROR_MORE_DATA
)
4170 prompt
= msi_alloc(size
* sizeof(WCHAR
));
4171 MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
4172 MSIINSTALLCONTEXT_USERMANAGED
, MSICODE_PRODUCT
,
4173 INSTALLPROPERTY_DISKPROMPTW
,prompt
,&size
);
4176 prompt
= strdupW(package
->PackagePath
);
4178 msg
= generate_error_string(package
,1302,1,prompt
);
4179 while(attrib
== INVALID_FILE_ATTRIBUTES
)
4181 rc
= MessageBoxW(NULL
,msg
,NULL
,MB_OKCANCEL
);
4184 rc
= ERROR_INSTALL_USEREXIT
;
4187 attrib
= GetFileAttributesW(package
->PackagePath
);
4193 return ERROR_SUCCESS
;
4198 static UINT
ACTION_RegisterUser(MSIPACKAGE
*package
)
4205 static const WCHAR szPropKeys
[][80] =
4207 {'P','r','o','d','u','c','t','I','D',0},
4208 {'U','S','E','R','N','A','M','E',0},
4209 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4213 static const WCHAR szRegKeys
[][80] =
4215 {'P','r','o','d','u','c','t','I','D',0},
4216 {'R','e','g','O','w','n','e','r',0},
4217 {'R','e','g','C','o','m','p','a','n','y',0},
4221 productid
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTIDW
);
4223 return ERROR_SUCCESS
;
4225 rc
= MSIREG_OpenUninstallKey(package
->ProductCode
,&hkey
,TRUE
);
4226 if (rc
!= ERROR_SUCCESS
)
4229 for( i
= 0; szPropKeys
[i
][0]; i
++ )
4231 buffer
= msi_dup_property( package
, szPropKeys
[i
] );
4232 msi_reg_set_val_str( hkey
, szRegKeys
[i
], buffer
);
4237 msi_free(productid
);
4240 /* FIXME: call ui_actiondata */
4242 return ERROR_SUCCESS
;
4246 static UINT
ACTION_ExecuteAction(MSIPACKAGE
*package
)
4250 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
4251 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
4256 static UINT
ITERATE_PublishComponent(MSIRECORD
*rec
, LPVOID param
)
4258 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4259 LPCWSTR compgroupid
=NULL
;
4260 LPCWSTR feature
=NULL
;
4261 LPCWSTR text
= NULL
;
4262 LPCWSTR qualifier
= NULL
;
4263 LPCWSTR component
= NULL
;
4264 LPWSTR advertise
= NULL
;
4265 LPWSTR output
= NULL
;
4267 UINT rc
= ERROR_SUCCESS
;
4272 component
= MSI_RecordGetString(rec
,3);
4273 comp
= get_loaded_component(package
,component
);
4275 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
) &&
4276 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_SOURCE
) &&
4277 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ADVERTISED
))
4279 TRACE("Skipping: Component %s not scheduled for install\n",
4280 debugstr_w(component
));
4282 return ERROR_SUCCESS
;
4285 compgroupid
= MSI_RecordGetString(rec
,1);
4286 qualifier
= MSI_RecordGetString(rec
,2);
4288 rc
= MSIREG_OpenUserComponentsKey(compgroupid
, &hkey
, TRUE
);
4289 if (rc
!= ERROR_SUCCESS
)
4292 text
= MSI_RecordGetString(rec
,4);
4293 feature
= MSI_RecordGetString(rec
,5);
4295 advertise
= create_component_advertise_string(package
, comp
, feature
);
4297 sz
= strlenW(advertise
);
4300 sz
+= lstrlenW(text
);
4303 sz
*= sizeof(WCHAR
);
4305 output
= msi_alloc_zero(sz
);
4306 strcpyW(output
,advertise
);
4307 msi_free(advertise
);
4310 strcatW(output
,text
);
4312 msi_reg_set_val_multi_str( hkey
, qualifier
, output
);
4319 uirow
= MSI_CreateRecord( 2 );
4320 MSI_RecordSetStringW( uirow
, 1, compgroupid
);
4321 MSI_RecordSetStringW( uirow
, 2, qualifier
);
4322 ui_actiondata( package
, szPublishComponents
, uirow
);
4323 msiobj_release( &uirow
->hdr
);
4324 /* FIXME: call ui_progress? */
4330 * At present I am ignorning the advertised components part of this and only
4331 * focusing on the qualified component sets
4333 static UINT
ACTION_PublishComponents(MSIPACKAGE
*package
)
4337 static const WCHAR ExecSeqQuery
[] =
4338 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4339 '`','P','u','b','l','i','s','h',
4340 'C','o','m','p','o','n','e','n','t','`',0};
4342 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4343 if (rc
!= ERROR_SUCCESS
)
4344 return ERROR_SUCCESS
;
4346 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_PublishComponent
, package
);
4347 msiobj_release(&view
->hdr
);
4352 static UINT
ITERATE_InstallService(MSIRECORD
*rec
, LPVOID param
)
4354 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4357 SC_HANDLE hscm
, service
= NULL
;
4358 LPCWSTR name
, disp
, comp
, depends
, pass
;
4359 LPCWSTR load_order
, serv_name
, key
;
4360 DWORD serv_type
, start_type
;
4363 static const WCHAR query
[] =
4364 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4365 '`','C','o','m','p','o','n','e','n','t','`',' ',
4366 'W','H','E','R','E',' ',
4367 '`','C','o','m','p','o','n','e','n','t','`',' ',
4368 '=','\'','%','s','\'',0};
4370 hscm
= OpenSCManagerW(NULL
, SERVICES_ACTIVE_DATABASEW
, GENERIC_WRITE
);
4373 ERR("Failed to open the SC Manager!\n");
4377 start_type
= MSI_RecordGetInteger(rec
, 5);
4378 if (start_type
== SERVICE_BOOT_START
|| start_type
== SERVICE_SYSTEM_START
)
4381 depends
= MSI_RecordGetString(rec
, 8);
4382 if (depends
&& *depends
)
4383 FIXME("Dependency list unhandled!\n");
4385 name
= MSI_RecordGetString(rec
, 2);
4386 disp
= MSI_RecordGetString(rec
, 3);
4387 serv_type
= MSI_RecordGetInteger(rec
, 4);
4388 err_control
= MSI_RecordGetInteger(rec
, 6);
4389 load_order
= MSI_RecordGetString(rec
, 7);
4390 serv_name
= MSI_RecordGetString(rec
, 9);
4391 pass
= MSI_RecordGetString(rec
, 10);
4392 comp
= MSI_RecordGetString(rec
, 12);
4394 /* fetch the service path */
4395 row
= MSI_QueryGetRecord(package
->db
, query
, comp
);
4398 ERR("Control query failed!\n");
4402 key
= MSI_RecordGetString(row
, 6);
4404 file
= get_loaded_file(package
, key
);
4405 msiobj_release(&row
->hdr
);
4408 ERR("Failed to load the service file\n");
4412 service
= CreateServiceW(hscm
, name
, disp
, GENERIC_ALL
, serv_type
,
4413 start_type
, err_control
, file
->TargetPath
,
4414 load_order
, NULL
, NULL
, serv_name
, pass
);
4417 if (GetLastError() != ERROR_SERVICE_EXISTS
)
4418 ERR("Failed to create service %s: %d\n", debugstr_w(name
), GetLastError());
4422 CloseServiceHandle(service
);
4423 CloseServiceHandle(hscm
);
4425 return ERROR_SUCCESS
;
4428 static UINT
ACTION_InstallServices( MSIPACKAGE
*package
)
4432 static const WCHAR ExecSeqQuery
[] =
4433 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4434 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4436 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4437 if (rc
!= ERROR_SUCCESS
)
4438 return ERROR_SUCCESS
;
4440 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallService
, package
);
4441 msiobj_release(&view
->hdr
);
4446 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4447 static LPCWSTR
*msi_service_args_to_vector(LPCWSTR name
, LPWSTR args
, DWORD
*numargs
)
4453 static const WCHAR separator
[] = {'[','~',']',0};
4456 sep_len
= sizeof(separator
) / sizeof(WCHAR
) - 1;
4461 vector
= msi_alloc(sizeof(LPWSTR
));
4469 vector
[*numargs
- 1] = p
;
4471 if ((q
= strstrW(p
, separator
)))
4475 vector
= msi_realloc(vector
, (*numargs
+ 1) * sizeof(LPWSTR
));
4486 static UINT
ITERATE_StartService(MSIRECORD
*rec
, LPVOID param
)
4488 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4490 SC_HANDLE scm
, service
= NULL
;
4491 LPCWSTR name
, *vector
= NULL
;
4493 DWORD event
, numargs
;
4494 UINT r
= ERROR_FUNCTION_FAILED
;
4496 comp
= get_loaded_component(package
, MSI_RecordGetString(rec
, 6));
4497 if (!comp
|| comp
->Action
== INSTALLSTATE_UNKNOWN
|| comp
->Action
== INSTALLSTATE_ABSENT
)
4498 return ERROR_SUCCESS
;
4500 name
= MSI_RecordGetString(rec
, 2);
4501 event
= MSI_RecordGetInteger(rec
, 3);
4502 args
= strdupW(MSI_RecordGetString(rec
, 4));
4504 if (!(event
& msidbServiceControlEventStart
))
4505 return ERROR_SUCCESS
;
4507 scm
= OpenSCManagerW(NULL
, NULL
, SC_MANAGER_CONNECT
);
4510 ERR("Failed to open the service control manager\n");
4514 service
= OpenServiceW(scm
, name
, SERVICE_START
);
4517 ERR("Failed to open service %s\n", debugstr_w(name
));
4521 vector
= msi_service_args_to_vector(name
, args
, &numargs
);
4523 if (!StartServiceW(service
, numargs
, vector
))
4525 ERR("Failed to start service %s\n", debugstr_w(name
));
4532 CloseServiceHandle(service
);
4533 CloseServiceHandle(scm
);
4540 static UINT
ACTION_StartServices( MSIPACKAGE
*package
)
4545 static const WCHAR query
[] = {
4546 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4547 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4549 rc
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
4550 if (rc
!= ERROR_SUCCESS
)
4551 return ERROR_SUCCESS
;
4553 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_StartService
, package
);
4554 msiobj_release(&view
->hdr
);
4559 static MSIFILE
*msi_find_file( MSIPACKAGE
*package
, LPCWSTR filename
)
4563 LIST_FOR_EACH_ENTRY(file
, &package
->files
, MSIFILE
, entry
)
4565 if (!lstrcmpW(file
->File
, filename
))
4572 static UINT
ITERATE_InstallODBCDriver( MSIRECORD
*rec
, LPVOID param
)
4574 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4575 LPWSTR driver
, driver_path
, ptr
;
4576 WCHAR outpath
[MAX_PATH
];
4577 MSIFILE
*driver_file
, *setup_file
;
4580 UINT r
= ERROR_SUCCESS
;
4582 static const WCHAR driver_fmt
[] = {
4583 'D','r','i','v','e','r','=','%','s',0};
4584 static const WCHAR setup_fmt
[] = {
4585 'S','e','t','u','p','=','%','s',0};
4586 static const WCHAR usage_fmt
[] = {
4587 'F','i','l','e','U','s','a','g','e','=','1',0};
4589 desc
= MSI_RecordGetString(rec
, 3);
4591 driver_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 4));
4592 setup_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 5));
4594 if (!driver_file
|| !setup_file
)
4596 ERR("ODBC Driver entry not found!\n");
4597 return ERROR_FUNCTION_FAILED
;
4600 len
= lstrlenW(desc
) + lstrlenW(driver_fmt
) + lstrlenW(driver_file
->FileName
) +
4601 lstrlenW(setup_fmt
) + lstrlenW(setup_file
->FileName
) +
4602 lstrlenW(usage_fmt
) + 1;
4603 driver
= msi_alloc(len
* sizeof(WCHAR
));
4605 return ERROR_OUTOFMEMORY
;
4608 lstrcpyW(ptr
, desc
);
4609 ptr
+= lstrlenW(ptr
) + 1;
4611 sprintfW(ptr
, driver_fmt
, driver_file
->FileName
);
4612 ptr
+= lstrlenW(ptr
) + 1;
4614 sprintfW(ptr
, setup_fmt
, setup_file
->FileName
);
4615 ptr
+= lstrlenW(ptr
) + 1;
4617 lstrcpyW(ptr
, usage_fmt
);
4618 ptr
+= lstrlenW(ptr
) + 1;
4621 driver_path
= strdupW(driver_file
->TargetPath
);
4622 ptr
= strrchrW(driver_path
, '\\');
4623 if (ptr
) *ptr
= '\0';
4625 if (!SQLInstallDriverExW(driver
, driver_path
, outpath
, MAX_PATH
,
4626 NULL
, ODBC_INSTALL_COMPLETE
, &usage
))
4628 ERR("Failed to install SQL driver!\n");
4629 r
= ERROR_FUNCTION_FAILED
;
4633 msi_free(driver_path
);
4638 static UINT
ITERATE_InstallODBCTranslator( MSIRECORD
*rec
, LPVOID param
)
4640 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4641 LPWSTR translator
, translator_path
, ptr
;
4642 WCHAR outpath
[MAX_PATH
];
4643 MSIFILE
*translator_file
, *setup_file
;
4646 UINT r
= ERROR_SUCCESS
;
4648 static const WCHAR translator_fmt
[] = {
4649 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4650 static const WCHAR setup_fmt
[] = {
4651 'S','e','t','u','p','=','%','s',0};
4653 desc
= MSI_RecordGetString(rec
, 3);
4655 translator_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 4));
4656 setup_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 5));
4658 if (!translator_file
|| !setup_file
)
4660 ERR("ODBC Translator entry not found!\n");
4661 return ERROR_FUNCTION_FAILED
;
4664 len
= lstrlenW(desc
) + lstrlenW(translator_fmt
) + lstrlenW(translator_file
->FileName
) +
4665 lstrlenW(setup_fmt
) + lstrlenW(setup_file
->FileName
) + 1;
4666 translator
= msi_alloc(len
* sizeof(WCHAR
));
4668 return ERROR_OUTOFMEMORY
;
4671 lstrcpyW(ptr
, desc
);
4672 ptr
+= lstrlenW(ptr
) + 1;
4674 sprintfW(ptr
, translator_fmt
, translator_file
->FileName
);
4675 ptr
+= lstrlenW(ptr
) + 1;
4677 sprintfW(ptr
, setup_fmt
, setup_file
->FileName
);
4678 ptr
+= lstrlenW(ptr
) + 1;
4681 translator_path
= strdupW(translator_file
->TargetPath
);
4682 ptr
= strrchrW(translator_path
, '\\');
4683 if (ptr
) *ptr
= '\0';
4685 if (!SQLInstallTranslatorExW(translator
, translator_path
, outpath
, MAX_PATH
,
4686 NULL
, ODBC_INSTALL_COMPLETE
, &usage
))
4688 ERR("Failed to install SQL translator!\n");
4689 r
= ERROR_FUNCTION_FAILED
;
4692 msi_free(translator
);
4693 msi_free(translator_path
);
4698 static UINT
ITERATE_InstallODBCDataSource( MSIRECORD
*rec
, LPVOID param
)
4701 LPCWSTR desc
, driver
;
4702 WORD request
= ODBC_ADD_SYS_DSN
;
4705 UINT r
= ERROR_SUCCESS
;
4707 static const WCHAR attrs_fmt
[] = {
4708 'D','S','N','=','%','s',0 };
4710 desc
= MSI_RecordGetString(rec
, 3);
4711 driver
= MSI_RecordGetString(rec
, 4);
4712 registration
= MSI_RecordGetInteger(rec
, 5);
4714 if (registration
== msidbODBCDataSourceRegistrationPerMachine
) request
= ODBC_ADD_SYS_DSN
;
4715 else if (registration
== msidbODBCDataSourceRegistrationPerUser
) request
= ODBC_ADD_DSN
;
4717 len
= lstrlenW(attrs_fmt
) + lstrlenW(desc
) + 1 + 1;
4718 attrs
= msi_alloc(len
* sizeof(WCHAR
));
4720 return ERROR_OUTOFMEMORY
;
4722 sprintfW(attrs
, attrs_fmt
, desc
);
4723 attrs
[len
- 1] = '\0';
4725 if (!SQLConfigDataSourceW(NULL
, request
, driver
, attrs
))
4727 ERR("Failed to install SQL data source!\n");
4728 r
= ERROR_FUNCTION_FAILED
;
4736 static UINT
ACTION_InstallODBC( MSIPACKAGE
*package
)
4741 static const WCHAR driver_query
[] = {
4742 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4743 'O','D','B','C','D','r','i','v','e','r',0 };
4745 static const WCHAR translator_query
[] = {
4746 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4747 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4749 static const WCHAR source_query
[] = {
4750 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4751 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4753 rc
= MSI_DatabaseOpenViewW(package
->db
, driver_query
, &view
);
4754 if (rc
!= ERROR_SUCCESS
)
4755 return ERROR_SUCCESS
;
4757 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCDriver
, package
);
4758 msiobj_release(&view
->hdr
);
4760 rc
= MSI_DatabaseOpenViewW(package
->db
, translator_query
, &view
);
4761 if (rc
!= ERROR_SUCCESS
)
4762 return ERROR_SUCCESS
;
4764 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCTranslator
, package
);
4765 msiobj_release(&view
->hdr
);
4767 rc
= MSI_DatabaseOpenViewW(package
->db
, source_query
, &view
);
4768 if (rc
!= ERROR_SUCCESS
)
4769 return ERROR_SUCCESS
;
4771 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCDataSource
, package
);
4772 msiobj_release(&view
->hdr
);
4777 #define ENV_ACT_SETALWAYS 0x1
4778 #define ENV_ACT_SETABSENT 0x2
4779 #define ENV_ACT_REMOVE 0x4
4780 #define ENV_ACT_REMOVEMATCH 0x8
4782 #define ENV_MOD_MACHINE 0x20000000
4783 #define ENV_MOD_APPEND 0x40000000
4784 #define ENV_MOD_PREFIX 0x80000000
4785 #define ENV_MOD_MASK 0xC0000000
4787 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4789 static LONG
env_set_flags( LPCWSTR
*name
, LPCWSTR
*value
, DWORD
*flags
)
4791 LPCWSTR cptr
= *name
;
4792 LPCWSTR ptr
= *value
;
4794 static const WCHAR prefix
[] = {'[','~',']',0};
4795 static const int prefix_len
= 3;
4801 *flags
|= ENV_ACT_SETALWAYS
;
4802 else if (*cptr
== '+')
4803 *flags
|= ENV_ACT_SETABSENT
;
4804 else if (*cptr
== '-')
4805 *flags
|= ENV_ACT_REMOVE
;
4806 else if (*cptr
== '!')
4807 *flags
|= ENV_ACT_REMOVEMATCH
;
4808 else if (*cptr
== '*')
4809 *flags
|= ENV_MOD_MACHINE
;
4819 ERR("Missing environment variable\n");
4820 return ERROR_FUNCTION_FAILED
;
4823 if (!strncmpW(ptr
, prefix
, prefix_len
))
4825 *flags
|= ENV_MOD_APPEND
;
4826 *value
+= lstrlenW(prefix
);
4828 else if (lstrlenW(*value
) >= prefix_len
)
4830 ptr
+= lstrlenW(ptr
) - prefix_len
;
4831 if (!lstrcmpW(ptr
, prefix
))
4833 *flags
|= ENV_MOD_PREFIX
;
4834 /* the "[~]" will be removed by deformat_string */;
4839 check_flag_combo(*flags
, ENV_ACT_SETALWAYS
| ENV_ACT_SETABSENT
) ||
4840 check_flag_combo(*flags
, ENV_ACT_REMOVEMATCH
| ENV_ACT_SETABSENT
) ||
4841 check_flag_combo(*flags
, ENV_ACT_REMOVEMATCH
| ENV_ACT_SETALWAYS
) ||
4842 check_flag_combo(*flags
, ENV_ACT_SETABSENT
| ENV_MOD_MASK
))
4844 ERR("Invalid flags: %08x\n", *flags
);
4845 return ERROR_FUNCTION_FAILED
;
4848 return ERROR_SUCCESS
;
4851 static UINT
ITERATE_WriteEnvironmentString( MSIRECORD
*rec
, LPVOID param
)
4853 MSIPACKAGE
*package
= param
;
4854 LPCWSTR name
, value
, comp
;
4855 LPWSTR data
= NULL
, newval
= NULL
;
4856 LPWSTR deformatted
= NULL
, ptr
;
4857 DWORD flags
, type
, size
;
4859 HKEY env
= NULL
, root
= HKEY_CURRENT_USER
;
4861 static const WCHAR environment
[] =
4862 {'S','y','s','t','e','m','\\',
4863 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4864 'C','o','n','t','r','o','l','\\',
4865 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4866 'E','n','v','i','r','o','n','m','e','n','t',0};
4867 static const WCHAR semicolon
[] = {';',0};
4869 name
= MSI_RecordGetString(rec
, 2);
4870 value
= MSI_RecordGetString(rec
, 3);
4871 comp
= MSI_RecordGetString(rec
, 4);
4873 res
= env_set_flags(&name
, &value
, &flags
);
4874 if (res
!= ERROR_SUCCESS
)
4877 deformat_string(package
, value
, &deformatted
);
4880 res
= ERROR_OUTOFMEMORY
;
4884 value
= deformatted
;
4886 if (flags
& ENV_MOD_MACHINE
)
4887 root
= HKEY_LOCAL_MACHINE
;
4889 res
= RegOpenKeyExW(root
, environment
, 0, KEY_ALL_ACCESS
, &env
);
4890 if (res
!= ERROR_SUCCESS
)
4893 if (flags
& ENV_ACT_REMOVE
)
4894 FIXME("Not removing environment variable on uninstall!\n");
4897 res
= RegQueryValueExW(env
, name
, NULL
, &type
, NULL
, &size
);
4898 if ((res
!= ERROR_SUCCESS
&& res
!= ERROR_FILE_NOT_FOUND
) ||
4899 (res
== ERROR_SUCCESS
&& type
!= REG_SZ
))
4902 if (res
!= ERROR_FILE_NOT_FOUND
)
4904 if (flags
& ENV_ACT_SETABSENT
)
4906 res
= ERROR_SUCCESS
;
4910 data
= msi_alloc(size
);
4914 return ERROR_OUTOFMEMORY
;
4917 res
= RegQueryValueExW(env
, name
, NULL
, &type
, (LPVOID
)data
, &size
);
4918 if (res
!= ERROR_SUCCESS
)
4921 if (flags
& ENV_ACT_REMOVEMATCH
&& (!value
|| !lstrcmpW(data
, value
)))
4923 res
= RegDeleteKeyW(env
, name
);
4927 size
= (lstrlenW(value
) + 1 + size
) * sizeof(WCHAR
);
4928 newval
= msi_alloc(size
);
4932 res
= ERROR_OUTOFMEMORY
;
4936 if (!(flags
& ENV_MOD_MASK
))
4937 lstrcpyW(newval
, value
);
4940 if (flags
& ENV_MOD_PREFIX
)
4942 lstrcpyW(newval
, value
);
4943 lstrcatW(newval
, semicolon
);
4944 ptr
= newval
+ lstrlenW(value
) + 1;
4947 lstrcpyW(ptr
, data
);
4949 if (flags
& ENV_MOD_APPEND
)
4951 lstrcatW(newval
, semicolon
);
4952 lstrcatW(newval
, value
);
4958 size
= (lstrlenW(value
) + 1) * sizeof(WCHAR
);
4959 newval
= msi_alloc(size
);
4962 res
= ERROR_OUTOFMEMORY
;
4966 lstrcpyW(newval
, value
);
4969 TRACE("setting %s to %s\n", debugstr_w(name
), debugstr_w(newval
));
4970 res
= RegSetValueExW(env
, name
, 0, type
, (LPVOID
)newval
, size
);
4973 if (env
) RegCloseKey(env
);
4974 msi_free(deformatted
);
4980 static UINT
ACTION_WriteEnvironmentStrings( MSIPACKAGE
*package
)
4984 static const WCHAR ExecSeqQuery
[] =
4985 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4986 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
4987 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4988 if (rc
!= ERROR_SUCCESS
)
4989 return ERROR_SUCCESS
;
4991 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteEnvironmentString
, package
);
4992 msiobj_release(&view
->hdr
);
4997 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5008 static BOOL
msi_move_file(LPCWSTR source
, LPCWSTR dest
, int options
)
5012 if (GetFileAttributesW(source
) == FILE_ATTRIBUTE_DIRECTORY
||
5013 GetFileAttributesW(dest
) == FILE_ATTRIBUTE_DIRECTORY
)
5015 WARN("Source or dest is directory, not moving\n");
5019 if (options
== msidbMoveFileOptionsMove
)
5021 TRACE("moving %s -> %s\n", debugstr_w(source
), debugstr_w(dest
));
5022 ret
= MoveFileExW(source
, dest
, MOVEFILE_REPLACE_EXISTING
);
5025 WARN("MoveFile failed: %d\n", GetLastError());
5031 TRACE("copying %s -> %s\n", debugstr_w(source
), debugstr_w(dest
));
5032 ret
= CopyFileW(source
, dest
, FALSE
);
5035 WARN("CopyFile failed: %d\n", GetLastError());
5043 static LPWSTR
wildcard_to_file(LPWSTR wildcard
, LPWSTR filename
)
5046 DWORD dirlen
, pathlen
;
5048 ptr
= strrchrW(wildcard
, '\\');
5049 dirlen
= ptr
- wildcard
+ 1;
5051 pathlen
= dirlen
+ lstrlenW(filename
) + 1;
5052 path
= msi_alloc(pathlen
* sizeof(WCHAR
));
5054 lstrcpynW(path
, wildcard
, dirlen
+ 1);
5055 lstrcatW(path
, filename
);
5060 static void free_file_entry(FILE_LIST
*file
)
5062 msi_free(file
->source
);
5063 msi_free(file
->dest
);
5067 static void free_list(FILE_LIST
*list
)
5069 while (!list_empty(&list
->entry
))
5071 FILE_LIST
*file
= LIST_ENTRY(list_head(&list
->entry
), FILE_LIST
, entry
);
5073 list_remove(&file
->entry
);
5074 free_file_entry(file
);
5078 static BOOL
add_wildcard(FILE_LIST
*files
, LPWSTR source
, LPWSTR dest
)
5080 FILE_LIST
*new, *file
;
5081 LPWSTR ptr
, filename
;
5084 new = msi_alloc_zero(sizeof(FILE_LIST
));
5088 new->source
= strdupW(source
);
5089 ptr
= strrchrW(dest
, '\\') + 1;
5090 filename
= strrchrW(new->source
, '\\') + 1;
5092 new->sourcename
= filename
;
5095 new->destname
= ptr
;
5097 new->destname
= new->sourcename
;
5099 size
= (ptr
- dest
) + lstrlenW(filename
) + 1;
5100 new->dest
= msi_alloc(size
* sizeof(WCHAR
));
5103 free_file_entry(new);
5107 lstrcpynW(new->dest
, dest
, ptr
- dest
+ 1);
5108 lstrcatW(new->dest
, filename
);
5110 if (list_empty(&files
->entry
))
5112 list_add_head(&files
->entry
, &new->entry
);
5116 LIST_FOR_EACH_ENTRY(file
, &files
->entry
, FILE_LIST
, entry
)
5118 if (lstrcmpW(source
, file
->source
) < 0)
5120 list_add_before(&file
->entry
, &new->entry
);
5125 list_add_after(&file
->entry
, &new->entry
);
5129 BOOL
move_files_wildcard(LPWSTR source
, LPWSTR dest
, int options
)
5131 WIN32_FIND_DATAW wfd
;
5135 FILE_LIST files
, *file
;
5138 hfile
= FindFirstFileW(source
, &wfd
);
5139 if (hfile
== INVALID_HANDLE_VALUE
) return FALSE
;
5141 list_init(&files
.entry
);
5143 for (res
= TRUE
; res
; res
= FindNextFileW(hfile
, &wfd
))
5145 if (is_dot_dir(wfd
.cFileName
)) continue;
5147 path
= wildcard_to_file(source
, wfd
.cFileName
);
5154 add_wildcard(&files
, path
, dest
);
5158 /* only the first wildcard match gets renamed to dest */
5159 file
= LIST_ENTRY(list_head(&files
.entry
), FILE_LIST
, entry
);
5160 size
= (strrchrW(file
->dest
, '\\') - file
->dest
) + lstrlenW(file
->destname
) + 2;
5161 file
->dest
= msi_realloc(file
->dest
, size
* sizeof(WCHAR
));
5168 lstrcpyW(strrchrW(file
->dest
, '\\') + 1, file
->destname
);
5170 while (!list_empty(&files
.entry
))
5172 file
= LIST_ENTRY(list_head(&files
.entry
), FILE_LIST
, entry
);
5174 msi_move_file((LPCWSTR
)file
->source
, (LPCWSTR
)file
->dest
, options
);
5176 list_remove(&file
->entry
);
5177 free_file_entry(file
);
5183 static UINT
ITERATE_MoveFiles( MSIRECORD
*rec
, LPVOID param
)
5185 MSIPACKAGE
*package
= param
;
5187 LPCWSTR sourcename
, destname
;
5188 LPWSTR sourcedir
= NULL
, destdir
= NULL
;
5189 LPWSTR source
= NULL
, dest
= NULL
;
5192 BOOL ret
, wildcards
;
5194 static const WCHAR backslash
[] = {'\\',0};
5196 comp
= get_loaded_component(package
, MSI_RecordGetString(rec
, 2));
5197 if (!comp
|| !comp
->Enabled
||
5198 !(comp
->Action
& (INSTALLSTATE_LOCAL
| INSTALLSTATE_SOURCE
)))
5200 TRACE("Component not set for install, not moving file\n");
5201 return ERROR_SUCCESS
;
5204 sourcename
= MSI_RecordGetString(rec
, 3);
5205 destname
= MSI_RecordGetString(rec
, 4);
5206 options
= MSI_RecordGetInteger(rec
, 7);
5208 sourcedir
= msi_dup_property(package
, MSI_RecordGetString(rec
, 5));
5212 destdir
= msi_dup_property(package
, MSI_RecordGetString(rec
, 6));
5218 if (GetFileAttributesW(sourcedir
) == INVALID_FILE_ATTRIBUTES
)
5221 source
= strdupW(sourcedir
);
5227 size
= lstrlenW(sourcedir
) + lstrlenW(sourcename
) + 2;
5228 source
= msi_alloc(size
* sizeof(WCHAR
));
5232 lstrcpyW(source
, sourcedir
);
5233 if (source
[lstrlenW(source
) - 1] != '\\')
5234 lstrcatW(source
, backslash
);
5235 lstrcatW(source
, sourcename
);
5238 wildcards
= strchrW(source
, '*') || strchrW(source
, '?');
5240 if (!destname
&& !wildcards
)
5242 destname
= strdupW(sourcename
);
5249 size
= lstrlenW(destname
);
5251 size
+= lstrlenW(destdir
) + 2;
5252 dest
= msi_alloc(size
* sizeof(WCHAR
));
5256 lstrcpyW(dest
, destdir
);
5257 if (dest
[lstrlenW(dest
) - 1] != '\\')
5258 lstrcatW(dest
, backslash
);
5261 lstrcatW(dest
, destname
);
5263 if (GetFileAttributesW(destdir
) == INVALID_FILE_ATTRIBUTES
)
5265 ret
= CreateDirectoryW(destdir
, NULL
);
5268 WARN("CreateDirectory failed: %d\n", GetLastError());
5269 return ERROR_SUCCESS
;
5274 msi_move_file(source
, dest
, options
);
5276 move_files_wildcard(source
, dest
, options
);
5279 msi_free(sourcedir
);
5284 return ERROR_SUCCESS
;
5287 static UINT
ACTION_MoveFiles( MSIPACKAGE
*package
)
5292 static const WCHAR ExecSeqQuery
[] =
5293 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5294 '`','M','o','v','e','F','i','l','e','`',0};
5296 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
5297 if (rc
!= ERROR_SUCCESS
)
5298 return ERROR_SUCCESS
;
5300 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_MoveFiles
, package
);
5301 msiobj_release(&view
->hdr
);
5306 static UINT
msi_unimplemented_action_stub( MSIPACKAGE
*package
,
5307 LPCSTR action
, LPCWSTR table
)
5309 static const WCHAR query
[] = {
5310 'S','E','L','E','C','T',' ','*',' ',
5311 'F','R','O','M',' ','`','%','s','`',0 };
5312 MSIQUERY
*view
= NULL
;
5316 r
= MSI_OpenQuery( package
->db
, &view
, query
, table
);
5317 if (r
== ERROR_SUCCESS
)
5319 r
= MSI_IterateRecords(view
, &count
, NULL
, package
);
5320 msiobj_release(&view
->hdr
);
5324 FIXME("%s -> %u ignored %s table values\n",
5325 action
, count
, debugstr_w(table
));
5327 return ERROR_SUCCESS
;
5330 static UINT
ACTION_AllocateRegistrySpace( MSIPACKAGE
*package
)
5332 TRACE("%p\n", package
);
5333 return ERROR_SUCCESS
;
5336 static UINT
ACTION_RemoveIniValues( MSIPACKAGE
*package
)
5338 static const WCHAR table
[] =
5339 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5340 return msi_unimplemented_action_stub( package
, "RemoveIniValues", table
);
5343 static UINT
ACTION_PatchFiles( MSIPACKAGE
*package
)
5345 static const WCHAR table
[] = { 'P','a','t','c','h',0 };
5346 return msi_unimplemented_action_stub( package
, "PatchFiles", table
);
5349 static UINT
ACTION_BindImage( MSIPACKAGE
*package
)
5351 static const WCHAR table
[] = { 'B','i','n','d','I','m','a','g','e',0 };
5352 return msi_unimplemented_action_stub( package
, "BindImage", table
);
5355 static UINT
ACTION_IsolateComponents( MSIPACKAGE
*package
)
5357 static const WCHAR table
[] = {
5358 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5359 return msi_unimplemented_action_stub( package
, "IsolateComponents", table
);
5362 static UINT
ACTION_MigrateFeatureStates( MSIPACKAGE
*package
)
5364 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
5365 return msi_unimplemented_action_stub( package
, "MigrateFeatureStates", table
);
5368 static UINT
ACTION_SelfUnregModules( MSIPACKAGE
*package
)
5370 static const WCHAR table
[] = { 'S','e','l','f','R','e','g',0 };
5371 return msi_unimplemented_action_stub( package
, "SelfUnregModules", table
);
5374 static UINT
ACTION_StopServices( MSIPACKAGE
*package
)
5376 static const WCHAR table
[] = {
5377 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5378 return msi_unimplemented_action_stub( package
, "StopServices", table
);
5381 static UINT
ACTION_DeleteServices( MSIPACKAGE
*package
)
5383 static const WCHAR table
[] = {
5384 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5385 return msi_unimplemented_action_stub( package
, "DeleteServices", table
);
5387 static UINT
ACTION_ValidateProductID( MSIPACKAGE
*package
)
5389 static const WCHAR table
[] = {
5390 'P','r','o','d','u','c','t','I','D',0 };
5391 return msi_unimplemented_action_stub( package
, "ValidateProductID", table
);
5394 static UINT
ACTION_RemoveEnvironmentStrings( MSIPACKAGE
*package
)
5396 static const WCHAR table
[] = {
5397 'E','n','v','i','r','o','n','m','e','n','t',0 };
5398 return msi_unimplemented_action_stub( package
, "RemoveEnvironmentStrings", table
);
5401 static UINT
ACTION_MsiPublishAssemblies( MSIPACKAGE
*package
)
5403 static const WCHAR table
[] = {
5404 'M','s','i','A','s','s','e','m','b','l','y',0 };
5405 return msi_unimplemented_action_stub( package
, "MsiPublishAssemblies", table
);
5408 static UINT
ACTION_MsiUnpublishAssemblies( MSIPACKAGE
*package
)
5410 static const WCHAR table
[] = {
5411 'M','s','i','A','s','s','e','m','b','l','y',0 };
5412 return msi_unimplemented_action_stub( package
, "MsiUnpublishAssemblies", table
);
5415 static UINT
ACTION_UnregisterFonts( MSIPACKAGE
*package
)
5417 static const WCHAR table
[] = { 'F','o','n','t',0 };
5418 return msi_unimplemented_action_stub( package
, "UnregisterFonts", table
);
5421 static UINT
ACTION_CCPSearch( MSIPACKAGE
*package
)
5423 static const WCHAR table
[] = { 'C','C','P','S','e','a','r','c','h',0 };
5424 return msi_unimplemented_action_stub( package
, "CCPSearch", table
);
5427 static UINT
ACTION_RMCCPSearch( MSIPACKAGE
*package
)
5429 static const WCHAR table
[] = { 'C','C','P','S','e','a','r','c','h',0 };
5430 return msi_unimplemented_action_stub( package
, "RMCCPSearch", table
);
5433 static UINT
ACTION_RegisterComPlus( MSIPACKAGE
*package
)
5435 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
5436 return msi_unimplemented_action_stub( package
, "RegisterComPlus", table
);
5439 static UINT
ACTION_UnregisterComPlus( MSIPACKAGE
*package
)
5441 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
5442 return msi_unimplemented_action_stub( package
, "UnregisterComPlus", table
);
5445 static UINT
ACTION_InstallSFPCatalogFile( MSIPACKAGE
*package
)
5447 static const WCHAR table
[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5448 return msi_unimplemented_action_stub( package
, "InstallSFPCatalogFile", table
);
5451 static UINT
ACTION_RemoveDuplicateFiles( MSIPACKAGE
*package
)
5453 static const WCHAR table
[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5454 return msi_unimplemented_action_stub( package
, "RemoveDuplicateFiles", table
);
5457 static UINT
ACTION_RemoveExistingProducts( MSIPACKAGE
*package
)
5459 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
5460 return msi_unimplemented_action_stub( package
, "RemoveExistingProducts", table
);
5463 static UINT
ACTION_RemoveFolders( MSIPACKAGE
*package
)
5465 static const WCHAR table
[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5466 return msi_unimplemented_action_stub( package
, "RemoveFolders", table
);
5469 static UINT
ACTION_RemoveODBC( MSIPACKAGE
*package
)
5471 static const WCHAR table
[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5472 return msi_unimplemented_action_stub( package
, "RemoveODBC", table
);
5475 static UINT
ACTION_RemoveRegistryValues( MSIPACKAGE
*package
)
5477 static const WCHAR table
[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5478 return msi_unimplemented_action_stub( package
, "RemoveRegistryValues", table
);
5481 static UINT
ACTION_RemoveShortcuts( MSIPACKAGE
*package
)
5483 static const WCHAR table
[] = { 'S','h','o','r','t','c','u','t',0 };
5484 return msi_unimplemented_action_stub( package
, "RemoveShortcuts", table
);
5487 static UINT
ACTION_UnpublishComponents( MSIPACKAGE
*package
)
5489 static const WCHAR table
[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5490 return msi_unimplemented_action_stub( package
, "UnpublishComponents", table
);
5493 static UINT
ACTION_UnregisterClassInfo( MSIPACKAGE
*package
)
5495 static const WCHAR table
[] = { 'A','p','p','I','d',0 };
5496 return msi_unimplemented_action_stub( package
, "UnregisterClassInfo", table
);
5499 static UINT
ACTION_UnregisterExtensionInfo( MSIPACKAGE
*package
)
5501 static const WCHAR table
[] = { 'E','x','t','e','n','s','i','o','n',0 };
5502 return msi_unimplemented_action_stub( package
, "UnregisterExtensionInfo", table
);
5505 static UINT
ACTION_UnregisterMIMEInfo( MSIPACKAGE
*package
)
5507 static const WCHAR table
[] = { 'M','I','M','E',0 };
5508 return msi_unimplemented_action_stub( package
, "UnregisterMIMEInfo", table
);
5511 static UINT
ACTION_UnregisterProgIdInfo( MSIPACKAGE
*package
)
5513 static const WCHAR table
[] = { 'P','r','o','g','I','d',0 };
5514 return msi_unimplemented_action_stub( package
, "UnregisterProgIdInfo", table
);
5517 static UINT
ACTION_UnregisterTypeLibraries( MSIPACKAGE
*package
)
5519 static const WCHAR table
[] = { 'T','y','p','e','L','i','b',0 };
5520 return msi_unimplemented_action_stub( package
, "UnregisterTypeLibraries", table
);
5523 static const struct _actions StandardActions
[] = {
5524 { szAllocateRegistrySpace
, ACTION_AllocateRegistrySpace
},
5525 { szAppSearch
, ACTION_AppSearch
},
5526 { szBindImage
, ACTION_BindImage
},
5527 { szCCPSearch
, ACTION_CCPSearch
},
5528 { szCostFinalize
, ACTION_CostFinalize
},
5529 { szCostInitialize
, ACTION_CostInitialize
},
5530 { szCreateFolders
, ACTION_CreateFolders
},
5531 { szCreateShortcuts
, ACTION_CreateShortcuts
},
5532 { szDeleteServices
, ACTION_DeleteServices
},
5533 { szDisableRollback
, NULL
},
5534 { szDuplicateFiles
, ACTION_DuplicateFiles
},
5535 { szExecuteAction
, ACTION_ExecuteAction
},
5536 { szFileCost
, ACTION_FileCost
},
5537 { szFindRelatedProducts
, ACTION_FindRelatedProducts
},
5538 { szForceReboot
, ACTION_ForceReboot
},
5539 { szInstallAdminPackage
, NULL
},
5540 { szInstallExecute
, ACTION_InstallExecute
},
5541 { szInstallExecuteAgain
, ACTION_InstallExecute
},
5542 { szInstallFiles
, ACTION_InstallFiles
},
5543 { szInstallFinalize
, ACTION_InstallFinalize
},
5544 { szInstallInitialize
, ACTION_InstallInitialize
},
5545 { szInstallSFPCatalogFile
, ACTION_InstallSFPCatalogFile
},
5546 { szInstallValidate
, ACTION_InstallValidate
},
5547 { szIsolateComponents
, ACTION_IsolateComponents
},
5548 { szLaunchConditions
, ACTION_LaunchConditions
},
5549 { szMigrateFeatureStates
, ACTION_MigrateFeatureStates
},
5550 { szMoveFiles
, ACTION_MoveFiles
},
5551 { szMsiPublishAssemblies
, ACTION_MsiPublishAssemblies
},
5552 { szMsiUnpublishAssemblies
, ACTION_MsiUnpublishAssemblies
},
5553 { szInstallODBC
, ACTION_InstallODBC
},
5554 { szInstallServices
, ACTION_InstallServices
},
5555 { szPatchFiles
, ACTION_PatchFiles
},
5556 { szProcessComponents
, ACTION_ProcessComponents
},
5557 { szPublishComponents
, ACTION_PublishComponents
},
5558 { szPublishFeatures
, ACTION_PublishFeatures
},
5559 { szPublishProduct
, ACTION_PublishProduct
},
5560 { szRegisterClassInfo
, ACTION_RegisterClassInfo
},
5561 { szRegisterComPlus
, ACTION_RegisterComPlus
},
5562 { szRegisterExtensionInfo
, ACTION_RegisterExtensionInfo
},
5563 { szRegisterFonts
, ACTION_RegisterFonts
},
5564 { szRegisterMIMEInfo
, ACTION_RegisterMIMEInfo
},
5565 { szRegisterProduct
, ACTION_RegisterProduct
},
5566 { szRegisterProgIdInfo
, ACTION_RegisterProgIdInfo
},
5567 { szRegisterTypeLibraries
, ACTION_RegisterTypeLibraries
},
5568 { szRegisterUser
, ACTION_RegisterUser
},
5569 { szRemoveDuplicateFiles
, ACTION_RemoveDuplicateFiles
},
5570 { szRemoveEnvironmentStrings
, ACTION_RemoveEnvironmentStrings
},
5571 { szRemoveExistingProducts
, ACTION_RemoveExistingProducts
},
5572 { szRemoveFiles
, ACTION_RemoveFiles
},
5573 { szRemoveFolders
, ACTION_RemoveFolders
},
5574 { szRemoveIniValues
, ACTION_RemoveIniValues
},
5575 { szRemoveODBC
, ACTION_RemoveODBC
},
5576 { szRemoveRegistryValues
, ACTION_RemoveRegistryValues
},
5577 { szRemoveShortcuts
, ACTION_RemoveShortcuts
},
5578 { szResolveSource
, ACTION_ResolveSource
},
5579 { szRMCCPSearch
, ACTION_RMCCPSearch
},
5580 { szScheduleReboot
, NULL
},
5581 { szSelfRegModules
, ACTION_SelfRegModules
},
5582 { szSelfUnregModules
, ACTION_SelfUnregModules
},
5583 { szSetODBCFolders
, NULL
},
5584 { szStartServices
, ACTION_StartServices
},
5585 { szStopServices
, ACTION_StopServices
},
5586 { szUnpublishComponents
, ACTION_UnpublishComponents
},
5587 { szUnpublishFeatures
, ACTION_UnpublishFeatures
},
5588 { szUnregisterClassInfo
, ACTION_UnregisterClassInfo
},
5589 { szUnregisterComPlus
, ACTION_UnregisterComPlus
},
5590 { szUnregisterExtensionInfo
, ACTION_UnregisterExtensionInfo
},
5591 { szUnregisterFonts
, ACTION_UnregisterFonts
},
5592 { szUnregisterMIMEInfo
, ACTION_UnregisterMIMEInfo
},
5593 { szUnregisterProgIdInfo
, ACTION_UnregisterProgIdInfo
},
5594 { szUnregisterTypeLibraries
, ACTION_UnregisterTypeLibraries
},
5595 { szValidateProductID
, ACTION_ValidateProductID
},
5596 { szWriteEnvironmentStrings
, ACTION_WriteEnvironmentStrings
},
5597 { szWriteIniValues
, ACTION_WriteIniValues
},
5598 { szWriteRegistryValues
, ACTION_WriteRegistryValues
},