2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wine/debug.h"
40 #include "wine/unicode.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
49 * consts and values used
51 static const WCHAR c_colon
[] = {'C',':','\\',0};
53 static const WCHAR szCreateFolders
[] =
54 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
55 static const WCHAR szCostFinalize
[] =
56 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
57 static const WCHAR szWriteRegistryValues
[] =
58 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
59 static const WCHAR szCostInitialize
[] =
60 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
61 static const WCHAR szFileCost
[] =
62 {'F','i','l','e','C','o','s','t',0};
63 static const WCHAR szInstallInitialize
[] =
64 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
65 static const WCHAR szInstallValidate
[] =
66 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
67 static const WCHAR szLaunchConditions
[] =
68 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
69 static const WCHAR szProcessComponents
[] =
70 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
71 static const WCHAR szRegisterTypeLibraries
[] =
72 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
73 static const WCHAR szCreateShortcuts
[] =
74 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
75 static const WCHAR szPublishProduct
[] =
76 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
77 static const WCHAR szWriteIniValues
[] =
78 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
79 static const WCHAR szSelfRegModules
[] =
80 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
81 static const WCHAR szPublishFeatures
[] =
82 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
83 static const WCHAR szRegisterProduct
[] =
84 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
85 static const WCHAR szInstallExecute
[] =
86 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
87 static const WCHAR szInstallExecuteAgain
[] =
88 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
89 static const WCHAR szInstallFinalize
[] =
90 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
91 static const WCHAR szForceReboot
[] =
92 {'F','o','r','c','e','R','e','b','o','o','t',0};
93 static const WCHAR szResolveSource
[] =
94 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
95 static const WCHAR szAppSearch
[] =
96 {'A','p','p','S','e','a','r','c','h',0};
97 static const WCHAR szAllocateRegistrySpace
[] =
98 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
99 static const WCHAR szBindImage
[] =
100 {'B','i','n','d','I','m','a','g','e',0};
101 static const WCHAR szCCPSearch
[] =
102 {'C','C','P','S','e','a','r','c','h',0};
103 static const WCHAR szDeleteServices
[] =
104 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
105 static const WCHAR szDisableRollback
[] =
106 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
107 static const WCHAR szExecuteAction
[] =
108 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
109 static const WCHAR szInstallAdminPackage
[] =
110 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
111 static const WCHAR szInstallSFPCatalogFile
[] =
112 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
113 static const WCHAR szIsolateComponents
[] =
114 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
115 static const WCHAR szMigrateFeatureStates
[] =
116 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
117 static const WCHAR szMoveFiles
[] =
118 {'M','o','v','e','F','i','l','e','s',0};
119 static const WCHAR szMsiPublishAssemblies
[] =
120 {'M','s','i','P','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
121 static const WCHAR szMsiUnpublishAssemblies
[] =
122 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
123 static const WCHAR szInstallODBC
[] =
124 {'I','n','s','t','a','l','l','O','D','B','C',0};
125 static const WCHAR szInstallServices
[] =
126 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
127 static const WCHAR szPatchFiles
[] =
128 {'P','a','t','c','h','F','i','l','e','s',0};
129 static const WCHAR szPublishComponents
[] =
130 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
131 static const WCHAR szRegisterComPlus
[] =
132 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
133 static const WCHAR szRegisterFonts
[] =
134 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
135 static const WCHAR szRegisterUser
[] =
136 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
137 static const WCHAR szRemoveDuplicateFiles
[] =
138 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
139 static const WCHAR szRemoveEnvironmentStrings
[] =
140 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
141 static const WCHAR szRemoveExistingProducts
[] =
142 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
143 static const WCHAR szRemoveFolders
[] =
144 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
145 static const WCHAR szRemoveIniValues
[] =
146 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
147 static const WCHAR szRemoveODBC
[] =
148 {'R','e','m','o','v','e','O','D','B','C',0};
149 static const WCHAR szRemoveRegistryValues
[] =
150 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
151 static const WCHAR szRemoveShortcuts
[] =
152 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
153 static const WCHAR szRMCCPSearch
[] =
154 {'R','M','C','C','P','S','e','a','r','c','h',0};
155 static const WCHAR szScheduleReboot
[] =
156 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
157 static const WCHAR szSelfUnregModules
[] =
158 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
159 static const WCHAR szSetODBCFolders
[] =
160 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
161 static const WCHAR szStartServices
[] =
162 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
163 static const WCHAR szStopServices
[] =
164 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
165 static const WCHAR szUnpublishComponents
[] =
166 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
167 static const WCHAR szUnpublishFeatures
[] =
168 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
169 static const WCHAR szUnregisterClassInfo
[] =
170 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
171 static const WCHAR szUnregisterComPlus
[] =
172 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
173 static const WCHAR szUnregisterExtensionInfo
[] =
174 {'U','n','r','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n','I','n','f','o',0};
175 static const WCHAR szUnregisterFonts
[] =
176 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
177 static const WCHAR szUnregisterMIMEInfo
[] =
178 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
179 static const WCHAR szUnregisterProgIdInfo
[] =
180 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
181 static const WCHAR szUnregisterTypeLibraries
[] =
182 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
183 static const WCHAR szValidateProductID
[] =
184 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
185 static const WCHAR szWriteEnvironmentStrings
[] =
186 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
188 /********************************************************
190 ********************************************************/
192 static void ui_actionstart(MSIPACKAGE
*package
, LPCWSTR action
)
194 static const WCHAR Query_t
[] =
195 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
196 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
197 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
198 ' ','\'','%','s','\'',0};
201 row
= MSI_QueryGetRecord( package
->db
, Query_t
, action
);
204 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONSTART
, row
);
205 msiobj_release(&row
->hdr
);
208 static void ui_actioninfo(MSIPACKAGE
*package
, LPCWSTR action
, BOOL start
,
212 static const WCHAR template_s
[]=
213 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
215 static const WCHAR template_e
[]=
216 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
217 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
219 static const WCHAR format
[] =
220 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
224 GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, NULL
, format
, timet
, 0x100);
226 sprintfW(message
,template_s
,timet
,action
);
228 sprintfW(message
,template_e
,timet
,action
,rc
);
230 row
= MSI_CreateRecord(1);
231 MSI_RecordSetStringW(row
,1,message
);
233 MSI_ProcessMessage(package
, INSTALLMESSAGE_INFO
, row
);
234 msiobj_release(&row
->hdr
);
237 UINT
msi_parse_command_line( MSIPACKAGE
*package
, LPCWSTR szCommandLine
,
243 LPWSTR prop
= NULL
, val
= NULL
;
246 return ERROR_SUCCESS
;
258 TRACE("Looking at %s\n",debugstr_w(ptr
));
260 ptr2
= strchrW(ptr
,'=');
263 ERR("command line contains unknown string : %s\n", debugstr_w(ptr
));
270 prop
= msi_alloc((len
+1)*sizeof(WCHAR
));
271 memcpy(prop
,ptr
,len
*sizeof(WCHAR
));
281 while (*ptr
&& (quote
|| (!quote
&& *ptr
!=' ')))
294 val
= msi_alloc((len
+1)*sizeof(WCHAR
));
295 memcpy(val
,ptr2
,len
*sizeof(WCHAR
));
298 if (lstrlenW(prop
) > 0)
300 TRACE("Found commandline property (%s) = (%s)\n",
301 debugstr_w(prop
), debugstr_w(val
));
302 MSI_SetPropertyW(package
,prop
,val
);
308 return ERROR_SUCCESS
;
312 static LPWSTR
* msi_split_string( LPCWSTR str
, WCHAR sep
)
315 LPWSTR p
, *ret
= NULL
;
321 /* count the number of substrings */
322 for ( pc
= str
, count
= 0; pc
; count
++ )
324 pc
= strchrW( pc
, sep
);
329 /* allocate space for an array of substring pointers and the substrings */
330 ret
= msi_alloc( (count
+1) * sizeof (LPWSTR
) +
331 (lstrlenW(str
)+1) * sizeof(WCHAR
) );
335 /* copy the string and set the pointers */
336 p
= (LPWSTR
) &ret
[count
+1];
338 for( count
= 0; (ret
[count
] = p
); count
++ )
340 p
= strchrW( p
, sep
);
348 static UINT
msi_check_transform_applicable( MSIPACKAGE
*package
, IStorage
*patch
)
350 static const WCHAR szSystemLanguageID
[] =
351 { 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0 };
353 LPWSTR prod_code
, patch_product
, langid
= NULL
, template = NULL
;
354 UINT ret
= ERROR_FUNCTION_FAILED
;
356 prod_code
= msi_dup_property( package
, szProductCode
);
357 patch_product
= msi_get_suminfo_product( patch
);
359 TRACE("db = %s patch = %s\n", debugstr_w(prod_code
), debugstr_w(patch_product
));
361 if ( strstrW( patch_product
, prod_code
) )
366 si
= MSI_GetSummaryInformationW( patch
, 0 );
369 ERR("no summary information!\n");
373 template = msi_suminfo_dup_string( si
, PID_TEMPLATE
);
376 ERR("no template property!\n");
377 msiobj_release( &si
->hdr
);
384 msiobj_release( &si
->hdr
);
388 langid
= msi_dup_property( package
, szSystemLanguageID
);
391 msiobj_release( &si
->hdr
);
395 p
= strchrW( template, ';' );
396 if (p
&& (!strcmpW( p
+ 1, langid
) || !strcmpW( p
+ 1, szZero
)))
398 TRACE("applicable transform\n");
402 /* FIXME: check platform */
404 msiobj_release( &si
->hdr
);
408 msi_free( patch_product
);
409 msi_free( prod_code
);
410 msi_free( template );
416 static UINT
msi_apply_substorage_transform( MSIPACKAGE
*package
,
417 MSIDATABASE
*patch_db
, LPCWSTR name
)
419 UINT ret
= ERROR_FUNCTION_FAILED
;
420 IStorage
*stg
= NULL
;
423 TRACE("%p %s\n", package
, debugstr_w(name
) );
427 ERR("expected a colon in %s\n", debugstr_w(name
));
428 return ERROR_FUNCTION_FAILED
;
431 r
= IStorage_OpenStorage( patch_db
->storage
, name
, NULL
, STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
434 ret
= msi_check_transform_applicable( package
, stg
);
435 if (ret
== ERROR_SUCCESS
)
436 msi_table_apply_transform( package
->db
, stg
);
438 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name
));
439 IStorage_Release( stg
);
442 ERR("failed to open substorage %s\n", debugstr_w(name
));
444 return ERROR_SUCCESS
;
447 UINT
msi_check_patch_applicable( MSIPACKAGE
*package
, MSISUMMARYINFO
*si
)
449 LPWSTR guid_list
, *guids
, product_code
;
450 UINT i
, ret
= ERROR_FUNCTION_FAILED
;
452 product_code
= msi_dup_property( package
, szProductCode
);
455 /* FIXME: the property ProductCode should be written into the DB somewhere */
456 ERR("no product code to check\n");
457 return ERROR_SUCCESS
;
460 guid_list
= msi_suminfo_dup_string( si
, PID_TEMPLATE
);
461 guids
= msi_split_string( guid_list
, ';' );
462 for ( i
= 0; guids
[i
] && ret
!= ERROR_SUCCESS
; i
++ )
464 if (!lstrcmpW( guids
[i
], product_code
))
468 msi_free( guid_list
);
469 msi_free( product_code
);
474 static UINT
msi_set_media_source_prop(MSIPACKAGE
*package
)
477 MSIRECORD
*rec
= NULL
;
482 static const WCHAR query
[] = {'S','E','L','E','C','T',' ',
483 '`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
484 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
485 '`','S','o','u','r','c','e','`',' ','I','S',' ',
486 'N','O','T',' ','N','U','L','L',0};
488 r
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
489 if (r
!= ERROR_SUCCESS
)
492 r
= MSI_ViewExecute(view
, 0);
493 if (r
!= ERROR_SUCCESS
)
496 if (MSI_ViewFetch(view
, &rec
) == ERROR_SUCCESS
)
498 prop
= MSI_RecordGetString(rec
, 1);
499 patch
= msi_dup_property(package
, szPatch
);
500 MSI_SetPropertyW(package
, prop
, patch
);
505 if (rec
) msiobj_release(&rec
->hdr
);
506 msiobj_release(&view
->hdr
);
511 static UINT
msi_parse_patch_summary( MSIPACKAGE
*package
, MSIDATABASE
*patch_db
)
514 LPWSTR str
, *substorage
;
515 UINT i
, r
= ERROR_SUCCESS
;
517 si
= MSI_GetSummaryInformationW( patch_db
->storage
, 0 );
519 return ERROR_FUNCTION_FAILED
;
521 if (msi_check_patch_applicable( package
, si
) != ERROR_SUCCESS
)
523 TRACE("Patch not applicable\n");
524 return ERROR_SUCCESS
;
527 package
->patch
= msi_alloc(sizeof(MSIPATCHINFO
));
529 return ERROR_OUTOFMEMORY
;
531 package
->patch
->patchcode
= msi_suminfo_dup_string(si
, PID_REVNUMBER
);
532 if (!package
->patch
->patchcode
)
533 return ERROR_OUTOFMEMORY
;
535 /* enumerate the substorage */
536 str
= msi_suminfo_dup_string( si
, PID_LASTAUTHOR
);
537 package
->patch
->transforms
= str
;
539 substorage
= msi_split_string( str
, ';' );
540 for ( i
= 0; substorage
&& substorage
[i
] && r
== ERROR_SUCCESS
; i
++ )
541 r
= msi_apply_substorage_transform( package
, patch_db
, substorage
[i
] );
543 msi_free( substorage
);
544 msiobj_release( &si
->hdr
);
546 msi_set_media_source_prop(package
);
551 static UINT
msi_apply_patch_package( MSIPACKAGE
*package
, LPCWSTR file
)
553 MSIDATABASE
*patch_db
= NULL
;
556 TRACE("%p %s\n", package
, debugstr_w( file
) );
559 * We probably want to make sure we only open a patch collection here.
560 * Patch collections (.msp) and databases (.msi) have different GUIDs
561 * but currently MSI_OpenDatabaseW will accept both.
563 r
= MSI_OpenDatabaseW( file
, MSIDBOPEN_READONLY
, &patch_db
);
564 if ( r
!= ERROR_SUCCESS
)
566 ERR("failed to open patch collection %s\n", debugstr_w( file
) );
570 msi_parse_patch_summary( package
, patch_db
);
573 * There might be a CAB file in the patch package,
574 * so append it to the list of storage to search for streams.
576 append_storage_to_db( package
->db
, patch_db
->storage
);
578 msiobj_release( &patch_db
->hdr
);
580 return ERROR_SUCCESS
;
583 /* get the PATCH property, and apply all the patches it specifies */
584 static UINT
msi_apply_patches( MSIPACKAGE
*package
)
586 LPWSTR patch_list
, *patches
;
587 UINT i
, r
= ERROR_SUCCESS
;
589 patch_list
= msi_dup_property( package
, szPatch
);
591 TRACE("patches to be applied: %s\n", debugstr_w( patch_list
) );
593 patches
= msi_split_string( patch_list
, ';' );
594 for( i
=0; patches
&& patches
[i
] && r
== ERROR_SUCCESS
; i
++ )
595 r
= msi_apply_patch_package( package
, patches
[i
] );
598 msi_free( patch_list
);
603 static UINT
msi_apply_transforms( MSIPACKAGE
*package
)
605 static const WCHAR szTransforms
[] = {
606 'T','R','A','N','S','F','O','R','M','S',0 };
607 LPWSTR xform_list
, *xforms
;
608 UINT i
, r
= ERROR_SUCCESS
;
610 xform_list
= msi_dup_property( package
, szTransforms
);
611 xforms
= msi_split_string( xform_list
, ';' );
613 for( i
=0; xforms
&& xforms
[i
] && r
== ERROR_SUCCESS
; i
++ )
615 if (xforms
[i
][0] == ':')
616 r
= msi_apply_substorage_transform( package
, package
->db
, xforms
[i
] );
618 r
= MSI_DatabaseApplyTransformW( package
->db
, xforms
[i
], 0 );
622 msi_free( xform_list
);
627 static BOOL
ui_sequence_exists( MSIPACKAGE
*package
)
632 static const WCHAR ExecSeqQuery
[] =
633 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
634 '`','I','n','s','t','a','l','l',
635 'U','I','S','e','q','u','e','n','c','e','`',
636 ' ','W','H','E','R','E',' ',
637 '`','S','e','q','u','e','n','c','e','`',' ',
638 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
639 '`','S','e','q','u','e','n','c','e','`',0};
641 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
642 if (rc
== ERROR_SUCCESS
)
644 msiobj_release(&view
->hdr
);
651 static UINT
msi_set_sourcedir_props(MSIPACKAGE
*package
, BOOL replace
)
654 LPWSTR source
, check
;
657 static const WCHAR szOriginalDatabase
[] =
658 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
660 db
= msi_dup_property( package
, szOriginalDatabase
);
662 return ERROR_OUTOFMEMORY
;
664 p
= strrchrW( db
, '\\' );
667 p
= strrchrW( db
, '/' );
671 return ERROR_SUCCESS
;
676 source
= msi_alloc( len
* sizeof(WCHAR
) );
677 lstrcpynW( source
, db
, len
);
679 check
= msi_dup_property( package
, cszSourceDir
);
680 if (!check
|| replace
)
681 MSI_SetPropertyW( package
, cszSourceDir
, source
);
685 check
= msi_dup_property( package
, cszSOURCEDIR
);
686 if (!check
|| replace
)
687 MSI_SetPropertyW( package
, cszSOURCEDIR
, source
);
693 return ERROR_SUCCESS
;
696 static BOOL
needs_ui_sequence(MSIPACKAGE
*package
)
698 INT level
= msi_get_property_int(package
, szUILevel
, 0);
699 return (level
& INSTALLUILEVEL_MASK
) >= INSTALLUILEVEL_REDUCED
;
702 static UINT
msi_set_context(MSIPACKAGE
*package
)
709 package
->Context
= MSIINSTALLCONTEXT_USERUNMANAGED
;
711 r
= MSI_GetPropertyW(package
, szAllUsers
, val
, &sz
);
712 if (r
== ERROR_SUCCESS
)
715 if (num
== 1 || num
== 2)
716 package
->Context
= MSIINSTALLCONTEXT_MACHINE
;
719 return ERROR_SUCCESS
;
722 static UINT
ITERATE_Actions(MSIRECORD
*row
, LPVOID param
)
725 LPCWSTR cond
, action
;
726 MSIPACKAGE
*package
= param
;
728 action
= MSI_RecordGetString(row
,1);
731 ERR("Error is retrieving action name\n");
732 return ERROR_FUNCTION_FAILED
;
735 /* check conditions */
736 cond
= MSI_RecordGetString(row
,2);
738 /* this is a hack to skip errors in the condition code */
739 if (MSI_EvaluateConditionW(package
, cond
) == MSICONDITION_FALSE
)
741 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action
));
742 return ERROR_SUCCESS
;
745 if (needs_ui_sequence(package
))
746 rc
= ACTION_PerformUIAction(package
, action
, -1);
748 rc
= ACTION_PerformAction(package
, action
, -1, FALSE
);
750 msi_dialog_check_messages( NULL
);
752 if (package
->CurrentInstallState
!= ERROR_SUCCESS
)
753 rc
= package
->CurrentInstallState
;
755 if (rc
== ERROR_FUNCTION_NOT_CALLED
)
758 if (rc
!= ERROR_SUCCESS
)
759 ERR("Execution halted, action %s returned %i\n", debugstr_w(action
), rc
);
764 UINT
MSI_Sequence( MSIPACKAGE
*package
, LPCWSTR szTable
, INT iSequenceMode
)
768 static const WCHAR query
[] =
769 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
771 ' ','W','H','E','R','E',' ',
772 '`','S','e','q','u','e','n','c','e','`',' ',
773 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
774 '`','S','e','q','u','e','n','c','e','`',0};
776 TRACE("%p %s %i\n", package
, debugstr_w(szTable
), iSequenceMode
);
778 r
= MSI_OpenQuery( package
->db
, &view
, query
, szTable
);
779 if (r
== ERROR_SUCCESS
)
781 r
= MSI_IterateRecords( view
, NULL
, ITERATE_Actions
, package
);
782 msiobj_release(&view
->hdr
);
788 static UINT
ACTION_ProcessExecSequence(MSIPACKAGE
*package
, BOOL UIran
)
792 static const WCHAR ExecSeqQuery
[] =
793 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
794 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
795 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
796 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
797 'O','R','D','E','R',' ', 'B','Y',' ',
798 '`','S','e','q','u','e','n','c','e','`',0 };
799 static const WCHAR IVQuery
[] =
800 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
801 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
802 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
803 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
804 ' ','\'', 'I','n','s','t','a','l','l',
805 'V','a','l','i','d','a','t','e','\'', 0};
808 if (package
->script
->ExecuteSequenceRun
)
810 TRACE("Execute Sequence already Run\n");
811 return ERROR_SUCCESS
;
814 package
->script
->ExecuteSequenceRun
= TRUE
;
816 /* get the sequence number */
819 MSIRECORD
*row
= MSI_QueryGetRecord(package
->db
, IVQuery
);
821 return ERROR_FUNCTION_FAILED
;
822 seq
= MSI_RecordGetInteger(row
,1);
823 msiobj_release(&row
->hdr
);
826 rc
= MSI_OpenQuery(package
->db
, &view
, ExecSeqQuery
, seq
);
827 if (rc
== ERROR_SUCCESS
)
829 TRACE("Running the actions\n");
831 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, package
);
832 msiobj_release(&view
->hdr
);
838 static UINT
ACTION_ProcessUISequence(MSIPACKAGE
*package
)
842 static const WCHAR ExecSeqQuery
[] =
843 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
844 '`','I','n','s','t','a','l','l',
845 'U','I','S','e','q','u','e','n','c','e','`',
846 ' ','W','H','E','R','E',' ',
847 '`','S','e','q','u','e','n','c','e','`',' ',
848 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
849 '`','S','e','q','u','e','n','c','e','`',0};
851 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
852 if (rc
== ERROR_SUCCESS
)
854 TRACE("Running the actions\n");
856 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, package
);
857 msiobj_release(&view
->hdr
);
863 /********************************************************
864 * ACTION helper functions and functions that perform the actions
865 *******************************************************/
866 static BOOL
ACTION_HandleCustomAction( MSIPACKAGE
* package
, LPCWSTR action
,
867 UINT
* rc
, UINT script
, BOOL force
)
872 arc
= ACTION_CustomAction(package
, action
, script
, force
);
874 if (arc
!= ERROR_CALL_NOT_IMPLEMENTED
)
883 * Actual Action Handlers
886 static UINT
ITERATE_CreateFolders(MSIRECORD
*row
, LPVOID param
)
888 MSIPACKAGE
*package
= param
;
894 dir
= MSI_RecordGetString(row
,1);
897 ERR("Unable to get folder id\n");
898 return ERROR_SUCCESS
;
901 full_path
= resolve_folder(package
,dir
,FALSE
,FALSE
,TRUE
,&folder
);
904 ERR("Unable to resolve folder id %s\n",debugstr_w(dir
));
905 return ERROR_SUCCESS
;
908 TRACE("Folder is %s\n",debugstr_w(full_path
));
911 uirow
= MSI_CreateRecord(1);
912 MSI_RecordSetStringW(uirow
,1,full_path
);
913 ui_actiondata(package
,szCreateFolders
,uirow
);
914 msiobj_release( &uirow
->hdr
);
916 if (folder
->State
== 0)
917 create_full_pathW(full_path
);
922 return ERROR_SUCCESS
;
925 /* FIXME: probably should merge this with the above function */
926 static UINT
msi_create_directory( MSIPACKAGE
* package
, LPCWSTR dir
)
928 UINT rc
= ERROR_SUCCESS
;
932 install_path
= resolve_folder(package
, dir
, FALSE
, FALSE
, TRUE
, &folder
);
934 return ERROR_FUNCTION_FAILED
;
936 /* create the path */
937 if (folder
->State
== 0)
939 create_full_pathW(install_path
);
942 msi_free(install_path
);
947 UINT
msi_create_component_directories( MSIPACKAGE
*package
)
951 /* create all the folders required by the components are going to install */
952 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
954 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
956 msi_create_directory( package
, comp
->Directory
);
959 return ERROR_SUCCESS
;
962 static UINT
ACTION_CreateFolders(MSIPACKAGE
*package
)
964 static const WCHAR ExecSeqQuery
[] =
965 {'S','E','L','E','C','T',' ',
966 '`','D','i','r','e','c','t','o','r','y','_','`',
967 ' ','F','R','O','M',' ',
968 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
972 /* create all the empty folders specified in the CreateFolder table */
973 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
974 if (rc
!= ERROR_SUCCESS
)
975 return ERROR_SUCCESS
;
977 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateFolders
, package
);
978 msiobj_release(&view
->hdr
);
983 static UINT
load_component( MSIRECORD
*row
, LPVOID param
)
985 MSIPACKAGE
*package
= param
;
988 comp
= msi_alloc_zero( sizeof(MSICOMPONENT
) );
990 return ERROR_FUNCTION_FAILED
;
992 list_add_tail( &package
->components
, &comp
->entry
);
994 /* fill in the data */
995 comp
->Component
= msi_dup_record_field( row
, 1 );
997 TRACE("Loading Component %s\n", debugstr_w(comp
->Component
));
999 comp
->ComponentId
= msi_dup_record_field( row
, 2 );
1000 comp
->Directory
= msi_dup_record_field( row
, 3 );
1001 comp
->Attributes
= MSI_RecordGetInteger(row
,4);
1002 comp
->Condition
= msi_dup_record_field( row
, 5 );
1003 comp
->KeyPath
= msi_dup_record_field( row
, 6 );
1005 comp
->Installed
= INSTALLSTATE_UNKNOWN
;
1006 msi_component_set_state(package
, comp
, INSTALLSTATE_UNKNOWN
);
1008 return ERROR_SUCCESS
;
1011 static UINT
load_all_components( MSIPACKAGE
*package
)
1013 static const WCHAR query
[] = {
1014 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1015 '`','C','o','m','p','o','n','e','n','t','`',0 };
1019 if (!list_empty(&package
->components
))
1020 return ERROR_SUCCESS
;
1022 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1023 if (r
!= ERROR_SUCCESS
)
1026 r
= MSI_IterateRecords(view
, NULL
, load_component
, package
);
1027 msiobj_release(&view
->hdr
);
1032 MSIPACKAGE
*package
;
1033 MSIFEATURE
*feature
;
1036 static UINT
add_feature_component( MSIFEATURE
*feature
, MSICOMPONENT
*comp
)
1040 cl
= msi_alloc( sizeof (*cl
) );
1042 return ERROR_NOT_ENOUGH_MEMORY
;
1043 cl
->component
= comp
;
1044 list_add_tail( &feature
->Components
, &cl
->entry
);
1046 return ERROR_SUCCESS
;
1049 static UINT
add_feature_child( MSIFEATURE
*parent
, MSIFEATURE
*child
)
1053 fl
= msi_alloc( sizeof(*fl
) );
1055 return ERROR_NOT_ENOUGH_MEMORY
;
1056 fl
->feature
= child
;
1057 list_add_tail( &parent
->Children
, &fl
->entry
);
1059 return ERROR_SUCCESS
;
1062 static UINT
iterate_load_featurecomponents(MSIRECORD
*row
, LPVOID param
)
1064 _ilfs
* ilfs
= param
;
1068 component
= MSI_RecordGetString(row
,1);
1070 /* check to see if the component is already loaded */
1071 comp
= get_loaded_component( ilfs
->package
, component
);
1074 ERR("unknown component %s\n", debugstr_w(component
));
1075 return ERROR_FUNCTION_FAILED
;
1078 add_feature_component( ilfs
->feature
, comp
);
1079 comp
->Enabled
= TRUE
;
1081 return ERROR_SUCCESS
;
1084 static MSIFEATURE
*find_feature_by_name( MSIPACKAGE
*package
, LPCWSTR name
)
1086 MSIFEATURE
*feature
;
1091 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1093 if ( !lstrcmpW( feature
->Feature
, name
) )
1100 static UINT
load_feature(MSIRECORD
* row
, LPVOID param
)
1102 MSIPACKAGE
* package
= param
;
1103 MSIFEATURE
* feature
;
1104 static const WCHAR Query1
[] =
1105 {'S','E','L','E','C','T',' ',
1106 '`','C','o','m','p','o','n','e','n','t','_','`',
1107 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1108 'C','o','m','p','o','n','e','n','t','s','`',' ',
1109 'W','H','E','R','E',' ',
1110 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1115 /* fill in the data */
1117 feature
= msi_alloc_zero( sizeof (MSIFEATURE
) );
1119 return ERROR_NOT_ENOUGH_MEMORY
;
1121 list_init( &feature
->Children
);
1122 list_init( &feature
->Components
);
1124 feature
->Feature
= msi_dup_record_field( row
, 1 );
1126 TRACE("Loading feature %s\n",debugstr_w(feature
->Feature
));
1128 feature
->Feature_Parent
= msi_dup_record_field( row
, 2 );
1129 feature
->Title
= msi_dup_record_field( row
, 3 );
1130 feature
->Description
= msi_dup_record_field( row
, 4 );
1132 if (!MSI_RecordIsNull(row
,5))
1133 feature
->Display
= MSI_RecordGetInteger(row
,5);
1135 feature
->Level
= MSI_RecordGetInteger(row
,6);
1136 feature
->Directory
= msi_dup_record_field( row
, 7 );
1137 feature
->Attributes
= MSI_RecordGetInteger(row
,8);
1139 feature
->Installed
= INSTALLSTATE_UNKNOWN
;
1140 msi_feature_set_state(package
, feature
, INSTALLSTATE_UNKNOWN
);
1142 list_add_tail( &package
->features
, &feature
->entry
);
1144 /* load feature components */
1146 rc
= MSI_OpenQuery( package
->db
, &view
, Query1
, feature
->Feature
);
1147 if (rc
!= ERROR_SUCCESS
)
1148 return ERROR_SUCCESS
;
1150 ilfs
.package
= package
;
1151 ilfs
.feature
= feature
;
1153 MSI_IterateRecords(view
, NULL
, iterate_load_featurecomponents
, &ilfs
);
1154 msiobj_release(&view
->hdr
);
1156 return ERROR_SUCCESS
;
1159 static UINT
find_feature_children(MSIRECORD
* row
, LPVOID param
)
1161 MSIPACKAGE
* package
= param
;
1162 MSIFEATURE
*parent
, *child
;
1164 child
= find_feature_by_name( package
, MSI_RecordGetString( row
, 1 ) );
1166 return ERROR_FUNCTION_FAILED
;
1168 if (!child
->Feature_Parent
)
1169 return ERROR_SUCCESS
;
1171 parent
= find_feature_by_name( package
, child
->Feature_Parent
);
1173 return ERROR_FUNCTION_FAILED
;
1175 add_feature_child( parent
, child
);
1176 return ERROR_SUCCESS
;
1179 static UINT
load_all_features( MSIPACKAGE
*package
)
1181 static const WCHAR query
[] = {
1182 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1183 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1184 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1188 if (!list_empty(&package
->features
))
1189 return ERROR_SUCCESS
;
1191 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1192 if (r
!= ERROR_SUCCESS
)
1195 r
= MSI_IterateRecords( view
, NULL
, load_feature
, package
);
1196 if (r
!= ERROR_SUCCESS
)
1199 r
= MSI_IterateRecords( view
, NULL
, find_feature_children
, package
);
1200 msiobj_release( &view
->hdr
);
1205 static LPWSTR
folder_split_path(LPWSTR p
, WCHAR ch
)
1216 static UINT
load_file_hash(MSIPACKAGE
*package
, MSIFILE
*file
)
1218 static const WCHAR query
[] = {
1219 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1220 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1221 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1222 MSIQUERY
*view
= NULL
;
1223 MSIRECORD
*row
= NULL
;
1226 TRACE("%s\n", debugstr_w(file
->File
));
1228 r
= MSI_OpenQuery(package
->db
, &view
, query
, file
->File
);
1229 if (r
!= ERROR_SUCCESS
)
1232 r
= MSI_ViewExecute(view
, NULL
);
1233 if (r
!= ERROR_SUCCESS
)
1236 r
= MSI_ViewFetch(view
, &row
);
1237 if (r
!= ERROR_SUCCESS
)
1240 file
->hash
.dwFileHashInfoSize
= sizeof(MSIFILEHASHINFO
);
1241 file
->hash
.dwData
[0] = MSI_RecordGetInteger(row
, 3);
1242 file
->hash
.dwData
[1] = MSI_RecordGetInteger(row
, 4);
1243 file
->hash
.dwData
[2] = MSI_RecordGetInteger(row
, 5);
1244 file
->hash
.dwData
[3] = MSI_RecordGetInteger(row
, 6);
1247 if (view
) msiobj_release(&view
->hdr
);
1248 if (row
) msiobj_release(&row
->hdr
);
1252 static UINT
load_file(MSIRECORD
*row
, LPVOID param
)
1254 MSIPACKAGE
* package
= param
;
1258 /* fill in the data */
1260 file
= msi_alloc_zero( sizeof (MSIFILE
) );
1262 return ERROR_NOT_ENOUGH_MEMORY
;
1264 file
->File
= msi_dup_record_field( row
, 1 );
1266 component
= MSI_RecordGetString( row
, 2 );
1267 file
->Component
= get_loaded_component( package
, component
);
1269 if (!file
->Component
)
1271 WARN("Component not found: %s\n", debugstr_w(component
));
1272 msi_free(file
->File
);
1274 return ERROR_SUCCESS
;
1277 file
->FileName
= msi_dup_record_field( row
, 3 );
1278 reduce_to_longfilename( file
->FileName
);
1280 file
->ShortName
= msi_dup_record_field( row
, 3 );
1281 file
->LongName
= strdupW( folder_split_path(file
->ShortName
, '|'));
1283 file
->FileSize
= MSI_RecordGetInteger( row
, 4 );
1284 file
->Version
= msi_dup_record_field( row
, 5 );
1285 file
->Language
= msi_dup_record_field( row
, 6 );
1286 file
->Attributes
= MSI_RecordGetInteger( row
, 7 );
1287 file
->Sequence
= MSI_RecordGetInteger( row
, 8 );
1289 file
->state
= msifs_invalid
;
1291 /* if the compressed bits are not set in the file attributes,
1292 * then read the information from the package word count property
1294 if (package
->WordCount
& msidbSumInfoSourceTypeAdminImage
)
1296 file
->IsCompressed
= FALSE
;
1298 else if (file
->Attributes
&
1299 (msidbFileAttributesCompressed
| msidbFileAttributesPatchAdded
))
1301 file
->IsCompressed
= TRUE
;
1303 else if (file
->Attributes
& msidbFileAttributesNoncompressed
)
1305 file
->IsCompressed
= FALSE
;
1309 file
->IsCompressed
= package
->WordCount
& msidbSumInfoSourceTypeCompressed
;
1312 load_file_hash(package
, file
);
1314 TRACE("File Loaded (%s)\n",debugstr_w(file
->File
));
1316 list_add_tail( &package
->files
, &file
->entry
);
1318 return ERROR_SUCCESS
;
1321 static UINT
load_all_files(MSIPACKAGE
*package
)
1325 static const WCHAR Query
[] =
1326 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1327 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1328 '`','S','e','q','u','e','n','c','e','`', 0};
1330 if (!list_empty(&package
->files
))
1331 return ERROR_SUCCESS
;
1333 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
1334 if (rc
!= ERROR_SUCCESS
)
1335 return ERROR_SUCCESS
;
1337 rc
= MSI_IterateRecords(view
, NULL
, load_file
, package
);
1338 msiobj_release(&view
->hdr
);
1340 return ERROR_SUCCESS
;
1343 static UINT
load_folder( MSIRECORD
*row
, LPVOID param
)
1345 MSIPACKAGE
*package
= param
;
1346 static WCHAR szEmpty
[] = { 0 };
1347 LPWSTR p
, tgt_short
, tgt_long
, src_short
, src_long
;
1350 folder
= msi_alloc_zero( sizeof (MSIFOLDER
) );
1352 return ERROR_NOT_ENOUGH_MEMORY
;
1354 folder
->Directory
= msi_dup_record_field( row
, 1 );
1356 TRACE("%s\n", debugstr_w(folder
->Directory
));
1358 p
= msi_dup_record_field(row
, 3);
1360 /* split src and target dir */
1362 src_short
= folder_split_path( p
, ':' );
1364 /* split the long and short paths */
1365 tgt_long
= folder_split_path( tgt_short
, '|' );
1366 src_long
= folder_split_path( src_short
, '|' );
1368 /* check for no-op dirs */
1369 if (!lstrcmpW(szDot
, tgt_short
))
1370 tgt_short
= szEmpty
;
1371 if (!lstrcmpW(szDot
, src_short
))
1372 src_short
= szEmpty
;
1375 tgt_long
= tgt_short
;
1378 src_short
= tgt_short
;
1379 src_long
= tgt_long
;
1383 src_long
= src_short
;
1385 /* FIXME: use the target short path too */
1386 folder
->TargetDefault
= strdupW(tgt_long
);
1387 folder
->SourceShortPath
= strdupW(src_short
);
1388 folder
->SourceLongPath
= strdupW(src_long
);
1391 TRACE("TargetDefault = %s\n",debugstr_w( folder
->TargetDefault
));
1392 TRACE("SourceLong = %s\n", debugstr_w( folder
->SourceLongPath
));
1393 TRACE("SourceShort = %s\n", debugstr_w( folder
->SourceShortPath
));
1395 folder
->Parent
= msi_dup_record_field( row
, 2 );
1397 folder
->Property
= msi_dup_property( package
, folder
->Directory
);
1399 list_add_tail( &package
->folders
, &folder
->entry
);
1401 TRACE("returning %p\n", folder
);
1403 return ERROR_SUCCESS
;
1406 static UINT
load_all_folders( MSIPACKAGE
*package
)
1408 static const WCHAR query
[] = {
1409 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1410 '`','D','i','r','e','c','t','o','r','y','`',0 };
1414 if (!list_empty(&package
->folders
))
1415 return ERROR_SUCCESS
;
1417 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1418 if (r
!= ERROR_SUCCESS
)
1421 r
= MSI_IterateRecords(view
, NULL
, load_folder
, package
);
1422 msiobj_release(&view
->hdr
);
1427 * I am not doing any of the costing functionality yet.
1428 * Mostly looking at doing the Component and Feature loading
1430 * The native MSI does A LOT of modification to tables here. Mostly adding
1431 * a lot of temporary columns to the Feature and Component tables.
1433 * note: Native msi also tracks the short filename. But I am only going to
1434 * track the long ones. Also looking at this directory table
1435 * it appears that the directory table does not get the parents
1436 * resolved base on property only based on their entries in the
1439 static UINT
ACTION_CostInitialize(MSIPACKAGE
*package
)
1441 static const WCHAR szCosting
[] =
1442 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1444 MSI_SetPropertyW(package
, szCosting
, szZero
);
1445 MSI_SetPropertyW(package
, cszRootDrive
, c_colon
);
1447 load_all_folders( package
);
1448 load_all_components( package
);
1449 load_all_features( package
);
1450 load_all_files( package
);
1452 return ERROR_SUCCESS
;
1455 static UINT
execute_script(MSIPACKAGE
*package
, UINT script
)
1458 UINT rc
= ERROR_SUCCESS
;
1460 TRACE("Executing Script %i\n",script
);
1462 if (!package
->script
)
1464 ERR("no script!\n");
1465 return ERROR_FUNCTION_FAILED
;
1468 for (i
= 0; i
< package
->script
->ActionCount
[script
]; i
++)
1471 action
= package
->script
->Actions
[script
][i
];
1472 ui_actionstart(package
, action
);
1473 TRACE("Executing Action (%s)\n",debugstr_w(action
));
1474 rc
= ACTION_PerformAction(package
, action
, script
, TRUE
);
1475 if (rc
!= ERROR_SUCCESS
)
1478 msi_free_action_script(package
, script
);
1482 static UINT
ACTION_FileCost(MSIPACKAGE
*package
)
1484 return ERROR_SUCCESS
;
1487 static void ACTION_GetComponentInstallStates(MSIPACKAGE
*package
)
1493 state
= MsiQueryProductStateW(package
->ProductCode
);
1495 LIST_FOR_EACH_ENTRY(comp
, &package
->components
, MSICOMPONENT
, entry
)
1497 if (!comp
->ComponentId
)
1500 if (state
!= INSTALLSTATE_LOCAL
&& state
!= INSTALLSTATE_DEFAULT
)
1501 comp
->Installed
= INSTALLSTATE_ABSENT
;
1504 r
= MsiQueryComponentStateW(package
->ProductCode
, NULL
,
1505 package
->Context
, comp
->ComponentId
,
1507 if (r
!= ERROR_SUCCESS
)
1508 comp
->Installed
= INSTALLSTATE_ABSENT
;
1513 static void ACTION_GetFeatureInstallStates(MSIPACKAGE
*package
)
1515 MSIFEATURE
*feature
;
1518 state
= MsiQueryProductStateW(package
->ProductCode
);
1520 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1522 if (state
!= INSTALLSTATE_LOCAL
&& state
!= INSTALLSTATE_DEFAULT
)
1523 feature
->Installed
= INSTALLSTATE_ABSENT
;
1526 feature
->Installed
= MsiQueryFeatureStateW(package
->ProductCode
,
1532 static BOOL
process_state_property(MSIPACKAGE
* package
, int level
,
1533 LPCWSTR property
, INSTALLSTATE state
)
1536 MSIFEATURE
*feature
;
1538 override
= msi_dup_property( package
, property
);
1542 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1544 if (lstrcmpW(property
, szRemove
) &&
1545 (feature
->Level
<= 0 || feature
->Level
> level
))
1548 if (!strcmpW(property
, szReinstall
)) state
= feature
->Installed
;
1550 if (strcmpiW(override
, szAll
)==0)
1551 msi_feature_set_state(package
, feature
, state
);
1554 LPWSTR ptr
= override
;
1555 LPWSTR ptr2
= strchrW(override
,',');
1559 int len
= ptr2
- ptr
;
1561 if ((ptr2
&& strlenW(feature
->Feature
) == len
&& !strncmpW(ptr
, feature
->Feature
, len
))
1562 || (!ptr2
&& !strcmpW(ptr
, feature
->Feature
)))
1564 msi_feature_set_state(package
, feature
, state
);
1570 ptr2
= strchrW(ptr
,',');
1582 static BOOL
process_overrides( MSIPACKAGE
*package
, int level
)
1584 static const WCHAR szAddLocal
[] =
1585 {'A','D','D','L','O','C','A','L',0};
1586 static const WCHAR szAddSource
[] =
1587 {'A','D','D','S','O','U','R','C','E',0};
1588 static const WCHAR szAdvertise
[] =
1589 {'A','D','V','E','R','T','I','S','E',0};
1592 /* all these activation/deactivation things happen in order and things
1593 * later on the list override things earlier on the list.
1595 * 0 INSTALLLEVEL processing
1608 ret
|= process_state_property( package
, level
, szAddLocal
, INSTALLSTATE_LOCAL
);
1609 ret
|= process_state_property( package
, level
, szRemove
, INSTALLSTATE_ABSENT
);
1610 ret
|= process_state_property( package
, level
, szAddSource
, INSTALLSTATE_SOURCE
);
1611 ret
|= process_state_property( package
, level
, szReinstall
, INSTALLSTATE_UNKNOWN
);
1612 ret
|= process_state_property( package
, level
, szAdvertise
, INSTALLSTATE_ADVERTISED
);
1615 MSI_SetPropertyW( package
, szPreselected
, szOne
);
1620 UINT
MSI_SetFeatureStates(MSIPACKAGE
*package
)
1623 static const WCHAR szlevel
[] =
1624 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1625 MSICOMPONENT
* component
;
1626 MSIFEATURE
*feature
;
1628 TRACE("Checking Install Level\n");
1630 level
= msi_get_property_int(package
, szlevel
, 1);
1632 if (!msi_get_property_int( package
, szPreselected
, 0 ))
1634 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1636 BOOL feature_state
= ((feature
->Level
> 0) &&
1637 (feature
->Level
<= level
));
1639 if ((feature_state
) && (feature
->Action
== INSTALLSTATE_UNKNOWN
))
1641 if (feature
->Attributes
& msidbFeatureAttributesFavorSource
)
1642 msi_feature_set_state(package
, feature
, INSTALLSTATE_SOURCE
);
1643 else if (feature
->Attributes
& msidbFeatureAttributesFavorAdvertise
)
1644 msi_feature_set_state(package
, feature
, INSTALLSTATE_ADVERTISED
);
1646 msi_feature_set_state(package
, feature
, INSTALLSTATE_LOCAL
);
1650 /* disable child features of unselected parent features */
1651 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1655 if (feature
->Level
> 0 && feature
->Level
<= level
)
1658 LIST_FOR_EACH_ENTRY( fl
, &feature
->Children
, FeatureList
, entry
)
1659 msi_feature_set_state(package
, fl
->feature
, INSTALLSTATE_UNKNOWN
);
1664 * now we want to enable or disable components base on feature
1667 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1671 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1672 debugstr_w(feature
->Feature
), feature
->Level
, feature
->Installed
, feature
->Action
);
1674 if (!feature
->Level
)
1677 /* features with components that have compressed files are made local */
1678 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1680 if (cl
->component
->Enabled
&&
1681 cl
->component
->ForceLocalState
&&
1682 feature
->Action
== INSTALLSTATE_SOURCE
)
1684 msi_feature_set_state(package
, feature
, INSTALLSTATE_LOCAL
);
1689 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1691 component
= cl
->component
;
1693 if (!component
->Enabled
)
1696 switch (feature
->Action
)
1698 case INSTALLSTATE_ABSENT
:
1699 component
->anyAbsent
= 1;
1701 case INSTALLSTATE_ADVERTISED
:
1702 component
->hasAdvertiseFeature
= 1;
1704 case INSTALLSTATE_SOURCE
:
1705 component
->hasSourceFeature
= 1;
1707 case INSTALLSTATE_LOCAL
:
1708 component
->hasLocalFeature
= 1;
1710 case INSTALLSTATE_DEFAULT
:
1711 if (feature
->Attributes
& msidbFeatureAttributesFavorAdvertise
)
1712 component
->hasAdvertiseFeature
= 1;
1713 else if (feature
->Attributes
& msidbFeatureAttributesFavorSource
)
1714 component
->hasSourceFeature
= 1;
1716 component
->hasLocalFeature
= 1;
1724 LIST_FOR_EACH_ENTRY( component
, &package
->components
, MSICOMPONENT
, entry
)
1726 /* if the component isn't enabled, leave it alone */
1727 if (!component
->Enabled
)
1730 /* check if it's local or source */
1731 if (!(component
->Attributes
& msidbComponentAttributesOptional
) &&
1732 (component
->hasLocalFeature
|| component
->hasSourceFeature
))
1734 if ((component
->Attributes
& msidbComponentAttributesSourceOnly
) &&
1735 !component
->ForceLocalState
)
1736 msi_component_set_state(package
, component
, INSTALLSTATE_SOURCE
);
1738 msi_component_set_state(package
, component
, INSTALLSTATE_LOCAL
);
1742 /* if any feature is local, the component must be local too */
1743 if (component
->hasLocalFeature
)
1745 msi_component_set_state(package
, component
, INSTALLSTATE_LOCAL
);
1749 if (component
->hasSourceFeature
)
1751 msi_component_set_state(package
, component
, INSTALLSTATE_SOURCE
);
1755 if (component
->hasAdvertiseFeature
)
1757 msi_component_set_state(package
, component
, INSTALLSTATE_ADVERTISED
);
1761 TRACE("nobody wants component %s\n", debugstr_w(component
->Component
));
1762 if (component
->anyAbsent
)
1763 msi_component_set_state(package
, component
, INSTALLSTATE_ABSENT
);
1766 LIST_FOR_EACH_ENTRY( component
, &package
->components
, MSICOMPONENT
, entry
)
1768 if (component
->Action
== INSTALLSTATE_DEFAULT
)
1770 TRACE("%s was default, setting to local\n", debugstr_w(component
->Component
));
1771 msi_component_set_state(package
, component
, INSTALLSTATE_LOCAL
);
1774 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1775 debugstr_w(component
->Component
), component
->Installed
, component
->Action
);
1779 return ERROR_SUCCESS
;
1782 static UINT
ITERATE_CostFinalizeDirectories(MSIRECORD
*row
, LPVOID param
)
1784 MSIPACKAGE
*package
= param
;
1789 name
= MSI_RecordGetString(row
,1);
1791 f
= get_loaded_folder(package
, name
);
1792 if (!f
) return ERROR_SUCCESS
;
1794 /* reset the ResolvedTarget */
1795 msi_free(f
->ResolvedTarget
);
1796 f
->ResolvedTarget
= NULL
;
1798 /* This helper function now does ALL the work */
1799 TRACE("Dir %s ...\n",debugstr_w(name
));
1800 path
= resolve_folder(package
,name
,FALSE
,TRUE
,TRUE
,NULL
);
1801 TRACE("resolves to %s\n",debugstr_w(path
));
1804 return ERROR_SUCCESS
;
1807 static UINT
ITERATE_CostFinalizeConditions(MSIRECORD
*row
, LPVOID param
)
1809 MSIPACKAGE
*package
= param
;
1811 MSIFEATURE
*feature
;
1813 name
= MSI_RecordGetString( row
, 1 );
1815 feature
= get_loaded_feature( package
, name
);
1817 ERR("FAILED to find loaded feature %s\n",debugstr_w(name
));
1821 Condition
= MSI_RecordGetString(row
,3);
1823 if (MSI_EvaluateConditionW(package
,Condition
) == MSICONDITION_TRUE
)
1825 int level
= MSI_RecordGetInteger(row
,2);
1826 TRACE("Resetting feature %s to level %i\n", debugstr_w(name
), level
);
1827 feature
->Level
= level
;
1830 return ERROR_SUCCESS
;
1833 static LPWSTR
msi_get_disk_file_version( LPCWSTR filename
)
1835 static const WCHAR name_fmt
[] =
1836 {'%','u','.','%','u','.','%','u','.','%','u',0};
1837 static const WCHAR name
[] = {'\\',0};
1838 VS_FIXEDFILEINFO
*lpVer
;
1839 WCHAR filever
[0x100];
1845 TRACE("%s\n", debugstr_w(filename
));
1847 versize
= GetFileVersionInfoSizeW( filename
, &handle
);
1851 version
= msi_alloc( versize
);
1852 GetFileVersionInfoW( filename
, 0, versize
, version
);
1854 if (!VerQueryValueW( version
, name
, (LPVOID
*)&lpVer
, &sz
))
1856 msi_free( version
);
1860 sprintfW( filever
, name_fmt
,
1861 HIWORD(lpVer
->dwFileVersionMS
),
1862 LOWORD(lpVer
->dwFileVersionMS
),
1863 HIWORD(lpVer
->dwFileVersionLS
),
1864 LOWORD(lpVer
->dwFileVersionLS
));
1866 msi_free( version
);
1868 return strdupW( filever
);
1871 static UINT
msi_check_file_install_states( MSIPACKAGE
*package
)
1873 LPWSTR file_version
;
1876 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
1878 MSICOMPONENT
* comp
= file
->Component
;
1884 if (file
->IsCompressed
)
1885 comp
->ForceLocalState
= TRUE
;
1887 /* calculate target */
1888 p
= resolve_folder(package
, comp
->Directory
, FALSE
, FALSE
, TRUE
, NULL
);
1890 msi_free(file
->TargetPath
);
1892 TRACE("file %s is named %s\n",
1893 debugstr_w(file
->File
), debugstr_w(file
->FileName
));
1895 file
->TargetPath
= build_directory_name(2, p
, file
->FileName
);
1899 TRACE("file %s resolves to %s\n",
1900 debugstr_w(file
->File
), debugstr_w(file
->TargetPath
));
1902 /* don't check files of components that aren't installed */
1903 if (comp
->Installed
== INSTALLSTATE_UNKNOWN
||
1904 comp
->Installed
== INSTALLSTATE_ABSENT
)
1906 file
->state
= msifs_missing
; /* assume files are missing */
1910 if (GetFileAttributesW(file
->TargetPath
) == INVALID_FILE_ATTRIBUTES
)
1912 file
->state
= msifs_missing
;
1913 comp
->Cost
+= file
->FileSize
;
1917 if (file
->Version
&&
1918 (file_version
= msi_get_disk_file_version( file
->TargetPath
)))
1920 TRACE("new %s old %s\n", debugstr_w(file
->Version
),
1921 debugstr_w(file_version
));
1922 /* FIXME: seems like a bad way to compare version numbers */
1923 if (lstrcmpiW(file_version
, file
->Version
)<0)
1925 file
->state
= msifs_overwrite
;
1926 comp
->Cost
+= file
->FileSize
;
1929 file
->state
= msifs_present
;
1930 msi_free( file_version
);
1933 file
->state
= msifs_present
;
1936 return ERROR_SUCCESS
;
1940 * A lot is done in this function aside from just the costing.
1941 * The costing needs to be implemented at some point but for now I am going
1942 * to focus on the directory building
1945 static UINT
ACTION_CostFinalize(MSIPACKAGE
*package
)
1947 static const WCHAR ExecSeqQuery
[] =
1948 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1949 '`','D','i','r','e','c','t','o','r','y','`',0};
1950 static const WCHAR ConditionQuery
[] =
1951 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1952 '`','C','o','n','d','i','t','i','o','n','`',0};
1953 static const WCHAR szCosting
[] =
1954 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1955 static const WCHAR szlevel
[] =
1956 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1957 static const WCHAR szOutOfDiskSpace
[] =
1958 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
1960 UINT rc
= ERROR_SUCCESS
;
1964 TRACE("Building Directory properties\n");
1966 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
1967 if (rc
== ERROR_SUCCESS
)
1969 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CostFinalizeDirectories
,
1971 msiobj_release(&view
->hdr
);
1974 /* read components states from the registry */
1975 ACTION_GetComponentInstallStates(package
);
1976 ACTION_GetFeatureInstallStates(package
);
1978 TRACE("File calculations\n");
1979 msi_check_file_install_states( package
);
1981 if (!process_overrides( package
, msi_get_property_int( package
, szlevel
, 1 ) ))
1983 TRACE("Evaluating Condition Table\n");
1985 rc
= MSI_DatabaseOpenViewW( package
->db
, ConditionQuery
, &view
);
1986 if (rc
== ERROR_SUCCESS
)
1988 rc
= MSI_IterateRecords( view
, NULL
, ITERATE_CostFinalizeConditions
, package
);
1989 msiobj_release( &view
->hdr
);
1992 TRACE("Enabling or Disabling Components\n");
1993 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1995 if (MSI_EvaluateConditionW( package
, comp
->Condition
) == MSICONDITION_FALSE
)
1997 TRACE("Disabling component %s\n", debugstr_w(comp
->Component
));
1998 comp
->Enabled
= FALSE
;
2001 comp
->Enabled
= TRUE
;
2005 MSI_SetPropertyW(package
,szCosting
,szOne
);
2006 /* set default run level if not set */
2007 level
= msi_dup_property( package
, szlevel
);
2009 MSI_SetPropertyW(package
,szlevel
, szOne
);
2012 /* FIXME: check volume disk space */
2013 MSI_SetPropertyW(package
, szOutOfDiskSpace
, szZero
);
2015 return MSI_SetFeatureStates(package
);
2018 /* OK this value is "interpreted" and then formatted based on the
2019 first few characters */
2020 static LPSTR
parse_value(MSIPACKAGE
*package
, LPCWSTR value
, DWORD
*type
,
2025 if (value
[0]=='#' && value
[1]!='#' && value
[1]!='%')
2031 LPWSTR deformated
= NULL
;
2034 deformat_string(package
, &value
[2], &deformated
);
2036 /* binary value type */
2040 *size
= (strlenW(ptr
)/2)+1;
2042 *size
= strlenW(ptr
)/2;
2044 data
= msi_alloc(*size
);
2050 /* if uneven pad with a zero in front */
2056 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2058 TRACE("Uneven byte count\n");
2066 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2069 msi_free(deformated
);
2071 TRACE("Data %i bytes(%i)\n",*size
,count
);
2078 deformat_string(package
, &value
[1], &deformated
);
2081 *size
= sizeof(DWORD
);
2082 data
= msi_alloc(*size
);
2088 if ( (*p
< '0') || (*p
> '9') )
2094 if (deformated
[0] == '-')
2097 TRACE("DWORD %i\n",*(LPDWORD
)data
);
2099 msi_free(deformated
);
2104 static const WCHAR szMulti
[] = {'[','~',']',0};
2113 *type
=REG_EXPAND_SZ
;
2121 if (strstrW(value
,szMulti
))
2122 *type
= REG_MULTI_SZ
;
2124 /* remove initial delimiter */
2125 if (!strncmpW(value
, szMulti
, 3))
2128 *size
= deformat_string(package
, ptr
,(LPWSTR
*)&data
);
2130 /* add double NULL terminator */
2131 if (*type
== REG_MULTI_SZ
)
2133 *size
+= 2 * sizeof(WCHAR
); /* two NULL terminators */
2134 data
= msi_realloc_zero(data
, *size
);
2140 static UINT
ITERATE_WriteRegistryValues(MSIRECORD
*row
, LPVOID param
)
2142 MSIPACKAGE
*package
= param
;
2143 static const WCHAR szHCR
[] =
2144 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2145 'R','O','O','T','\\',0};
2146 static const WCHAR szHCU
[] =
2147 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2148 'U','S','E','R','\\',0};
2149 static const WCHAR szHLM
[] =
2150 {'H','K','E','Y','_','L','O','C','A','L','_',
2151 'M','A','C','H','I','N','E','\\',0};
2152 static const WCHAR szHU
[] =
2153 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2155 LPSTR value_data
= NULL
;
2156 HKEY root_key
, hkey
;
2159 LPCWSTR szRoot
, component
, name
, key
, value
;
2164 BOOL check_first
= FALSE
;
2167 ui_progress(package
,2,0,0,0);
2174 component
= MSI_RecordGetString(row
, 6);
2175 comp
= get_loaded_component(package
,component
);
2177 return ERROR_SUCCESS
;
2179 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2181 TRACE("Skipping write due to disabled component %s\n",
2182 debugstr_w(component
));
2184 comp
->Action
= comp
->Installed
;
2186 return ERROR_SUCCESS
;
2189 comp
->Action
= INSTALLSTATE_LOCAL
;
2191 name
= MSI_RecordGetString(row
, 4);
2192 if( MSI_RecordIsNull(row
,5) && name
)
2194 /* null values can have special meanings */
2195 if (name
[0]=='-' && name
[1] == 0)
2196 return ERROR_SUCCESS
;
2197 else if ((name
[0]=='+' && name
[1] == 0) ||
2198 (name
[0] == '*' && name
[1] == 0))
2203 root
= MSI_RecordGetInteger(row
,2);
2204 key
= MSI_RecordGetString(row
, 3);
2206 /* get the root key */
2211 LPWSTR all_users
= msi_dup_property( package
, szAllUsers
);
2212 if (all_users
&& all_users
[0] == '1')
2214 root_key
= HKEY_LOCAL_MACHINE
;
2219 root_key
= HKEY_CURRENT_USER
;
2222 msi_free(all_users
);
2225 case 0: root_key
= HKEY_CLASSES_ROOT
;
2228 case 1: root_key
= HKEY_CURRENT_USER
;
2231 case 2: root_key
= HKEY_LOCAL_MACHINE
;
2234 case 3: root_key
= HKEY_USERS
;
2238 ERR("Unknown root %i\n",root
);
2244 return ERROR_SUCCESS
;
2246 deformat_string(package
, key
, &deformated
);
2247 size
= strlenW(deformated
) + strlenW(szRoot
) + 1;
2248 uikey
= msi_alloc(size
*sizeof(WCHAR
));
2249 strcpyW(uikey
,szRoot
);
2250 strcatW(uikey
,deformated
);
2252 if (RegCreateKeyW( root_key
, deformated
, &hkey
))
2254 ERR("Could not create key %s\n",debugstr_w(deformated
));
2255 msi_free(deformated
);
2257 return ERROR_SUCCESS
;
2259 msi_free(deformated
);
2261 value
= MSI_RecordGetString(row
,5);
2263 value_data
= parse_value(package
, value
, &type
, &size
);
2266 value_data
= (LPSTR
)strdupW(szEmpty
);
2267 size
= sizeof(szEmpty
);
2271 deformat_string(package
, name
, &deformated
);
2275 TRACE("Setting value %s of %s\n",debugstr_w(deformated
),
2277 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
)value_data
, size
);
2282 rc
= RegQueryValueExW(hkey
, deformated
, NULL
, NULL
, NULL
, &sz
);
2283 if (rc
== ERROR_SUCCESS
|| rc
== ERROR_MORE_DATA
)
2285 TRACE("value %s of %s checked already exists\n",
2286 debugstr_w(deformated
), debugstr_w(uikey
));
2290 TRACE("Checked and setting value %s of %s\n",
2291 debugstr_w(deformated
), debugstr_w(uikey
));
2292 if (deformated
|| size
)
2293 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
) value_data
, size
);
2298 uirow
= MSI_CreateRecord(3);
2299 MSI_RecordSetStringW(uirow
,2,deformated
);
2300 MSI_RecordSetStringW(uirow
,1,uikey
);
2303 MSI_RecordSetStringW(uirow
,3,(LPWSTR
)value_data
);
2305 MSI_RecordSetStringW(uirow
,3,value
);
2307 ui_actiondata(package
,szWriteRegistryValues
,uirow
);
2308 msiobj_release( &uirow
->hdr
);
2310 msi_free(value_data
);
2311 msi_free(deformated
);
2314 return ERROR_SUCCESS
;
2317 static UINT
ACTION_WriteRegistryValues(MSIPACKAGE
*package
)
2321 static const WCHAR ExecSeqQuery
[] =
2322 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2323 '`','R','e','g','i','s','t','r','y','`',0 };
2325 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2326 if (rc
!= ERROR_SUCCESS
)
2327 return ERROR_SUCCESS
;
2329 /* increment progress bar each time action data is sent */
2330 ui_progress(package
,1,REG_PROGRESS_VALUE
,1,0);
2332 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteRegistryValues
, package
);
2334 msiobj_release(&view
->hdr
);
2338 static UINT
ACTION_InstallInitialize(MSIPACKAGE
*package
)
2340 package
->script
->CurrentlyScripting
= TRUE
;
2342 return ERROR_SUCCESS
;
2346 static UINT
ACTION_InstallValidate(MSIPACKAGE
*package
)
2351 static const WCHAR q1
[]=
2352 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2353 '`','R','e','g','i','s','t','r','y','`',0};
2356 MSIFEATURE
*feature
;
2359 TRACE("InstallValidate\n");
2361 rc
= MSI_DatabaseOpenViewW(package
->db
, q1
, &view
);
2362 if (rc
== ERROR_SUCCESS
)
2364 MSI_IterateRecords( view
, &progress
, NULL
, package
);
2365 msiobj_release( &view
->hdr
);
2366 total
+= progress
* REG_PROGRESS_VALUE
;
2369 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2370 total
+= COMPONENT_PROGRESS_VALUE
;
2372 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2373 total
+= file
->FileSize
;
2375 ui_progress(package
,0,total
,0,0);
2377 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2379 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2380 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
,
2381 feature
->ActionRequest
);
2384 return ERROR_SUCCESS
;
2387 static UINT
ITERATE_LaunchConditions(MSIRECORD
*row
, LPVOID param
)
2389 MSIPACKAGE
* package
= param
;
2390 LPCWSTR cond
= NULL
;
2391 LPCWSTR message
= NULL
;
2394 static const WCHAR title
[]=
2395 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2397 cond
= MSI_RecordGetString(row
,1);
2399 r
= MSI_EvaluateConditionW(package
,cond
);
2400 if (r
== MSICONDITION_FALSE
)
2402 if ((gUILevel
& INSTALLUILEVEL_MASK
) != INSTALLUILEVEL_NONE
)
2405 message
= MSI_RecordGetString(row
,2);
2406 deformat_string(package
,message
,&deformated
);
2407 MessageBoxW(NULL
,deformated
,title
,MB_OK
);
2408 msi_free(deformated
);
2411 return ERROR_INSTALL_FAILURE
;
2414 return ERROR_SUCCESS
;
2417 static UINT
ACTION_LaunchConditions(MSIPACKAGE
*package
)
2420 MSIQUERY
* view
= NULL
;
2421 static const WCHAR ExecSeqQuery
[] =
2422 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2423 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2425 TRACE("Checking launch conditions\n");
2427 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2428 if (rc
!= ERROR_SUCCESS
)
2429 return ERROR_SUCCESS
;
2431 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_LaunchConditions
, package
);
2432 msiobj_release(&view
->hdr
);
2437 static LPWSTR
resolve_keypath( MSIPACKAGE
* package
, MSICOMPONENT
*cmp
)
2441 return resolve_folder(package
,cmp
->Directory
,FALSE
,FALSE
,TRUE
,NULL
);
2443 if (cmp
->Attributes
& msidbComponentAttributesRegistryKeyPath
)
2445 MSIRECORD
* row
= 0;
2447 LPWSTR deformated
,buffer
,deformated_name
;
2449 static const WCHAR ExecSeqQuery
[] =
2450 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2451 '`','R','e','g','i','s','t','r','y','`',' ',
2452 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2453 ' ','=',' ' ,'\'','%','s','\'',0 };
2454 static const WCHAR fmt
[]={'%','0','2','i',':','\\','%','s','\\',0};
2455 static const WCHAR fmt2
[]=
2456 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2458 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
,cmp
->KeyPath
);
2462 root
= MSI_RecordGetInteger(row
,2);
2463 key
= MSI_RecordGetString(row
, 3);
2464 name
= MSI_RecordGetString(row
, 4);
2465 deformat_string(package
, key
, &deformated
);
2466 deformat_string(package
, name
, &deformated_name
);
2468 len
= strlenW(deformated
) + 6;
2469 if (deformated_name
)
2470 len
+=strlenW(deformated_name
);
2472 buffer
= msi_alloc( len
*sizeof(WCHAR
));
2474 if (deformated_name
)
2475 sprintfW(buffer
,fmt2
,root
,deformated
,deformated_name
);
2477 sprintfW(buffer
,fmt
,root
,deformated
);
2479 msi_free(deformated
);
2480 msi_free(deformated_name
);
2481 msiobj_release(&row
->hdr
);
2485 else if (cmp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2487 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2492 MSIFILE
*file
= get_loaded_file( package
, cmp
->KeyPath
);
2495 return strdupW( file
->TargetPath
);
2500 static HKEY
openSharedDLLsKey(void)
2503 static const WCHAR path
[] =
2504 {'S','o','f','t','w','a','r','e','\\',
2505 'M','i','c','r','o','s','o','f','t','\\',
2506 'W','i','n','d','o','w','s','\\',
2507 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2508 'S','h','a','r','e','d','D','L','L','s',0};
2510 RegCreateKeyW(HKEY_LOCAL_MACHINE
,path
,&hkey
);
2514 static UINT
ACTION_GetSharedDLLsCount(LPCWSTR dll
)
2519 DWORD sz
= sizeof(count
);
2522 hkey
= openSharedDLLsKey();
2523 rc
= RegQueryValueExW(hkey
, dll
, NULL
, &type
, (LPBYTE
)&count
, &sz
);
2524 if (rc
!= ERROR_SUCCESS
)
2530 static UINT
ACTION_WriteSharedDLLsCount(LPCWSTR path
, UINT count
)
2534 hkey
= openSharedDLLsKey();
2536 msi_reg_set_val_dword( hkey
, path
, count
);
2538 RegDeleteValueW(hkey
,path
);
2544 * Return TRUE if the count should be written out and FALSE if not
2546 static void ACTION_RefCountComponent( MSIPACKAGE
* package
, MSICOMPONENT
*comp
)
2548 MSIFEATURE
*feature
;
2552 /* only refcount DLLs */
2553 if (comp
->KeyPath
== NULL
||
2554 comp
->Attributes
& msidbComponentAttributesRegistryKeyPath
||
2555 comp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2559 count
= ACTION_GetSharedDLLsCount( comp
->FullKeypath
);
2560 write
= (count
> 0);
2562 if (comp
->Attributes
& msidbComponentAttributesSharedDllRefCount
)
2566 /* increment counts */
2567 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2571 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
))
2574 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2576 if ( cl
->component
== comp
)
2581 /* decrement counts */
2582 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2586 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ABSENT
))
2589 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2591 if ( cl
->component
== comp
)
2596 /* ref count all the files in the component */
2601 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2603 if (file
->Component
== comp
)
2604 ACTION_WriteSharedDLLsCount( file
->TargetPath
, count
);
2608 /* add a count for permanent */
2609 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2612 comp
->RefCount
= count
;
2615 ACTION_WriteSharedDLLsCount( comp
->FullKeypath
, comp
->RefCount
);
2618 static UINT
ACTION_ProcessComponents(MSIPACKAGE
*package
)
2620 WCHAR squished_pc
[GUID_SIZE
];
2621 WCHAR squished_cc
[GUID_SIZE
];
2628 squash_guid(package
->ProductCode
,squished_pc
);
2629 ui_progress(package
,1,COMPONENT_PROGRESS_VALUE
,1,0);
2631 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2635 ui_progress(package
,2,0,0,0);
2636 if (!comp
->ComponentId
)
2639 squash_guid(comp
->ComponentId
,squished_cc
);
2641 msi_free(comp
->FullKeypath
);
2642 comp
->FullKeypath
= resolve_keypath( package
, comp
);
2644 ACTION_RefCountComponent( package
, comp
);
2646 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2647 debugstr_w(comp
->Component
),
2648 debugstr_w(squished_cc
),
2649 debugstr_w(comp
->FullKeypath
),
2652 if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
) ||
2653 ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_SOURCE
))
2655 if (!comp
->FullKeypath
)
2658 if (package
->Context
== MSIINSTALLCONTEXT_MACHINE
)
2659 rc
= MSIREG_OpenUserDataComponentKey(comp
->ComponentId
, szLocalSid
,
2662 rc
= MSIREG_OpenUserDataComponentKey(comp
->ComponentId
, NULL
,
2665 if (rc
!= ERROR_SUCCESS
)
2668 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2670 static const WCHAR szPermKey
[] =
2671 { '0','0','0','0','0','0','0','0','0','0','0','0',
2672 '0','0','0','0','0','0','0','0','0','0','0','0',
2673 '0','0','0','0','0','0','0','0',0 };
2675 msi_reg_set_val_str(hkey
, szPermKey
, comp
->FullKeypath
);
2678 if (comp
->Action
== INSTALLSTATE_LOCAL
)
2679 msi_reg_set_val_str(hkey
, squished_pc
, comp
->FullKeypath
);
2685 WCHAR source
[MAX_PATH
];
2686 WCHAR base
[MAX_PATH
];
2689 static const WCHAR fmt
[] = {'%','0','2','d','\\',0};
2690 static const WCHAR query
[] = {
2691 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2692 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2693 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2694 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2695 '`','D','i','s','k','I','d','`',0};
2697 file
= get_loaded_file(package
, comp
->KeyPath
);
2701 row
= MSI_QueryGetRecord(package
->db
, query
, file
->Sequence
);
2702 sprintfW(source
, fmt
, MSI_RecordGetInteger(row
, 1));
2703 ptr2
= strrchrW(source
, '\\') + 1;
2704 msiobj_release(&row
->hdr
);
2706 lstrcpyW(base
, package
->PackagePath
);
2707 ptr
= strrchrW(base
, '\\');
2710 sourcepath
= resolve_file_source(package
, file
);
2711 ptr
= sourcepath
+ lstrlenW(base
);
2712 lstrcpyW(ptr2
, ptr
);
2713 msi_free(sourcepath
);
2715 msi_reg_set_val_str(hkey
, squished_pc
, source
);
2719 else if (ACTION_VerifyComponentForAction(comp
, INSTALLSTATE_ABSENT
))
2721 if (package
->Context
== MSIINSTALLCONTEXT_MACHINE
)
2722 MSIREG_DeleteUserDataComponentKey(comp
->ComponentId
, szLocalSid
);
2724 MSIREG_DeleteUserDataComponentKey(comp
->ComponentId
, NULL
);
2728 uirow
= MSI_CreateRecord(3);
2729 MSI_RecordSetStringW(uirow
,1,package
->ProductCode
);
2730 MSI_RecordSetStringW(uirow
,2,comp
->ComponentId
);
2731 MSI_RecordSetStringW(uirow
,3,comp
->FullKeypath
);
2732 ui_actiondata(package
,szProcessComponents
,uirow
);
2733 msiobj_release( &uirow
->hdr
);
2736 return ERROR_SUCCESS
;
2747 static BOOL CALLBACK
Typelib_EnumResNameProc( HMODULE hModule
, LPCWSTR lpszType
,
2748 LPWSTR lpszName
, LONG_PTR lParam
)
2751 typelib_struct
*tl_struct
= (typelib_struct
*) lParam
;
2752 static const WCHAR fmt
[] = {'%','s','\\','%','i',0};
2756 if (!IS_INTRESOURCE(lpszName
))
2758 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName
));
2762 sz
= strlenW(tl_struct
->source
)+4;
2763 sz
*= sizeof(WCHAR
);
2765 if ((INT_PTR
)lpszName
== 1)
2766 tl_struct
->path
= strdupW(tl_struct
->source
);
2769 tl_struct
->path
= msi_alloc(sz
);
2770 sprintfW(tl_struct
->path
,fmt
,tl_struct
->source
, lpszName
);
2773 TRACE("trying %s\n", debugstr_w(tl_struct
->path
));
2774 res
= LoadTypeLib(tl_struct
->path
,&tl_struct
->ptLib
);
2777 msi_free(tl_struct
->path
);
2778 tl_struct
->path
= NULL
;
2783 ITypeLib_GetLibAttr(tl_struct
->ptLib
, &attr
);
2784 if (IsEqualGUID(&(tl_struct
->clsid
),&(attr
->guid
)))
2786 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2790 msi_free(tl_struct
->path
);
2791 tl_struct
->path
= NULL
;
2793 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2794 ITypeLib_Release(tl_struct
->ptLib
);
2799 static UINT
ITERATE_RegisterTypeLibraries(MSIRECORD
*row
, LPVOID param
)
2801 MSIPACKAGE
* package
= param
;
2805 typelib_struct tl_struct
;
2810 static const WCHAR szTYPELIB
[] = {'T','Y','P','E','L','I','B',0};
2812 component
= MSI_RecordGetString(row
,3);
2813 comp
= get_loaded_component(package
,component
);
2815 return ERROR_SUCCESS
;
2817 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2819 TRACE("Skipping typelib reg due to disabled component\n");
2821 comp
->Action
= comp
->Installed
;
2823 return ERROR_SUCCESS
;
2826 comp
->Action
= INSTALLSTATE_LOCAL
;
2828 file
= get_loaded_file( package
, comp
->KeyPath
);
2830 return ERROR_SUCCESS
;
2832 module
= LoadLibraryExW( file
->TargetPath
, NULL
, LOAD_LIBRARY_AS_DATAFILE
);
2836 guid
= MSI_RecordGetString(row
,1);
2837 CLSIDFromString((LPWSTR
)guid
, &tl_struct
.clsid
);
2838 tl_struct
.source
= strdupW( file
->TargetPath
);
2839 tl_struct
.path
= NULL
;
2841 EnumResourceNamesW(module
, szTYPELIB
, Typelib_EnumResNameProc
,
2842 (LONG_PTR
)&tl_struct
);
2850 helpid
= MSI_RecordGetString(row
,6);
2853 help
= resolve_folder(package
,helpid
,FALSE
,FALSE
,TRUE
,NULL
);
2854 res
= RegisterTypeLib(tl_struct
.ptLib
,tl_struct
.path
,help
);
2858 ERR("Failed to register type library %s\n",
2859 debugstr_w(tl_struct
.path
));
2862 ui_actiondata(package
,szRegisterTypeLibraries
,row
);
2864 TRACE("Registered %s\n", debugstr_w(tl_struct
.path
));
2867 ITypeLib_Release(tl_struct
.ptLib
);
2868 msi_free(tl_struct
.path
);
2871 ERR("Failed to load type library %s\n",
2872 debugstr_w(tl_struct
.source
));
2874 FreeLibrary(module
);
2875 msi_free(tl_struct
.source
);
2879 hr
= LoadTypeLibEx(file
->TargetPath
, REGKIND_REGISTER
, &tlib
);
2882 ERR("Failed to load type library: %08x\n", hr
);
2883 return ERROR_FUNCTION_FAILED
;
2886 ITypeLib_Release(tlib
);
2889 return ERROR_SUCCESS
;
2892 static UINT
ACTION_RegisterTypeLibraries(MSIPACKAGE
*package
)
2895 * OK this is a bit confusing.. I am given a _Component key and I believe
2896 * that the file that is being registered as a type library is the "key file
2897 * of that component" which I interpret to mean "The file in the KeyPath of
2902 static const WCHAR Query
[] =
2903 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2904 '`','T','y','p','e','L','i','b','`',0};
2906 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
2907 if (rc
!= ERROR_SUCCESS
)
2908 return ERROR_SUCCESS
;
2910 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_RegisterTypeLibraries
, package
);
2911 msiobj_release(&view
->hdr
);
2915 static UINT
ITERATE_CreateShortcuts(MSIRECORD
*row
, LPVOID param
)
2917 MSIPACKAGE
*package
= param
;
2918 LPWSTR target_file
, target_folder
, filename
;
2919 LPCWSTR buffer
, extension
;
2921 static const WCHAR szlnk
[]={'.','l','n','k',0};
2922 IShellLinkW
*sl
= NULL
;
2923 IPersistFile
*pf
= NULL
;
2926 buffer
= MSI_RecordGetString(row
,4);
2927 comp
= get_loaded_component(package
,buffer
);
2929 return ERROR_SUCCESS
;
2931 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2933 TRACE("Skipping shortcut creation due to disabled component\n");
2935 comp
->Action
= comp
->Installed
;
2937 return ERROR_SUCCESS
;
2940 comp
->Action
= INSTALLSTATE_LOCAL
;
2942 ui_actiondata(package
,szCreateShortcuts
,row
);
2944 res
= CoCreateInstance( &CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
,
2945 &IID_IShellLinkW
, (LPVOID
*) &sl
);
2949 ERR("CLSID_ShellLink not available\n");
2953 res
= IShellLinkW_QueryInterface( sl
, &IID_IPersistFile
,(LPVOID
*) &pf
);
2956 ERR("QueryInterface(IID_IPersistFile) failed\n");
2960 buffer
= MSI_RecordGetString(row
,2);
2961 target_folder
= resolve_folder(package
, buffer
,FALSE
,FALSE
,TRUE
,NULL
);
2963 /* may be needed because of a bug somewhere else */
2964 create_full_pathW(target_folder
);
2966 filename
= msi_dup_record_field( row
, 3 );
2967 reduce_to_longfilename(filename
);
2969 extension
= strchrW(filename
,'.');
2970 if (!extension
|| strcmpiW(extension
,szlnk
))
2972 int len
= strlenW(filename
);
2973 filename
= msi_realloc(filename
, len
* sizeof(WCHAR
) + sizeof(szlnk
));
2974 memcpy(filename
+ len
, szlnk
, sizeof(szlnk
));
2976 target_file
= build_directory_name(2, target_folder
, filename
);
2977 msi_free(target_folder
);
2980 buffer
= MSI_RecordGetString(row
,5);
2981 if (strchrW(buffer
,'['))
2984 deformat_string(package
,buffer
,&deformated
);
2985 IShellLinkW_SetPath(sl
,deformated
);
2986 msi_free(deformated
);
2990 FIXME("poorly handled shortcut format, advertised shortcut\n");
2991 IShellLinkW_SetPath(sl
,comp
->FullKeypath
);
2994 if (!MSI_RecordIsNull(row
,6))
2997 buffer
= MSI_RecordGetString(row
,6);
2998 deformat_string(package
,buffer
,&deformated
);
2999 IShellLinkW_SetArguments(sl
,deformated
);
3000 msi_free(deformated
);
3003 if (!MSI_RecordIsNull(row
,7))
3005 buffer
= MSI_RecordGetString(row
,7);
3006 IShellLinkW_SetDescription(sl
,buffer
);
3009 if (!MSI_RecordIsNull(row
,8))
3010 IShellLinkW_SetHotkey(sl
,MSI_RecordGetInteger(row
,8));
3012 if (!MSI_RecordIsNull(row
,9))
3017 buffer
= MSI_RecordGetString(row
,9);
3019 Path
= build_icon_path(package
,buffer
);
3020 index
= MSI_RecordGetInteger(row
,10);
3022 /* no value means 0 */
3023 if (index
== MSI_NULL_INTEGER
)
3026 IShellLinkW_SetIconLocation(sl
,Path
,index
);
3030 if (!MSI_RecordIsNull(row
,11))
3031 IShellLinkW_SetShowCmd(sl
,MSI_RecordGetInteger(row
,11));
3033 if (!MSI_RecordIsNull(row
,12))
3036 buffer
= MSI_RecordGetString(row
,12);
3037 Path
= resolve_folder(package
, buffer
, FALSE
, FALSE
, TRUE
, NULL
);
3039 IShellLinkW_SetWorkingDirectory(sl
,Path
);
3043 TRACE("Writing shortcut to %s\n",debugstr_w(target_file
));
3044 IPersistFile_Save(pf
,target_file
,FALSE
);
3046 msi_free(target_file
);
3050 IPersistFile_Release( pf
);
3052 IShellLinkW_Release( sl
);
3054 return ERROR_SUCCESS
;
3057 static UINT
ACTION_CreateShortcuts(MSIPACKAGE
*package
)
3062 static const WCHAR Query
[] =
3063 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3064 '`','S','h','o','r','t','c','u','t','`',0};
3066 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3067 if (rc
!= ERROR_SUCCESS
)
3068 return ERROR_SUCCESS
;
3070 res
= CoInitialize( NULL
);
3072 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateShortcuts
, package
);
3073 msiobj_release(&view
->hdr
);
3081 static UINT
ITERATE_PublishIcon(MSIRECORD
*row
, LPVOID param
)
3083 MSIPACKAGE
* package
= param
;
3092 FileName
= MSI_RecordGetString(row
,1);
3095 ERR("Unable to get FileName\n");
3096 return ERROR_SUCCESS
;
3099 FilePath
= build_icon_path(package
,FileName
);
3101 TRACE("Creating icon file at %s\n",debugstr_w(FilePath
));
3103 the_file
= CreateFileW(FilePath
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
,
3104 FILE_ATTRIBUTE_NORMAL
, NULL
);
3106 if (the_file
== INVALID_HANDLE_VALUE
)
3108 ERR("Unable to create file %s\n",debugstr_w(FilePath
));
3110 return ERROR_SUCCESS
;
3117 rc
= MSI_RecordReadStream(row
,2,buffer
,&sz
);
3118 if (rc
!= ERROR_SUCCESS
)
3120 ERR("Failed to get stream\n");
3121 CloseHandle(the_file
);
3122 DeleteFileW(FilePath
);
3125 WriteFile(the_file
,buffer
,sz
,&write
,NULL
);
3126 } while (sz
== 1024);
3130 CloseHandle(the_file
);
3132 uirow
= MSI_CreateRecord(1);
3133 MSI_RecordSetStringW(uirow
,1,FileName
);
3134 ui_actiondata(package
,szPublishProduct
,uirow
);
3135 msiobj_release( &uirow
->hdr
);
3137 return ERROR_SUCCESS
;
3140 static UINT
msi_publish_icons(MSIPACKAGE
*package
)
3145 static const WCHAR query
[]= {
3146 'S','E','L','E','C','T',' ','*',' ',
3147 'F','R','O','M',' ','`','I','c','o','n','`',0};
3149 r
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
3150 if (r
== ERROR_SUCCESS
)
3152 MSI_IterateRecords(view
, NULL
, ITERATE_PublishIcon
, package
);
3153 msiobj_release(&view
->hdr
);
3156 return ERROR_SUCCESS
;
3159 static UINT
msi_publish_sourcelist(MSIPACKAGE
*package
, HKEY hkey
)
3165 MSISOURCELISTINFO
*info
;
3167 r
= RegCreateKeyW(hkey
, szSourceList
, &source
);
3168 if (r
!= ERROR_SUCCESS
)
3171 RegCloseKey(source
);
3173 buffer
= strrchrW(package
->PackagePath
, '\\') + 1;
3174 r
= MsiSourceListSetInfoW(package
->ProductCode
, NULL
,
3175 package
->Context
, MSICODE_PRODUCT
,
3176 INSTALLPROPERTY_PACKAGENAMEW
, buffer
);
3177 if (r
!= ERROR_SUCCESS
)
3180 r
= MsiSourceListSetInfoW(package
->ProductCode
, NULL
,
3181 package
->Context
, MSICODE_PRODUCT
,
3182 INSTALLPROPERTY_MEDIAPACKAGEPATHW
, szEmpty
);
3183 if (r
!= ERROR_SUCCESS
)
3186 r
= MsiSourceListSetInfoW(package
->ProductCode
, NULL
,
3187 package
->Context
, MSICODE_PRODUCT
,
3188 INSTALLPROPERTY_DISKPROMPTW
, szEmpty
);
3189 if (r
!= ERROR_SUCCESS
)
3192 LIST_FOR_EACH_ENTRY(info
, &package
->sourcelist_info
, MSISOURCELISTINFO
, entry
)
3194 if (!lstrcmpW(info
->property
, INSTALLPROPERTY_LASTUSEDSOURCEW
))
3195 msi_set_last_used_source(package
->ProductCode
, NULL
, info
->context
,
3196 info
->options
, info
->value
);
3198 MsiSourceListSetInfoW(package
->ProductCode
, NULL
,
3199 info
->context
, info
->options
,
3200 info
->property
, info
->value
);
3203 LIST_FOR_EACH_ENTRY(disk
, &package
->sourcelist_media
, MSIMEDIADISK
, entry
)
3205 MsiSourceListAddMediaDiskW(package
->ProductCode
, NULL
,
3206 disk
->context
, disk
->options
,
3207 disk
->disk_id
, disk
->volume_label
, disk
->disk_prompt
);
3210 return ERROR_SUCCESS
;
3213 static UINT
msi_publish_product_properties(MSIPACKAGE
*package
, HKEY hkey
)
3215 MSIHANDLE hdb
, suminfo
;
3216 WCHAR guids
[MAX_PATH
];
3217 WCHAR packcode
[SQUISH_GUID_SIZE
];
3224 static const WCHAR szProductLanguage
[] =
3225 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3226 static const WCHAR szARPProductIcon
[] =
3227 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3228 static const WCHAR szProductVersion
[] =
3229 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3230 static const WCHAR szAssignment
[] =
3231 {'A','s','s','i','g','n','m','e','n','t',0};
3232 static const WCHAR szAdvertiseFlags
[] =
3233 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3234 static const WCHAR szClients
[] =
3235 {'C','l','i','e','n','t','s',0};
3236 static const WCHAR szColon
[] = {':',0};
3238 buffer
= msi_dup_property(package
, INSTALLPROPERTY_PRODUCTNAMEW
);
3239 msi_reg_set_val_str(hkey
, INSTALLPROPERTY_PRODUCTNAMEW
, buffer
);
3242 langid
= msi_get_property_int(package
, szProductLanguage
, 0);
3243 msi_reg_set_val_dword(hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
3246 msi_reg_set_val_dword(hkey
, INSTALLPROPERTY_AUTHORIZED_LUA_APPW
, 0);
3248 buffer
= msi_dup_property(package
, szARPProductIcon
);
3251 LPWSTR path
= build_icon_path(package
,buffer
);
3252 msi_reg_set_val_str(hkey
, INSTALLPROPERTY_PRODUCTICONW
, path
);
3257 buffer
= msi_dup_property(package
, szProductVersion
);
3260 DWORD verdword
= msi_version_str_to_dword(buffer
);
3261 msi_reg_set_val_dword(hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
3265 msi_reg_set_val_dword(hkey
, szAssignment
, 0);
3266 msi_reg_set_val_dword(hkey
, szAdvertiseFlags
, 0x184);
3267 msi_reg_set_val_dword(hkey
, INSTALLPROPERTY_INSTANCETYPEW
, 0);
3268 msi_reg_set_val_str(hkey
, szClients
, szColon
);
3270 hdb
= alloc_msihandle(&package
->db
->hdr
);
3272 return ERROR_NOT_ENOUGH_MEMORY
;
3274 r
= MsiGetSummaryInformationW(hdb
, NULL
, 0, &suminfo
);
3275 MsiCloseHandle(hdb
);
3276 if (r
!= ERROR_SUCCESS
)
3280 r
= MsiSummaryInfoGetPropertyW(suminfo
, PID_REVNUMBER
, NULL
, NULL
,
3281 NULL
, guids
, &size
);
3282 if (r
!= ERROR_SUCCESS
)
3285 ptr
= strchrW(guids
, ';');
3287 squash_guid(guids
, packcode
);
3288 msi_reg_set_val_str(hkey
, INSTALLPROPERTY_PACKAGECODEW
, packcode
);
3291 MsiCloseHandle(suminfo
);
3292 return ERROR_SUCCESS
;
3295 static UINT
msi_publish_upgrade_code(MSIPACKAGE
*package
)
3300 WCHAR squashed_pc
[SQUISH_GUID_SIZE
];
3302 static const WCHAR szUpgradeCode
[] =
3303 {'U','p','g','r','a','d','e','C','o','d','e',0};
3305 upgrade
= msi_dup_property(package
, szUpgradeCode
);
3307 return ERROR_SUCCESS
;
3309 if (package
->Context
== MSIINSTALLCONTEXT_MACHINE
)
3311 r
= MSIREG_OpenClassesUpgradeCodesKey(upgrade
, &hkey
, TRUE
);
3312 if (r
!= ERROR_SUCCESS
)
3317 r
= MSIREG_OpenUserUpgradeCodesKey(upgrade
, &hkey
, TRUE
);
3318 if (r
!= ERROR_SUCCESS
)
3322 squash_guid(package
->ProductCode
, squashed_pc
);
3323 msi_reg_set_val_str(hkey
, squashed_pc
, NULL
);
3332 static BOOL
msi_check_publish(MSIPACKAGE
*package
)
3334 MSIFEATURE
*feature
;
3336 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3338 if (feature
->ActionRequest
== INSTALLSTATE_LOCAL
)
3345 static BOOL
msi_check_unpublish(MSIPACKAGE
*package
)
3347 MSIFEATURE
*feature
;
3349 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3351 if (feature
->ActionRequest
!= INSTALLSTATE_ABSENT
)
3358 static UINT
msi_publish_patch(MSIPACKAGE
*package
, HKEY prodkey
, HKEY hudkey
)
3360 WCHAR patch_squashed
[GUID_SIZE
];
3363 UINT r
= ERROR_FUNCTION_FAILED
;
3365 res
= RegCreateKeyExW(prodkey
, szPatches
, 0, NULL
, 0, KEY_ALL_ACCESS
, NULL
,
3367 if (res
!= ERROR_SUCCESS
)
3368 return ERROR_FUNCTION_FAILED
;
3370 squash_guid(package
->patch
->patchcode
, patch_squashed
);
3372 res
= RegSetValueExW(patches
, szPatches
, 0, REG_MULTI_SZ
,
3373 (const BYTE
*)patch_squashed
,
3374 (lstrlenW(patch_squashed
) + 1) * sizeof(WCHAR
));
3375 if (res
!= ERROR_SUCCESS
)
3378 res
= RegSetValueExW(patches
, patch_squashed
, 0, REG_SZ
,
3379 (const BYTE
*)package
->patch
->transforms
,
3380 (lstrlenW(package
->patch
->transforms
) + 1) * sizeof(WCHAR
));
3381 if (res
== ERROR_SUCCESS
)
3385 RegCloseKey(patches
);
3390 * 99% of the work done here is only done for
3391 * advertised installs. However this is where the
3392 * Icon table is processed and written out
3393 * so that is what I am going to do here.
3395 static UINT
ACTION_PublishProduct(MSIPACKAGE
*package
)
3401 /* FIXME: also need to publish if the product is in advertise mode */
3402 if (!msi_check_publish(package
))
3403 return ERROR_SUCCESS
;
3405 rc
= MSIREG_OpenProductKey(package
->ProductCode
, NULL
, package
->Context
,
3407 if (rc
!= ERROR_SUCCESS
)
3410 rc
= MSIREG_OpenUserDataProductKey(package
->ProductCode
, package
->Context
,
3411 NULL
, &hudkey
, TRUE
);
3412 if (rc
!= ERROR_SUCCESS
)
3415 rc
= msi_publish_upgrade_code(package
);
3416 if (rc
!= ERROR_SUCCESS
)
3421 rc
= msi_publish_patch(package
, hukey
, hudkey
);
3422 if (rc
!= ERROR_SUCCESS
)
3426 rc
= msi_publish_product_properties(package
, hukey
);
3427 if (rc
!= ERROR_SUCCESS
)
3430 rc
= msi_publish_sourcelist(package
, hukey
);
3431 if (rc
!= ERROR_SUCCESS
)
3434 rc
= msi_publish_icons(package
);
3438 RegCloseKey(hudkey
);
3443 static UINT
ITERATE_WriteIniValues(MSIRECORD
*row
, LPVOID param
)
3445 MSIPACKAGE
*package
= param
;
3446 LPCWSTR component
, section
, key
, value
, identifier
, dirproperty
;
3447 LPWSTR deformated_section
, deformated_key
, deformated_value
;
3448 LPWSTR folder
, filename
, fullname
= NULL
;
3449 LPCWSTR filenameptr
;
3453 static const WCHAR szWindowsFolder
[] =
3454 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3456 component
= MSI_RecordGetString(row
, 8);
3457 comp
= get_loaded_component(package
,component
);
3459 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3461 TRACE("Skipping ini file due to disabled component %s\n",
3462 debugstr_w(component
));
3464 comp
->Action
= comp
->Installed
;
3466 return ERROR_SUCCESS
;
3469 comp
->Action
= INSTALLSTATE_LOCAL
;
3471 identifier
= MSI_RecordGetString(row
,1);
3472 dirproperty
= MSI_RecordGetString(row
,3);
3473 section
= MSI_RecordGetString(row
,4);
3474 key
= MSI_RecordGetString(row
,5);
3475 value
= MSI_RecordGetString(row
,6);
3476 action
= MSI_RecordGetInteger(row
,7);
3478 deformat_string(package
,section
,&deformated_section
);
3479 deformat_string(package
,key
,&deformated_key
);
3480 deformat_string(package
,value
,&deformated_value
);
3482 filename
= msi_dup_record_field(row
, 2);
3483 if (filename
&& (filenameptr
= strchrW(filename
, '|')))
3486 filenameptr
= filename
;
3490 folder
= resolve_folder(package
, dirproperty
, FALSE
, FALSE
, TRUE
, NULL
);
3492 folder
= msi_dup_property( package
, dirproperty
);
3495 folder
= msi_dup_property( package
, szWindowsFolder
);
3499 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty
));
3503 fullname
= build_directory_name(2, folder
, filenameptr
);
3507 TRACE("Adding value %s to section %s in %s\n",
3508 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3509 debugstr_w(fullname
));
3510 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3511 deformated_value
, fullname
);
3513 else if (action
== 1)
3516 GetPrivateProfileStringW(deformated_section
, deformated_key
, NULL
,
3517 returned
, 10, fullname
);
3518 if (returned
[0] == 0)
3520 TRACE("Adding value %s to section %s in %s\n",
3521 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3522 debugstr_w(fullname
));
3524 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3525 deformated_value
, fullname
);
3528 else if (action
== 3)
3529 FIXME("Append to existing section not yet implemented\n");
3531 uirow
= MSI_CreateRecord(4);
3532 MSI_RecordSetStringW(uirow
,1,identifier
);
3533 MSI_RecordSetStringW(uirow
,2,deformated_section
);
3534 MSI_RecordSetStringW(uirow
,3,deformated_key
);
3535 MSI_RecordSetStringW(uirow
,4,deformated_value
);
3536 ui_actiondata(package
,szWriteIniValues
,uirow
);
3537 msiobj_release( &uirow
->hdr
);
3543 msi_free(deformated_key
);
3544 msi_free(deformated_value
);
3545 msi_free(deformated_section
);
3546 return ERROR_SUCCESS
;
3549 static UINT
ACTION_WriteIniValues(MSIPACKAGE
*package
)
3553 static const WCHAR ExecSeqQuery
[] =
3554 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3555 '`','I','n','i','F','i','l','e','`',0};
3557 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3558 if (rc
!= ERROR_SUCCESS
)
3560 TRACE("no IniFile table\n");
3561 return ERROR_SUCCESS
;
3564 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteIniValues
, package
);
3565 msiobj_release(&view
->hdr
);
3569 static UINT
ITERATE_SelfRegModules(MSIRECORD
*row
, LPVOID param
)
3571 MSIPACKAGE
*package
= param
;
3576 static const WCHAR ExeStr
[] =
3577 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3578 static const WCHAR close
[] = {'\"',0};
3580 PROCESS_INFORMATION info
;
3585 memset(&si
,0,sizeof(STARTUPINFOW
));
3587 filename
= MSI_RecordGetString(row
,1);
3588 file
= get_loaded_file( package
, filename
);
3592 ERR("Unable to find file id %s\n",debugstr_w(filename
));
3593 return ERROR_SUCCESS
;
3596 len
= strlenW(ExeStr
) + strlenW( file
->TargetPath
) + 2;
3598 FullName
= msi_alloc(len
*sizeof(WCHAR
));
3599 strcpyW(FullName
,ExeStr
);
3600 strcatW( FullName
, file
->TargetPath
);
3601 strcatW(FullName
,close
);
3603 TRACE("Registering %s\n",debugstr_w(FullName
));
3604 brc
= CreateProcessW(NULL
, FullName
, NULL
, NULL
, FALSE
, 0, NULL
, c_colon
,
3609 CloseHandle(info
.hThread
);
3610 msi_dialog_check_messages(info
.hProcess
);
3611 CloseHandle(info
.hProcess
);
3617 uirow
= MSI_CreateRecord( 2 );
3618 uipath
= strdupW( file
->TargetPath
);
3619 p
= strrchrW(uipath
,'\\');
3622 MSI_RecordSetStringW( uirow
, 1, &p
[1] );
3623 MSI_RecordSetStringW( uirow
, 2, uipath
);
3624 ui_actiondata( package
, szSelfRegModules
, uirow
);
3625 msiobj_release( &uirow
->hdr
);
3627 /* FIXME: call ui_progress? */
3629 return ERROR_SUCCESS
;
3632 static UINT
ACTION_SelfRegModules(MSIPACKAGE
*package
)
3636 static const WCHAR ExecSeqQuery
[] =
3637 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3638 '`','S','e','l','f','R','e','g','`',0};
3640 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3641 if (rc
!= ERROR_SUCCESS
)
3643 TRACE("no SelfReg table\n");
3644 return ERROR_SUCCESS
;
3647 MSI_IterateRecords(view
, NULL
, ITERATE_SelfRegModules
, package
);
3648 msiobj_release(&view
->hdr
);
3650 return ERROR_SUCCESS
;
3653 static UINT
ACTION_PublishFeatures(MSIPACKAGE
*package
)
3655 MSIFEATURE
*feature
;
3658 HKEY userdata
= NULL
;
3660 if (!msi_check_publish(package
))
3661 return ERROR_SUCCESS
;
3663 rc
= MSIREG_OpenFeaturesKey(package
->ProductCode
, package
->Context
,
3665 if (rc
!= ERROR_SUCCESS
)
3668 rc
= MSIREG_OpenUserDataFeaturesKey(package
->ProductCode
, package
->Context
,
3670 if (rc
!= ERROR_SUCCESS
)
3673 /* here the guids are base 85 encoded */
3674 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
3680 BOOL absent
= FALSE
;
3683 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
) &&
3684 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_SOURCE
) &&
3685 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ADVERTISED
))
3689 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3693 if (feature
->Feature_Parent
)
3694 size
+= strlenW( feature
->Feature_Parent
)+2;
3696 data
= msi_alloc(size
* sizeof(WCHAR
));
3699 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3701 MSICOMPONENT
* component
= cl
->component
;
3705 if (component
->ComponentId
)
3707 TRACE("From %s\n",debugstr_w(component
->ComponentId
));
3708 CLSIDFromString(component
->ComponentId
, &clsid
);
3709 encode_base85_guid(&clsid
,buf
);
3710 TRACE("to %s\n",debugstr_w(buf
));
3715 if (feature
->Feature_Parent
)
3717 static const WCHAR sep
[] = {'\2',0};
3719 strcatW(data
,feature
->Feature_Parent
);
3722 msi_reg_set_val_str( userdata
, feature
->Feature
, data
);
3726 if (feature
->Feature_Parent
)
3727 size
= strlenW(feature
->Feature_Parent
)*sizeof(WCHAR
);
3730 size
+= sizeof(WCHAR
);
3731 RegSetValueExW(hkey
,feature
->Feature
,0,REG_SZ
,
3732 (LPBYTE
)(feature
->Feature_Parent
? feature
->Feature_Parent
: szEmpty
),size
);
3736 size
+= 2*sizeof(WCHAR
);
3737 data
= msi_alloc(size
);
3740 if (feature
->Feature_Parent
)
3741 strcpyW( &data
[1], feature
->Feature_Parent
);
3742 RegSetValueExW(hkey
,feature
->Feature
,0,REG_SZ
,
3748 uirow
= MSI_CreateRecord( 1 );
3749 MSI_RecordSetStringW( uirow
, 1, feature
->Feature
);
3750 ui_actiondata( package
, szPublishFeatures
, uirow
);
3751 msiobj_release( &uirow
->hdr
);
3752 /* FIXME: call ui_progress? */
3757 RegCloseKey(userdata
);
3761 static UINT
msi_unpublish_feature(MSIPACKAGE
*package
, MSIFEATURE
*feature
)
3766 TRACE("unpublishing feature %s\n", debugstr_w(feature
->Feature
));
3768 r
= MSIREG_OpenFeaturesKey(package
->ProductCode
, package
->Context
,
3770 if (r
== ERROR_SUCCESS
)
3772 RegDeleteValueW(hkey
, feature
->Feature
);
3776 r
= MSIREG_OpenUserDataFeaturesKey(package
->ProductCode
, package
->Context
,
3778 if (r
== ERROR_SUCCESS
)
3780 RegDeleteValueW(hkey
, feature
->Feature
);
3784 return ERROR_SUCCESS
;
3787 static UINT
ACTION_UnpublishFeatures(MSIPACKAGE
*package
)
3789 MSIFEATURE
*feature
;
3791 if (!msi_check_unpublish(package
))
3792 return ERROR_SUCCESS
;
3794 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3796 msi_unpublish_feature(package
, feature
);
3799 return ERROR_SUCCESS
;
3802 static UINT
msi_publish_install_properties(MSIPACKAGE
*package
, HKEY hkey
)
3804 LPWSTR prop
, val
, key
;
3810 static const WCHAR date_fmt
[] = {'%','i','%','0','2','i','%','0','2','i',0};
3811 static const WCHAR szWindowsInstaller
[] =
3812 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3813 static const WCHAR modpath_fmt
[] =
3814 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3815 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3816 static const WCHAR szModifyPath
[] =
3817 {'M','o','d','i','f','y','P','a','t','h',0};
3818 static const WCHAR szUninstallString
[] =
3819 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3820 static const WCHAR szEstimatedSize
[] =
3821 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3822 static const WCHAR szProductLanguage
[] =
3823 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3824 static const WCHAR szProductVersion
[] =
3825 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3826 static const WCHAR szProductName
[] =
3827 {'P','r','o','d','u','c','t','N','a','m','e',0};
3828 static const WCHAR szDisplayName
[] =
3829 {'D','i','s','p','l','a','y','N','a','m','e',0};
3830 static const WCHAR szDisplayVersion
[] =
3831 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
3832 static const WCHAR szManufacturer
[] =
3833 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
3835 static const LPCSTR propval
[] = {
3836 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3837 "ARPCONTACT", "Contact",
3838 "ARPCOMMENTS", "Comments",
3839 "ProductName", "DisplayName",
3840 "ProductVersion", "DisplayVersion",
3841 "ARPHELPLINK", "HelpLink",
3842 "ARPHELPTELEPHONE", "HelpTelephone",
3843 "ARPINSTALLLOCATION", "InstallLocation",
3844 "SourceDir", "InstallSource",
3845 "Manufacturer", "Publisher",
3846 "ARPREADME", "Readme",
3848 "ARPURLINFOABOUT", "URLInfoAbout",
3849 "ARPURLUPDATEINFO", "URLUpdateInfo",
3852 const LPCSTR
*p
= propval
;
3856 prop
= strdupAtoW(*p
++);
3857 key
= strdupAtoW(*p
++);
3858 val
= msi_dup_property(package
, prop
);
3859 msi_reg_set_val_str(hkey
, key
, val
);
3865 msi_reg_set_val_dword(hkey
, szWindowsInstaller
, 1);
3867 size
= deformat_string(package
, modpath_fmt
, &buffer
);
3868 RegSetValueExW(hkey
, szModifyPath
, 0, REG_EXPAND_SZ
, (LPBYTE
)buffer
, size
);
3869 RegSetValueExW(hkey
, szUninstallString
, 0, REG_EXPAND_SZ
, (LPBYTE
)buffer
, size
);
3872 /* FIXME: Write real Estimated Size when we have it */
3873 msi_reg_set_val_dword(hkey
, szEstimatedSize
, 0);
3875 buffer
= msi_dup_property(package
, szProductName
);
3876 msi_reg_set_val_str(hkey
, szDisplayName
, buffer
);
3879 buffer
= msi_dup_property(package
, cszSourceDir
);
3880 msi_reg_set_val_str(hkey
, INSTALLPROPERTY_INSTALLSOURCEW
, buffer
);
3883 buffer
= msi_dup_property(package
, szManufacturer
);
3884 msi_reg_set_val_str(hkey
, INSTALLPROPERTY_PUBLISHERW
, buffer
);
3887 GetLocalTime(&systime
);
3888 sprintfW(date
, date_fmt
, systime
.wYear
, systime
.wMonth
, systime
.wDay
);
3889 msi_reg_set_val_str(hkey
, INSTALLPROPERTY_INSTALLDATEW
, date
);
3891 langid
= msi_get_property_int(package
, szProductLanguage
, 0);
3892 msi_reg_set_val_dword(hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
3894 buffer
= msi_dup_property(package
, szProductVersion
);
3895 msi_reg_set_val_str(hkey
, szDisplayVersion
, buffer
);
3898 DWORD verdword
= msi_version_str_to_dword(buffer
);
3900 msi_reg_set_val_dword(hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
3901 msi_reg_set_val_dword(hkey
, INSTALLPROPERTY_VERSIONMAJORW
, verdword
>> 24);
3902 msi_reg_set_val_dword(hkey
, INSTALLPROPERTY_VERSIONMINORW
, (verdword
>> 16) & 0xFF);
3906 return ERROR_SUCCESS
;
3909 static UINT
ACTION_RegisterProduct(MSIPACKAGE
*package
)
3911 WCHAR squashed_pc
[SQUISH_GUID_SIZE
];
3912 LPWSTR upgrade_code
;
3917 static const WCHAR szUpgradeCode
[] = {
3918 'U','p','g','r','a','d','e','C','o','d','e',0};
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 rc
= MSIREG_OpenInstallProps(package
->ProductCode
, package
->Context
,
3929 NULL
, &props
, TRUE
);
3930 if (rc
!= ERROR_SUCCESS
)
3933 msi_reg_set_val_str( props
, INSTALLPROPERTY_LOCALPACKAGEW
, package
->db
->localfile
);
3934 msi_free( package
->db
->localfile
);
3935 package
->db
->localfile
= NULL
;
3937 rc
= msi_publish_install_properties(package
, hkey
);
3938 if (rc
!= ERROR_SUCCESS
)
3941 rc
= msi_publish_install_properties(package
, props
);
3942 if (rc
!= ERROR_SUCCESS
)
3945 upgrade_code
= msi_dup_property(package
, szUpgradeCode
);
3948 MSIREG_OpenUpgradeCodesKey(upgrade_code
, &upgrade
, TRUE
);
3949 squash_guid(package
->ProductCode
, squashed_pc
);
3950 msi_reg_set_val_str(upgrade
, squashed_pc
, NULL
);
3951 RegCloseKey(upgrade
);
3952 msi_free(upgrade_code
);
3958 return ERROR_SUCCESS
;
3961 static UINT
ACTION_InstallExecute(MSIPACKAGE
*package
)
3963 return execute_script(package
,INSTALL_SCRIPT
);
3966 static UINT
msi_unpublish_product(MSIPACKAGE
*package
)
3969 LPWSTR remove
= NULL
;
3970 LPWSTR
*features
= NULL
;
3971 BOOL full_uninstall
= TRUE
;
3972 MSIFEATURE
*feature
;
3974 static const WCHAR szUpgradeCode
[] =
3975 {'U','p','g','r','a','d','e','C','o','d','e',0};
3977 remove
= msi_dup_property(package
, szRemove
);
3979 return ERROR_SUCCESS
;
3981 features
= msi_split_string(remove
, ',');
3985 ERR("REMOVE feature list is empty!\n");
3986 return ERROR_FUNCTION_FAILED
;
3989 if (!lstrcmpW(features
[0], szAll
))
3990 full_uninstall
= TRUE
;
3993 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3995 if (feature
->Action
!= INSTALLSTATE_ABSENT
)
3996 full_uninstall
= FALSE
;
4000 if (!full_uninstall
)
4003 MSIREG_DeleteProductKey(package
->ProductCode
);
4004 MSIREG_DeleteUserDataProductKey(package
->ProductCode
);
4005 MSIREG_DeleteUninstallKey(package
->ProductCode
);
4007 if (package
->Context
== MSIINSTALLCONTEXT_MACHINE
)
4009 MSIREG_DeleteLocalClassesProductKey(package
->ProductCode
);
4010 MSIREG_DeleteLocalClassesFeaturesKey(package
->ProductCode
);
4014 MSIREG_DeleteUserProductKey(package
->ProductCode
);
4015 MSIREG_DeleteUserFeaturesKey(package
->ProductCode
);
4018 upgrade
= msi_dup_property(package
, szUpgradeCode
);
4021 MSIREG_DeleteUserUpgradeCodesKey(upgrade
);
4028 return ERROR_SUCCESS
;
4031 static UINT
ACTION_InstallFinalize(MSIPACKAGE
*package
)
4035 rc
= msi_unpublish_product(package
);
4036 if (rc
!= ERROR_SUCCESS
)
4039 /* turn off scheduling */
4040 package
->script
->CurrentlyScripting
= FALSE
;
4042 /* first do the same as an InstallExecute */
4043 rc
= ACTION_InstallExecute(package
);
4044 if (rc
!= ERROR_SUCCESS
)
4047 /* then handle Commit Actions */
4048 rc
= execute_script(package
,COMMIT_SCRIPT
);
4053 UINT
ACTION_ForceReboot(MSIPACKAGE
*package
)
4055 static const WCHAR RunOnce
[] = {
4056 'S','o','f','t','w','a','r','e','\\',
4057 'M','i','c','r','o','s','o','f','t','\\',
4058 'W','i','n','d','o','w','s','\\',
4059 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4060 'R','u','n','O','n','c','e',0};
4061 static const WCHAR InstallRunOnce
[] = {
4062 'S','o','f','t','w','a','r','e','\\',
4063 'M','i','c','r','o','s','o','f','t','\\',
4064 'W','i','n','d','o','w','s','\\',
4065 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4066 'I','n','s','t','a','l','l','e','r','\\',
4067 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4069 static const WCHAR msiexec_fmt
[] = {
4071 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4072 '\"','%','s','\"',0};
4073 static const WCHAR install_fmt
[] = {
4074 '/','I',' ','\"','%','s','\"',' ',
4075 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4076 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4077 WCHAR buffer
[256], sysdir
[MAX_PATH
];
4079 WCHAR squished_pc
[100];
4081 squash_guid(package
->ProductCode
,squished_pc
);
4083 GetSystemDirectoryW(sysdir
, sizeof(sysdir
)/sizeof(sysdir
[0]));
4084 RegCreateKeyW(HKEY_LOCAL_MACHINE
,RunOnce
,&hkey
);
4085 snprintfW(buffer
,sizeof(buffer
)/sizeof(buffer
[0]),msiexec_fmt
,sysdir
,
4088 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
4091 TRACE("Reboot command %s\n",debugstr_w(buffer
));
4093 RegCreateKeyW(HKEY_LOCAL_MACHINE
,InstallRunOnce
,&hkey
);
4094 sprintfW(buffer
,install_fmt
,package
->ProductCode
,squished_pc
);
4096 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
4099 return ERROR_INSTALL_SUSPEND
;
4102 static UINT
ACTION_ResolveSource(MSIPACKAGE
* package
)
4108 * We are currently doing what should be done here in the top level Install
4109 * however for Administrative and uninstalls this step will be needed
4111 if (!package
->PackagePath
)
4112 return ERROR_SUCCESS
;
4114 msi_set_sourcedir_props(package
, TRUE
);
4116 attrib
= GetFileAttributesW(package
->db
->path
);
4117 if (attrib
== INVALID_FILE_ATTRIBUTES
)
4123 rc
= MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
4124 package
->Context
, MSICODE_PRODUCT
,
4125 INSTALLPROPERTY_DISKPROMPTW
,NULL
,&size
);
4126 if (rc
== ERROR_MORE_DATA
)
4128 prompt
= msi_alloc(size
* sizeof(WCHAR
));
4129 MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
4130 package
->Context
, MSICODE_PRODUCT
,
4131 INSTALLPROPERTY_DISKPROMPTW
,prompt
,&size
);
4134 prompt
= strdupW(package
->db
->path
);
4136 msg
= generate_error_string(package
,1302,1,prompt
);
4137 while(attrib
== INVALID_FILE_ATTRIBUTES
)
4139 rc
= MessageBoxW(NULL
,msg
,NULL
,MB_OKCANCEL
);
4142 rc
= ERROR_INSTALL_USEREXIT
;
4145 attrib
= GetFileAttributesW(package
->db
->path
);
4151 return ERROR_SUCCESS
;
4156 static UINT
ACTION_RegisterUser(MSIPACKAGE
*package
)
4163 static const WCHAR szPropKeys
[][80] =
4165 {'P','r','o','d','u','c','t','I','D',0},
4166 {'U','S','E','R','N','A','M','E',0},
4167 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4171 static const WCHAR szRegKeys
[][80] =
4173 {'P','r','o','d','u','c','t','I','D',0},
4174 {'R','e','g','O','w','n','e','r',0},
4175 {'R','e','g','C','o','m','p','a','n','y',0},
4179 if (msi_check_unpublish(package
))
4181 MSIREG_DeleteUserDataProductKey(package
->ProductCode
);
4182 return ERROR_SUCCESS
;
4185 productid
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTIDW
);
4187 return ERROR_SUCCESS
;
4189 rc
= MSIREG_OpenInstallProps(package
->ProductCode
, package
->Context
,
4191 if (rc
!= ERROR_SUCCESS
)
4194 for( i
= 0; szPropKeys
[i
][0]; i
++ )
4196 buffer
= msi_dup_property( package
, szPropKeys
[i
] );
4197 msi_reg_set_val_str( hkey
, szRegKeys
[i
], buffer
);
4202 msi_free(productid
);
4205 /* FIXME: call ui_actiondata */
4211 static UINT
ACTION_ExecuteAction(MSIPACKAGE
*package
)
4215 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
4216 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
4221 static UINT
ITERATE_PublishComponent(MSIRECORD
*rec
, LPVOID param
)
4223 MSIPACKAGE
*package
= param
;
4224 LPCWSTR compgroupid
=NULL
;
4225 LPCWSTR feature
=NULL
;
4226 LPCWSTR text
= NULL
;
4227 LPCWSTR qualifier
= NULL
;
4228 LPCWSTR component
= NULL
;
4229 LPWSTR advertise
= NULL
;
4230 LPWSTR output
= NULL
;
4232 UINT rc
= ERROR_SUCCESS
;
4237 component
= MSI_RecordGetString(rec
,3);
4238 comp
= get_loaded_component(package
,component
);
4240 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
) &&
4241 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_SOURCE
) &&
4242 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ADVERTISED
))
4244 TRACE("Skipping: Component %s not scheduled for install\n",
4245 debugstr_w(component
));
4247 return ERROR_SUCCESS
;
4250 compgroupid
= MSI_RecordGetString(rec
,1);
4251 qualifier
= MSI_RecordGetString(rec
,2);
4253 rc
= MSIREG_OpenUserComponentsKey(compgroupid
, &hkey
, TRUE
);
4254 if (rc
!= ERROR_SUCCESS
)
4257 text
= MSI_RecordGetString(rec
,4);
4258 feature
= MSI_RecordGetString(rec
,5);
4260 advertise
= create_component_advertise_string(package
, comp
, feature
);
4262 sz
= strlenW(advertise
);
4265 sz
+= lstrlenW(text
);
4268 sz
*= sizeof(WCHAR
);
4270 output
= msi_alloc_zero(sz
);
4271 strcpyW(output
,advertise
);
4272 msi_free(advertise
);
4275 strcatW(output
,text
);
4277 msi_reg_set_val_multi_str( hkey
, qualifier
, output
);
4284 uirow
= MSI_CreateRecord( 2 );
4285 MSI_RecordSetStringW( uirow
, 1, compgroupid
);
4286 MSI_RecordSetStringW( uirow
, 2, qualifier
);
4287 ui_actiondata( package
, szPublishComponents
, uirow
);
4288 msiobj_release( &uirow
->hdr
);
4289 /* FIXME: call ui_progress? */
4295 * At present I am ignorning the advertised components part of this and only
4296 * focusing on the qualified component sets
4298 static UINT
ACTION_PublishComponents(MSIPACKAGE
*package
)
4302 static const WCHAR ExecSeqQuery
[] =
4303 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4304 '`','P','u','b','l','i','s','h',
4305 'C','o','m','p','o','n','e','n','t','`',0};
4307 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4308 if (rc
!= ERROR_SUCCESS
)
4309 return ERROR_SUCCESS
;
4311 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_PublishComponent
, package
);
4312 msiobj_release(&view
->hdr
);
4317 static UINT
ITERATE_InstallService(MSIRECORD
*rec
, LPVOID param
)
4319 MSIPACKAGE
*package
= param
;
4322 SC_HANDLE hscm
, service
= NULL
;
4323 LPCWSTR comp
, depends
, pass
;
4324 LPWSTR name
= NULL
, disp
= NULL
;
4325 LPCWSTR load_order
, serv_name
, key
;
4326 DWORD serv_type
, start_type
;
4329 static const WCHAR query
[] =
4330 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4331 '`','C','o','m','p','o','n','e','n','t','`',' ',
4332 'W','H','E','R','E',' ',
4333 '`','C','o','m','p','o','n','e','n','t','`',' ',
4334 '=','\'','%','s','\'',0};
4336 hscm
= OpenSCManagerW(NULL
, SERVICES_ACTIVE_DATABASEW
, GENERIC_WRITE
);
4339 ERR("Failed to open the SC Manager!\n");
4343 start_type
= MSI_RecordGetInteger(rec
, 5);
4344 if (start_type
== SERVICE_BOOT_START
|| start_type
== SERVICE_SYSTEM_START
)
4347 depends
= MSI_RecordGetString(rec
, 8);
4348 if (depends
&& *depends
)
4349 FIXME("Dependency list unhandled!\n");
4351 deformat_string(package
, MSI_RecordGetString(rec
, 2), &name
);
4352 deformat_string(package
, MSI_RecordGetString(rec
, 3), &disp
);
4353 serv_type
= MSI_RecordGetInteger(rec
, 4);
4354 err_control
= MSI_RecordGetInteger(rec
, 6);
4355 load_order
= MSI_RecordGetString(rec
, 7);
4356 serv_name
= MSI_RecordGetString(rec
, 9);
4357 pass
= MSI_RecordGetString(rec
, 10);
4358 comp
= MSI_RecordGetString(rec
, 12);
4360 /* fetch the service path */
4361 row
= MSI_QueryGetRecord(package
->db
, query
, comp
);
4364 ERR("Control query failed!\n");
4368 key
= MSI_RecordGetString(row
, 6);
4370 file
= get_loaded_file(package
, key
);
4371 msiobj_release(&row
->hdr
);
4374 ERR("Failed to load the service file\n");
4378 service
= CreateServiceW(hscm
, name
, disp
, GENERIC_ALL
, serv_type
,
4379 start_type
, err_control
, file
->TargetPath
,
4380 load_order
, NULL
, NULL
, serv_name
, pass
);
4383 if (GetLastError() != ERROR_SERVICE_EXISTS
)
4384 ERR("Failed to create service %s: %d\n", debugstr_w(name
), GetLastError());
4388 CloseServiceHandle(service
);
4389 CloseServiceHandle(hscm
);
4393 return ERROR_SUCCESS
;
4396 static UINT
ACTION_InstallServices( MSIPACKAGE
*package
)
4400 static const WCHAR ExecSeqQuery
[] =
4401 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4402 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4404 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4405 if (rc
!= ERROR_SUCCESS
)
4406 return ERROR_SUCCESS
;
4408 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallService
, package
);
4409 msiobj_release(&view
->hdr
);
4414 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4415 static LPCWSTR
*msi_service_args_to_vector(LPWSTR args
, DWORD
*numargs
)
4417 LPCWSTR
*vector
, *temp_vector
;
4421 static const WCHAR separator
[] = {'[','~',']',0};
4424 sep_len
= sizeof(separator
) / sizeof(WCHAR
) - 1;
4429 vector
= msi_alloc(sizeof(LPWSTR
));
4437 vector
[*numargs
- 1] = p
;
4439 if ((q
= strstrW(p
, separator
)))
4443 temp_vector
= msi_realloc(vector
, (*numargs
+ 1) * sizeof(LPWSTR
));
4449 vector
= temp_vector
;
4458 static UINT
ITERATE_StartService(MSIRECORD
*rec
, LPVOID param
)
4460 MSIPACKAGE
*package
= param
;
4462 SC_HANDLE scm
, service
= NULL
;
4463 LPCWSTR
*vector
= NULL
;
4465 DWORD event
, numargs
;
4466 UINT r
= ERROR_FUNCTION_FAILED
;
4468 comp
= get_loaded_component(package
, MSI_RecordGetString(rec
, 6));
4469 if (!comp
|| comp
->Action
== INSTALLSTATE_UNKNOWN
|| comp
->Action
== INSTALLSTATE_ABSENT
)
4470 return ERROR_SUCCESS
;
4472 deformat_string(package
, MSI_RecordGetString(rec
, 2), &name
);
4473 deformat_string(package
, MSI_RecordGetString(rec
, 4), &args
);
4474 event
= MSI_RecordGetInteger(rec
, 3);
4476 if (!(event
& msidbServiceControlEventStart
))
4477 return ERROR_SUCCESS
;
4479 scm
= OpenSCManagerW(NULL
, NULL
, SC_MANAGER_CONNECT
);
4482 ERR("Failed to open the service control manager\n");
4486 service
= OpenServiceW(scm
, name
, SERVICE_START
);
4489 ERR("Failed to open service %s (%u)\n", debugstr_w(name
), GetLastError());
4493 vector
= msi_service_args_to_vector(args
, &numargs
);
4495 if (!StartServiceW(service
, numargs
, vector
) &&
4496 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING
)
4498 ERR("Failed to start service %s (%u)\n", debugstr_w(name
), GetLastError());
4505 CloseServiceHandle(service
);
4506 CloseServiceHandle(scm
);
4514 static UINT
ACTION_StartServices( MSIPACKAGE
*package
)
4519 static const WCHAR query
[] = {
4520 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4521 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4523 rc
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
4524 if (rc
!= ERROR_SUCCESS
)
4525 return ERROR_SUCCESS
;
4527 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_StartService
, package
);
4528 msiobj_release(&view
->hdr
);
4533 static BOOL
stop_service_dependents(SC_HANDLE scm
, SC_HANDLE service
)
4535 DWORD i
, needed
, count
;
4536 ENUM_SERVICE_STATUSW
*dependencies
;
4540 if (EnumDependentServicesW(service
, SERVICE_ACTIVE
, NULL
,
4541 0, &needed
, &count
))
4544 if (GetLastError() != ERROR_MORE_DATA
)
4547 dependencies
= msi_alloc(needed
);
4551 if (!EnumDependentServicesW(service
, SERVICE_ACTIVE
, dependencies
,
4552 needed
, &needed
, &count
))
4555 for (i
= 0; i
< count
; i
++)
4557 depserv
= OpenServiceW(scm
, dependencies
[i
].lpServiceName
,
4558 SERVICE_STOP
| SERVICE_QUERY_STATUS
);
4562 if (!ControlService(depserv
, SERVICE_CONTROL_STOP
, &ss
))
4569 msi_free(dependencies
);
4573 static UINT
stop_service( LPCWSTR name
)
4575 SC_HANDLE scm
= NULL
, service
= NULL
;
4576 SERVICE_STATUS status
;
4577 SERVICE_STATUS_PROCESS ssp
;
4580 scm
= OpenSCManagerW(NULL
, NULL
, SC_MANAGER_ALL_ACCESS
);
4583 WARN("Failed to open the SCM: %d\n", GetLastError());
4587 service
= OpenServiceW(scm
, name
,
4589 SERVICE_QUERY_STATUS
|
4590 SERVICE_ENUMERATE_DEPENDENTS
);
4593 WARN("Failed to open service (%s): %d\n", debugstr_w(name
), GetLastError());
4597 if (!QueryServiceStatusEx(service
, SC_STATUS_PROCESS_INFO
, (LPBYTE
)&ssp
,
4598 sizeof(SERVICE_STATUS_PROCESS
), &needed
))
4600 WARN("Failed to query service status (%s): %d\n", debugstr_w(name
), GetLastError());
4604 if (ssp
.dwCurrentState
== SERVICE_STOPPED
)
4607 stop_service_dependents(scm
, service
);
4609 if (!ControlService(service
, SERVICE_CONTROL_STOP
, &status
))
4610 WARN("Failed to stop service (%s): %d\n", debugstr_w(name
), GetLastError());
4613 CloseServiceHandle(service
);
4614 CloseServiceHandle(scm
);
4616 return ERROR_SUCCESS
;
4619 static UINT
ITERATE_StopService( MSIRECORD
*rec
, LPVOID param
)
4621 MSIPACKAGE
*package
= param
;
4626 event
= MSI_RecordGetInteger( rec
, 3 );
4627 if (!(event
& msidbServiceControlEventStop
))
4628 return ERROR_SUCCESS
;
4630 comp
= get_loaded_component( package
, MSI_RecordGetString( rec
, 6 ) );
4631 if (!comp
|| comp
->Action
== INSTALLSTATE_UNKNOWN
|| comp
->Action
== INSTALLSTATE_ABSENT
)
4632 return ERROR_SUCCESS
;
4634 deformat_string( package
, MSI_RecordGetString( rec
, 2 ), &name
);
4635 stop_service( name
);
4638 return ERROR_SUCCESS
;
4641 static UINT
ACTION_StopServices( MSIPACKAGE
*package
)
4646 static const WCHAR query
[] = {
4647 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4648 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4650 rc
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
4651 if (rc
!= ERROR_SUCCESS
)
4652 return ERROR_SUCCESS
;
4654 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_StopService
, package
);
4655 msiobj_release(&view
->hdr
);
4660 static UINT
ITERATE_DeleteService( MSIRECORD
*rec
, LPVOID param
)
4662 MSIPACKAGE
*package
= param
;
4666 SC_HANDLE scm
= NULL
, service
= NULL
;
4668 event
= MSI_RecordGetInteger( rec
, 3 );
4669 if (!(event
& msidbServiceControlEventDelete
))
4670 return ERROR_SUCCESS
;
4672 comp
= get_loaded_component( package
, MSI_RecordGetString(rec
, 6) );
4673 if (!comp
|| comp
->Action
== INSTALLSTATE_UNKNOWN
|| comp
->Action
== INSTALLSTATE_ABSENT
)
4674 return ERROR_SUCCESS
;
4676 deformat_string( package
, MSI_RecordGetString(rec
, 2), &name
);
4677 stop_service( name
);
4679 scm
= OpenSCManagerW( NULL
, NULL
, SC_MANAGER_ALL_ACCESS
);
4682 WARN("Failed to open the SCM: %d\n", GetLastError());
4686 service
= OpenServiceW( scm
, name
, DELETE
);
4689 WARN("Failed to open service (%s): %u\n", debugstr_w(name
), GetLastError());
4693 if (!DeleteService( service
))
4694 WARN("Failed to delete service (%s): %u\n", debugstr_w(name
), GetLastError());
4697 CloseServiceHandle( service
);
4698 CloseServiceHandle( scm
);
4701 return ERROR_SUCCESS
;
4704 static UINT
ACTION_DeleteServices( MSIPACKAGE
*package
)
4709 static const WCHAR query
[] = {
4710 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4711 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4713 rc
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
4714 if (rc
!= ERROR_SUCCESS
)
4715 return ERROR_SUCCESS
;
4717 rc
= MSI_IterateRecords( view
, NULL
, ITERATE_DeleteService
, package
);
4718 msiobj_release( &view
->hdr
);
4723 static MSIFILE
*msi_find_file( MSIPACKAGE
*package
, LPCWSTR filename
)
4727 LIST_FOR_EACH_ENTRY(file
, &package
->files
, MSIFILE
, entry
)
4729 if (!lstrcmpW(file
->File
, filename
))
4736 static UINT
ITERATE_InstallODBCDriver( MSIRECORD
*rec
, LPVOID param
)
4738 MSIPACKAGE
*package
= param
;
4739 LPWSTR driver
, driver_path
, ptr
;
4740 WCHAR outpath
[MAX_PATH
];
4741 MSIFILE
*driver_file
, *setup_file
;
4744 UINT r
= ERROR_SUCCESS
;
4746 static const WCHAR driver_fmt
[] = {
4747 'D','r','i','v','e','r','=','%','s',0};
4748 static const WCHAR setup_fmt
[] = {
4749 'S','e','t','u','p','=','%','s',0};
4750 static const WCHAR usage_fmt
[] = {
4751 'F','i','l','e','U','s','a','g','e','=','1',0};
4753 desc
= MSI_RecordGetString(rec
, 3);
4755 driver_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 4));
4756 setup_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 5));
4758 if (!driver_file
|| !setup_file
)
4760 ERR("ODBC Driver entry not found!\n");
4761 return ERROR_FUNCTION_FAILED
;
4764 len
= lstrlenW(desc
) + lstrlenW(driver_fmt
) + lstrlenW(driver_file
->FileName
) +
4765 lstrlenW(setup_fmt
) + lstrlenW(setup_file
->FileName
) +
4766 lstrlenW(usage_fmt
) + 1;
4767 driver
= msi_alloc(len
* sizeof(WCHAR
));
4769 return ERROR_OUTOFMEMORY
;
4772 lstrcpyW(ptr
, desc
);
4773 ptr
+= lstrlenW(ptr
) + 1;
4775 sprintfW(ptr
, driver_fmt
, driver_file
->FileName
);
4776 ptr
+= lstrlenW(ptr
) + 1;
4778 sprintfW(ptr
, setup_fmt
, setup_file
->FileName
);
4779 ptr
+= lstrlenW(ptr
) + 1;
4781 lstrcpyW(ptr
, usage_fmt
);
4782 ptr
+= lstrlenW(ptr
) + 1;
4785 driver_path
= strdupW(driver_file
->TargetPath
);
4786 ptr
= strrchrW(driver_path
, '\\');
4787 if (ptr
) *ptr
= '\0';
4789 if (!SQLInstallDriverExW(driver
, driver_path
, outpath
, MAX_PATH
,
4790 NULL
, ODBC_INSTALL_COMPLETE
, &usage
))
4792 ERR("Failed to install SQL driver!\n");
4793 r
= ERROR_FUNCTION_FAILED
;
4797 msi_free(driver_path
);
4802 static UINT
ITERATE_InstallODBCTranslator( MSIRECORD
*rec
, LPVOID param
)
4804 MSIPACKAGE
*package
= param
;
4805 LPWSTR translator
, translator_path
, ptr
;
4806 WCHAR outpath
[MAX_PATH
];
4807 MSIFILE
*translator_file
, *setup_file
;
4810 UINT r
= ERROR_SUCCESS
;
4812 static const WCHAR translator_fmt
[] = {
4813 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4814 static const WCHAR setup_fmt
[] = {
4815 'S','e','t','u','p','=','%','s',0};
4817 desc
= MSI_RecordGetString(rec
, 3);
4819 translator_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 4));
4820 setup_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 5));
4822 if (!translator_file
|| !setup_file
)
4824 ERR("ODBC Translator entry not found!\n");
4825 return ERROR_FUNCTION_FAILED
;
4828 len
= lstrlenW(desc
) + lstrlenW(translator_fmt
) + lstrlenW(translator_file
->FileName
) +
4829 lstrlenW(setup_fmt
) + lstrlenW(setup_file
->FileName
) + 1;
4830 translator
= msi_alloc(len
* sizeof(WCHAR
));
4832 return ERROR_OUTOFMEMORY
;
4835 lstrcpyW(ptr
, desc
);
4836 ptr
+= lstrlenW(ptr
) + 1;
4838 sprintfW(ptr
, translator_fmt
, translator_file
->FileName
);
4839 ptr
+= lstrlenW(ptr
) + 1;
4841 sprintfW(ptr
, setup_fmt
, setup_file
->FileName
);
4842 ptr
+= lstrlenW(ptr
) + 1;
4845 translator_path
= strdupW(translator_file
->TargetPath
);
4846 ptr
= strrchrW(translator_path
, '\\');
4847 if (ptr
) *ptr
= '\0';
4849 if (!SQLInstallTranslatorExW(translator
, translator_path
, outpath
, MAX_PATH
,
4850 NULL
, ODBC_INSTALL_COMPLETE
, &usage
))
4852 ERR("Failed to install SQL translator!\n");
4853 r
= ERROR_FUNCTION_FAILED
;
4856 msi_free(translator
);
4857 msi_free(translator_path
);
4862 static UINT
ITERATE_InstallODBCDataSource( MSIRECORD
*rec
, LPVOID param
)
4865 LPCWSTR desc
, driver
;
4866 WORD request
= ODBC_ADD_SYS_DSN
;
4869 UINT r
= ERROR_SUCCESS
;
4871 static const WCHAR attrs_fmt
[] = {
4872 'D','S','N','=','%','s',0 };
4874 desc
= MSI_RecordGetString(rec
, 3);
4875 driver
= MSI_RecordGetString(rec
, 4);
4876 registration
= MSI_RecordGetInteger(rec
, 5);
4878 if (registration
== msidbODBCDataSourceRegistrationPerMachine
) request
= ODBC_ADD_SYS_DSN
;
4879 else if (registration
== msidbODBCDataSourceRegistrationPerUser
) request
= ODBC_ADD_DSN
;
4881 len
= lstrlenW(attrs_fmt
) + lstrlenW(desc
) + 1 + 1;
4882 attrs
= msi_alloc(len
* sizeof(WCHAR
));
4884 return ERROR_OUTOFMEMORY
;
4886 sprintfW(attrs
, attrs_fmt
, desc
);
4887 attrs
[len
- 1] = '\0';
4889 if (!SQLConfigDataSourceW(NULL
, request
, driver
, attrs
))
4891 ERR("Failed to install SQL data source!\n");
4892 r
= ERROR_FUNCTION_FAILED
;
4900 static UINT
ACTION_InstallODBC( MSIPACKAGE
*package
)
4905 static const WCHAR driver_query
[] = {
4906 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4907 'O','D','B','C','D','r','i','v','e','r',0 };
4909 static const WCHAR translator_query
[] = {
4910 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4911 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4913 static const WCHAR source_query
[] = {
4914 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4915 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4917 rc
= MSI_DatabaseOpenViewW(package
->db
, driver_query
, &view
);
4918 if (rc
!= ERROR_SUCCESS
)
4919 return ERROR_SUCCESS
;
4921 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCDriver
, package
);
4922 msiobj_release(&view
->hdr
);
4924 rc
= MSI_DatabaseOpenViewW(package
->db
, translator_query
, &view
);
4925 if (rc
!= ERROR_SUCCESS
)
4926 return ERROR_SUCCESS
;
4928 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCTranslator
, package
);
4929 msiobj_release(&view
->hdr
);
4931 rc
= MSI_DatabaseOpenViewW(package
->db
, source_query
, &view
);
4932 if (rc
!= ERROR_SUCCESS
)
4933 return ERROR_SUCCESS
;
4935 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCDataSource
, package
);
4936 msiobj_release(&view
->hdr
);
4941 #define ENV_ACT_SETALWAYS 0x1
4942 #define ENV_ACT_SETABSENT 0x2
4943 #define ENV_ACT_REMOVE 0x4
4944 #define ENV_ACT_REMOVEMATCH 0x8
4946 #define ENV_MOD_MACHINE 0x20000000
4947 #define ENV_MOD_APPEND 0x40000000
4948 #define ENV_MOD_PREFIX 0x80000000
4949 #define ENV_MOD_MASK 0xC0000000
4951 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4953 static LONG
env_set_flags( LPCWSTR
*name
, LPCWSTR
*value
, DWORD
*flags
)
4955 LPCWSTR cptr
= *name
;
4957 static const WCHAR prefix
[] = {'[','~',']',0};
4958 static const int prefix_len
= 3;
4964 *flags
|= ENV_ACT_SETALWAYS
;
4965 else if (*cptr
== '+')
4966 *flags
|= ENV_ACT_SETABSENT
;
4967 else if (*cptr
== '-')
4968 *flags
|= ENV_ACT_REMOVE
;
4969 else if (*cptr
== '!')
4970 *flags
|= ENV_ACT_REMOVEMATCH
;
4971 else if (*cptr
== '*')
4972 *flags
|= ENV_MOD_MACHINE
;
4982 ERR("Missing environment variable\n");
4983 return ERROR_FUNCTION_FAILED
;
4988 LPCWSTR ptr
= *value
;
4989 if (!strncmpW(ptr
, prefix
, prefix_len
))
4991 if (ptr
[prefix_len
] == szSemiColon
[0])
4993 *flags
|= ENV_MOD_APPEND
;
4994 *value
+= lstrlenW(prefix
);
5001 else if (lstrlenW(*value
) >= prefix_len
)
5003 ptr
+= lstrlenW(ptr
) - prefix_len
;
5004 if (!lstrcmpW(ptr
, prefix
))
5006 if ((ptr
-1) > *value
&& *(ptr
-1) == szSemiColon
[0])
5008 *flags
|= ENV_MOD_PREFIX
;
5009 /* the "[~]" will be removed by deformat_string */;
5019 if (check_flag_combo(*flags
, ENV_ACT_SETALWAYS
| ENV_ACT_SETABSENT
) ||
5020 check_flag_combo(*flags
, ENV_ACT_REMOVEMATCH
| ENV_ACT_SETABSENT
) ||
5021 check_flag_combo(*flags
, ENV_ACT_REMOVEMATCH
| ENV_ACT_SETALWAYS
) ||
5022 check_flag_combo(*flags
, ENV_ACT_SETABSENT
| ENV_MOD_MASK
))
5024 ERR("Invalid flags: %08x\n", *flags
);
5025 return ERROR_FUNCTION_FAILED
;
5029 *flags
= ENV_ACT_SETALWAYS
| ENV_ACT_REMOVE
;
5031 return ERROR_SUCCESS
;
5034 static UINT
ITERATE_WriteEnvironmentString( MSIRECORD
*rec
, LPVOID param
)
5036 MSIPACKAGE
*package
= param
;
5037 LPCWSTR name
, value
;
5038 LPWSTR data
= NULL
, newval
= NULL
;
5039 LPWSTR deformatted
= NULL
, ptr
;
5040 DWORD flags
, type
, size
;
5042 HKEY env
= NULL
, root
;
5043 LPCWSTR environment
;
5045 static const WCHAR user_env
[] =
5046 {'E','n','v','i','r','o','n','m','e','n','t',0};
5047 static const WCHAR machine_env
[] =
5048 {'S','y','s','t','e','m','\\',
5049 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5050 'C','o','n','t','r','o','l','\\',
5051 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5052 'E','n','v','i','r','o','n','m','e','n','t',0};
5054 name
= MSI_RecordGetString(rec
, 2);
5055 value
= MSI_RecordGetString(rec
, 3);
5057 TRACE("name %s value %s\n", debugstr_w(name
), debugstr_w(value
));
5059 res
= env_set_flags(&name
, &value
, &flags
);
5060 if (res
!= ERROR_SUCCESS
|| !value
)
5063 if (value
&& !deformat_string(package
, value
, &deformatted
))
5065 res
= ERROR_OUTOFMEMORY
;
5069 value
= deformatted
;
5071 if (flags
& ENV_MOD_MACHINE
)
5073 environment
= machine_env
;
5074 root
= HKEY_LOCAL_MACHINE
;
5078 environment
= user_env
;
5079 root
= HKEY_CURRENT_USER
;
5082 res
= RegCreateKeyExW(root
, environment
, 0, NULL
, 0,
5083 KEY_ALL_ACCESS
, NULL
, &env
, NULL
);
5084 if (res
!= ERROR_SUCCESS
)
5087 if (flags
& ENV_ACT_REMOVE
)
5088 FIXME("Not removing environment variable on uninstall!\n");
5092 res
= RegQueryValueExW(env
, name
, NULL
, &type
, NULL
, &size
);
5093 if ((res
!= ERROR_SUCCESS
&& res
!= ERROR_FILE_NOT_FOUND
) ||
5094 (res
== ERROR_SUCCESS
&& type
!= REG_SZ
&& type
!= REG_EXPAND_SZ
))
5097 if ((res
== ERROR_FILE_NOT_FOUND
|| !(flags
& ENV_MOD_MASK
)))
5099 /* Nothing to do. */
5102 res
= ERROR_SUCCESS
;
5106 /* If we are appending but the string was empty, strip ; */
5107 if ((flags
& ENV_MOD_APPEND
) && (value
[0] == szSemiColon
[0])) value
++;
5109 size
= (lstrlenW(value
) + 1) * sizeof(WCHAR
);
5110 newval
= strdupW(value
);
5113 res
= ERROR_OUTOFMEMORY
;
5119 /* Contrary to MSDN, +-variable to [~];path works */
5120 if (flags
& ENV_ACT_SETABSENT
&& !(flags
& ENV_MOD_MASK
))
5122 res
= ERROR_SUCCESS
;
5126 data
= msi_alloc(size
);
5130 return ERROR_OUTOFMEMORY
;
5133 res
= RegQueryValueExW(env
, name
, NULL
, &type
, (LPVOID
)data
, &size
);
5134 if (res
!= ERROR_SUCCESS
)
5137 if (flags
& ENV_ACT_REMOVEMATCH
&& (!value
|| !lstrcmpW(data
, value
)))
5139 res
= RegDeleteKeyW(env
, name
);
5143 size
= (lstrlenW(data
) + 1) * sizeof(WCHAR
);
5144 if (flags
& ENV_MOD_MASK
)
5148 if (flags
& ENV_MOD_APPEND
) multiplier
++;
5149 if (flags
& ENV_MOD_PREFIX
) multiplier
++;
5150 mod_size
= lstrlenW(value
) * multiplier
;
5151 size
+= mod_size
* sizeof(WCHAR
);
5154 newval
= msi_alloc(size
);
5158 res
= ERROR_OUTOFMEMORY
;
5162 if (flags
& ENV_MOD_PREFIX
)
5164 lstrcpyW(newval
, value
);
5165 ptr
= newval
+ lstrlenW(value
);
5168 lstrcpyW(ptr
, data
);
5170 if (flags
& ENV_MOD_APPEND
)
5172 lstrcatW(newval
, value
);
5175 TRACE("setting %s to %s\n", debugstr_w(name
), debugstr_w(newval
));
5176 res
= RegSetValueExW(env
, name
, 0, type
, (LPVOID
)newval
, size
);
5179 if (env
) RegCloseKey(env
);
5180 msi_free(deformatted
);
5186 static UINT
ACTION_WriteEnvironmentStrings( MSIPACKAGE
*package
)
5190 static const WCHAR ExecSeqQuery
[] =
5191 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5192 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5193 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
5194 if (rc
!= ERROR_SUCCESS
)
5195 return ERROR_SUCCESS
;
5197 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteEnvironmentString
, package
);
5198 msiobj_release(&view
->hdr
);
5203 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5214 static BOOL
msi_move_file(LPCWSTR source
, LPCWSTR dest
, int options
)
5218 if (GetFileAttributesW(source
) == FILE_ATTRIBUTE_DIRECTORY
||
5219 GetFileAttributesW(dest
) == FILE_ATTRIBUTE_DIRECTORY
)
5221 WARN("Source or dest is directory, not moving\n");
5225 if (options
== msidbMoveFileOptionsMove
)
5227 TRACE("moving %s -> %s\n", debugstr_w(source
), debugstr_w(dest
));
5228 ret
= MoveFileExW(source
, dest
, MOVEFILE_REPLACE_EXISTING
);
5231 WARN("MoveFile failed: %d\n", GetLastError());
5237 TRACE("copying %s -> %s\n", debugstr_w(source
), debugstr_w(dest
));
5238 ret
= CopyFileW(source
, dest
, FALSE
);
5241 WARN("CopyFile failed: %d\n", GetLastError());
5249 static LPWSTR
wildcard_to_file(LPWSTR wildcard
, LPWSTR filename
)
5252 DWORD dirlen
, pathlen
;
5254 ptr
= strrchrW(wildcard
, '\\');
5255 dirlen
= ptr
- wildcard
+ 1;
5257 pathlen
= dirlen
+ lstrlenW(filename
) + 1;
5258 path
= msi_alloc(pathlen
* sizeof(WCHAR
));
5260 lstrcpynW(path
, wildcard
, dirlen
+ 1);
5261 lstrcatW(path
, filename
);
5266 static void free_file_entry(FILE_LIST
*file
)
5268 msi_free(file
->source
);
5269 msi_free(file
->dest
);
5273 static void free_list(FILE_LIST
*list
)
5275 while (!list_empty(&list
->entry
))
5277 FILE_LIST
*file
= LIST_ENTRY(list_head(&list
->entry
), FILE_LIST
, entry
);
5279 list_remove(&file
->entry
);
5280 free_file_entry(file
);
5284 static BOOL
add_wildcard(FILE_LIST
*files
, LPWSTR source
, LPWSTR dest
)
5286 FILE_LIST
*new, *file
;
5287 LPWSTR ptr
, filename
;
5290 new = msi_alloc_zero(sizeof(FILE_LIST
));
5294 new->source
= strdupW(source
);
5295 ptr
= strrchrW(dest
, '\\') + 1;
5296 filename
= strrchrW(new->source
, '\\') + 1;
5298 new->sourcename
= filename
;
5301 new->destname
= ptr
;
5303 new->destname
= new->sourcename
;
5305 size
= (ptr
- dest
) + lstrlenW(filename
) + 1;
5306 new->dest
= msi_alloc(size
* sizeof(WCHAR
));
5309 free_file_entry(new);
5313 lstrcpynW(new->dest
, dest
, ptr
- dest
+ 1);
5314 lstrcatW(new->dest
, filename
);
5316 if (list_empty(&files
->entry
))
5318 list_add_head(&files
->entry
, &new->entry
);
5322 LIST_FOR_EACH_ENTRY(file
, &files
->entry
, FILE_LIST
, entry
)
5324 if (lstrcmpW(source
, file
->source
) < 0)
5326 list_add_before(&file
->entry
, &new->entry
);
5331 list_add_after(&file
->entry
, &new->entry
);
5335 static BOOL
move_files_wildcard(LPWSTR source
, LPWSTR dest
, int options
)
5337 WIN32_FIND_DATAW wfd
;
5341 FILE_LIST files
, *file
;
5344 hfile
= FindFirstFileW(source
, &wfd
);
5345 if (hfile
== INVALID_HANDLE_VALUE
) return FALSE
;
5347 list_init(&files
.entry
);
5349 for (res
= TRUE
; res
; res
= FindNextFileW(hfile
, &wfd
))
5351 if (is_dot_dir(wfd
.cFileName
)) continue;
5353 path
= wildcard_to_file(source
, wfd
.cFileName
);
5360 add_wildcard(&files
, path
, dest
);
5364 /* no files match the wildcard */
5365 if (list_empty(&files
.entry
))
5368 /* only the first wildcard match gets renamed to dest */
5369 file
= LIST_ENTRY(list_head(&files
.entry
), FILE_LIST
, entry
);
5370 size
= (strrchrW(file
->dest
, '\\') - file
->dest
) + lstrlenW(file
->destname
) + 2;
5371 file
->dest
= msi_realloc(file
->dest
, size
* sizeof(WCHAR
));
5378 /* file->dest may be shorter after the reallocation, so add a NULL
5379 * terminator. This is needed for the call to strrchrW, as there will no
5380 * longer be a NULL terminator within the bounds of the allocation in this case.
5382 file
->dest
[size
- 1] = '\0';
5383 lstrcpyW(strrchrW(file
->dest
, '\\') + 1, file
->destname
);
5385 while (!list_empty(&files
.entry
))
5387 file
= LIST_ENTRY(list_head(&files
.entry
), FILE_LIST
, entry
);
5389 msi_move_file(file
->source
, file
->dest
, options
);
5391 list_remove(&file
->entry
);
5392 free_file_entry(file
);
5403 static UINT
ITERATE_MoveFiles( MSIRECORD
*rec
, LPVOID param
)
5405 MSIPACKAGE
*package
= param
;
5408 LPWSTR destname
= NULL
;
5409 LPWSTR sourcedir
= NULL
, destdir
= NULL
;
5410 LPWSTR source
= NULL
, dest
= NULL
;
5413 BOOL ret
, wildcards
;
5415 comp
= get_loaded_component(package
, MSI_RecordGetString(rec
, 2));
5416 if (!comp
|| !comp
->Enabled
||
5417 !(comp
->Action
& (INSTALLSTATE_LOCAL
| INSTALLSTATE_SOURCE
)))
5419 TRACE("Component not set for install, not moving file\n");
5420 return ERROR_SUCCESS
;
5423 sourcename
= MSI_RecordGetString(rec
, 3);
5424 options
= MSI_RecordGetInteger(rec
, 7);
5426 sourcedir
= msi_dup_property(package
, MSI_RecordGetString(rec
, 5));
5430 destdir
= msi_dup_property(package
, MSI_RecordGetString(rec
, 6));
5436 if (GetFileAttributesW(sourcedir
) == INVALID_FILE_ATTRIBUTES
)
5439 source
= strdupW(sourcedir
);
5445 size
= lstrlenW(sourcedir
) + lstrlenW(sourcename
) + 2;
5446 source
= msi_alloc(size
* sizeof(WCHAR
));
5450 lstrcpyW(source
, sourcedir
);
5451 if (source
[lstrlenW(source
) - 1] != '\\')
5452 lstrcatW(source
, szBackSlash
);
5453 lstrcatW(source
, sourcename
);
5456 wildcards
= strchrW(source
, '*') || strchrW(source
, '?');
5458 if (MSI_RecordIsNull(rec
, 4))
5462 destname
= strdupW(sourcename
);
5469 destname
= strdupW(MSI_RecordGetString(rec
, 4));
5471 reduce_to_longfilename(destname
);
5476 size
= lstrlenW(destname
);
5478 size
+= lstrlenW(destdir
) + 2;
5479 dest
= msi_alloc(size
* sizeof(WCHAR
));
5483 lstrcpyW(dest
, destdir
);
5484 if (dest
[lstrlenW(dest
) - 1] != '\\')
5485 lstrcatW(dest
, szBackSlash
);
5488 lstrcatW(dest
, destname
);
5490 if (GetFileAttributesW(destdir
) == INVALID_FILE_ATTRIBUTES
)
5492 ret
= CreateDirectoryW(destdir
, NULL
);
5495 WARN("CreateDirectory failed: %d\n", GetLastError());
5496 return ERROR_SUCCESS
;
5501 msi_move_file(source
, dest
, options
);
5503 move_files_wildcard(source
, dest
, options
);
5506 msi_free(sourcedir
);
5512 return ERROR_SUCCESS
;
5515 static UINT
ACTION_MoveFiles( MSIPACKAGE
*package
)
5520 static const WCHAR ExecSeqQuery
[] =
5521 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5522 '`','M','o','v','e','F','i','l','e','`',0};
5524 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
5525 if (rc
!= ERROR_SUCCESS
)
5526 return ERROR_SUCCESS
;
5528 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_MoveFiles
, package
);
5529 msiobj_release(&view
->hdr
);
5534 typedef struct tagMSIASSEMBLY
5537 MSICOMPONENT
*component
;
5538 MSIFEATURE
*feature
;
5546 static HRESULT (WINAPI
*pCreateAssemblyCache
)(IAssemblyCache
**ppAsmCache
,
5548 static HRESULT (WINAPI
*pLoadLibraryShim
)(LPCWSTR szDllName
, LPCWSTR szVersion
,
5549 LPVOID pvReserved
, HMODULE
*phModDll
);
5551 static BOOL
init_functionpointers(void)
5557 static const WCHAR szFusion
[] = {'f','u','s','i','o','n','.','d','l','l',0};
5559 hmscoree
= LoadLibraryA("mscoree.dll");
5562 WARN("mscoree.dll not available\n");
5566 pLoadLibraryShim
= (void *)GetProcAddress(hmscoree
, "LoadLibraryShim");
5567 if (!pLoadLibraryShim
)
5569 WARN("LoadLibraryShim not available\n");
5570 FreeLibrary(hmscoree
);
5574 hr
= pLoadLibraryShim(szFusion
, NULL
, NULL
, &hfusion
);
5577 WARN("fusion.dll not available\n");
5578 FreeLibrary(hmscoree
);
5582 pCreateAssemblyCache
= (void *)GetProcAddress(hfusion
, "CreateAssemblyCache");
5584 FreeLibrary(hmscoree
);
5588 static UINT
install_assembly(MSIPACKAGE
*package
, MSIASSEMBLY
*assembly
,
5591 IAssemblyCache
*cache
;
5593 UINT r
= ERROR_FUNCTION_FAILED
;
5595 TRACE("installing assembly: %s\n", debugstr_w(path
));
5597 if (assembly
->feature
)
5598 msi_feature_set_state(package
, assembly
->feature
, INSTALLSTATE_LOCAL
);
5600 if (assembly
->manifest
)
5601 FIXME("Manifest unhandled\n");
5603 if (assembly
->application
)
5605 FIXME("Assembly should be privately installed\n");
5606 return ERROR_SUCCESS
;
5609 if (assembly
->attributes
== msidbAssemblyAttributesWin32
)
5611 FIXME("Win32 assemblies not handled\n");
5612 return ERROR_SUCCESS
;
5615 hr
= pCreateAssemblyCache(&cache
, 0);
5619 hr
= IAssemblyCache_InstallAssembly(cache
, 0, path
, NULL
);
5621 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path
), hr
);
5626 IAssemblyCache_Release(cache
);
5630 typedef struct tagASSEMBLY_LIST
5632 MSIPACKAGE
*package
;
5633 IAssemblyCache
*cache
;
5634 struct list
*assemblies
;
5637 typedef struct tagASSEMBLY_NAME
5645 static UINT
parse_assembly_name(MSIRECORD
*rec
, LPVOID param
)
5647 ASSEMBLY_NAME
*asmname
= param
;
5648 LPCWSTR name
= MSI_RecordGetString(rec
, 2);
5649 LPWSTR val
= msi_dup_record_field(rec
, 3);
5651 static const WCHAR Name
[] = {'N','a','m','e',0};
5652 static const WCHAR Version
[] = {'V','e','r','s','i','o','n',0};
5653 static const WCHAR Culture
[] = {'C','u','l','t','u','r','e',0};
5654 static const WCHAR PublicKeyToken
[] = {
5655 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
5657 if (!strcmpiW(name
, Name
))
5658 asmname
->name
= val
;
5659 else if (!strcmpiW(name
, Version
))
5660 asmname
->version
= val
;
5661 else if (!strcmpiW(name
, Culture
))
5662 asmname
->culture
= val
;
5663 else if (!strcmpiW(name
, PublicKeyToken
))
5664 asmname
->pubkeytoken
= val
;
5668 return ERROR_SUCCESS
;
5671 static void append_str(LPWSTR
*str
, DWORD
*size
, LPCWSTR append
)
5675 *size
= lstrlenW(append
) + 1;
5676 *str
= msi_alloc((*size
) * sizeof(WCHAR
));
5677 lstrcpyW(*str
, append
);
5681 (*size
) += lstrlenW(append
);
5682 *str
= msi_realloc(*str
, (*size
) * sizeof(WCHAR
));
5683 lstrcatW(*str
, append
);
5686 static BOOL
check_assembly_installed(MSIDATABASE
*db
, IAssemblyCache
*cache
,
5689 ASSEMBLY_INFO asminfo
;
5697 static const WCHAR separator
[] = {',',' ',0};
5698 static const WCHAR Version
[] = {'V','e','r','s','i','o','n','=',0};
5699 static const WCHAR Culture
[] = {'C','u','l','t','u','r','e','=',0};
5700 static const WCHAR PublicKeyToken
[] = {
5701 'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
5702 static const WCHAR query
[] = {
5703 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5704 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
5705 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
5706 '=','\'','%','s','\'',0};
5710 ZeroMemory(&name
, sizeof(ASSEMBLY_NAME
));
5711 ZeroMemory(&asminfo
, sizeof(ASSEMBLY_INFO
));
5713 r
= MSI_OpenQuery(db
, &view
, query
, comp
->Component
);
5714 if (r
!= ERROR_SUCCESS
)
5715 return ERROR_SUCCESS
;
5717 MSI_IterateRecords(view
, NULL
, parse_assembly_name
, &name
);
5718 msiobj_release(&view
->hdr
);
5722 ERR("No assembly name specified!\n");
5726 append_str(&disp
, &size
, name
.name
);
5730 append_str(&disp
, &size
, separator
);
5731 append_str(&disp
, &size
, Version
);
5732 append_str(&disp
, &size
, name
.version
);
5737 append_str(&disp
, &size
, separator
);
5738 append_str(&disp
, &size
, Culture
);
5739 append_str(&disp
, &size
, name
.culture
);
5742 if (name
.pubkeytoken
)
5744 append_str(&disp
, &size
, separator
);
5745 append_str(&disp
, &size
, PublicKeyToken
);
5746 append_str(&disp
, &size
, name
.pubkeytoken
);
5749 asminfo
.cbAssemblyInfo
= sizeof(ASSEMBLY_INFO
);
5750 IAssemblyCache_QueryAssemblyInfo(cache
, QUERYASMINFO_FLAG_VALIDATE
,
5752 found
= (asminfo
.dwAssemblyFlags
== ASSEMBLYINFO_FLAG_INSTALLED
);
5756 msi_free(name
.name
);
5757 msi_free(name
.version
);
5758 msi_free(name
.culture
);
5759 msi_free(name
.pubkeytoken
);
5764 static UINT
load_assembly(MSIRECORD
*rec
, LPVOID param
)
5766 ASSEMBLY_LIST
*list
= param
;
5767 MSIASSEMBLY
*assembly
;
5769 assembly
= msi_alloc_zero(sizeof(MSIASSEMBLY
));
5771 return ERROR_OUTOFMEMORY
;
5773 assembly
->component
= get_loaded_component(list
->package
, MSI_RecordGetString(rec
, 1));
5775 if (!assembly
->component
|| !assembly
->component
->Enabled
||
5776 !(assembly
->component
->Action
& (INSTALLSTATE_LOCAL
| INSTALLSTATE_SOURCE
)))
5778 TRACE("Component not set for install, not publishing assembly\n");
5780 return ERROR_SUCCESS
;
5783 assembly
->feature
= find_feature_by_name(list
->package
, MSI_RecordGetString(rec
, 2));
5784 assembly
->file
= msi_find_file(list
->package
, assembly
->component
->KeyPath
);
5786 if (!assembly
->file
)
5788 ERR("File %s not found\n", debugstr_w(assembly
->component
->KeyPath
));
5789 return ERROR_FUNCTION_FAILED
;
5792 assembly
->manifest
= strdupW(MSI_RecordGetString(rec
, 3));
5793 assembly
->application
= strdupW(MSI_RecordGetString(rec
, 4));
5794 assembly
->attributes
= MSI_RecordGetInteger(rec
, 5);
5796 if (assembly
->application
)
5799 DWORD size
= sizeof(version
)/sizeof(WCHAR
);
5801 /* FIXME: we should probably check the manifest file here */
5803 if (!MsiGetFileVersionW(assembly
->file
->TargetPath
, version
, &size
, NULL
, NULL
) &&
5804 (!assembly
->file
->Version
|| strcmpW(version
, assembly
->file
->Version
) >= 0))
5806 assembly
->installed
= TRUE
;
5810 assembly
->installed
= check_assembly_installed(list
->package
->db
,
5812 assembly
->component
);
5814 list_add_head(list
->assemblies
, &assembly
->entry
);
5815 return ERROR_SUCCESS
;
5818 static UINT
load_assemblies(MSIPACKAGE
*package
, struct list
*assemblies
)
5820 IAssemblyCache
*cache
= NULL
;
5826 static const WCHAR query
[] =
5827 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5828 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5830 r
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
5831 if (r
!= ERROR_SUCCESS
)
5832 return ERROR_SUCCESS
;
5834 hr
= pCreateAssemblyCache(&cache
, 0);
5836 return ERROR_FUNCTION_FAILED
;
5838 list
.package
= package
;
5840 list
.assemblies
= assemblies
;
5842 r
= MSI_IterateRecords(view
, NULL
, load_assembly
, &list
);
5843 msiobj_release(&view
->hdr
);
5845 IAssemblyCache_Release(cache
);
5850 static void free_assemblies(struct list
*assemblies
)
5852 struct list
*item
, *cursor
;
5854 LIST_FOR_EACH_SAFE(item
, cursor
, assemblies
)
5856 MSIASSEMBLY
*assembly
= LIST_ENTRY(item
, MSIASSEMBLY
, entry
);
5858 list_remove(&assembly
->entry
);
5859 msi_free(assembly
->application
);
5860 msi_free(assembly
->manifest
);
5865 static BOOL
find_assembly(struct list
*assemblies
, LPCWSTR file
, MSIASSEMBLY
**out
)
5867 MSIASSEMBLY
*assembly
;
5869 LIST_FOR_EACH_ENTRY(assembly
, assemblies
, MSIASSEMBLY
, entry
)
5871 if (!lstrcmpW(assembly
->file
->File
, file
))
5881 static BOOL
installassembly_cb(MSIPACKAGE
*package
, LPCWSTR file
, DWORD action
,
5882 LPWSTR
*path
, DWORD
*attrs
, PVOID user
)
5884 MSIASSEMBLY
*assembly
;
5885 WCHAR temppath
[MAX_PATH
];
5886 struct list
*assemblies
= user
;
5889 if (!find_assembly(assemblies
, file
, &assembly
))
5892 GetTempPathW(MAX_PATH
, temppath
);
5893 PathAddBackslashW(temppath
);
5894 lstrcatW(temppath
, assembly
->file
->FileName
);
5896 if (action
== MSICABEXTRACT_BEGINEXTRACT
)
5898 if (assembly
->installed
)
5901 *path
= strdupW(temppath
);
5902 *attrs
= assembly
->file
->Attributes
;
5904 else if (action
== MSICABEXTRACT_FILEEXTRACTED
)
5906 assembly
->installed
= TRUE
;
5908 r
= install_assembly(package
, assembly
, temppath
);
5909 if (r
!= ERROR_SUCCESS
)
5910 ERR("Failed to install assembly\n");
5916 static UINT
ACTION_MsiPublishAssemblies( MSIPACKAGE
*package
)
5919 struct list assemblies
= LIST_INIT(assemblies
);
5920 MSIASSEMBLY
*assembly
;
5923 if (!init_functionpointers() || !pCreateAssemblyCache
)
5924 return ERROR_FUNCTION_FAILED
;
5926 r
= load_assemblies(package
, &assemblies
);
5927 if (r
!= ERROR_SUCCESS
)
5930 if (list_empty(&assemblies
))
5933 mi
= msi_alloc_zero(sizeof(MSIMEDIAINFO
));
5936 r
= ERROR_OUTOFMEMORY
;
5940 LIST_FOR_EACH_ENTRY(assembly
, &assemblies
, MSIASSEMBLY
, entry
)
5942 if (assembly
->installed
&& !mi
->is_continuous
)
5945 if (assembly
->file
->Sequence
> mi
->last_sequence
|| mi
->is_continuous
||
5946 (assembly
->file
->IsCompressed
&& !mi
->is_extracted
))
5950 r
= ready_media(package
, assembly
->file
, mi
);
5951 if (r
!= ERROR_SUCCESS
)
5953 ERR("Failed to ready media\n");
5958 data
.package
= package
;
5959 data
.cb
= installassembly_cb
;
5960 data
.user
= &assemblies
;
5962 if (assembly
->file
->IsCompressed
&&
5963 !msi_cabextract(package
, mi
, &data
))
5965 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi
->cabinet
));
5966 r
= ERROR_FUNCTION_FAILED
;
5971 if (!assembly
->file
->IsCompressed
)
5973 LPWSTR source
= resolve_file_source(package
, assembly
->file
);
5975 r
= install_assembly(package
, assembly
, source
);
5976 if (r
!= ERROR_SUCCESS
)
5977 ERR("Failed to install assembly\n");
5982 /* FIXME: write Installer assembly reg values */
5986 free_assemblies(&assemblies
);
5990 static UINT
ACTION_ScheduleReboot( MSIPACKAGE
*package
)
5993 package
->need_reboot
= 1;
5994 return ERROR_SUCCESS
;
5997 static UINT
msi_unimplemented_action_stub( MSIPACKAGE
*package
,
5998 LPCSTR action
, LPCWSTR table
)
6000 static const WCHAR query
[] = {
6001 'S','E','L','E','C','T',' ','*',' ',
6002 'F','R','O','M',' ','`','%','s','`',0 };
6003 MSIQUERY
*view
= NULL
;
6007 r
= MSI_OpenQuery( package
->db
, &view
, query
, table
);
6008 if (r
== ERROR_SUCCESS
)
6010 r
= MSI_IterateRecords(view
, &count
, NULL
, package
);
6011 msiobj_release(&view
->hdr
);
6015 FIXME("%s -> %u ignored %s table values\n",
6016 action
, count
, debugstr_w(table
));
6018 return ERROR_SUCCESS
;
6021 static UINT
ACTION_AllocateRegistrySpace( MSIPACKAGE
*package
)
6023 TRACE("%p\n", package
);
6024 return ERROR_SUCCESS
;
6027 static UINT
ACTION_RemoveIniValues( MSIPACKAGE
*package
)
6029 static const WCHAR table
[] =
6030 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6031 return msi_unimplemented_action_stub( package
, "RemoveIniValues", table
);
6034 static UINT
ACTION_PatchFiles( MSIPACKAGE
*package
)
6036 static const WCHAR table
[] = { 'P','a','t','c','h',0 };
6037 return msi_unimplemented_action_stub( package
, "PatchFiles", table
);
6040 static UINT
ACTION_BindImage( MSIPACKAGE
*package
)
6042 static const WCHAR table
[] = { 'B','i','n','d','I','m','a','g','e',0 };
6043 return msi_unimplemented_action_stub( package
, "BindImage", table
);
6046 static UINT
ACTION_IsolateComponents( MSIPACKAGE
*package
)
6048 static const WCHAR table
[] = {
6049 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6050 return msi_unimplemented_action_stub( package
, "IsolateComponents", table
);
6053 static UINT
ACTION_MigrateFeatureStates( MSIPACKAGE
*package
)
6055 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
6056 return msi_unimplemented_action_stub( package
, "MigrateFeatureStates", table
);
6059 static UINT
ACTION_SelfUnregModules( MSIPACKAGE
*package
)
6061 static const WCHAR table
[] = { 'S','e','l','f','R','e','g',0 };
6062 return msi_unimplemented_action_stub( package
, "SelfUnregModules", table
);
6065 static UINT
ACTION_ValidateProductID( MSIPACKAGE
*package
)
6067 static const WCHAR table
[] = {
6068 'P','r','o','d','u','c','t','I','D',0 };
6069 return msi_unimplemented_action_stub( package
, "ValidateProductID", table
);
6072 static UINT
ACTION_RemoveEnvironmentStrings( MSIPACKAGE
*package
)
6074 static const WCHAR table
[] = {
6075 'E','n','v','i','r','o','n','m','e','n','t',0 };
6076 return msi_unimplemented_action_stub( package
, "RemoveEnvironmentStrings", table
);
6079 static UINT
ACTION_MsiUnpublishAssemblies( MSIPACKAGE
*package
)
6081 static const WCHAR table
[] = {
6082 'M','s','i','A','s','s','e','m','b','l','y',0 };
6083 return msi_unimplemented_action_stub( package
, "MsiUnpublishAssemblies", table
);
6086 static UINT
ACTION_UnregisterFonts( MSIPACKAGE
*package
)
6088 static const WCHAR table
[] = { 'F','o','n','t',0 };
6089 return msi_unimplemented_action_stub( package
, "UnregisterFonts", table
);
6092 static UINT
ACTION_RMCCPSearch( MSIPACKAGE
*package
)
6094 static const WCHAR table
[] = { 'C','C','P','S','e','a','r','c','h',0 };
6095 return msi_unimplemented_action_stub( package
, "RMCCPSearch", table
);
6098 static UINT
ACTION_RegisterComPlus( MSIPACKAGE
*package
)
6100 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
6101 return msi_unimplemented_action_stub( package
, "RegisterComPlus", table
);
6104 static UINT
ACTION_UnregisterComPlus( MSIPACKAGE
*package
)
6106 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
6107 return msi_unimplemented_action_stub( package
, "UnregisterComPlus", table
);
6110 static UINT
ACTION_InstallSFPCatalogFile( MSIPACKAGE
*package
)
6112 static const WCHAR table
[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6113 return msi_unimplemented_action_stub( package
, "InstallSFPCatalogFile", table
);
6116 static UINT
ACTION_RemoveDuplicateFiles( MSIPACKAGE
*package
)
6118 static const WCHAR table
[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6119 return msi_unimplemented_action_stub( package
, "RemoveDuplicateFiles", table
);
6122 static UINT
ACTION_RemoveExistingProducts( MSIPACKAGE
*package
)
6124 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
6125 return msi_unimplemented_action_stub( package
, "RemoveExistingProducts", table
);
6128 static UINT
ACTION_RemoveFolders( MSIPACKAGE
*package
)
6130 static const WCHAR table
[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
6131 return msi_unimplemented_action_stub( package
, "RemoveFolders", table
);
6134 static UINT
ACTION_RemoveODBC( MSIPACKAGE
*package
)
6136 static const WCHAR table
[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
6137 return msi_unimplemented_action_stub( package
, "RemoveODBC", table
);
6140 static UINT
ACTION_RemoveRegistryValues( MSIPACKAGE
*package
)
6142 static const WCHAR table
[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6143 return msi_unimplemented_action_stub( package
, "RemoveRegistryValues", table
);
6146 static UINT
ACTION_RemoveShortcuts( MSIPACKAGE
*package
)
6148 static const WCHAR table
[] = { 'S','h','o','r','t','c','u','t',0 };
6149 return msi_unimplemented_action_stub( package
, "RemoveShortcuts", table
);
6152 static UINT
ACTION_SetODBCFolders( MSIPACKAGE
*package
)
6154 static const WCHAR table
[] = { 'D','i','r','e','c','t','o','r','y',0 };
6155 return msi_unimplemented_action_stub( package
, "SetODBCFolders", table
);
6158 static UINT
ACTION_UnpublishComponents( MSIPACKAGE
*package
)
6160 static const WCHAR table
[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6161 return msi_unimplemented_action_stub( package
, "UnpublishComponents", table
);
6164 static UINT
ACTION_UnregisterClassInfo( MSIPACKAGE
*package
)
6166 static const WCHAR table
[] = { 'A','p','p','I','d',0 };
6167 return msi_unimplemented_action_stub( package
, "UnregisterClassInfo", table
);
6170 static UINT
ACTION_UnregisterExtensionInfo( MSIPACKAGE
*package
)
6172 static const WCHAR table
[] = { 'E','x','t','e','n','s','i','o','n',0 };
6173 return msi_unimplemented_action_stub( package
, "UnregisterExtensionInfo", table
);
6176 static UINT
ACTION_UnregisterMIMEInfo( MSIPACKAGE
*package
)
6178 static const WCHAR table
[] = { 'M','I','M','E',0 };
6179 return msi_unimplemented_action_stub( package
, "UnregisterMIMEInfo", table
);
6182 static UINT
ACTION_UnregisterProgIdInfo( MSIPACKAGE
*package
)
6184 static const WCHAR table
[] = { 'P','r','o','g','I','d',0 };
6185 return msi_unimplemented_action_stub( package
, "UnregisterProgIdInfo", table
);
6188 static UINT
ACTION_UnregisterTypeLibraries( MSIPACKAGE
*package
)
6190 static const WCHAR table
[] = { 'T','y','p','e','L','i','b',0 };
6191 return msi_unimplemented_action_stub( package
, "UnregisterTypeLibraries", table
);
6194 typedef UINT (*STANDARDACTIONHANDLER
)(MSIPACKAGE
*);
6198 const WCHAR
*action
;
6199 UINT (*handler
)(MSIPACKAGE
*);
6203 { szAllocateRegistrySpace
, ACTION_AllocateRegistrySpace
},
6204 { szAppSearch
, ACTION_AppSearch
},
6205 { szBindImage
, ACTION_BindImage
},
6206 { szCCPSearch
, ACTION_CCPSearch
},
6207 { szCostFinalize
, ACTION_CostFinalize
},
6208 { szCostInitialize
, ACTION_CostInitialize
},
6209 { szCreateFolders
, ACTION_CreateFolders
},
6210 { szCreateShortcuts
, ACTION_CreateShortcuts
},
6211 { szDeleteServices
, ACTION_DeleteServices
},
6212 { szDisableRollback
, NULL
},
6213 { szDuplicateFiles
, ACTION_DuplicateFiles
},
6214 { szExecuteAction
, ACTION_ExecuteAction
},
6215 { szFileCost
, ACTION_FileCost
},
6216 { szFindRelatedProducts
, ACTION_FindRelatedProducts
},
6217 { szForceReboot
, ACTION_ForceReboot
},
6218 { szInstallAdminPackage
, NULL
},
6219 { szInstallExecute
, ACTION_InstallExecute
},
6220 { szInstallExecuteAgain
, ACTION_InstallExecute
},
6221 { szInstallFiles
, ACTION_InstallFiles
},
6222 { szInstallFinalize
, ACTION_InstallFinalize
},
6223 { szInstallInitialize
, ACTION_InstallInitialize
},
6224 { szInstallSFPCatalogFile
, ACTION_InstallSFPCatalogFile
},
6225 { szInstallValidate
, ACTION_InstallValidate
},
6226 { szIsolateComponents
, ACTION_IsolateComponents
},
6227 { szLaunchConditions
, ACTION_LaunchConditions
},
6228 { szMigrateFeatureStates
, ACTION_MigrateFeatureStates
},
6229 { szMoveFiles
, ACTION_MoveFiles
},
6230 { szMsiPublishAssemblies
, ACTION_MsiPublishAssemblies
},
6231 { szMsiUnpublishAssemblies
, ACTION_MsiUnpublishAssemblies
},
6232 { szInstallODBC
, ACTION_InstallODBC
},
6233 { szInstallServices
, ACTION_InstallServices
},
6234 { szPatchFiles
, ACTION_PatchFiles
},
6235 { szProcessComponents
, ACTION_ProcessComponents
},
6236 { szPublishComponents
, ACTION_PublishComponents
},
6237 { szPublishFeatures
, ACTION_PublishFeatures
},
6238 { szPublishProduct
, ACTION_PublishProduct
},
6239 { szRegisterClassInfo
, ACTION_RegisterClassInfo
},
6240 { szRegisterComPlus
, ACTION_RegisterComPlus
},
6241 { szRegisterExtensionInfo
, ACTION_RegisterExtensionInfo
},
6242 { szRegisterFonts
, ACTION_RegisterFonts
},
6243 { szRegisterMIMEInfo
, ACTION_RegisterMIMEInfo
},
6244 { szRegisterProduct
, ACTION_RegisterProduct
},
6245 { szRegisterProgIdInfo
, ACTION_RegisterProgIdInfo
},
6246 { szRegisterTypeLibraries
, ACTION_RegisterTypeLibraries
},
6247 { szRegisterUser
, ACTION_RegisterUser
},
6248 { szRemoveDuplicateFiles
, ACTION_RemoveDuplicateFiles
},
6249 { szRemoveEnvironmentStrings
, ACTION_RemoveEnvironmentStrings
},
6250 { szRemoveExistingProducts
, ACTION_RemoveExistingProducts
},
6251 { szRemoveFiles
, ACTION_RemoveFiles
},
6252 { szRemoveFolders
, ACTION_RemoveFolders
},
6253 { szRemoveIniValues
, ACTION_RemoveIniValues
},
6254 { szRemoveODBC
, ACTION_RemoveODBC
},
6255 { szRemoveRegistryValues
, ACTION_RemoveRegistryValues
},
6256 { szRemoveShortcuts
, ACTION_RemoveShortcuts
},
6257 { szResolveSource
, ACTION_ResolveSource
},
6258 { szRMCCPSearch
, ACTION_RMCCPSearch
},
6259 { szScheduleReboot
, ACTION_ScheduleReboot
},
6260 { szSelfRegModules
, ACTION_SelfRegModules
},
6261 { szSelfUnregModules
, ACTION_SelfUnregModules
},
6262 { szSetODBCFolders
, ACTION_SetODBCFolders
},
6263 { szStartServices
, ACTION_StartServices
},
6264 { szStopServices
, ACTION_StopServices
},
6265 { szUnpublishComponents
, ACTION_UnpublishComponents
},
6266 { szUnpublishFeatures
, ACTION_UnpublishFeatures
},
6267 { szUnregisterClassInfo
, ACTION_UnregisterClassInfo
},
6268 { szUnregisterComPlus
, ACTION_UnregisterComPlus
},
6269 { szUnregisterExtensionInfo
, ACTION_UnregisterExtensionInfo
},
6270 { szUnregisterFonts
, ACTION_UnregisterFonts
},
6271 { szUnregisterMIMEInfo
, ACTION_UnregisterMIMEInfo
},
6272 { szUnregisterProgIdInfo
, ACTION_UnregisterProgIdInfo
},
6273 { szUnregisterTypeLibraries
, ACTION_UnregisterTypeLibraries
},
6274 { szValidateProductID
, ACTION_ValidateProductID
},
6275 { szWriteEnvironmentStrings
, ACTION_WriteEnvironmentStrings
},
6276 { szWriteIniValues
, ACTION_WriteIniValues
},
6277 { szWriteRegistryValues
, ACTION_WriteRegistryValues
},
6281 static BOOL
ACTION_HandleStandardAction(MSIPACKAGE
*package
, LPCWSTR action
,
6282 UINT
* rc
, BOOL force
)
6288 if (!run
&& !package
->script
->CurrentlyScripting
)
6293 if (strcmpW(action
,szInstallFinalize
) == 0 ||
6294 strcmpW(action
,szInstallExecute
) == 0 ||
6295 strcmpW(action
,szInstallExecuteAgain
) == 0)
6300 while (StandardActions
[i
].action
!= NULL
)
6302 if (strcmpW(StandardActions
[i
].action
, action
)==0)
6306 ui_actioninfo(package
, action
, TRUE
, 0);
6307 *rc
= schedule_action(package
,INSTALL_SCRIPT
,action
);
6308 ui_actioninfo(package
, action
, FALSE
, *rc
);
6312 ui_actionstart(package
, action
);
6313 if (StandardActions
[i
].handler
)
6315 *rc
= StandardActions
[i
].handler(package
);
6319 FIXME("unhandled standard action %s\n",debugstr_w(action
));
6320 *rc
= ERROR_SUCCESS
;
6331 UINT
ACTION_PerformAction(MSIPACKAGE
*package
, const WCHAR
*action
, UINT script
, BOOL force
)
6333 UINT rc
= ERROR_SUCCESS
;
6336 TRACE("Performing action (%s)\n", debugstr_w(action
));
6338 handled
= ACTION_HandleStandardAction(package
, action
, &rc
, force
);
6341 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, script
, force
);
6345 WARN("unhandled msi action %s\n", debugstr_w(action
));
6346 rc
= ERROR_FUNCTION_NOT_CALLED
;
6352 UINT
ACTION_PerformUIAction(MSIPACKAGE
*package
, const WCHAR
*action
, UINT script
)
6354 UINT rc
= ERROR_SUCCESS
;
6355 BOOL handled
= FALSE
;
6357 TRACE("Performing action (%s)\n", debugstr_w(action
));
6359 handled
= ACTION_HandleStandardAction(package
, action
, &rc
,TRUE
);
6362 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, script
, FALSE
);
6364 if( !handled
&& ACTION_DialogBox(package
, action
) == ERROR_SUCCESS
)
6369 WARN("unhandled msi action %s\n", debugstr_w(action
));
6370 rc
= ERROR_FUNCTION_NOT_CALLED
;
6376 static UINT
ACTION_PerformActionSequence(MSIPACKAGE
*package
, UINT seq
)
6378 UINT rc
= ERROR_SUCCESS
;
6381 static const WCHAR ExecSeqQuery
[] =
6382 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6383 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
6384 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
6385 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
6386 static const WCHAR UISeqQuery
[] =
6387 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6388 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
6389 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
6390 ' ', '=',' ','%','i',0};
6392 if (needs_ui_sequence(package
))
6393 row
= MSI_QueryGetRecord(package
->db
, UISeqQuery
, seq
);
6395 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
, seq
);
6399 LPCWSTR action
, cond
;
6401 TRACE("Running the actions\n");
6403 /* check conditions */
6404 cond
= MSI_RecordGetString(row
, 2);
6406 /* this is a hack to skip errors in the condition code */
6407 if (MSI_EvaluateConditionW(package
, cond
) == MSICONDITION_FALSE
)
6409 msiobj_release(&row
->hdr
);
6410 return ERROR_SUCCESS
;
6413 action
= MSI_RecordGetString(row
, 1);
6416 ERR("failed to fetch action\n");
6417 msiobj_release(&row
->hdr
);
6418 return ERROR_FUNCTION_FAILED
;
6421 if (needs_ui_sequence(package
))
6422 rc
= ACTION_PerformUIAction(package
, action
, -1);
6424 rc
= ACTION_PerformAction(package
, action
, -1, FALSE
);
6426 msiobj_release(&row
->hdr
);
6432 /****************************************************
6433 * TOP level entry points
6434 *****************************************************/
6436 UINT
MSI_InstallPackage( MSIPACKAGE
*package
, LPCWSTR szPackagePath
,
6437 LPCWSTR szCommandLine
)
6442 static const WCHAR szAction
[] = {'A','C','T','I','O','N',0};
6443 static const WCHAR szInstall
[] = {'I','N','S','T','A','L','L',0};
6445 MSI_SetPropertyW(package
, szAction
, szInstall
);
6447 package
->script
->InWhatSequence
= SEQUENCE_INSTALL
;
6454 dir
= strdupW(szPackagePath
);
6455 p
= strrchrW(dir
, '\\');
6459 file
= szPackagePath
+ (p
- dir
);
6464 dir
= msi_alloc(MAX_PATH
* sizeof(WCHAR
));
6465 GetCurrentDirectoryW(MAX_PATH
, dir
);
6466 lstrcatW(dir
, szBackSlash
);
6467 file
= szPackagePath
;
6470 msi_free( package
->PackagePath
);
6471 package
->PackagePath
= msi_alloc((lstrlenW(dir
) + lstrlenW(file
) + 1) * sizeof(WCHAR
));
6472 if (!package
->PackagePath
)
6475 return ERROR_OUTOFMEMORY
;
6478 lstrcpyW(package
->PackagePath
, dir
);
6479 lstrcatW(package
->PackagePath
, file
);
6482 msi_set_sourcedir_props(package
, FALSE
);
6485 msi_parse_command_line( package
, szCommandLine
, FALSE
);
6487 msi_apply_transforms( package
);
6488 msi_apply_patches( package
);
6490 if (!szCommandLine
&& msi_get_property_int( package
, szInstalled
, 0 ))
6492 TRACE("setting reinstall property\n");
6493 MSI_SetPropertyW( package
, szReinstall
, szAll
);
6496 /* properties may have been added by a transform */
6497 msi_clone_properties( package
);
6498 msi_set_context( package
);
6500 if (needs_ui_sequence( package
))
6502 package
->script
->InWhatSequence
|= SEQUENCE_UI
;
6503 rc
= ACTION_ProcessUISequence(package
);
6504 ui_exists
= ui_sequence_exists(package
);
6505 if (rc
== ERROR_SUCCESS
|| !ui_exists
)
6507 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
6508 rc
= ACTION_ProcessExecSequence(package
, ui_exists
);
6512 rc
= ACTION_ProcessExecSequence(package
, FALSE
);
6514 package
->script
->CurrentlyScripting
= FALSE
;
6516 /* process the ending type action */
6517 if (rc
== ERROR_SUCCESS
)
6518 ACTION_PerformActionSequence(package
, -1);
6519 else if (rc
== ERROR_INSTALL_USEREXIT
)
6520 ACTION_PerformActionSequence(package
, -2);
6521 else if (rc
== ERROR_INSTALL_SUSPEND
)
6522 ACTION_PerformActionSequence(package
, -4);
6524 ACTION_PerformActionSequence(package
, -3);
6526 /* finish up running custom actions */
6527 ACTION_FinishCustomActions(package
);
6529 if (rc
== ERROR_SUCCESS
&& package
->need_reboot
)
6530 return ERROR_SUCCESS_REBOOT_REQUIRED
;