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
ITERATE_RemoveFolders( MSIRECORD
*row
, LPVOID param
)
985 MSIPACKAGE
*package
= param
;
991 dir
= MSI_RecordGetString( row
, 1 );
994 ERR("Unable to get folder id\n");
995 return ERROR_SUCCESS
;
998 full_path
= resolve_folder( package
, dir
, FALSE
, FALSE
, TRUE
, &folder
);
1001 ERR("Unable to resolve folder id %s\n", debugstr_w(dir
));
1002 return ERROR_SUCCESS
;
1005 TRACE("folder is %s\n", debugstr_w(full_path
));
1007 uirow
= MSI_CreateRecord( 1 );
1008 MSI_RecordSetStringW( uirow
, 1, full_path
);
1009 ui_actiondata( package
, szRemoveFolders
, uirow
);
1010 msiobj_release( &uirow
->hdr
);
1012 RemoveDirectoryW( full_path
);
1015 msi_free( full_path
);
1016 return ERROR_SUCCESS
;
1019 static UINT
ACTION_RemoveFolders( MSIPACKAGE
*package
)
1021 static const WCHAR query
[] =
1022 {'S','E','L','E','C','T',' ', '`','D','i','r','e','c','t','o','r','y','_','`',
1023 ' ','F','R','O','M',' ', '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1028 rc
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1029 if (rc
!= ERROR_SUCCESS
)
1030 return ERROR_SUCCESS
;
1032 rc
= MSI_IterateRecords( view
, NULL
, ITERATE_RemoveFolders
, package
);
1033 msiobj_release( &view
->hdr
);
1038 static UINT
load_component( MSIRECORD
*row
, LPVOID param
)
1040 MSIPACKAGE
*package
= param
;
1043 comp
= msi_alloc_zero( sizeof(MSICOMPONENT
) );
1045 return ERROR_FUNCTION_FAILED
;
1047 list_add_tail( &package
->components
, &comp
->entry
);
1049 /* fill in the data */
1050 comp
->Component
= msi_dup_record_field( row
, 1 );
1052 TRACE("Loading Component %s\n", debugstr_w(comp
->Component
));
1054 comp
->ComponentId
= msi_dup_record_field( row
, 2 );
1055 comp
->Directory
= msi_dup_record_field( row
, 3 );
1056 comp
->Attributes
= MSI_RecordGetInteger(row
,4);
1057 comp
->Condition
= msi_dup_record_field( row
, 5 );
1058 comp
->KeyPath
= msi_dup_record_field( row
, 6 );
1060 comp
->Installed
= INSTALLSTATE_UNKNOWN
;
1061 msi_component_set_state(package
, comp
, INSTALLSTATE_UNKNOWN
);
1063 return ERROR_SUCCESS
;
1066 static UINT
load_all_components( MSIPACKAGE
*package
)
1068 static const WCHAR query
[] = {
1069 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1070 '`','C','o','m','p','o','n','e','n','t','`',0 };
1074 if (!list_empty(&package
->components
))
1075 return ERROR_SUCCESS
;
1077 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1078 if (r
!= ERROR_SUCCESS
)
1081 r
= MSI_IterateRecords(view
, NULL
, load_component
, package
);
1082 msiobj_release(&view
->hdr
);
1087 MSIPACKAGE
*package
;
1088 MSIFEATURE
*feature
;
1091 static UINT
add_feature_component( MSIFEATURE
*feature
, MSICOMPONENT
*comp
)
1095 cl
= msi_alloc( sizeof (*cl
) );
1097 return ERROR_NOT_ENOUGH_MEMORY
;
1098 cl
->component
= comp
;
1099 list_add_tail( &feature
->Components
, &cl
->entry
);
1101 return ERROR_SUCCESS
;
1104 static UINT
add_feature_child( MSIFEATURE
*parent
, MSIFEATURE
*child
)
1108 fl
= msi_alloc( sizeof(*fl
) );
1110 return ERROR_NOT_ENOUGH_MEMORY
;
1111 fl
->feature
= child
;
1112 list_add_tail( &parent
->Children
, &fl
->entry
);
1114 return ERROR_SUCCESS
;
1117 static UINT
iterate_load_featurecomponents(MSIRECORD
*row
, LPVOID param
)
1119 _ilfs
* ilfs
= param
;
1123 component
= MSI_RecordGetString(row
,1);
1125 /* check to see if the component is already loaded */
1126 comp
= get_loaded_component( ilfs
->package
, component
);
1129 ERR("unknown component %s\n", debugstr_w(component
));
1130 return ERROR_FUNCTION_FAILED
;
1133 add_feature_component( ilfs
->feature
, comp
);
1134 comp
->Enabled
= TRUE
;
1136 return ERROR_SUCCESS
;
1139 static MSIFEATURE
*find_feature_by_name( MSIPACKAGE
*package
, LPCWSTR name
)
1141 MSIFEATURE
*feature
;
1146 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1148 if ( !lstrcmpW( feature
->Feature
, name
) )
1155 static UINT
load_feature(MSIRECORD
* row
, LPVOID param
)
1157 MSIPACKAGE
* package
= param
;
1158 MSIFEATURE
* feature
;
1159 static const WCHAR Query1
[] =
1160 {'S','E','L','E','C','T',' ',
1161 '`','C','o','m','p','o','n','e','n','t','_','`',
1162 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1163 'C','o','m','p','o','n','e','n','t','s','`',' ',
1164 'W','H','E','R','E',' ',
1165 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1170 /* fill in the data */
1172 feature
= msi_alloc_zero( sizeof (MSIFEATURE
) );
1174 return ERROR_NOT_ENOUGH_MEMORY
;
1176 list_init( &feature
->Children
);
1177 list_init( &feature
->Components
);
1179 feature
->Feature
= msi_dup_record_field( row
, 1 );
1181 TRACE("Loading feature %s\n",debugstr_w(feature
->Feature
));
1183 feature
->Feature_Parent
= msi_dup_record_field( row
, 2 );
1184 feature
->Title
= msi_dup_record_field( row
, 3 );
1185 feature
->Description
= msi_dup_record_field( row
, 4 );
1187 if (!MSI_RecordIsNull(row
,5))
1188 feature
->Display
= MSI_RecordGetInteger(row
,5);
1190 feature
->Level
= MSI_RecordGetInteger(row
,6);
1191 feature
->Directory
= msi_dup_record_field( row
, 7 );
1192 feature
->Attributes
= MSI_RecordGetInteger(row
,8);
1194 feature
->Installed
= INSTALLSTATE_UNKNOWN
;
1195 msi_feature_set_state(package
, feature
, INSTALLSTATE_UNKNOWN
);
1197 list_add_tail( &package
->features
, &feature
->entry
);
1199 /* load feature components */
1201 rc
= MSI_OpenQuery( package
->db
, &view
, Query1
, feature
->Feature
);
1202 if (rc
!= ERROR_SUCCESS
)
1203 return ERROR_SUCCESS
;
1205 ilfs
.package
= package
;
1206 ilfs
.feature
= feature
;
1208 MSI_IterateRecords(view
, NULL
, iterate_load_featurecomponents
, &ilfs
);
1209 msiobj_release(&view
->hdr
);
1211 return ERROR_SUCCESS
;
1214 static UINT
find_feature_children(MSIRECORD
* row
, LPVOID param
)
1216 MSIPACKAGE
* package
= param
;
1217 MSIFEATURE
*parent
, *child
;
1219 child
= find_feature_by_name( package
, MSI_RecordGetString( row
, 1 ) );
1221 return ERROR_FUNCTION_FAILED
;
1223 if (!child
->Feature_Parent
)
1224 return ERROR_SUCCESS
;
1226 parent
= find_feature_by_name( package
, child
->Feature_Parent
);
1228 return ERROR_FUNCTION_FAILED
;
1230 add_feature_child( parent
, child
);
1231 return ERROR_SUCCESS
;
1234 static UINT
load_all_features( MSIPACKAGE
*package
)
1236 static const WCHAR query
[] = {
1237 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1238 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1239 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1243 if (!list_empty(&package
->features
))
1244 return ERROR_SUCCESS
;
1246 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1247 if (r
!= ERROR_SUCCESS
)
1250 r
= MSI_IterateRecords( view
, NULL
, load_feature
, package
);
1251 if (r
!= ERROR_SUCCESS
)
1254 r
= MSI_IterateRecords( view
, NULL
, find_feature_children
, package
);
1255 msiobj_release( &view
->hdr
);
1260 static LPWSTR
folder_split_path(LPWSTR p
, WCHAR ch
)
1271 static UINT
load_file_hash(MSIPACKAGE
*package
, MSIFILE
*file
)
1273 static const WCHAR query
[] = {
1274 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1275 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1276 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1277 MSIQUERY
*view
= NULL
;
1278 MSIRECORD
*row
= NULL
;
1281 TRACE("%s\n", debugstr_w(file
->File
));
1283 r
= MSI_OpenQuery(package
->db
, &view
, query
, file
->File
);
1284 if (r
!= ERROR_SUCCESS
)
1287 r
= MSI_ViewExecute(view
, NULL
);
1288 if (r
!= ERROR_SUCCESS
)
1291 r
= MSI_ViewFetch(view
, &row
);
1292 if (r
!= ERROR_SUCCESS
)
1295 file
->hash
.dwFileHashInfoSize
= sizeof(MSIFILEHASHINFO
);
1296 file
->hash
.dwData
[0] = MSI_RecordGetInteger(row
, 3);
1297 file
->hash
.dwData
[1] = MSI_RecordGetInteger(row
, 4);
1298 file
->hash
.dwData
[2] = MSI_RecordGetInteger(row
, 5);
1299 file
->hash
.dwData
[3] = MSI_RecordGetInteger(row
, 6);
1302 if (view
) msiobj_release(&view
->hdr
);
1303 if (row
) msiobj_release(&row
->hdr
);
1307 static UINT
load_file(MSIRECORD
*row
, LPVOID param
)
1309 MSIPACKAGE
* package
= param
;
1313 /* fill in the data */
1315 file
= msi_alloc_zero( sizeof (MSIFILE
) );
1317 return ERROR_NOT_ENOUGH_MEMORY
;
1319 file
->File
= msi_dup_record_field( row
, 1 );
1321 component
= MSI_RecordGetString( row
, 2 );
1322 file
->Component
= get_loaded_component( package
, component
);
1324 if (!file
->Component
)
1326 WARN("Component not found: %s\n", debugstr_w(component
));
1327 msi_free(file
->File
);
1329 return ERROR_SUCCESS
;
1332 file
->FileName
= msi_dup_record_field( row
, 3 );
1333 reduce_to_longfilename( file
->FileName
);
1335 file
->ShortName
= msi_dup_record_field( row
, 3 );
1336 file
->LongName
= strdupW( folder_split_path(file
->ShortName
, '|'));
1338 file
->FileSize
= MSI_RecordGetInteger( row
, 4 );
1339 file
->Version
= msi_dup_record_field( row
, 5 );
1340 file
->Language
= msi_dup_record_field( row
, 6 );
1341 file
->Attributes
= MSI_RecordGetInteger( row
, 7 );
1342 file
->Sequence
= MSI_RecordGetInteger( row
, 8 );
1344 file
->state
= msifs_invalid
;
1346 /* if the compressed bits are not set in the file attributes,
1347 * then read the information from the package word count property
1349 if (package
->WordCount
& msidbSumInfoSourceTypeAdminImage
)
1351 file
->IsCompressed
= FALSE
;
1353 else if (file
->Attributes
&
1354 (msidbFileAttributesCompressed
| msidbFileAttributesPatchAdded
))
1356 file
->IsCompressed
= TRUE
;
1358 else if (file
->Attributes
& msidbFileAttributesNoncompressed
)
1360 file
->IsCompressed
= FALSE
;
1364 file
->IsCompressed
= package
->WordCount
& msidbSumInfoSourceTypeCompressed
;
1367 load_file_hash(package
, file
);
1369 TRACE("File Loaded (%s)\n",debugstr_w(file
->File
));
1371 list_add_tail( &package
->files
, &file
->entry
);
1373 return ERROR_SUCCESS
;
1376 static UINT
load_all_files(MSIPACKAGE
*package
)
1380 static const WCHAR Query
[] =
1381 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1382 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1383 '`','S','e','q','u','e','n','c','e','`', 0};
1385 if (!list_empty(&package
->files
))
1386 return ERROR_SUCCESS
;
1388 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
1389 if (rc
!= ERROR_SUCCESS
)
1390 return ERROR_SUCCESS
;
1392 rc
= MSI_IterateRecords(view
, NULL
, load_file
, package
);
1393 msiobj_release(&view
->hdr
);
1395 return ERROR_SUCCESS
;
1398 static UINT
load_folder( MSIRECORD
*row
, LPVOID param
)
1400 MSIPACKAGE
*package
= param
;
1401 static WCHAR szEmpty
[] = { 0 };
1402 LPWSTR p
, tgt_short
, tgt_long
, src_short
, src_long
;
1405 folder
= msi_alloc_zero( sizeof (MSIFOLDER
) );
1407 return ERROR_NOT_ENOUGH_MEMORY
;
1409 folder
->Directory
= msi_dup_record_field( row
, 1 );
1411 TRACE("%s\n", debugstr_w(folder
->Directory
));
1413 p
= msi_dup_record_field(row
, 3);
1415 /* split src and target dir */
1417 src_short
= folder_split_path( p
, ':' );
1419 /* split the long and short paths */
1420 tgt_long
= folder_split_path( tgt_short
, '|' );
1421 src_long
= folder_split_path( src_short
, '|' );
1423 /* check for no-op dirs */
1424 if (!lstrcmpW(szDot
, tgt_short
))
1425 tgt_short
= szEmpty
;
1426 if (!lstrcmpW(szDot
, src_short
))
1427 src_short
= szEmpty
;
1430 tgt_long
= tgt_short
;
1433 src_short
= tgt_short
;
1434 src_long
= tgt_long
;
1438 src_long
= src_short
;
1440 /* FIXME: use the target short path too */
1441 folder
->TargetDefault
= strdupW(tgt_long
);
1442 folder
->SourceShortPath
= strdupW(src_short
);
1443 folder
->SourceLongPath
= strdupW(src_long
);
1446 TRACE("TargetDefault = %s\n",debugstr_w( folder
->TargetDefault
));
1447 TRACE("SourceLong = %s\n", debugstr_w( folder
->SourceLongPath
));
1448 TRACE("SourceShort = %s\n", debugstr_w( folder
->SourceShortPath
));
1450 folder
->Parent
= msi_dup_record_field( row
, 2 );
1452 folder
->Property
= msi_dup_property( package
, folder
->Directory
);
1454 list_add_tail( &package
->folders
, &folder
->entry
);
1456 TRACE("returning %p\n", folder
);
1458 return ERROR_SUCCESS
;
1461 static UINT
load_all_folders( MSIPACKAGE
*package
)
1463 static const WCHAR query
[] = {
1464 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1465 '`','D','i','r','e','c','t','o','r','y','`',0 };
1469 if (!list_empty(&package
->folders
))
1470 return ERROR_SUCCESS
;
1472 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1473 if (r
!= ERROR_SUCCESS
)
1476 r
= MSI_IterateRecords(view
, NULL
, load_folder
, package
);
1477 msiobj_release(&view
->hdr
);
1482 * I am not doing any of the costing functionality yet.
1483 * Mostly looking at doing the Component and Feature loading
1485 * The native MSI does A LOT of modification to tables here. Mostly adding
1486 * a lot of temporary columns to the Feature and Component tables.
1488 * note: Native msi also tracks the short filename. But I am only going to
1489 * track the long ones. Also looking at this directory table
1490 * it appears that the directory table does not get the parents
1491 * resolved base on property only based on their entries in the
1494 static UINT
ACTION_CostInitialize(MSIPACKAGE
*package
)
1496 static const WCHAR szCosting
[] =
1497 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1499 MSI_SetPropertyW(package
, szCosting
, szZero
);
1500 MSI_SetPropertyW(package
, cszRootDrive
, c_colon
);
1502 load_all_folders( package
);
1503 load_all_components( package
);
1504 load_all_features( package
);
1505 load_all_files( package
);
1507 return ERROR_SUCCESS
;
1510 static UINT
execute_script(MSIPACKAGE
*package
, UINT script
)
1513 UINT rc
= ERROR_SUCCESS
;
1515 TRACE("Executing Script %i\n",script
);
1517 if (!package
->script
)
1519 ERR("no script!\n");
1520 return ERROR_FUNCTION_FAILED
;
1523 for (i
= 0; i
< package
->script
->ActionCount
[script
]; i
++)
1526 action
= package
->script
->Actions
[script
][i
];
1527 ui_actionstart(package
, action
);
1528 TRACE("Executing Action (%s)\n",debugstr_w(action
));
1529 rc
= ACTION_PerformAction(package
, action
, script
, TRUE
);
1530 if (rc
!= ERROR_SUCCESS
)
1533 msi_free_action_script(package
, script
);
1537 static UINT
ACTION_FileCost(MSIPACKAGE
*package
)
1539 return ERROR_SUCCESS
;
1542 static void ACTION_GetComponentInstallStates(MSIPACKAGE
*package
)
1548 state
= MsiQueryProductStateW(package
->ProductCode
);
1550 LIST_FOR_EACH_ENTRY(comp
, &package
->components
, MSICOMPONENT
, entry
)
1552 if (!comp
->ComponentId
)
1555 if (state
!= INSTALLSTATE_LOCAL
&& state
!= INSTALLSTATE_DEFAULT
)
1556 comp
->Installed
= INSTALLSTATE_ABSENT
;
1559 r
= MsiQueryComponentStateW(package
->ProductCode
, NULL
,
1560 package
->Context
, comp
->ComponentId
,
1562 if (r
!= ERROR_SUCCESS
)
1563 comp
->Installed
= INSTALLSTATE_ABSENT
;
1568 static void ACTION_GetFeatureInstallStates(MSIPACKAGE
*package
)
1570 MSIFEATURE
*feature
;
1573 state
= MsiQueryProductStateW(package
->ProductCode
);
1575 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1577 if (state
!= INSTALLSTATE_LOCAL
&& state
!= INSTALLSTATE_DEFAULT
)
1578 feature
->Installed
= INSTALLSTATE_ABSENT
;
1581 feature
->Installed
= MsiQueryFeatureStateW(package
->ProductCode
,
1587 static BOOL
process_state_property(MSIPACKAGE
* package
, int level
,
1588 LPCWSTR property
, INSTALLSTATE state
)
1591 MSIFEATURE
*feature
;
1593 override
= msi_dup_property( package
, property
);
1597 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1599 if (lstrcmpW(property
, szRemove
) &&
1600 (feature
->Level
<= 0 || feature
->Level
> level
))
1603 if (!strcmpW(property
, szReinstall
)) state
= feature
->Installed
;
1605 if (strcmpiW(override
, szAll
)==0)
1606 msi_feature_set_state(package
, feature
, state
);
1609 LPWSTR ptr
= override
;
1610 LPWSTR ptr2
= strchrW(override
,',');
1614 int len
= ptr2
- ptr
;
1616 if ((ptr2
&& strlenW(feature
->Feature
) == len
&& !strncmpW(ptr
, feature
->Feature
, len
))
1617 || (!ptr2
&& !strcmpW(ptr
, feature
->Feature
)))
1619 msi_feature_set_state(package
, feature
, state
);
1625 ptr2
= strchrW(ptr
,',');
1637 static BOOL
process_overrides( MSIPACKAGE
*package
, int level
)
1639 static const WCHAR szAddLocal
[] =
1640 {'A','D','D','L','O','C','A','L',0};
1641 static const WCHAR szAddSource
[] =
1642 {'A','D','D','S','O','U','R','C','E',0};
1643 static const WCHAR szAdvertise
[] =
1644 {'A','D','V','E','R','T','I','S','E',0};
1647 /* all these activation/deactivation things happen in order and things
1648 * later on the list override things earlier on the list.
1650 * 0 INSTALLLEVEL processing
1663 ret
|= process_state_property( package
, level
, szAddLocal
, INSTALLSTATE_LOCAL
);
1664 ret
|= process_state_property( package
, level
, szRemove
, INSTALLSTATE_ABSENT
);
1665 ret
|= process_state_property( package
, level
, szAddSource
, INSTALLSTATE_SOURCE
);
1666 ret
|= process_state_property( package
, level
, szReinstall
, INSTALLSTATE_UNKNOWN
);
1667 ret
|= process_state_property( package
, level
, szAdvertise
, INSTALLSTATE_ADVERTISED
);
1670 MSI_SetPropertyW( package
, szPreselected
, szOne
);
1675 UINT
MSI_SetFeatureStates(MSIPACKAGE
*package
)
1678 static const WCHAR szlevel
[] =
1679 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1680 MSICOMPONENT
* component
;
1681 MSIFEATURE
*feature
;
1683 TRACE("Checking Install Level\n");
1685 level
= msi_get_property_int(package
, szlevel
, 1);
1687 if (!msi_get_property_int( package
, szPreselected
, 0 ))
1689 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1691 BOOL feature_state
= ((feature
->Level
> 0) &&
1692 (feature
->Level
<= level
));
1694 if ((feature_state
) && (feature
->Action
== INSTALLSTATE_UNKNOWN
))
1696 if (feature
->Attributes
& msidbFeatureAttributesFavorSource
)
1697 msi_feature_set_state(package
, feature
, INSTALLSTATE_SOURCE
);
1698 else if (feature
->Attributes
& msidbFeatureAttributesFavorAdvertise
)
1699 msi_feature_set_state(package
, feature
, INSTALLSTATE_ADVERTISED
);
1701 msi_feature_set_state(package
, feature
, INSTALLSTATE_LOCAL
);
1705 /* disable child features of unselected parent features */
1706 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1710 if (feature
->Level
> 0 && feature
->Level
<= level
)
1713 LIST_FOR_EACH_ENTRY( fl
, &feature
->Children
, FeatureList
, entry
)
1714 msi_feature_set_state(package
, fl
->feature
, INSTALLSTATE_UNKNOWN
);
1719 * now we want to enable or disable components base on feature
1722 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1726 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1727 debugstr_w(feature
->Feature
), feature
->Level
, feature
->Installed
, feature
->Action
);
1729 if (!feature
->Level
)
1732 /* features with components that have compressed files are made local */
1733 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1735 if (cl
->component
->Enabled
&&
1736 cl
->component
->ForceLocalState
&&
1737 feature
->Action
== INSTALLSTATE_SOURCE
)
1739 msi_feature_set_state(package
, feature
, INSTALLSTATE_LOCAL
);
1744 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1746 component
= cl
->component
;
1748 if (!component
->Enabled
)
1751 switch (feature
->Action
)
1753 case INSTALLSTATE_ABSENT
:
1754 component
->anyAbsent
= 1;
1756 case INSTALLSTATE_ADVERTISED
:
1757 component
->hasAdvertiseFeature
= 1;
1759 case INSTALLSTATE_SOURCE
:
1760 component
->hasSourceFeature
= 1;
1762 case INSTALLSTATE_LOCAL
:
1763 component
->hasLocalFeature
= 1;
1765 case INSTALLSTATE_DEFAULT
:
1766 if (feature
->Attributes
& msidbFeatureAttributesFavorAdvertise
)
1767 component
->hasAdvertiseFeature
= 1;
1768 else if (feature
->Attributes
& msidbFeatureAttributesFavorSource
)
1769 component
->hasSourceFeature
= 1;
1771 component
->hasLocalFeature
= 1;
1779 LIST_FOR_EACH_ENTRY( component
, &package
->components
, MSICOMPONENT
, entry
)
1781 /* if the component isn't enabled, leave it alone */
1782 if (!component
->Enabled
)
1785 /* check if it's local or source */
1786 if (!(component
->Attributes
& msidbComponentAttributesOptional
) &&
1787 (component
->hasLocalFeature
|| component
->hasSourceFeature
))
1789 if ((component
->Attributes
& msidbComponentAttributesSourceOnly
) &&
1790 !component
->ForceLocalState
)
1791 msi_component_set_state(package
, component
, INSTALLSTATE_SOURCE
);
1793 msi_component_set_state(package
, component
, INSTALLSTATE_LOCAL
);
1797 /* if any feature is local, the component must be local too */
1798 if (component
->hasLocalFeature
)
1800 msi_component_set_state(package
, component
, INSTALLSTATE_LOCAL
);
1804 if (component
->hasSourceFeature
)
1806 msi_component_set_state(package
, component
, INSTALLSTATE_SOURCE
);
1810 if (component
->hasAdvertiseFeature
)
1812 msi_component_set_state(package
, component
, INSTALLSTATE_ADVERTISED
);
1816 TRACE("nobody wants component %s\n", debugstr_w(component
->Component
));
1817 if (component
->anyAbsent
)
1818 msi_component_set_state(package
, component
, INSTALLSTATE_ABSENT
);
1821 LIST_FOR_EACH_ENTRY( component
, &package
->components
, MSICOMPONENT
, entry
)
1823 if (component
->Action
== INSTALLSTATE_DEFAULT
)
1825 TRACE("%s was default, setting to local\n", debugstr_w(component
->Component
));
1826 msi_component_set_state(package
, component
, INSTALLSTATE_LOCAL
);
1829 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1830 debugstr_w(component
->Component
), component
->Installed
, component
->Action
);
1834 return ERROR_SUCCESS
;
1837 static UINT
ITERATE_CostFinalizeDirectories(MSIRECORD
*row
, LPVOID param
)
1839 MSIPACKAGE
*package
= param
;
1844 name
= MSI_RecordGetString(row
,1);
1846 f
= get_loaded_folder(package
, name
);
1847 if (!f
) return ERROR_SUCCESS
;
1849 /* reset the ResolvedTarget */
1850 msi_free(f
->ResolvedTarget
);
1851 f
->ResolvedTarget
= NULL
;
1853 /* This helper function now does ALL the work */
1854 TRACE("Dir %s ...\n",debugstr_w(name
));
1855 path
= resolve_folder(package
,name
,FALSE
,TRUE
,TRUE
,NULL
);
1856 TRACE("resolves to %s\n",debugstr_w(path
));
1859 return ERROR_SUCCESS
;
1862 static UINT
ITERATE_CostFinalizeConditions(MSIRECORD
*row
, LPVOID param
)
1864 MSIPACKAGE
*package
= param
;
1866 MSIFEATURE
*feature
;
1868 name
= MSI_RecordGetString( row
, 1 );
1870 feature
= get_loaded_feature( package
, name
);
1872 ERR("FAILED to find loaded feature %s\n",debugstr_w(name
));
1876 Condition
= MSI_RecordGetString(row
,3);
1878 if (MSI_EvaluateConditionW(package
,Condition
) == MSICONDITION_TRUE
)
1880 int level
= MSI_RecordGetInteger(row
,2);
1881 TRACE("Resetting feature %s to level %i\n", debugstr_w(name
), level
);
1882 feature
->Level
= level
;
1885 return ERROR_SUCCESS
;
1888 static LPWSTR
msi_get_disk_file_version( LPCWSTR filename
)
1890 static const WCHAR name_fmt
[] =
1891 {'%','u','.','%','u','.','%','u','.','%','u',0};
1892 static const WCHAR name
[] = {'\\',0};
1893 VS_FIXEDFILEINFO
*lpVer
;
1894 WCHAR filever
[0x100];
1900 TRACE("%s\n", debugstr_w(filename
));
1902 versize
= GetFileVersionInfoSizeW( filename
, &handle
);
1906 version
= msi_alloc( versize
);
1907 GetFileVersionInfoW( filename
, 0, versize
, version
);
1909 if (!VerQueryValueW( version
, name
, (LPVOID
*)&lpVer
, &sz
))
1911 msi_free( version
);
1915 sprintfW( filever
, name_fmt
,
1916 HIWORD(lpVer
->dwFileVersionMS
),
1917 LOWORD(lpVer
->dwFileVersionMS
),
1918 HIWORD(lpVer
->dwFileVersionLS
),
1919 LOWORD(lpVer
->dwFileVersionLS
));
1921 msi_free( version
);
1923 return strdupW( filever
);
1926 static UINT
msi_check_file_install_states( MSIPACKAGE
*package
)
1928 LPWSTR file_version
;
1931 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
1933 MSICOMPONENT
* comp
= file
->Component
;
1939 if (file
->IsCompressed
)
1940 comp
->ForceLocalState
= TRUE
;
1942 /* calculate target */
1943 p
= resolve_folder(package
, comp
->Directory
, FALSE
, FALSE
, TRUE
, NULL
);
1945 msi_free(file
->TargetPath
);
1947 TRACE("file %s is named %s\n",
1948 debugstr_w(file
->File
), debugstr_w(file
->FileName
));
1950 file
->TargetPath
= build_directory_name(2, p
, file
->FileName
);
1954 TRACE("file %s resolves to %s\n",
1955 debugstr_w(file
->File
), debugstr_w(file
->TargetPath
));
1957 /* don't check files of components that aren't installed */
1958 if (comp
->Installed
== INSTALLSTATE_UNKNOWN
||
1959 comp
->Installed
== INSTALLSTATE_ABSENT
)
1961 file
->state
= msifs_missing
; /* assume files are missing */
1965 if (GetFileAttributesW(file
->TargetPath
) == INVALID_FILE_ATTRIBUTES
)
1967 file
->state
= msifs_missing
;
1968 comp
->Cost
+= file
->FileSize
;
1972 if (file
->Version
&&
1973 (file_version
= msi_get_disk_file_version( file
->TargetPath
)))
1975 TRACE("new %s old %s\n", debugstr_w(file
->Version
),
1976 debugstr_w(file_version
));
1977 /* FIXME: seems like a bad way to compare version numbers */
1978 if (lstrcmpiW(file_version
, file
->Version
)<0)
1980 file
->state
= msifs_overwrite
;
1981 comp
->Cost
+= file
->FileSize
;
1984 file
->state
= msifs_present
;
1985 msi_free( file_version
);
1988 file
->state
= msifs_present
;
1991 return ERROR_SUCCESS
;
1995 * A lot is done in this function aside from just the costing.
1996 * The costing needs to be implemented at some point but for now I am going
1997 * to focus on the directory building
2000 static UINT
ACTION_CostFinalize(MSIPACKAGE
*package
)
2002 static const WCHAR ExecSeqQuery
[] =
2003 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2004 '`','D','i','r','e','c','t','o','r','y','`',0};
2005 static const WCHAR ConditionQuery
[] =
2006 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2007 '`','C','o','n','d','i','t','i','o','n','`',0};
2008 static const WCHAR szCosting
[] =
2009 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2010 static const WCHAR szlevel
[] =
2011 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2012 static const WCHAR szOutOfDiskSpace
[] =
2013 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2015 UINT rc
= ERROR_SUCCESS
;
2019 TRACE("Building Directory properties\n");
2021 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2022 if (rc
== ERROR_SUCCESS
)
2024 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CostFinalizeDirectories
,
2026 msiobj_release(&view
->hdr
);
2029 /* read components states from the registry */
2030 ACTION_GetComponentInstallStates(package
);
2031 ACTION_GetFeatureInstallStates(package
);
2033 TRACE("File calculations\n");
2034 msi_check_file_install_states( package
);
2036 if (!process_overrides( package
, msi_get_property_int( package
, szlevel
, 1 ) ))
2038 TRACE("Evaluating Condition Table\n");
2040 rc
= MSI_DatabaseOpenViewW( package
->db
, ConditionQuery
, &view
);
2041 if (rc
== ERROR_SUCCESS
)
2043 rc
= MSI_IterateRecords( view
, NULL
, ITERATE_CostFinalizeConditions
, package
);
2044 msiobj_release( &view
->hdr
);
2047 TRACE("Enabling or Disabling Components\n");
2048 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2050 if (MSI_EvaluateConditionW( package
, comp
->Condition
) == MSICONDITION_FALSE
)
2052 TRACE("Disabling component %s\n", debugstr_w(comp
->Component
));
2053 comp
->Enabled
= FALSE
;
2056 comp
->Enabled
= TRUE
;
2060 MSI_SetPropertyW(package
,szCosting
,szOne
);
2061 /* set default run level if not set */
2062 level
= msi_dup_property( package
, szlevel
);
2064 MSI_SetPropertyW(package
,szlevel
, szOne
);
2067 /* FIXME: check volume disk space */
2068 MSI_SetPropertyW(package
, szOutOfDiskSpace
, szZero
);
2070 return MSI_SetFeatureStates(package
);
2073 /* OK this value is "interpreted" and then formatted based on the
2074 first few characters */
2075 static LPSTR
parse_value(MSIPACKAGE
*package
, LPCWSTR value
, DWORD
*type
,
2080 if (value
[0]=='#' && value
[1]!='#' && value
[1]!='%')
2086 LPWSTR deformated
= NULL
;
2089 deformat_string(package
, &value
[2], &deformated
);
2091 /* binary value type */
2095 *size
= (strlenW(ptr
)/2)+1;
2097 *size
= strlenW(ptr
)/2;
2099 data
= msi_alloc(*size
);
2105 /* if uneven pad with a zero in front */
2111 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2113 TRACE("Uneven byte count\n");
2121 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2124 msi_free(deformated
);
2126 TRACE("Data %i bytes(%i)\n",*size
,count
);
2133 deformat_string(package
, &value
[1], &deformated
);
2136 *size
= sizeof(DWORD
);
2137 data
= msi_alloc(*size
);
2143 if ( (*p
< '0') || (*p
> '9') )
2149 if (deformated
[0] == '-')
2152 TRACE("DWORD %i\n",*(LPDWORD
)data
);
2154 msi_free(deformated
);
2159 static const WCHAR szMulti
[] = {'[','~',']',0};
2168 *type
=REG_EXPAND_SZ
;
2176 if (strstrW(value
,szMulti
))
2177 *type
= REG_MULTI_SZ
;
2179 /* remove initial delimiter */
2180 if (!strncmpW(value
, szMulti
, 3))
2183 *size
= deformat_string(package
, ptr
,(LPWSTR
*)&data
);
2185 /* add double NULL terminator */
2186 if (*type
== REG_MULTI_SZ
)
2188 *size
+= 2 * sizeof(WCHAR
); /* two NULL terminators */
2189 data
= msi_realloc_zero(data
, *size
);
2195 static UINT
ITERATE_WriteRegistryValues(MSIRECORD
*row
, LPVOID param
)
2197 MSIPACKAGE
*package
= param
;
2198 static const WCHAR szHCR
[] =
2199 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2200 'R','O','O','T','\\',0};
2201 static const WCHAR szHCU
[] =
2202 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2203 'U','S','E','R','\\',0};
2204 static const WCHAR szHLM
[] =
2205 {'H','K','E','Y','_','L','O','C','A','L','_',
2206 'M','A','C','H','I','N','E','\\',0};
2207 static const WCHAR szHU
[] =
2208 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2210 LPSTR value_data
= NULL
;
2211 HKEY root_key
, hkey
;
2214 LPCWSTR szRoot
, component
, name
, key
, value
;
2219 BOOL check_first
= FALSE
;
2222 ui_progress(package
,2,0,0,0);
2229 component
= MSI_RecordGetString(row
, 6);
2230 comp
= get_loaded_component(package
,component
);
2232 return ERROR_SUCCESS
;
2234 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2236 TRACE("Skipping write due to disabled component %s\n",
2237 debugstr_w(component
));
2239 comp
->Action
= comp
->Installed
;
2241 return ERROR_SUCCESS
;
2244 comp
->Action
= INSTALLSTATE_LOCAL
;
2246 name
= MSI_RecordGetString(row
, 4);
2247 if( MSI_RecordIsNull(row
,5) && name
)
2249 /* null values can have special meanings */
2250 if (name
[0]=='-' && name
[1] == 0)
2251 return ERROR_SUCCESS
;
2252 else if ((name
[0]=='+' && name
[1] == 0) ||
2253 (name
[0] == '*' && name
[1] == 0))
2258 root
= MSI_RecordGetInteger(row
,2);
2259 key
= MSI_RecordGetString(row
, 3);
2261 /* get the root key */
2266 LPWSTR all_users
= msi_dup_property( package
, szAllUsers
);
2267 if (all_users
&& all_users
[0] == '1')
2269 root_key
= HKEY_LOCAL_MACHINE
;
2274 root_key
= HKEY_CURRENT_USER
;
2277 msi_free(all_users
);
2280 case 0: root_key
= HKEY_CLASSES_ROOT
;
2283 case 1: root_key
= HKEY_CURRENT_USER
;
2286 case 2: root_key
= HKEY_LOCAL_MACHINE
;
2289 case 3: root_key
= HKEY_USERS
;
2293 ERR("Unknown root %i\n",root
);
2299 return ERROR_SUCCESS
;
2301 deformat_string(package
, key
, &deformated
);
2302 size
= strlenW(deformated
) + strlenW(szRoot
) + 1;
2303 uikey
= msi_alloc(size
*sizeof(WCHAR
));
2304 strcpyW(uikey
,szRoot
);
2305 strcatW(uikey
,deformated
);
2307 if (RegCreateKeyW( root_key
, deformated
, &hkey
))
2309 ERR("Could not create key %s\n",debugstr_w(deformated
));
2310 msi_free(deformated
);
2312 return ERROR_SUCCESS
;
2314 msi_free(deformated
);
2316 value
= MSI_RecordGetString(row
,5);
2318 value_data
= parse_value(package
, value
, &type
, &size
);
2321 value_data
= (LPSTR
)strdupW(szEmpty
);
2322 size
= sizeof(szEmpty
);
2326 deformat_string(package
, name
, &deformated
);
2330 TRACE("Setting value %s of %s\n",debugstr_w(deformated
),
2332 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
)value_data
, size
);
2337 rc
= RegQueryValueExW(hkey
, deformated
, NULL
, NULL
, NULL
, &sz
);
2338 if (rc
== ERROR_SUCCESS
|| rc
== ERROR_MORE_DATA
)
2340 TRACE("value %s of %s checked already exists\n",
2341 debugstr_w(deformated
), debugstr_w(uikey
));
2345 TRACE("Checked and setting value %s of %s\n",
2346 debugstr_w(deformated
), debugstr_w(uikey
));
2347 if (deformated
|| size
)
2348 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
) value_data
, size
);
2353 uirow
= MSI_CreateRecord(3);
2354 MSI_RecordSetStringW(uirow
,2,deformated
);
2355 MSI_RecordSetStringW(uirow
,1,uikey
);
2358 MSI_RecordSetStringW(uirow
,3,(LPWSTR
)value_data
);
2360 MSI_RecordSetStringW(uirow
,3,value
);
2362 ui_actiondata(package
,szWriteRegistryValues
,uirow
);
2363 msiobj_release( &uirow
->hdr
);
2365 msi_free(value_data
);
2366 msi_free(deformated
);
2369 return ERROR_SUCCESS
;
2372 static UINT
ACTION_WriteRegistryValues(MSIPACKAGE
*package
)
2376 static const WCHAR ExecSeqQuery
[] =
2377 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2378 '`','R','e','g','i','s','t','r','y','`',0 };
2380 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2381 if (rc
!= ERROR_SUCCESS
)
2382 return ERROR_SUCCESS
;
2384 /* increment progress bar each time action data is sent */
2385 ui_progress(package
,1,REG_PROGRESS_VALUE
,1,0);
2387 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteRegistryValues
, package
);
2389 msiobj_release(&view
->hdr
);
2393 static UINT
ACTION_InstallInitialize(MSIPACKAGE
*package
)
2395 package
->script
->CurrentlyScripting
= TRUE
;
2397 return ERROR_SUCCESS
;
2401 static UINT
ACTION_InstallValidate(MSIPACKAGE
*package
)
2406 static const WCHAR q1
[]=
2407 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2408 '`','R','e','g','i','s','t','r','y','`',0};
2411 MSIFEATURE
*feature
;
2414 TRACE("InstallValidate\n");
2416 rc
= MSI_DatabaseOpenViewW(package
->db
, q1
, &view
);
2417 if (rc
== ERROR_SUCCESS
)
2419 MSI_IterateRecords( view
, &progress
, NULL
, package
);
2420 msiobj_release( &view
->hdr
);
2421 total
+= progress
* REG_PROGRESS_VALUE
;
2424 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2425 total
+= COMPONENT_PROGRESS_VALUE
;
2427 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2428 total
+= file
->FileSize
;
2430 ui_progress(package
,0,total
,0,0);
2432 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2434 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2435 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
,
2436 feature
->ActionRequest
);
2439 return ERROR_SUCCESS
;
2442 static UINT
ITERATE_LaunchConditions(MSIRECORD
*row
, LPVOID param
)
2444 MSIPACKAGE
* package
= param
;
2445 LPCWSTR cond
= NULL
;
2446 LPCWSTR message
= NULL
;
2449 static const WCHAR title
[]=
2450 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2452 cond
= MSI_RecordGetString(row
,1);
2454 r
= MSI_EvaluateConditionW(package
,cond
);
2455 if (r
== MSICONDITION_FALSE
)
2457 if ((gUILevel
& INSTALLUILEVEL_MASK
) != INSTALLUILEVEL_NONE
)
2460 message
= MSI_RecordGetString(row
,2);
2461 deformat_string(package
,message
,&deformated
);
2462 MessageBoxW(NULL
,deformated
,title
,MB_OK
);
2463 msi_free(deformated
);
2466 return ERROR_INSTALL_FAILURE
;
2469 return ERROR_SUCCESS
;
2472 static UINT
ACTION_LaunchConditions(MSIPACKAGE
*package
)
2475 MSIQUERY
* view
= NULL
;
2476 static const WCHAR ExecSeqQuery
[] =
2477 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2478 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2480 TRACE("Checking launch conditions\n");
2482 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2483 if (rc
!= ERROR_SUCCESS
)
2484 return ERROR_SUCCESS
;
2486 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_LaunchConditions
, package
);
2487 msiobj_release(&view
->hdr
);
2492 static LPWSTR
resolve_keypath( MSIPACKAGE
* package
, MSICOMPONENT
*cmp
)
2496 return resolve_folder(package
,cmp
->Directory
,FALSE
,FALSE
,TRUE
,NULL
);
2498 if (cmp
->Attributes
& msidbComponentAttributesRegistryKeyPath
)
2500 MSIRECORD
* row
= 0;
2502 LPWSTR deformated
,buffer
,deformated_name
;
2504 static const WCHAR ExecSeqQuery
[] =
2505 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2506 '`','R','e','g','i','s','t','r','y','`',' ',
2507 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2508 ' ','=',' ' ,'\'','%','s','\'',0 };
2509 static const WCHAR fmt
[]={'%','0','2','i',':','\\','%','s','\\',0};
2510 static const WCHAR fmt2
[]=
2511 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2513 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
,cmp
->KeyPath
);
2517 root
= MSI_RecordGetInteger(row
,2);
2518 key
= MSI_RecordGetString(row
, 3);
2519 name
= MSI_RecordGetString(row
, 4);
2520 deformat_string(package
, key
, &deformated
);
2521 deformat_string(package
, name
, &deformated_name
);
2523 len
= strlenW(deformated
) + 6;
2524 if (deformated_name
)
2525 len
+=strlenW(deformated_name
);
2527 buffer
= msi_alloc( len
*sizeof(WCHAR
));
2529 if (deformated_name
)
2530 sprintfW(buffer
,fmt2
,root
,deformated
,deformated_name
);
2532 sprintfW(buffer
,fmt
,root
,deformated
);
2534 msi_free(deformated
);
2535 msi_free(deformated_name
);
2536 msiobj_release(&row
->hdr
);
2540 else if (cmp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2542 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2547 MSIFILE
*file
= get_loaded_file( package
, cmp
->KeyPath
);
2550 return strdupW( file
->TargetPath
);
2555 static HKEY
openSharedDLLsKey(void)
2558 static const WCHAR path
[] =
2559 {'S','o','f','t','w','a','r','e','\\',
2560 'M','i','c','r','o','s','o','f','t','\\',
2561 'W','i','n','d','o','w','s','\\',
2562 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2563 'S','h','a','r','e','d','D','L','L','s',0};
2565 RegCreateKeyW(HKEY_LOCAL_MACHINE
,path
,&hkey
);
2569 static UINT
ACTION_GetSharedDLLsCount(LPCWSTR dll
)
2574 DWORD sz
= sizeof(count
);
2577 hkey
= openSharedDLLsKey();
2578 rc
= RegQueryValueExW(hkey
, dll
, NULL
, &type
, (LPBYTE
)&count
, &sz
);
2579 if (rc
!= ERROR_SUCCESS
)
2585 static UINT
ACTION_WriteSharedDLLsCount(LPCWSTR path
, UINT count
)
2589 hkey
= openSharedDLLsKey();
2591 msi_reg_set_val_dword( hkey
, path
, count
);
2593 RegDeleteValueW(hkey
,path
);
2599 * Return TRUE if the count should be written out and FALSE if not
2601 static void ACTION_RefCountComponent( MSIPACKAGE
* package
, MSICOMPONENT
*comp
)
2603 MSIFEATURE
*feature
;
2607 /* only refcount DLLs */
2608 if (comp
->KeyPath
== NULL
||
2609 comp
->Attributes
& msidbComponentAttributesRegistryKeyPath
||
2610 comp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2614 count
= ACTION_GetSharedDLLsCount( comp
->FullKeypath
);
2615 write
= (count
> 0);
2617 if (comp
->Attributes
& msidbComponentAttributesSharedDllRefCount
)
2621 /* increment counts */
2622 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2626 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
))
2629 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2631 if ( cl
->component
== comp
)
2636 /* decrement counts */
2637 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2641 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ABSENT
))
2644 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2646 if ( cl
->component
== comp
)
2651 /* ref count all the files in the component */
2656 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2658 if (file
->Component
== comp
)
2659 ACTION_WriteSharedDLLsCount( file
->TargetPath
, count
);
2663 /* add a count for permanent */
2664 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2667 comp
->RefCount
= count
;
2670 ACTION_WriteSharedDLLsCount( comp
->FullKeypath
, comp
->RefCount
);
2673 static UINT
ACTION_ProcessComponents(MSIPACKAGE
*package
)
2675 WCHAR squished_pc
[GUID_SIZE
];
2676 WCHAR squished_cc
[GUID_SIZE
];
2683 squash_guid(package
->ProductCode
,squished_pc
);
2684 ui_progress(package
,1,COMPONENT_PROGRESS_VALUE
,1,0);
2686 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2690 ui_progress(package
,2,0,0,0);
2691 if (!comp
->ComponentId
)
2694 squash_guid(comp
->ComponentId
,squished_cc
);
2696 msi_free(comp
->FullKeypath
);
2697 comp
->FullKeypath
= resolve_keypath( package
, comp
);
2699 ACTION_RefCountComponent( package
, comp
);
2701 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2702 debugstr_w(comp
->Component
),
2703 debugstr_w(squished_cc
),
2704 debugstr_w(comp
->FullKeypath
),
2707 if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
) ||
2708 ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_SOURCE
))
2710 if (!comp
->FullKeypath
)
2713 if (package
->Context
== MSIINSTALLCONTEXT_MACHINE
)
2714 rc
= MSIREG_OpenUserDataComponentKey(comp
->ComponentId
, szLocalSid
,
2717 rc
= MSIREG_OpenUserDataComponentKey(comp
->ComponentId
, NULL
,
2720 if (rc
!= ERROR_SUCCESS
)
2723 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2725 static const WCHAR szPermKey
[] =
2726 { '0','0','0','0','0','0','0','0','0','0','0','0',
2727 '0','0','0','0','0','0','0','0','0','0','0','0',
2728 '0','0','0','0','0','0','0','0',0 };
2730 msi_reg_set_val_str(hkey
, szPermKey
, comp
->FullKeypath
);
2733 if (comp
->Action
== INSTALLSTATE_LOCAL
)
2734 msi_reg_set_val_str(hkey
, squished_pc
, comp
->FullKeypath
);
2740 WCHAR source
[MAX_PATH
];
2741 WCHAR base
[MAX_PATH
];
2744 static const WCHAR fmt
[] = {'%','0','2','d','\\',0};
2745 static const WCHAR query
[] = {
2746 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2747 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2748 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2749 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2750 '`','D','i','s','k','I','d','`',0};
2752 file
= get_loaded_file(package
, comp
->KeyPath
);
2756 row
= MSI_QueryGetRecord(package
->db
, query
, file
->Sequence
);
2757 sprintfW(source
, fmt
, MSI_RecordGetInteger(row
, 1));
2758 ptr2
= strrchrW(source
, '\\') + 1;
2759 msiobj_release(&row
->hdr
);
2761 lstrcpyW(base
, package
->PackagePath
);
2762 ptr
= strrchrW(base
, '\\');
2765 sourcepath
= resolve_file_source(package
, file
);
2766 ptr
= sourcepath
+ lstrlenW(base
);
2767 lstrcpyW(ptr2
, ptr
);
2768 msi_free(sourcepath
);
2770 msi_reg_set_val_str(hkey
, squished_pc
, source
);
2774 else if (ACTION_VerifyComponentForAction(comp
, INSTALLSTATE_ABSENT
))
2776 if (package
->Context
== MSIINSTALLCONTEXT_MACHINE
)
2777 MSIREG_DeleteUserDataComponentKey(comp
->ComponentId
, szLocalSid
);
2779 MSIREG_DeleteUserDataComponentKey(comp
->ComponentId
, NULL
);
2783 uirow
= MSI_CreateRecord(3);
2784 MSI_RecordSetStringW(uirow
,1,package
->ProductCode
);
2785 MSI_RecordSetStringW(uirow
,2,comp
->ComponentId
);
2786 MSI_RecordSetStringW(uirow
,3,comp
->FullKeypath
);
2787 ui_actiondata(package
,szProcessComponents
,uirow
);
2788 msiobj_release( &uirow
->hdr
);
2791 return ERROR_SUCCESS
;
2802 static BOOL CALLBACK
Typelib_EnumResNameProc( HMODULE hModule
, LPCWSTR lpszType
,
2803 LPWSTR lpszName
, LONG_PTR lParam
)
2806 typelib_struct
*tl_struct
= (typelib_struct
*) lParam
;
2807 static const WCHAR fmt
[] = {'%','s','\\','%','i',0};
2811 if (!IS_INTRESOURCE(lpszName
))
2813 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName
));
2817 sz
= strlenW(tl_struct
->source
)+4;
2818 sz
*= sizeof(WCHAR
);
2820 if ((INT_PTR
)lpszName
== 1)
2821 tl_struct
->path
= strdupW(tl_struct
->source
);
2824 tl_struct
->path
= msi_alloc(sz
);
2825 sprintfW(tl_struct
->path
,fmt
,tl_struct
->source
, lpszName
);
2828 TRACE("trying %s\n", debugstr_w(tl_struct
->path
));
2829 res
= LoadTypeLib(tl_struct
->path
,&tl_struct
->ptLib
);
2832 msi_free(tl_struct
->path
);
2833 tl_struct
->path
= NULL
;
2838 ITypeLib_GetLibAttr(tl_struct
->ptLib
, &attr
);
2839 if (IsEqualGUID(&(tl_struct
->clsid
),&(attr
->guid
)))
2841 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2845 msi_free(tl_struct
->path
);
2846 tl_struct
->path
= NULL
;
2848 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2849 ITypeLib_Release(tl_struct
->ptLib
);
2854 static UINT
ITERATE_RegisterTypeLibraries(MSIRECORD
*row
, LPVOID param
)
2856 MSIPACKAGE
* package
= param
;
2860 typelib_struct tl_struct
;
2865 static const WCHAR szTYPELIB
[] = {'T','Y','P','E','L','I','B',0};
2867 component
= MSI_RecordGetString(row
,3);
2868 comp
= get_loaded_component(package
,component
);
2870 return ERROR_SUCCESS
;
2872 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2874 TRACE("Skipping typelib reg due to disabled component\n");
2876 comp
->Action
= comp
->Installed
;
2878 return ERROR_SUCCESS
;
2881 comp
->Action
= INSTALLSTATE_LOCAL
;
2883 file
= get_loaded_file( package
, comp
->KeyPath
);
2885 return ERROR_SUCCESS
;
2887 module
= LoadLibraryExW( file
->TargetPath
, NULL
, LOAD_LIBRARY_AS_DATAFILE
);
2891 guid
= MSI_RecordGetString(row
,1);
2892 CLSIDFromString((LPWSTR
)guid
, &tl_struct
.clsid
);
2893 tl_struct
.source
= strdupW( file
->TargetPath
);
2894 tl_struct
.path
= NULL
;
2896 EnumResourceNamesW(module
, szTYPELIB
, Typelib_EnumResNameProc
,
2897 (LONG_PTR
)&tl_struct
);
2905 helpid
= MSI_RecordGetString(row
,6);
2908 help
= resolve_folder(package
,helpid
,FALSE
,FALSE
,TRUE
,NULL
);
2909 res
= RegisterTypeLib(tl_struct
.ptLib
,tl_struct
.path
,help
);
2913 ERR("Failed to register type library %s\n",
2914 debugstr_w(tl_struct
.path
));
2917 ui_actiondata(package
,szRegisterTypeLibraries
,row
);
2919 TRACE("Registered %s\n", debugstr_w(tl_struct
.path
));
2922 ITypeLib_Release(tl_struct
.ptLib
);
2923 msi_free(tl_struct
.path
);
2926 ERR("Failed to load type library %s\n",
2927 debugstr_w(tl_struct
.source
));
2929 FreeLibrary(module
);
2930 msi_free(tl_struct
.source
);
2934 hr
= LoadTypeLibEx(file
->TargetPath
, REGKIND_REGISTER
, &tlib
);
2937 ERR("Failed to load type library: %08x\n", hr
);
2938 return ERROR_FUNCTION_FAILED
;
2941 ITypeLib_Release(tlib
);
2944 return ERROR_SUCCESS
;
2947 static UINT
ACTION_RegisterTypeLibraries(MSIPACKAGE
*package
)
2950 * OK this is a bit confusing.. I am given a _Component key and I believe
2951 * that the file that is being registered as a type library is the "key file
2952 * of that component" which I interpret to mean "The file in the KeyPath of
2957 static const WCHAR Query
[] =
2958 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2959 '`','T','y','p','e','L','i','b','`',0};
2961 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
2962 if (rc
!= ERROR_SUCCESS
)
2963 return ERROR_SUCCESS
;
2965 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_RegisterTypeLibraries
, package
);
2966 msiobj_release(&view
->hdr
);
2970 static UINT
ITERATE_CreateShortcuts(MSIRECORD
*row
, LPVOID param
)
2972 MSIPACKAGE
*package
= param
;
2973 LPWSTR target_file
, target_folder
, filename
;
2974 LPCWSTR buffer
, extension
;
2976 static const WCHAR szlnk
[]={'.','l','n','k',0};
2977 IShellLinkW
*sl
= NULL
;
2978 IPersistFile
*pf
= NULL
;
2981 buffer
= MSI_RecordGetString(row
,4);
2982 comp
= get_loaded_component(package
,buffer
);
2984 return ERROR_SUCCESS
;
2986 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2988 TRACE("Skipping shortcut creation due to disabled component\n");
2990 comp
->Action
= comp
->Installed
;
2992 return ERROR_SUCCESS
;
2995 comp
->Action
= INSTALLSTATE_LOCAL
;
2997 ui_actiondata(package
,szCreateShortcuts
,row
);
2999 res
= CoCreateInstance( &CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
,
3000 &IID_IShellLinkW
, (LPVOID
*) &sl
);
3004 ERR("CLSID_ShellLink not available\n");
3008 res
= IShellLinkW_QueryInterface( sl
, &IID_IPersistFile
,(LPVOID
*) &pf
);
3011 ERR("QueryInterface(IID_IPersistFile) failed\n");
3015 buffer
= MSI_RecordGetString(row
,2);
3016 target_folder
= resolve_folder(package
, buffer
,FALSE
,FALSE
,TRUE
,NULL
);
3018 /* may be needed because of a bug somewhere else */
3019 create_full_pathW(target_folder
);
3021 filename
= msi_dup_record_field( row
, 3 );
3022 reduce_to_longfilename(filename
);
3024 extension
= strchrW(filename
,'.');
3025 if (!extension
|| strcmpiW(extension
,szlnk
))
3027 int len
= strlenW(filename
);
3028 filename
= msi_realloc(filename
, len
* sizeof(WCHAR
) + sizeof(szlnk
));
3029 memcpy(filename
+ len
, szlnk
, sizeof(szlnk
));
3031 target_file
= build_directory_name(2, target_folder
, filename
);
3032 msi_free(target_folder
);
3035 buffer
= MSI_RecordGetString(row
,5);
3036 if (strchrW(buffer
,'['))
3039 deformat_string(package
,buffer
,&deformated
);
3040 IShellLinkW_SetPath(sl
,deformated
);
3041 msi_free(deformated
);
3045 FIXME("poorly handled shortcut format, advertised shortcut\n");
3046 IShellLinkW_SetPath(sl
,comp
->FullKeypath
);
3049 if (!MSI_RecordIsNull(row
,6))
3052 buffer
= MSI_RecordGetString(row
,6);
3053 deformat_string(package
,buffer
,&deformated
);
3054 IShellLinkW_SetArguments(sl
,deformated
);
3055 msi_free(deformated
);
3058 if (!MSI_RecordIsNull(row
,7))
3060 buffer
= MSI_RecordGetString(row
,7);
3061 IShellLinkW_SetDescription(sl
,buffer
);
3064 if (!MSI_RecordIsNull(row
,8))
3065 IShellLinkW_SetHotkey(sl
,MSI_RecordGetInteger(row
,8));
3067 if (!MSI_RecordIsNull(row
,9))
3072 buffer
= MSI_RecordGetString(row
,9);
3074 Path
= build_icon_path(package
,buffer
);
3075 index
= MSI_RecordGetInteger(row
,10);
3077 /* no value means 0 */
3078 if (index
== MSI_NULL_INTEGER
)
3081 IShellLinkW_SetIconLocation(sl
,Path
,index
);
3085 if (!MSI_RecordIsNull(row
,11))
3086 IShellLinkW_SetShowCmd(sl
,MSI_RecordGetInteger(row
,11));
3088 if (!MSI_RecordIsNull(row
,12))
3091 buffer
= MSI_RecordGetString(row
,12);
3092 Path
= resolve_folder(package
, buffer
, FALSE
, FALSE
, TRUE
, NULL
);
3094 IShellLinkW_SetWorkingDirectory(sl
,Path
);
3098 TRACE("Writing shortcut to %s\n",debugstr_w(target_file
));
3099 IPersistFile_Save(pf
,target_file
,FALSE
);
3101 msi_free(target_file
);
3105 IPersistFile_Release( pf
);
3107 IShellLinkW_Release( sl
);
3109 return ERROR_SUCCESS
;
3112 static UINT
ACTION_CreateShortcuts(MSIPACKAGE
*package
)
3117 static const WCHAR Query
[] =
3118 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3119 '`','S','h','o','r','t','c','u','t','`',0};
3121 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3122 if (rc
!= ERROR_SUCCESS
)
3123 return ERROR_SUCCESS
;
3125 res
= CoInitialize( NULL
);
3127 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateShortcuts
, package
);
3128 msiobj_release(&view
->hdr
);
3136 static UINT
ITERATE_PublishIcon(MSIRECORD
*row
, LPVOID param
)
3138 MSIPACKAGE
* package
= param
;
3147 FileName
= MSI_RecordGetString(row
,1);
3150 ERR("Unable to get FileName\n");
3151 return ERROR_SUCCESS
;
3154 FilePath
= build_icon_path(package
,FileName
);
3156 TRACE("Creating icon file at %s\n",debugstr_w(FilePath
));
3158 the_file
= CreateFileW(FilePath
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
,
3159 FILE_ATTRIBUTE_NORMAL
, NULL
);
3161 if (the_file
== INVALID_HANDLE_VALUE
)
3163 ERR("Unable to create file %s\n",debugstr_w(FilePath
));
3165 return ERROR_SUCCESS
;
3172 rc
= MSI_RecordReadStream(row
,2,buffer
,&sz
);
3173 if (rc
!= ERROR_SUCCESS
)
3175 ERR("Failed to get stream\n");
3176 CloseHandle(the_file
);
3177 DeleteFileW(FilePath
);
3180 WriteFile(the_file
,buffer
,sz
,&write
,NULL
);
3181 } while (sz
== 1024);
3185 CloseHandle(the_file
);
3187 uirow
= MSI_CreateRecord(1);
3188 MSI_RecordSetStringW(uirow
,1,FileName
);
3189 ui_actiondata(package
,szPublishProduct
,uirow
);
3190 msiobj_release( &uirow
->hdr
);
3192 return ERROR_SUCCESS
;
3195 static UINT
msi_publish_icons(MSIPACKAGE
*package
)
3200 static const WCHAR query
[]= {
3201 'S','E','L','E','C','T',' ','*',' ',
3202 'F','R','O','M',' ','`','I','c','o','n','`',0};
3204 r
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
3205 if (r
== ERROR_SUCCESS
)
3207 MSI_IterateRecords(view
, NULL
, ITERATE_PublishIcon
, package
);
3208 msiobj_release(&view
->hdr
);
3211 return ERROR_SUCCESS
;
3214 static UINT
msi_publish_sourcelist(MSIPACKAGE
*package
, HKEY hkey
)
3220 MSISOURCELISTINFO
*info
;
3222 r
= RegCreateKeyW(hkey
, szSourceList
, &source
);
3223 if (r
!= ERROR_SUCCESS
)
3226 RegCloseKey(source
);
3228 buffer
= strrchrW(package
->PackagePath
, '\\') + 1;
3229 r
= MsiSourceListSetInfoW(package
->ProductCode
, NULL
,
3230 package
->Context
, MSICODE_PRODUCT
,
3231 INSTALLPROPERTY_PACKAGENAMEW
, buffer
);
3232 if (r
!= ERROR_SUCCESS
)
3235 r
= MsiSourceListSetInfoW(package
->ProductCode
, NULL
,
3236 package
->Context
, MSICODE_PRODUCT
,
3237 INSTALLPROPERTY_MEDIAPACKAGEPATHW
, szEmpty
);
3238 if (r
!= ERROR_SUCCESS
)
3241 r
= MsiSourceListSetInfoW(package
->ProductCode
, NULL
,
3242 package
->Context
, MSICODE_PRODUCT
,
3243 INSTALLPROPERTY_DISKPROMPTW
, szEmpty
);
3244 if (r
!= ERROR_SUCCESS
)
3247 LIST_FOR_EACH_ENTRY(info
, &package
->sourcelist_info
, MSISOURCELISTINFO
, entry
)
3249 if (!lstrcmpW(info
->property
, INSTALLPROPERTY_LASTUSEDSOURCEW
))
3250 msi_set_last_used_source(package
->ProductCode
, NULL
, info
->context
,
3251 info
->options
, info
->value
);
3253 MsiSourceListSetInfoW(package
->ProductCode
, NULL
,
3254 info
->context
, info
->options
,
3255 info
->property
, info
->value
);
3258 LIST_FOR_EACH_ENTRY(disk
, &package
->sourcelist_media
, MSIMEDIADISK
, entry
)
3260 MsiSourceListAddMediaDiskW(package
->ProductCode
, NULL
,
3261 disk
->context
, disk
->options
,
3262 disk
->disk_id
, disk
->volume_label
, disk
->disk_prompt
);
3265 return ERROR_SUCCESS
;
3268 static UINT
msi_publish_product_properties(MSIPACKAGE
*package
, HKEY hkey
)
3270 MSIHANDLE hdb
, suminfo
;
3271 WCHAR guids
[MAX_PATH
];
3272 WCHAR packcode
[SQUISH_GUID_SIZE
];
3279 static const WCHAR szProductLanguage
[] =
3280 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3281 static const WCHAR szARPProductIcon
[] =
3282 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3283 static const WCHAR szProductVersion
[] =
3284 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3285 static const WCHAR szAssignment
[] =
3286 {'A','s','s','i','g','n','m','e','n','t',0};
3287 static const WCHAR szAdvertiseFlags
[] =
3288 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3289 static const WCHAR szClients
[] =
3290 {'C','l','i','e','n','t','s',0};
3291 static const WCHAR szColon
[] = {':',0};
3293 buffer
= msi_dup_property(package
, INSTALLPROPERTY_PRODUCTNAMEW
);
3294 msi_reg_set_val_str(hkey
, INSTALLPROPERTY_PRODUCTNAMEW
, buffer
);
3297 langid
= msi_get_property_int(package
, szProductLanguage
, 0);
3298 msi_reg_set_val_dword(hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
3301 msi_reg_set_val_dword(hkey
, INSTALLPROPERTY_AUTHORIZED_LUA_APPW
, 0);
3303 buffer
= msi_dup_property(package
, szARPProductIcon
);
3306 LPWSTR path
= build_icon_path(package
,buffer
);
3307 msi_reg_set_val_str(hkey
, INSTALLPROPERTY_PRODUCTICONW
, path
);
3312 buffer
= msi_dup_property(package
, szProductVersion
);
3315 DWORD verdword
= msi_version_str_to_dword(buffer
);
3316 msi_reg_set_val_dword(hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
3320 msi_reg_set_val_dword(hkey
, szAssignment
, 0);
3321 msi_reg_set_val_dword(hkey
, szAdvertiseFlags
, 0x184);
3322 msi_reg_set_val_dword(hkey
, INSTALLPROPERTY_INSTANCETYPEW
, 0);
3323 msi_reg_set_val_str(hkey
, szClients
, szColon
);
3325 hdb
= alloc_msihandle(&package
->db
->hdr
);
3327 return ERROR_NOT_ENOUGH_MEMORY
;
3329 r
= MsiGetSummaryInformationW(hdb
, NULL
, 0, &suminfo
);
3330 MsiCloseHandle(hdb
);
3331 if (r
!= ERROR_SUCCESS
)
3335 r
= MsiSummaryInfoGetPropertyW(suminfo
, PID_REVNUMBER
, NULL
, NULL
,
3336 NULL
, guids
, &size
);
3337 if (r
!= ERROR_SUCCESS
)
3340 ptr
= strchrW(guids
, ';');
3342 squash_guid(guids
, packcode
);
3343 msi_reg_set_val_str(hkey
, INSTALLPROPERTY_PACKAGECODEW
, packcode
);
3346 MsiCloseHandle(suminfo
);
3347 return ERROR_SUCCESS
;
3350 static UINT
msi_publish_upgrade_code(MSIPACKAGE
*package
)
3355 WCHAR squashed_pc
[SQUISH_GUID_SIZE
];
3357 static const WCHAR szUpgradeCode
[] =
3358 {'U','p','g','r','a','d','e','C','o','d','e',0};
3360 upgrade
= msi_dup_property(package
, szUpgradeCode
);
3362 return ERROR_SUCCESS
;
3364 if (package
->Context
== MSIINSTALLCONTEXT_MACHINE
)
3366 r
= MSIREG_OpenClassesUpgradeCodesKey(upgrade
, &hkey
, TRUE
);
3367 if (r
!= ERROR_SUCCESS
)
3372 r
= MSIREG_OpenUserUpgradeCodesKey(upgrade
, &hkey
, TRUE
);
3373 if (r
!= ERROR_SUCCESS
)
3377 squash_guid(package
->ProductCode
, squashed_pc
);
3378 msi_reg_set_val_str(hkey
, squashed_pc
, NULL
);
3387 static BOOL
msi_check_publish(MSIPACKAGE
*package
)
3389 MSIFEATURE
*feature
;
3391 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3393 if (feature
->ActionRequest
== INSTALLSTATE_LOCAL
)
3400 static BOOL
msi_check_unpublish(MSIPACKAGE
*package
)
3402 MSIFEATURE
*feature
;
3404 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3406 if (feature
->ActionRequest
!= INSTALLSTATE_ABSENT
)
3413 static UINT
msi_publish_patch(MSIPACKAGE
*package
, HKEY prodkey
, HKEY hudkey
)
3415 WCHAR patch_squashed
[GUID_SIZE
];
3418 UINT r
= ERROR_FUNCTION_FAILED
;
3420 res
= RegCreateKeyExW(prodkey
, szPatches
, 0, NULL
, 0, KEY_ALL_ACCESS
, NULL
,
3422 if (res
!= ERROR_SUCCESS
)
3423 return ERROR_FUNCTION_FAILED
;
3425 squash_guid(package
->patch
->patchcode
, patch_squashed
);
3427 res
= RegSetValueExW(patches
, szPatches
, 0, REG_MULTI_SZ
,
3428 (const BYTE
*)patch_squashed
,
3429 (lstrlenW(patch_squashed
) + 1) * sizeof(WCHAR
));
3430 if (res
!= ERROR_SUCCESS
)
3433 res
= RegSetValueExW(patches
, patch_squashed
, 0, REG_SZ
,
3434 (const BYTE
*)package
->patch
->transforms
,
3435 (lstrlenW(package
->patch
->transforms
) + 1) * sizeof(WCHAR
));
3436 if (res
== ERROR_SUCCESS
)
3440 RegCloseKey(patches
);
3445 * 99% of the work done here is only done for
3446 * advertised installs. However this is where the
3447 * Icon table is processed and written out
3448 * so that is what I am going to do here.
3450 static UINT
ACTION_PublishProduct(MSIPACKAGE
*package
)
3456 /* FIXME: also need to publish if the product is in advertise mode */
3457 if (!msi_check_publish(package
))
3458 return ERROR_SUCCESS
;
3460 rc
= MSIREG_OpenProductKey(package
->ProductCode
, NULL
, package
->Context
,
3462 if (rc
!= ERROR_SUCCESS
)
3465 rc
= MSIREG_OpenUserDataProductKey(package
->ProductCode
, package
->Context
,
3466 NULL
, &hudkey
, TRUE
);
3467 if (rc
!= ERROR_SUCCESS
)
3470 rc
= msi_publish_upgrade_code(package
);
3471 if (rc
!= ERROR_SUCCESS
)
3476 rc
= msi_publish_patch(package
, hukey
, hudkey
);
3477 if (rc
!= ERROR_SUCCESS
)
3481 rc
= msi_publish_product_properties(package
, hukey
);
3482 if (rc
!= ERROR_SUCCESS
)
3485 rc
= msi_publish_sourcelist(package
, hukey
);
3486 if (rc
!= ERROR_SUCCESS
)
3489 rc
= msi_publish_icons(package
);
3493 RegCloseKey(hudkey
);
3498 static UINT
ITERATE_WriteIniValues(MSIRECORD
*row
, LPVOID param
)
3500 MSIPACKAGE
*package
= param
;
3501 LPCWSTR component
, section
, key
, value
, identifier
, dirproperty
;
3502 LPWSTR deformated_section
, deformated_key
, deformated_value
;
3503 LPWSTR folder
, filename
, fullname
= NULL
;
3504 LPCWSTR filenameptr
;
3508 static const WCHAR szWindowsFolder
[] =
3509 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3511 component
= MSI_RecordGetString(row
, 8);
3512 comp
= get_loaded_component(package
,component
);
3514 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3516 TRACE("Skipping ini file due to disabled component %s\n",
3517 debugstr_w(component
));
3519 comp
->Action
= comp
->Installed
;
3521 return ERROR_SUCCESS
;
3524 comp
->Action
= INSTALLSTATE_LOCAL
;
3526 identifier
= MSI_RecordGetString(row
,1);
3527 dirproperty
= MSI_RecordGetString(row
,3);
3528 section
= MSI_RecordGetString(row
,4);
3529 key
= MSI_RecordGetString(row
,5);
3530 value
= MSI_RecordGetString(row
,6);
3531 action
= MSI_RecordGetInteger(row
,7);
3533 deformat_string(package
,section
,&deformated_section
);
3534 deformat_string(package
,key
,&deformated_key
);
3535 deformat_string(package
,value
,&deformated_value
);
3537 filename
= msi_dup_record_field(row
, 2);
3538 if (filename
&& (filenameptr
= strchrW(filename
, '|')))
3541 filenameptr
= filename
;
3545 folder
= resolve_folder(package
, dirproperty
, FALSE
, FALSE
, TRUE
, NULL
);
3547 folder
= msi_dup_property( package
, dirproperty
);
3550 folder
= msi_dup_property( package
, szWindowsFolder
);
3554 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty
));
3558 fullname
= build_directory_name(2, folder
, filenameptr
);
3562 TRACE("Adding value %s to section %s in %s\n",
3563 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3564 debugstr_w(fullname
));
3565 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3566 deformated_value
, fullname
);
3568 else if (action
== 1)
3571 GetPrivateProfileStringW(deformated_section
, deformated_key
, NULL
,
3572 returned
, 10, fullname
);
3573 if (returned
[0] == 0)
3575 TRACE("Adding value %s to section %s in %s\n",
3576 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3577 debugstr_w(fullname
));
3579 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3580 deformated_value
, fullname
);
3583 else if (action
== 3)
3584 FIXME("Append to existing section not yet implemented\n");
3586 uirow
= MSI_CreateRecord(4);
3587 MSI_RecordSetStringW(uirow
,1,identifier
);
3588 MSI_RecordSetStringW(uirow
,2,deformated_section
);
3589 MSI_RecordSetStringW(uirow
,3,deformated_key
);
3590 MSI_RecordSetStringW(uirow
,4,deformated_value
);
3591 ui_actiondata(package
,szWriteIniValues
,uirow
);
3592 msiobj_release( &uirow
->hdr
);
3598 msi_free(deformated_key
);
3599 msi_free(deformated_value
);
3600 msi_free(deformated_section
);
3601 return ERROR_SUCCESS
;
3604 static UINT
ACTION_WriteIniValues(MSIPACKAGE
*package
)
3608 static const WCHAR ExecSeqQuery
[] =
3609 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3610 '`','I','n','i','F','i','l','e','`',0};
3612 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3613 if (rc
!= ERROR_SUCCESS
)
3615 TRACE("no IniFile table\n");
3616 return ERROR_SUCCESS
;
3619 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteIniValues
, package
);
3620 msiobj_release(&view
->hdr
);
3624 static UINT
ITERATE_SelfRegModules(MSIRECORD
*row
, LPVOID param
)
3626 MSIPACKAGE
*package
= param
;
3631 static const WCHAR ExeStr
[] =
3632 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3633 static const WCHAR close
[] = {'\"',0};
3635 PROCESS_INFORMATION info
;
3640 memset(&si
,0,sizeof(STARTUPINFOW
));
3642 filename
= MSI_RecordGetString(row
,1);
3643 file
= get_loaded_file( package
, filename
);
3647 ERR("Unable to find file id %s\n",debugstr_w(filename
));
3648 return ERROR_SUCCESS
;
3651 len
= strlenW(ExeStr
) + strlenW( file
->TargetPath
) + 2;
3653 FullName
= msi_alloc(len
*sizeof(WCHAR
));
3654 strcpyW(FullName
,ExeStr
);
3655 strcatW( FullName
, file
->TargetPath
);
3656 strcatW(FullName
,close
);
3658 TRACE("Registering %s\n",debugstr_w(FullName
));
3659 brc
= CreateProcessW(NULL
, FullName
, NULL
, NULL
, FALSE
, 0, NULL
, c_colon
,
3664 CloseHandle(info
.hThread
);
3665 msi_dialog_check_messages(info
.hProcess
);
3666 CloseHandle(info
.hProcess
);
3672 uirow
= MSI_CreateRecord( 2 );
3673 uipath
= strdupW( file
->TargetPath
);
3674 p
= strrchrW(uipath
,'\\');
3677 MSI_RecordSetStringW( uirow
, 1, &p
[1] );
3678 MSI_RecordSetStringW( uirow
, 2, uipath
);
3679 ui_actiondata( package
, szSelfRegModules
, uirow
);
3680 msiobj_release( &uirow
->hdr
);
3682 /* FIXME: call ui_progress? */
3684 return ERROR_SUCCESS
;
3687 static UINT
ACTION_SelfRegModules(MSIPACKAGE
*package
)
3691 static const WCHAR ExecSeqQuery
[] =
3692 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3693 '`','S','e','l','f','R','e','g','`',0};
3695 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3696 if (rc
!= ERROR_SUCCESS
)
3698 TRACE("no SelfReg table\n");
3699 return ERROR_SUCCESS
;
3702 MSI_IterateRecords(view
, NULL
, ITERATE_SelfRegModules
, package
);
3703 msiobj_release(&view
->hdr
);
3705 return ERROR_SUCCESS
;
3708 static UINT
ITERATE_SelfUnregModules( MSIRECORD
*row
, LPVOID param
)
3710 static const WCHAR regsvr32
[] =
3711 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"',0};
3712 static const WCHAR close
[] = {'\"',0};
3713 MSIPACKAGE
*package
= param
;
3719 PROCESS_INFORMATION pi
;
3724 memset( &si
, 0, sizeof(STARTUPINFOW
) );
3726 filename
= MSI_RecordGetString( row
, 1 );
3727 file
= get_loaded_file( package
, filename
);
3731 ERR("Unable to find file id %s\n", debugstr_w(filename
));
3732 return ERROR_SUCCESS
;
3735 len
= strlenW( regsvr32
) + strlenW( file
->TargetPath
) + 2;
3737 cmdline
= msi_alloc( len
* sizeof(WCHAR
) );
3738 strcpyW( cmdline
, regsvr32
);
3739 strcatW( cmdline
, file
->TargetPath
);
3740 strcatW( cmdline
, close
);
3742 TRACE("Unregistering %s\n", debugstr_w(cmdline
));
3744 ret
= CreateProcessW( NULL
, cmdline
, NULL
, NULL
, FALSE
, 0, NULL
, c_colon
, &si
, &pi
);
3747 CloseHandle( pi
.hThread
);
3748 msi_dialog_check_messages( pi
.hProcess
);
3749 CloseHandle( pi
.hProcess
);
3752 msi_free( cmdline
);
3754 uirow
= MSI_CreateRecord( 2 );
3755 uipath
= strdupW( file
->TargetPath
);
3756 if ((p
= strrchrW( uipath
, '\\' )))
3759 MSI_RecordSetStringW( uirow
, 1, ++p
);
3761 MSI_RecordSetStringW( uirow
, 2, uipath
);
3762 ui_actiondata( package
, szSelfUnregModules
, uirow
);
3763 msiobj_release( &uirow
->hdr
);
3765 /* FIXME call ui_progress? */
3767 return ERROR_SUCCESS
;
3770 static UINT
ACTION_SelfUnregModules( MSIPACKAGE
*package
)
3774 static const WCHAR query
[] =
3775 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3776 '`','S','e','l','f','R','e','g','`',0};
3778 rc
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
3779 if (rc
!= ERROR_SUCCESS
)
3781 TRACE("no SelfReg table\n");
3782 return ERROR_SUCCESS
;
3785 MSI_IterateRecords( view
, NULL
, ITERATE_SelfUnregModules
, package
);
3786 msiobj_release( &view
->hdr
);
3788 return ERROR_SUCCESS
;
3791 static UINT
ACTION_PublishFeatures(MSIPACKAGE
*package
)
3793 MSIFEATURE
*feature
;
3796 HKEY userdata
= NULL
;
3798 if (!msi_check_publish(package
))
3799 return ERROR_SUCCESS
;
3801 rc
= MSIREG_OpenFeaturesKey(package
->ProductCode
, package
->Context
,
3803 if (rc
!= ERROR_SUCCESS
)
3806 rc
= MSIREG_OpenUserDataFeaturesKey(package
->ProductCode
, package
->Context
,
3808 if (rc
!= ERROR_SUCCESS
)
3811 /* here the guids are base 85 encoded */
3812 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
3818 BOOL absent
= FALSE
;
3821 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
) &&
3822 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_SOURCE
) &&
3823 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ADVERTISED
))
3827 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3831 if (feature
->Feature_Parent
)
3832 size
+= strlenW( feature
->Feature_Parent
)+2;
3834 data
= msi_alloc(size
* sizeof(WCHAR
));
3837 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3839 MSICOMPONENT
* component
= cl
->component
;
3843 if (component
->ComponentId
)
3845 TRACE("From %s\n",debugstr_w(component
->ComponentId
));
3846 CLSIDFromString(component
->ComponentId
, &clsid
);
3847 encode_base85_guid(&clsid
,buf
);
3848 TRACE("to %s\n",debugstr_w(buf
));
3853 if (feature
->Feature_Parent
)
3855 static const WCHAR sep
[] = {'\2',0};
3857 strcatW(data
,feature
->Feature_Parent
);
3860 msi_reg_set_val_str( userdata
, feature
->Feature
, data
);
3864 if (feature
->Feature_Parent
)
3865 size
= strlenW(feature
->Feature_Parent
)*sizeof(WCHAR
);
3868 size
+= sizeof(WCHAR
);
3869 RegSetValueExW(hkey
,feature
->Feature
,0,REG_SZ
,
3870 (LPBYTE
)(feature
->Feature_Parent
? feature
->Feature_Parent
: szEmpty
),size
);
3874 size
+= 2*sizeof(WCHAR
);
3875 data
= msi_alloc(size
);
3878 if (feature
->Feature_Parent
)
3879 strcpyW( &data
[1], feature
->Feature_Parent
);
3880 RegSetValueExW(hkey
,feature
->Feature
,0,REG_SZ
,
3886 uirow
= MSI_CreateRecord( 1 );
3887 MSI_RecordSetStringW( uirow
, 1, feature
->Feature
);
3888 ui_actiondata( package
, szPublishFeatures
, uirow
);
3889 msiobj_release( &uirow
->hdr
);
3890 /* FIXME: call ui_progress? */
3895 RegCloseKey(userdata
);
3899 static UINT
msi_unpublish_feature(MSIPACKAGE
*package
, MSIFEATURE
*feature
)
3904 TRACE("unpublishing feature %s\n", debugstr_w(feature
->Feature
));
3906 r
= MSIREG_OpenFeaturesKey(package
->ProductCode
, package
->Context
,
3908 if (r
== ERROR_SUCCESS
)
3910 RegDeleteValueW(hkey
, feature
->Feature
);
3914 r
= MSIREG_OpenUserDataFeaturesKey(package
->ProductCode
, package
->Context
,
3916 if (r
== ERROR_SUCCESS
)
3918 RegDeleteValueW(hkey
, feature
->Feature
);
3922 return ERROR_SUCCESS
;
3925 static UINT
ACTION_UnpublishFeatures(MSIPACKAGE
*package
)
3927 MSIFEATURE
*feature
;
3929 if (!msi_check_unpublish(package
))
3930 return ERROR_SUCCESS
;
3932 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3934 msi_unpublish_feature(package
, feature
);
3937 return ERROR_SUCCESS
;
3940 static UINT
msi_publish_install_properties(MSIPACKAGE
*package
, HKEY hkey
)
3942 LPWSTR prop
, val
, key
;
3948 static const WCHAR date_fmt
[] = {'%','i','%','0','2','i','%','0','2','i',0};
3949 static const WCHAR szWindowsInstaller
[] =
3950 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3951 static const WCHAR modpath_fmt
[] =
3952 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3953 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3954 static const WCHAR szModifyPath
[] =
3955 {'M','o','d','i','f','y','P','a','t','h',0};
3956 static const WCHAR szUninstallString
[] =
3957 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3958 static const WCHAR szEstimatedSize
[] =
3959 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3960 static const WCHAR szProductLanguage
[] =
3961 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3962 static const WCHAR szProductVersion
[] =
3963 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3964 static const WCHAR szProductName
[] =
3965 {'P','r','o','d','u','c','t','N','a','m','e',0};
3966 static const WCHAR szDisplayName
[] =
3967 {'D','i','s','p','l','a','y','N','a','m','e',0};
3968 static const WCHAR szDisplayVersion
[] =
3969 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
3970 static const WCHAR szManufacturer
[] =
3971 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
3973 static const LPCSTR propval
[] = {
3974 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3975 "ARPCONTACT", "Contact",
3976 "ARPCOMMENTS", "Comments",
3977 "ProductName", "DisplayName",
3978 "ProductVersion", "DisplayVersion",
3979 "ARPHELPLINK", "HelpLink",
3980 "ARPHELPTELEPHONE", "HelpTelephone",
3981 "ARPINSTALLLOCATION", "InstallLocation",
3982 "SourceDir", "InstallSource",
3983 "Manufacturer", "Publisher",
3984 "ARPREADME", "Readme",
3986 "ARPURLINFOABOUT", "URLInfoAbout",
3987 "ARPURLUPDATEINFO", "URLUpdateInfo",
3990 const LPCSTR
*p
= propval
;
3994 prop
= strdupAtoW(*p
++);
3995 key
= strdupAtoW(*p
++);
3996 val
= msi_dup_property(package
, prop
);
3997 msi_reg_set_val_str(hkey
, key
, val
);
4003 msi_reg_set_val_dword(hkey
, szWindowsInstaller
, 1);
4005 size
= deformat_string(package
, modpath_fmt
, &buffer
);
4006 RegSetValueExW(hkey
, szModifyPath
, 0, REG_EXPAND_SZ
, (LPBYTE
)buffer
, size
);
4007 RegSetValueExW(hkey
, szUninstallString
, 0, REG_EXPAND_SZ
, (LPBYTE
)buffer
, size
);
4010 /* FIXME: Write real Estimated Size when we have it */
4011 msi_reg_set_val_dword(hkey
, szEstimatedSize
, 0);
4013 buffer
= msi_dup_property(package
, szProductName
);
4014 msi_reg_set_val_str(hkey
, szDisplayName
, buffer
);
4017 buffer
= msi_dup_property(package
, cszSourceDir
);
4018 msi_reg_set_val_str(hkey
, INSTALLPROPERTY_INSTALLSOURCEW
, buffer
);
4021 buffer
= msi_dup_property(package
, szManufacturer
);
4022 msi_reg_set_val_str(hkey
, INSTALLPROPERTY_PUBLISHERW
, buffer
);
4025 GetLocalTime(&systime
);
4026 sprintfW(date
, date_fmt
, systime
.wYear
, systime
.wMonth
, systime
.wDay
);
4027 msi_reg_set_val_str(hkey
, INSTALLPROPERTY_INSTALLDATEW
, date
);
4029 langid
= msi_get_property_int(package
, szProductLanguage
, 0);
4030 msi_reg_set_val_dword(hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
4032 buffer
= msi_dup_property(package
, szProductVersion
);
4033 msi_reg_set_val_str(hkey
, szDisplayVersion
, buffer
);
4036 DWORD verdword
= msi_version_str_to_dword(buffer
);
4038 msi_reg_set_val_dword(hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
4039 msi_reg_set_val_dword(hkey
, INSTALLPROPERTY_VERSIONMAJORW
, verdword
>> 24);
4040 msi_reg_set_val_dword(hkey
, INSTALLPROPERTY_VERSIONMINORW
, (verdword
>> 16) & 0xFF);
4044 return ERROR_SUCCESS
;
4047 static UINT
ACTION_RegisterProduct(MSIPACKAGE
*package
)
4049 WCHAR squashed_pc
[SQUISH_GUID_SIZE
];
4050 LPWSTR upgrade_code
;
4055 static const WCHAR szUpgradeCode
[] = {
4056 'U','p','g','r','a','d','e','C','o','d','e',0};
4058 /* FIXME: also need to publish if the product is in advertise mode */
4059 if (!msi_check_publish(package
))
4060 return ERROR_SUCCESS
;
4062 rc
= MSIREG_OpenUninstallKey(package
->ProductCode
, &hkey
, TRUE
);
4063 if (rc
!= ERROR_SUCCESS
)
4066 rc
= MSIREG_OpenInstallProps(package
->ProductCode
, package
->Context
,
4067 NULL
, &props
, TRUE
);
4068 if (rc
!= ERROR_SUCCESS
)
4071 msi_reg_set_val_str( props
, INSTALLPROPERTY_LOCALPACKAGEW
, package
->db
->localfile
);
4072 msi_free( package
->db
->localfile
);
4073 package
->db
->localfile
= NULL
;
4075 rc
= msi_publish_install_properties(package
, hkey
);
4076 if (rc
!= ERROR_SUCCESS
)
4079 rc
= msi_publish_install_properties(package
, props
);
4080 if (rc
!= ERROR_SUCCESS
)
4083 upgrade_code
= msi_dup_property(package
, szUpgradeCode
);
4086 MSIREG_OpenUpgradeCodesKey(upgrade_code
, &upgrade
, TRUE
);
4087 squash_guid(package
->ProductCode
, squashed_pc
);
4088 msi_reg_set_val_str(upgrade
, squashed_pc
, NULL
);
4089 RegCloseKey(upgrade
);
4090 msi_free(upgrade_code
);
4096 return ERROR_SUCCESS
;
4099 static UINT
ACTION_InstallExecute(MSIPACKAGE
*package
)
4101 return execute_script(package
,INSTALL_SCRIPT
);
4104 static UINT
msi_unpublish_product(MSIPACKAGE
*package
)
4107 LPWSTR remove
= NULL
;
4108 LPWSTR
*features
= NULL
;
4109 BOOL full_uninstall
= TRUE
;
4110 MSIFEATURE
*feature
;
4112 static const WCHAR szUpgradeCode
[] =
4113 {'U','p','g','r','a','d','e','C','o','d','e',0};
4115 remove
= msi_dup_property(package
, szRemove
);
4117 return ERROR_SUCCESS
;
4119 features
= msi_split_string(remove
, ',');
4123 ERR("REMOVE feature list is empty!\n");
4124 return ERROR_FUNCTION_FAILED
;
4127 if (!lstrcmpW(features
[0], szAll
))
4128 full_uninstall
= TRUE
;
4131 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
4133 if (feature
->Action
!= INSTALLSTATE_ABSENT
)
4134 full_uninstall
= FALSE
;
4138 if (!full_uninstall
)
4141 MSIREG_DeleteProductKey(package
->ProductCode
);
4142 MSIREG_DeleteUserDataProductKey(package
->ProductCode
);
4143 MSIREG_DeleteUninstallKey(package
->ProductCode
);
4145 if (package
->Context
== MSIINSTALLCONTEXT_MACHINE
)
4147 MSIREG_DeleteLocalClassesProductKey(package
->ProductCode
);
4148 MSIREG_DeleteLocalClassesFeaturesKey(package
->ProductCode
);
4152 MSIREG_DeleteUserProductKey(package
->ProductCode
);
4153 MSIREG_DeleteUserFeaturesKey(package
->ProductCode
);
4156 upgrade
= msi_dup_property(package
, szUpgradeCode
);
4159 MSIREG_DeleteUserUpgradeCodesKey(upgrade
);
4166 return ERROR_SUCCESS
;
4169 static UINT
ACTION_InstallFinalize(MSIPACKAGE
*package
)
4173 rc
= msi_unpublish_product(package
);
4174 if (rc
!= ERROR_SUCCESS
)
4177 /* turn off scheduling */
4178 package
->script
->CurrentlyScripting
= FALSE
;
4180 /* first do the same as an InstallExecute */
4181 rc
= ACTION_InstallExecute(package
);
4182 if (rc
!= ERROR_SUCCESS
)
4185 /* then handle Commit Actions */
4186 rc
= execute_script(package
,COMMIT_SCRIPT
);
4191 UINT
ACTION_ForceReboot(MSIPACKAGE
*package
)
4193 static const WCHAR RunOnce
[] = {
4194 'S','o','f','t','w','a','r','e','\\',
4195 'M','i','c','r','o','s','o','f','t','\\',
4196 'W','i','n','d','o','w','s','\\',
4197 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4198 'R','u','n','O','n','c','e',0};
4199 static const WCHAR InstallRunOnce
[] = {
4200 'S','o','f','t','w','a','r','e','\\',
4201 'M','i','c','r','o','s','o','f','t','\\',
4202 'W','i','n','d','o','w','s','\\',
4203 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4204 'I','n','s','t','a','l','l','e','r','\\',
4205 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4207 static const WCHAR msiexec_fmt
[] = {
4209 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4210 '\"','%','s','\"',0};
4211 static const WCHAR install_fmt
[] = {
4212 '/','I',' ','\"','%','s','\"',' ',
4213 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4214 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4215 WCHAR buffer
[256], sysdir
[MAX_PATH
];
4217 WCHAR squished_pc
[100];
4219 squash_guid(package
->ProductCode
,squished_pc
);
4221 GetSystemDirectoryW(sysdir
, sizeof(sysdir
)/sizeof(sysdir
[0]));
4222 RegCreateKeyW(HKEY_LOCAL_MACHINE
,RunOnce
,&hkey
);
4223 snprintfW(buffer
,sizeof(buffer
)/sizeof(buffer
[0]),msiexec_fmt
,sysdir
,
4226 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
4229 TRACE("Reboot command %s\n",debugstr_w(buffer
));
4231 RegCreateKeyW(HKEY_LOCAL_MACHINE
,InstallRunOnce
,&hkey
);
4232 sprintfW(buffer
,install_fmt
,package
->ProductCode
,squished_pc
);
4234 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
4237 return ERROR_INSTALL_SUSPEND
;
4240 static UINT
ACTION_ResolveSource(MSIPACKAGE
* package
)
4246 * We are currently doing what should be done here in the top level Install
4247 * however for Administrative and uninstalls this step will be needed
4249 if (!package
->PackagePath
)
4250 return ERROR_SUCCESS
;
4252 msi_set_sourcedir_props(package
, TRUE
);
4254 attrib
= GetFileAttributesW(package
->db
->path
);
4255 if (attrib
== INVALID_FILE_ATTRIBUTES
)
4261 rc
= MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
4262 package
->Context
, MSICODE_PRODUCT
,
4263 INSTALLPROPERTY_DISKPROMPTW
,NULL
,&size
);
4264 if (rc
== ERROR_MORE_DATA
)
4266 prompt
= msi_alloc(size
* sizeof(WCHAR
));
4267 MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
4268 package
->Context
, MSICODE_PRODUCT
,
4269 INSTALLPROPERTY_DISKPROMPTW
,prompt
,&size
);
4272 prompt
= strdupW(package
->db
->path
);
4274 msg
= generate_error_string(package
,1302,1,prompt
);
4275 while(attrib
== INVALID_FILE_ATTRIBUTES
)
4277 rc
= MessageBoxW(NULL
,msg
,NULL
,MB_OKCANCEL
);
4280 rc
= ERROR_INSTALL_USEREXIT
;
4283 attrib
= GetFileAttributesW(package
->db
->path
);
4289 return ERROR_SUCCESS
;
4294 static UINT
ACTION_RegisterUser(MSIPACKAGE
*package
)
4301 static const WCHAR szPropKeys
[][80] =
4303 {'P','r','o','d','u','c','t','I','D',0},
4304 {'U','S','E','R','N','A','M','E',0},
4305 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4309 static const WCHAR szRegKeys
[][80] =
4311 {'P','r','o','d','u','c','t','I','D',0},
4312 {'R','e','g','O','w','n','e','r',0},
4313 {'R','e','g','C','o','m','p','a','n','y',0},
4317 if (msi_check_unpublish(package
))
4319 MSIREG_DeleteUserDataProductKey(package
->ProductCode
);
4320 return ERROR_SUCCESS
;
4323 productid
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTIDW
);
4325 return ERROR_SUCCESS
;
4327 rc
= MSIREG_OpenInstallProps(package
->ProductCode
, package
->Context
,
4329 if (rc
!= ERROR_SUCCESS
)
4332 for( i
= 0; szPropKeys
[i
][0]; i
++ )
4334 buffer
= msi_dup_property( package
, szPropKeys
[i
] );
4335 msi_reg_set_val_str( hkey
, szRegKeys
[i
], buffer
);
4340 msi_free(productid
);
4343 /* FIXME: call ui_actiondata */
4349 static UINT
ACTION_ExecuteAction(MSIPACKAGE
*package
)
4353 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
4354 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
4359 static UINT
ITERATE_PublishComponent(MSIRECORD
*rec
, LPVOID param
)
4361 MSIPACKAGE
*package
= param
;
4362 LPCWSTR compgroupid
=NULL
;
4363 LPCWSTR feature
=NULL
;
4364 LPCWSTR text
= NULL
;
4365 LPCWSTR qualifier
= NULL
;
4366 LPCWSTR component
= NULL
;
4367 LPWSTR advertise
= NULL
;
4368 LPWSTR output
= NULL
;
4370 UINT rc
= ERROR_SUCCESS
;
4375 component
= MSI_RecordGetString(rec
,3);
4376 comp
= get_loaded_component(package
,component
);
4378 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
) &&
4379 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_SOURCE
) &&
4380 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ADVERTISED
))
4382 TRACE("Skipping: Component %s not scheduled for install\n",
4383 debugstr_w(component
));
4385 return ERROR_SUCCESS
;
4388 compgroupid
= MSI_RecordGetString(rec
,1);
4389 qualifier
= MSI_RecordGetString(rec
,2);
4391 rc
= MSIREG_OpenUserComponentsKey(compgroupid
, &hkey
, TRUE
);
4392 if (rc
!= ERROR_SUCCESS
)
4395 text
= MSI_RecordGetString(rec
,4);
4396 feature
= MSI_RecordGetString(rec
,5);
4398 advertise
= create_component_advertise_string(package
, comp
, feature
);
4400 sz
= strlenW(advertise
);
4403 sz
+= lstrlenW(text
);
4406 sz
*= sizeof(WCHAR
);
4408 output
= msi_alloc_zero(sz
);
4409 strcpyW(output
,advertise
);
4410 msi_free(advertise
);
4413 strcatW(output
,text
);
4415 msi_reg_set_val_multi_str( hkey
, qualifier
, output
);
4422 uirow
= MSI_CreateRecord( 2 );
4423 MSI_RecordSetStringW( uirow
, 1, compgroupid
);
4424 MSI_RecordSetStringW( uirow
, 2, qualifier
);
4425 ui_actiondata( package
, szPublishComponents
, uirow
);
4426 msiobj_release( &uirow
->hdr
);
4427 /* FIXME: call ui_progress? */
4433 * At present I am ignorning the advertised components part of this and only
4434 * focusing on the qualified component sets
4436 static UINT
ACTION_PublishComponents(MSIPACKAGE
*package
)
4440 static const WCHAR ExecSeqQuery
[] =
4441 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4442 '`','P','u','b','l','i','s','h',
4443 'C','o','m','p','o','n','e','n','t','`',0};
4445 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4446 if (rc
!= ERROR_SUCCESS
)
4447 return ERROR_SUCCESS
;
4449 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_PublishComponent
, package
);
4450 msiobj_release(&view
->hdr
);
4455 static UINT
ITERATE_InstallService(MSIRECORD
*rec
, LPVOID param
)
4457 MSIPACKAGE
*package
= param
;
4460 SC_HANDLE hscm
, service
= NULL
;
4461 LPCWSTR comp
, depends
, pass
;
4462 LPWSTR name
= NULL
, disp
= NULL
;
4463 LPCWSTR load_order
, serv_name
, key
;
4464 DWORD serv_type
, start_type
;
4467 static const WCHAR query
[] =
4468 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4469 '`','C','o','m','p','o','n','e','n','t','`',' ',
4470 'W','H','E','R','E',' ',
4471 '`','C','o','m','p','o','n','e','n','t','`',' ',
4472 '=','\'','%','s','\'',0};
4474 hscm
= OpenSCManagerW(NULL
, SERVICES_ACTIVE_DATABASEW
, GENERIC_WRITE
);
4477 ERR("Failed to open the SC Manager!\n");
4481 start_type
= MSI_RecordGetInteger(rec
, 5);
4482 if (start_type
== SERVICE_BOOT_START
|| start_type
== SERVICE_SYSTEM_START
)
4485 depends
= MSI_RecordGetString(rec
, 8);
4486 if (depends
&& *depends
)
4487 FIXME("Dependency list unhandled!\n");
4489 deformat_string(package
, MSI_RecordGetString(rec
, 2), &name
);
4490 deformat_string(package
, MSI_RecordGetString(rec
, 3), &disp
);
4491 serv_type
= MSI_RecordGetInteger(rec
, 4);
4492 err_control
= MSI_RecordGetInteger(rec
, 6);
4493 load_order
= MSI_RecordGetString(rec
, 7);
4494 serv_name
= MSI_RecordGetString(rec
, 9);
4495 pass
= MSI_RecordGetString(rec
, 10);
4496 comp
= MSI_RecordGetString(rec
, 12);
4498 /* fetch the service path */
4499 row
= MSI_QueryGetRecord(package
->db
, query
, comp
);
4502 ERR("Control query failed!\n");
4506 key
= MSI_RecordGetString(row
, 6);
4508 file
= get_loaded_file(package
, key
);
4509 msiobj_release(&row
->hdr
);
4512 ERR("Failed to load the service file\n");
4516 service
= CreateServiceW(hscm
, name
, disp
, GENERIC_ALL
, serv_type
,
4517 start_type
, err_control
, file
->TargetPath
,
4518 load_order
, NULL
, NULL
, serv_name
, pass
);
4521 if (GetLastError() != ERROR_SERVICE_EXISTS
)
4522 ERR("Failed to create service %s: %d\n", debugstr_w(name
), GetLastError());
4526 CloseServiceHandle(service
);
4527 CloseServiceHandle(hscm
);
4531 return ERROR_SUCCESS
;
4534 static UINT
ACTION_InstallServices( MSIPACKAGE
*package
)
4538 static const WCHAR ExecSeqQuery
[] =
4539 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4540 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4542 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4543 if (rc
!= ERROR_SUCCESS
)
4544 return ERROR_SUCCESS
;
4546 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallService
, package
);
4547 msiobj_release(&view
->hdr
);
4552 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4553 static LPCWSTR
*msi_service_args_to_vector(LPWSTR args
, DWORD
*numargs
)
4555 LPCWSTR
*vector
, *temp_vector
;
4559 static const WCHAR separator
[] = {'[','~',']',0};
4562 sep_len
= sizeof(separator
) / sizeof(WCHAR
) - 1;
4567 vector
= msi_alloc(sizeof(LPWSTR
));
4575 vector
[*numargs
- 1] = p
;
4577 if ((q
= strstrW(p
, separator
)))
4581 temp_vector
= msi_realloc(vector
, (*numargs
+ 1) * sizeof(LPWSTR
));
4587 vector
= temp_vector
;
4596 static UINT
ITERATE_StartService(MSIRECORD
*rec
, LPVOID param
)
4598 MSIPACKAGE
*package
= param
;
4600 SC_HANDLE scm
, service
= NULL
;
4601 LPCWSTR
*vector
= NULL
;
4603 DWORD event
, numargs
;
4604 UINT r
= ERROR_FUNCTION_FAILED
;
4606 comp
= get_loaded_component(package
, MSI_RecordGetString(rec
, 6));
4607 if (!comp
|| comp
->Action
== INSTALLSTATE_UNKNOWN
|| comp
->Action
== INSTALLSTATE_ABSENT
)
4608 return ERROR_SUCCESS
;
4610 deformat_string(package
, MSI_RecordGetString(rec
, 2), &name
);
4611 deformat_string(package
, MSI_RecordGetString(rec
, 4), &args
);
4612 event
= MSI_RecordGetInteger(rec
, 3);
4614 if (!(event
& msidbServiceControlEventStart
))
4615 return ERROR_SUCCESS
;
4617 scm
= OpenSCManagerW(NULL
, NULL
, SC_MANAGER_CONNECT
);
4620 ERR("Failed to open the service control manager\n");
4624 service
= OpenServiceW(scm
, name
, SERVICE_START
);
4627 ERR("Failed to open service %s (%u)\n", debugstr_w(name
), GetLastError());
4631 vector
= msi_service_args_to_vector(args
, &numargs
);
4633 if (!StartServiceW(service
, numargs
, vector
) &&
4634 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING
)
4636 ERR("Failed to start service %s (%u)\n", debugstr_w(name
), GetLastError());
4643 CloseServiceHandle(service
);
4644 CloseServiceHandle(scm
);
4652 static UINT
ACTION_StartServices( MSIPACKAGE
*package
)
4657 static const WCHAR query
[] = {
4658 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4659 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4661 rc
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
4662 if (rc
!= ERROR_SUCCESS
)
4663 return ERROR_SUCCESS
;
4665 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_StartService
, package
);
4666 msiobj_release(&view
->hdr
);
4671 static BOOL
stop_service_dependents(SC_HANDLE scm
, SC_HANDLE service
)
4673 DWORD i
, needed
, count
;
4674 ENUM_SERVICE_STATUSW
*dependencies
;
4678 if (EnumDependentServicesW(service
, SERVICE_ACTIVE
, NULL
,
4679 0, &needed
, &count
))
4682 if (GetLastError() != ERROR_MORE_DATA
)
4685 dependencies
= msi_alloc(needed
);
4689 if (!EnumDependentServicesW(service
, SERVICE_ACTIVE
, dependencies
,
4690 needed
, &needed
, &count
))
4693 for (i
= 0; i
< count
; i
++)
4695 depserv
= OpenServiceW(scm
, dependencies
[i
].lpServiceName
,
4696 SERVICE_STOP
| SERVICE_QUERY_STATUS
);
4700 if (!ControlService(depserv
, SERVICE_CONTROL_STOP
, &ss
))
4707 msi_free(dependencies
);
4711 static UINT
stop_service( LPCWSTR name
)
4713 SC_HANDLE scm
= NULL
, service
= NULL
;
4714 SERVICE_STATUS status
;
4715 SERVICE_STATUS_PROCESS ssp
;
4718 scm
= OpenSCManagerW(NULL
, NULL
, SC_MANAGER_ALL_ACCESS
);
4721 WARN("Failed to open the SCM: %d\n", GetLastError());
4725 service
= OpenServiceW(scm
, name
,
4727 SERVICE_QUERY_STATUS
|
4728 SERVICE_ENUMERATE_DEPENDENTS
);
4731 WARN("Failed to open service (%s): %d\n", debugstr_w(name
), GetLastError());
4735 if (!QueryServiceStatusEx(service
, SC_STATUS_PROCESS_INFO
, (LPBYTE
)&ssp
,
4736 sizeof(SERVICE_STATUS_PROCESS
), &needed
))
4738 WARN("Failed to query service status (%s): %d\n", debugstr_w(name
), GetLastError());
4742 if (ssp
.dwCurrentState
== SERVICE_STOPPED
)
4745 stop_service_dependents(scm
, service
);
4747 if (!ControlService(service
, SERVICE_CONTROL_STOP
, &status
))
4748 WARN("Failed to stop service (%s): %d\n", debugstr_w(name
), GetLastError());
4751 CloseServiceHandle(service
);
4752 CloseServiceHandle(scm
);
4754 return ERROR_SUCCESS
;
4757 static UINT
ITERATE_StopService( MSIRECORD
*rec
, LPVOID param
)
4759 MSIPACKAGE
*package
= param
;
4764 event
= MSI_RecordGetInteger( rec
, 3 );
4765 if (!(event
& msidbServiceControlEventStop
))
4766 return ERROR_SUCCESS
;
4768 comp
= get_loaded_component( package
, MSI_RecordGetString( rec
, 6 ) );
4769 if (!comp
|| comp
->Action
== INSTALLSTATE_UNKNOWN
|| comp
->Action
== INSTALLSTATE_ABSENT
)
4770 return ERROR_SUCCESS
;
4772 deformat_string( package
, MSI_RecordGetString( rec
, 2 ), &name
);
4773 stop_service( name
);
4776 return ERROR_SUCCESS
;
4779 static UINT
ACTION_StopServices( MSIPACKAGE
*package
)
4784 static const WCHAR query
[] = {
4785 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4786 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4788 rc
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
4789 if (rc
!= ERROR_SUCCESS
)
4790 return ERROR_SUCCESS
;
4792 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_StopService
, package
);
4793 msiobj_release(&view
->hdr
);
4798 static UINT
ITERATE_DeleteService( MSIRECORD
*rec
, LPVOID param
)
4800 MSIPACKAGE
*package
= param
;
4804 SC_HANDLE scm
= NULL
, service
= NULL
;
4806 event
= MSI_RecordGetInteger( rec
, 3 );
4807 if (!(event
& msidbServiceControlEventDelete
))
4808 return ERROR_SUCCESS
;
4810 comp
= get_loaded_component( package
, MSI_RecordGetString(rec
, 6) );
4811 if (!comp
|| comp
->Action
== INSTALLSTATE_UNKNOWN
|| comp
->Action
== INSTALLSTATE_ABSENT
)
4812 return ERROR_SUCCESS
;
4814 deformat_string( package
, MSI_RecordGetString(rec
, 2), &name
);
4815 stop_service( name
);
4817 scm
= OpenSCManagerW( NULL
, NULL
, SC_MANAGER_ALL_ACCESS
);
4820 WARN("Failed to open the SCM: %d\n", GetLastError());
4824 service
= OpenServiceW( scm
, name
, DELETE
);
4827 WARN("Failed to open service (%s): %u\n", debugstr_w(name
), GetLastError());
4831 if (!DeleteService( service
))
4832 WARN("Failed to delete service (%s): %u\n", debugstr_w(name
), GetLastError());
4835 CloseServiceHandle( service
);
4836 CloseServiceHandle( scm
);
4839 return ERROR_SUCCESS
;
4842 static UINT
ACTION_DeleteServices( MSIPACKAGE
*package
)
4847 static const WCHAR query
[] = {
4848 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4849 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4851 rc
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
4852 if (rc
!= ERROR_SUCCESS
)
4853 return ERROR_SUCCESS
;
4855 rc
= MSI_IterateRecords( view
, NULL
, ITERATE_DeleteService
, package
);
4856 msiobj_release( &view
->hdr
);
4861 static MSIFILE
*msi_find_file( MSIPACKAGE
*package
, LPCWSTR filename
)
4865 LIST_FOR_EACH_ENTRY(file
, &package
->files
, MSIFILE
, entry
)
4867 if (!lstrcmpW(file
->File
, filename
))
4874 static UINT
ITERATE_InstallODBCDriver( MSIRECORD
*rec
, LPVOID param
)
4876 MSIPACKAGE
*package
= param
;
4877 LPWSTR driver
, driver_path
, ptr
;
4878 WCHAR outpath
[MAX_PATH
];
4879 MSIFILE
*driver_file
, *setup_file
;
4882 UINT r
= ERROR_SUCCESS
;
4884 static const WCHAR driver_fmt
[] = {
4885 'D','r','i','v','e','r','=','%','s',0};
4886 static const WCHAR setup_fmt
[] = {
4887 'S','e','t','u','p','=','%','s',0};
4888 static const WCHAR usage_fmt
[] = {
4889 'F','i','l','e','U','s','a','g','e','=','1',0};
4891 desc
= MSI_RecordGetString(rec
, 3);
4893 driver_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 4));
4894 setup_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 5));
4896 if (!driver_file
|| !setup_file
)
4898 ERR("ODBC Driver entry not found!\n");
4899 return ERROR_FUNCTION_FAILED
;
4902 len
= lstrlenW(desc
) + lstrlenW(driver_fmt
) + lstrlenW(driver_file
->FileName
) +
4903 lstrlenW(setup_fmt
) + lstrlenW(setup_file
->FileName
) +
4904 lstrlenW(usage_fmt
) + 1;
4905 driver
= msi_alloc(len
* sizeof(WCHAR
));
4907 return ERROR_OUTOFMEMORY
;
4910 lstrcpyW(ptr
, desc
);
4911 ptr
+= lstrlenW(ptr
) + 1;
4913 sprintfW(ptr
, driver_fmt
, driver_file
->FileName
);
4914 ptr
+= lstrlenW(ptr
) + 1;
4916 sprintfW(ptr
, setup_fmt
, setup_file
->FileName
);
4917 ptr
+= lstrlenW(ptr
) + 1;
4919 lstrcpyW(ptr
, usage_fmt
);
4920 ptr
+= lstrlenW(ptr
) + 1;
4923 driver_path
= strdupW(driver_file
->TargetPath
);
4924 ptr
= strrchrW(driver_path
, '\\');
4925 if (ptr
) *ptr
= '\0';
4927 if (!SQLInstallDriverExW(driver
, driver_path
, outpath
, MAX_PATH
,
4928 NULL
, ODBC_INSTALL_COMPLETE
, &usage
))
4930 ERR("Failed to install SQL driver!\n");
4931 r
= ERROR_FUNCTION_FAILED
;
4935 msi_free(driver_path
);
4940 static UINT
ITERATE_InstallODBCTranslator( MSIRECORD
*rec
, LPVOID param
)
4942 MSIPACKAGE
*package
= param
;
4943 LPWSTR translator
, translator_path
, ptr
;
4944 WCHAR outpath
[MAX_PATH
];
4945 MSIFILE
*translator_file
, *setup_file
;
4948 UINT r
= ERROR_SUCCESS
;
4950 static const WCHAR translator_fmt
[] = {
4951 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4952 static const WCHAR setup_fmt
[] = {
4953 'S','e','t','u','p','=','%','s',0};
4955 desc
= MSI_RecordGetString(rec
, 3);
4957 translator_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 4));
4958 setup_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 5));
4960 if (!translator_file
|| !setup_file
)
4962 ERR("ODBC Translator entry not found!\n");
4963 return ERROR_FUNCTION_FAILED
;
4966 len
= lstrlenW(desc
) + lstrlenW(translator_fmt
) + lstrlenW(translator_file
->FileName
) +
4967 lstrlenW(setup_fmt
) + lstrlenW(setup_file
->FileName
) + 1;
4968 translator
= msi_alloc(len
* sizeof(WCHAR
));
4970 return ERROR_OUTOFMEMORY
;
4973 lstrcpyW(ptr
, desc
);
4974 ptr
+= lstrlenW(ptr
) + 1;
4976 sprintfW(ptr
, translator_fmt
, translator_file
->FileName
);
4977 ptr
+= lstrlenW(ptr
) + 1;
4979 sprintfW(ptr
, setup_fmt
, setup_file
->FileName
);
4980 ptr
+= lstrlenW(ptr
) + 1;
4983 translator_path
= strdupW(translator_file
->TargetPath
);
4984 ptr
= strrchrW(translator_path
, '\\');
4985 if (ptr
) *ptr
= '\0';
4987 if (!SQLInstallTranslatorExW(translator
, translator_path
, outpath
, MAX_PATH
,
4988 NULL
, ODBC_INSTALL_COMPLETE
, &usage
))
4990 ERR("Failed to install SQL translator!\n");
4991 r
= ERROR_FUNCTION_FAILED
;
4994 msi_free(translator
);
4995 msi_free(translator_path
);
5000 static UINT
ITERATE_InstallODBCDataSource( MSIRECORD
*rec
, LPVOID param
)
5003 LPCWSTR desc
, driver
;
5004 WORD request
= ODBC_ADD_SYS_DSN
;
5007 UINT r
= ERROR_SUCCESS
;
5009 static const WCHAR attrs_fmt
[] = {
5010 'D','S','N','=','%','s',0 };
5012 desc
= MSI_RecordGetString(rec
, 3);
5013 driver
= MSI_RecordGetString(rec
, 4);
5014 registration
= MSI_RecordGetInteger(rec
, 5);
5016 if (registration
== msidbODBCDataSourceRegistrationPerMachine
) request
= ODBC_ADD_SYS_DSN
;
5017 else if (registration
== msidbODBCDataSourceRegistrationPerUser
) request
= ODBC_ADD_DSN
;
5019 len
= lstrlenW(attrs_fmt
) + lstrlenW(desc
) + 1 + 1;
5020 attrs
= msi_alloc(len
* sizeof(WCHAR
));
5022 return ERROR_OUTOFMEMORY
;
5024 sprintfW(attrs
, attrs_fmt
, desc
);
5025 attrs
[len
- 1] = '\0';
5027 if (!SQLConfigDataSourceW(NULL
, request
, driver
, attrs
))
5029 ERR("Failed to install SQL data source!\n");
5030 r
= ERROR_FUNCTION_FAILED
;
5038 static UINT
ACTION_InstallODBC( MSIPACKAGE
*package
)
5043 static const WCHAR driver_query
[] = {
5044 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5045 'O','D','B','C','D','r','i','v','e','r',0 };
5047 static const WCHAR translator_query
[] = {
5048 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5049 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5051 static const WCHAR source_query
[] = {
5052 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5053 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5055 rc
= MSI_DatabaseOpenViewW(package
->db
, driver_query
, &view
);
5056 if (rc
!= ERROR_SUCCESS
)
5057 return ERROR_SUCCESS
;
5059 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCDriver
, package
);
5060 msiobj_release(&view
->hdr
);
5062 rc
= MSI_DatabaseOpenViewW(package
->db
, translator_query
, &view
);
5063 if (rc
!= ERROR_SUCCESS
)
5064 return ERROR_SUCCESS
;
5066 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCTranslator
, package
);
5067 msiobj_release(&view
->hdr
);
5069 rc
= MSI_DatabaseOpenViewW(package
->db
, source_query
, &view
);
5070 if (rc
!= ERROR_SUCCESS
)
5071 return ERROR_SUCCESS
;
5073 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCDataSource
, package
);
5074 msiobj_release(&view
->hdr
);
5079 #define ENV_ACT_SETALWAYS 0x1
5080 #define ENV_ACT_SETABSENT 0x2
5081 #define ENV_ACT_REMOVE 0x4
5082 #define ENV_ACT_REMOVEMATCH 0x8
5084 #define ENV_MOD_MACHINE 0x20000000
5085 #define ENV_MOD_APPEND 0x40000000
5086 #define ENV_MOD_PREFIX 0x80000000
5087 #define ENV_MOD_MASK 0xC0000000
5089 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5091 static LONG
env_set_flags( LPCWSTR
*name
, LPCWSTR
*value
, DWORD
*flags
)
5093 LPCWSTR cptr
= *name
;
5095 static const WCHAR prefix
[] = {'[','~',']',0};
5096 static const int prefix_len
= 3;
5102 *flags
|= ENV_ACT_SETALWAYS
;
5103 else if (*cptr
== '+')
5104 *flags
|= ENV_ACT_SETABSENT
;
5105 else if (*cptr
== '-')
5106 *flags
|= ENV_ACT_REMOVE
;
5107 else if (*cptr
== '!')
5108 *flags
|= ENV_ACT_REMOVEMATCH
;
5109 else if (*cptr
== '*')
5110 *flags
|= ENV_MOD_MACHINE
;
5120 ERR("Missing environment variable\n");
5121 return ERROR_FUNCTION_FAILED
;
5126 LPCWSTR ptr
= *value
;
5127 if (!strncmpW(ptr
, prefix
, prefix_len
))
5129 if (ptr
[prefix_len
] == szSemiColon
[0])
5131 *flags
|= ENV_MOD_APPEND
;
5132 *value
+= lstrlenW(prefix
);
5139 else if (lstrlenW(*value
) >= prefix_len
)
5141 ptr
+= lstrlenW(ptr
) - prefix_len
;
5142 if (!lstrcmpW(ptr
, prefix
))
5144 if ((ptr
-1) > *value
&& *(ptr
-1) == szSemiColon
[0])
5146 *flags
|= ENV_MOD_PREFIX
;
5147 /* the "[~]" will be removed by deformat_string */;
5157 if (check_flag_combo(*flags
, ENV_ACT_SETALWAYS
| ENV_ACT_SETABSENT
) ||
5158 check_flag_combo(*flags
, ENV_ACT_REMOVEMATCH
| ENV_ACT_SETABSENT
) ||
5159 check_flag_combo(*flags
, ENV_ACT_REMOVEMATCH
| ENV_ACT_SETALWAYS
) ||
5160 check_flag_combo(*flags
, ENV_ACT_SETABSENT
| ENV_MOD_MASK
))
5162 ERR("Invalid flags: %08x\n", *flags
);
5163 return ERROR_FUNCTION_FAILED
;
5167 *flags
= ENV_ACT_SETALWAYS
| ENV_ACT_REMOVE
;
5169 return ERROR_SUCCESS
;
5172 static UINT
ITERATE_WriteEnvironmentString( MSIRECORD
*rec
, LPVOID param
)
5174 MSIPACKAGE
*package
= param
;
5175 LPCWSTR name
, value
;
5176 LPWSTR data
= NULL
, newval
= NULL
;
5177 LPWSTR deformatted
= NULL
, ptr
;
5178 DWORD flags
, type
, size
;
5180 HKEY env
= NULL
, root
;
5181 LPCWSTR environment
;
5183 static const WCHAR user_env
[] =
5184 {'E','n','v','i','r','o','n','m','e','n','t',0};
5185 static const WCHAR machine_env
[] =
5186 {'S','y','s','t','e','m','\\',
5187 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5188 'C','o','n','t','r','o','l','\\',
5189 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5190 'E','n','v','i','r','o','n','m','e','n','t',0};
5192 name
= MSI_RecordGetString(rec
, 2);
5193 value
= MSI_RecordGetString(rec
, 3);
5195 TRACE("name %s value %s\n", debugstr_w(name
), debugstr_w(value
));
5197 res
= env_set_flags(&name
, &value
, &flags
);
5198 if (res
!= ERROR_SUCCESS
|| !value
)
5201 if (value
&& !deformat_string(package
, value
, &deformatted
))
5203 res
= ERROR_OUTOFMEMORY
;
5207 value
= deformatted
;
5209 if (flags
& ENV_MOD_MACHINE
)
5211 environment
= machine_env
;
5212 root
= HKEY_LOCAL_MACHINE
;
5216 environment
= user_env
;
5217 root
= HKEY_CURRENT_USER
;
5220 res
= RegCreateKeyExW(root
, environment
, 0, NULL
, 0,
5221 KEY_ALL_ACCESS
, NULL
, &env
, NULL
);
5222 if (res
!= ERROR_SUCCESS
)
5225 if (flags
& ENV_ACT_REMOVE
)
5226 FIXME("Not removing environment variable on uninstall!\n");
5230 res
= RegQueryValueExW(env
, name
, NULL
, &type
, NULL
, &size
);
5231 if ((res
!= ERROR_SUCCESS
&& res
!= ERROR_FILE_NOT_FOUND
) ||
5232 (res
== ERROR_SUCCESS
&& type
!= REG_SZ
&& type
!= REG_EXPAND_SZ
))
5235 if ((res
== ERROR_FILE_NOT_FOUND
|| !(flags
& ENV_MOD_MASK
)))
5237 /* Nothing to do. */
5240 res
= ERROR_SUCCESS
;
5244 /* If we are appending but the string was empty, strip ; */
5245 if ((flags
& ENV_MOD_APPEND
) && (value
[0] == szSemiColon
[0])) value
++;
5247 size
= (lstrlenW(value
) + 1) * sizeof(WCHAR
);
5248 newval
= strdupW(value
);
5251 res
= ERROR_OUTOFMEMORY
;
5257 /* Contrary to MSDN, +-variable to [~];path works */
5258 if (flags
& ENV_ACT_SETABSENT
&& !(flags
& ENV_MOD_MASK
))
5260 res
= ERROR_SUCCESS
;
5264 data
= msi_alloc(size
);
5268 return ERROR_OUTOFMEMORY
;
5271 res
= RegQueryValueExW(env
, name
, NULL
, &type
, (LPVOID
)data
, &size
);
5272 if (res
!= ERROR_SUCCESS
)
5275 if (flags
& ENV_ACT_REMOVEMATCH
&& (!value
|| !lstrcmpW(data
, value
)))
5277 res
= RegDeleteKeyW(env
, name
);
5281 size
= (lstrlenW(data
) + 1) * sizeof(WCHAR
);
5282 if (flags
& ENV_MOD_MASK
)
5286 if (flags
& ENV_MOD_APPEND
) multiplier
++;
5287 if (flags
& ENV_MOD_PREFIX
) multiplier
++;
5288 mod_size
= lstrlenW(value
) * multiplier
;
5289 size
+= mod_size
* sizeof(WCHAR
);
5292 newval
= msi_alloc(size
);
5296 res
= ERROR_OUTOFMEMORY
;
5300 if (flags
& ENV_MOD_PREFIX
)
5302 lstrcpyW(newval
, value
);
5303 ptr
= newval
+ lstrlenW(value
);
5306 lstrcpyW(ptr
, data
);
5308 if (flags
& ENV_MOD_APPEND
)
5310 lstrcatW(newval
, value
);
5313 TRACE("setting %s to %s\n", debugstr_w(name
), debugstr_w(newval
));
5314 res
= RegSetValueExW(env
, name
, 0, type
, (LPVOID
)newval
, size
);
5317 if (env
) RegCloseKey(env
);
5318 msi_free(deformatted
);
5324 static UINT
ACTION_WriteEnvironmentStrings( MSIPACKAGE
*package
)
5328 static const WCHAR ExecSeqQuery
[] =
5329 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5330 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5331 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
5332 if (rc
!= ERROR_SUCCESS
)
5333 return ERROR_SUCCESS
;
5335 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteEnvironmentString
, package
);
5336 msiobj_release(&view
->hdr
);
5341 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5352 static BOOL
msi_move_file(LPCWSTR source
, LPCWSTR dest
, int options
)
5356 if (GetFileAttributesW(source
) == FILE_ATTRIBUTE_DIRECTORY
||
5357 GetFileAttributesW(dest
) == FILE_ATTRIBUTE_DIRECTORY
)
5359 WARN("Source or dest is directory, not moving\n");
5363 if (options
== msidbMoveFileOptionsMove
)
5365 TRACE("moving %s -> %s\n", debugstr_w(source
), debugstr_w(dest
));
5366 ret
= MoveFileExW(source
, dest
, MOVEFILE_REPLACE_EXISTING
);
5369 WARN("MoveFile failed: %d\n", GetLastError());
5375 TRACE("copying %s -> %s\n", debugstr_w(source
), debugstr_w(dest
));
5376 ret
= CopyFileW(source
, dest
, FALSE
);
5379 WARN("CopyFile failed: %d\n", GetLastError());
5387 static LPWSTR
wildcard_to_file(LPWSTR wildcard
, LPWSTR filename
)
5390 DWORD dirlen
, pathlen
;
5392 ptr
= strrchrW(wildcard
, '\\');
5393 dirlen
= ptr
- wildcard
+ 1;
5395 pathlen
= dirlen
+ lstrlenW(filename
) + 1;
5396 path
= msi_alloc(pathlen
* sizeof(WCHAR
));
5398 lstrcpynW(path
, wildcard
, dirlen
+ 1);
5399 lstrcatW(path
, filename
);
5404 static void free_file_entry(FILE_LIST
*file
)
5406 msi_free(file
->source
);
5407 msi_free(file
->dest
);
5411 static void free_list(FILE_LIST
*list
)
5413 while (!list_empty(&list
->entry
))
5415 FILE_LIST
*file
= LIST_ENTRY(list_head(&list
->entry
), FILE_LIST
, entry
);
5417 list_remove(&file
->entry
);
5418 free_file_entry(file
);
5422 static BOOL
add_wildcard(FILE_LIST
*files
, LPWSTR source
, LPWSTR dest
)
5424 FILE_LIST
*new, *file
;
5425 LPWSTR ptr
, filename
;
5428 new = msi_alloc_zero(sizeof(FILE_LIST
));
5432 new->source
= strdupW(source
);
5433 ptr
= strrchrW(dest
, '\\') + 1;
5434 filename
= strrchrW(new->source
, '\\') + 1;
5436 new->sourcename
= filename
;
5439 new->destname
= ptr
;
5441 new->destname
= new->sourcename
;
5443 size
= (ptr
- dest
) + lstrlenW(filename
) + 1;
5444 new->dest
= msi_alloc(size
* sizeof(WCHAR
));
5447 free_file_entry(new);
5451 lstrcpynW(new->dest
, dest
, ptr
- dest
+ 1);
5452 lstrcatW(new->dest
, filename
);
5454 if (list_empty(&files
->entry
))
5456 list_add_head(&files
->entry
, &new->entry
);
5460 LIST_FOR_EACH_ENTRY(file
, &files
->entry
, FILE_LIST
, entry
)
5462 if (lstrcmpW(source
, file
->source
) < 0)
5464 list_add_before(&file
->entry
, &new->entry
);
5469 list_add_after(&file
->entry
, &new->entry
);
5473 static BOOL
move_files_wildcard(LPWSTR source
, LPWSTR dest
, int options
)
5475 WIN32_FIND_DATAW wfd
;
5479 FILE_LIST files
, *file
;
5482 hfile
= FindFirstFileW(source
, &wfd
);
5483 if (hfile
== INVALID_HANDLE_VALUE
) return FALSE
;
5485 list_init(&files
.entry
);
5487 for (res
= TRUE
; res
; res
= FindNextFileW(hfile
, &wfd
))
5489 if (is_dot_dir(wfd
.cFileName
)) continue;
5491 path
= wildcard_to_file(source
, wfd
.cFileName
);
5498 add_wildcard(&files
, path
, dest
);
5502 /* no files match the wildcard */
5503 if (list_empty(&files
.entry
))
5506 /* only the first wildcard match gets renamed to dest */
5507 file
= LIST_ENTRY(list_head(&files
.entry
), FILE_LIST
, entry
);
5508 size
= (strrchrW(file
->dest
, '\\') - file
->dest
) + lstrlenW(file
->destname
) + 2;
5509 file
->dest
= msi_realloc(file
->dest
, size
* sizeof(WCHAR
));
5516 /* file->dest may be shorter after the reallocation, so add a NULL
5517 * terminator. This is needed for the call to strrchrW, as there will no
5518 * longer be a NULL terminator within the bounds of the allocation in this case.
5520 file
->dest
[size
- 1] = '\0';
5521 lstrcpyW(strrchrW(file
->dest
, '\\') + 1, file
->destname
);
5523 while (!list_empty(&files
.entry
))
5525 file
= LIST_ENTRY(list_head(&files
.entry
), FILE_LIST
, entry
);
5527 msi_move_file(file
->source
, file
->dest
, options
);
5529 list_remove(&file
->entry
);
5530 free_file_entry(file
);
5541 static UINT
ITERATE_MoveFiles( MSIRECORD
*rec
, LPVOID param
)
5543 MSIPACKAGE
*package
= param
;
5546 LPWSTR destname
= NULL
;
5547 LPWSTR sourcedir
= NULL
, destdir
= NULL
;
5548 LPWSTR source
= NULL
, dest
= NULL
;
5551 BOOL ret
, wildcards
;
5553 comp
= get_loaded_component(package
, MSI_RecordGetString(rec
, 2));
5554 if (!comp
|| !comp
->Enabled
||
5555 !(comp
->Action
& (INSTALLSTATE_LOCAL
| INSTALLSTATE_SOURCE
)))
5557 TRACE("Component not set for install, not moving file\n");
5558 return ERROR_SUCCESS
;
5561 sourcename
= MSI_RecordGetString(rec
, 3);
5562 options
= MSI_RecordGetInteger(rec
, 7);
5564 sourcedir
= msi_dup_property(package
, MSI_RecordGetString(rec
, 5));
5568 destdir
= msi_dup_property(package
, MSI_RecordGetString(rec
, 6));
5574 if (GetFileAttributesW(sourcedir
) == INVALID_FILE_ATTRIBUTES
)
5577 source
= strdupW(sourcedir
);
5583 size
= lstrlenW(sourcedir
) + lstrlenW(sourcename
) + 2;
5584 source
= msi_alloc(size
* sizeof(WCHAR
));
5588 lstrcpyW(source
, sourcedir
);
5589 if (source
[lstrlenW(source
) - 1] != '\\')
5590 lstrcatW(source
, szBackSlash
);
5591 lstrcatW(source
, sourcename
);
5594 wildcards
= strchrW(source
, '*') || strchrW(source
, '?');
5596 if (MSI_RecordIsNull(rec
, 4))
5600 destname
= strdupW(sourcename
);
5607 destname
= strdupW(MSI_RecordGetString(rec
, 4));
5609 reduce_to_longfilename(destname
);
5614 size
= lstrlenW(destname
);
5616 size
+= lstrlenW(destdir
) + 2;
5617 dest
= msi_alloc(size
* sizeof(WCHAR
));
5621 lstrcpyW(dest
, destdir
);
5622 if (dest
[lstrlenW(dest
) - 1] != '\\')
5623 lstrcatW(dest
, szBackSlash
);
5626 lstrcatW(dest
, destname
);
5628 if (GetFileAttributesW(destdir
) == INVALID_FILE_ATTRIBUTES
)
5630 ret
= CreateDirectoryW(destdir
, NULL
);
5633 WARN("CreateDirectory failed: %d\n", GetLastError());
5634 return ERROR_SUCCESS
;
5639 msi_move_file(source
, dest
, options
);
5641 move_files_wildcard(source
, dest
, options
);
5644 msi_free(sourcedir
);
5650 return ERROR_SUCCESS
;
5653 static UINT
ACTION_MoveFiles( MSIPACKAGE
*package
)
5658 static const WCHAR ExecSeqQuery
[] =
5659 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5660 '`','M','o','v','e','F','i','l','e','`',0};
5662 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
5663 if (rc
!= ERROR_SUCCESS
)
5664 return ERROR_SUCCESS
;
5666 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_MoveFiles
, package
);
5667 msiobj_release(&view
->hdr
);
5672 typedef struct tagMSIASSEMBLY
5675 MSICOMPONENT
*component
;
5676 MSIFEATURE
*feature
;
5684 static HRESULT (WINAPI
*pCreateAssemblyCache
)(IAssemblyCache
**ppAsmCache
,
5686 static HRESULT (WINAPI
*pLoadLibraryShim
)(LPCWSTR szDllName
, LPCWSTR szVersion
,
5687 LPVOID pvReserved
, HMODULE
*phModDll
);
5689 static BOOL
init_functionpointers(void)
5695 static const WCHAR szFusion
[] = {'f','u','s','i','o','n','.','d','l','l',0};
5697 hmscoree
= LoadLibraryA("mscoree.dll");
5700 WARN("mscoree.dll not available\n");
5704 pLoadLibraryShim
= (void *)GetProcAddress(hmscoree
, "LoadLibraryShim");
5705 if (!pLoadLibraryShim
)
5707 WARN("LoadLibraryShim not available\n");
5708 FreeLibrary(hmscoree
);
5712 hr
= pLoadLibraryShim(szFusion
, NULL
, NULL
, &hfusion
);
5715 WARN("fusion.dll not available\n");
5716 FreeLibrary(hmscoree
);
5720 pCreateAssemblyCache
= (void *)GetProcAddress(hfusion
, "CreateAssemblyCache");
5722 FreeLibrary(hmscoree
);
5726 static UINT
install_assembly(MSIPACKAGE
*package
, MSIASSEMBLY
*assembly
,
5729 IAssemblyCache
*cache
;
5731 UINT r
= ERROR_FUNCTION_FAILED
;
5733 TRACE("installing assembly: %s\n", debugstr_w(path
));
5735 if (assembly
->feature
)
5736 msi_feature_set_state(package
, assembly
->feature
, INSTALLSTATE_LOCAL
);
5738 if (assembly
->manifest
)
5739 FIXME("Manifest unhandled\n");
5741 if (assembly
->application
)
5743 FIXME("Assembly should be privately installed\n");
5744 return ERROR_SUCCESS
;
5747 if (assembly
->attributes
== msidbAssemblyAttributesWin32
)
5749 FIXME("Win32 assemblies not handled\n");
5750 return ERROR_SUCCESS
;
5753 hr
= pCreateAssemblyCache(&cache
, 0);
5757 hr
= IAssemblyCache_InstallAssembly(cache
, 0, path
, NULL
);
5759 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path
), hr
);
5764 IAssemblyCache_Release(cache
);
5768 typedef struct tagASSEMBLY_LIST
5770 MSIPACKAGE
*package
;
5771 IAssemblyCache
*cache
;
5772 struct list
*assemblies
;
5775 typedef struct tagASSEMBLY_NAME
5783 static UINT
parse_assembly_name(MSIRECORD
*rec
, LPVOID param
)
5785 ASSEMBLY_NAME
*asmname
= param
;
5786 LPCWSTR name
= MSI_RecordGetString(rec
, 2);
5787 LPWSTR val
= msi_dup_record_field(rec
, 3);
5789 static const WCHAR Name
[] = {'N','a','m','e',0};
5790 static const WCHAR Version
[] = {'V','e','r','s','i','o','n',0};
5791 static const WCHAR Culture
[] = {'C','u','l','t','u','r','e',0};
5792 static const WCHAR PublicKeyToken
[] = {
5793 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
5795 if (!strcmpiW(name
, Name
))
5796 asmname
->name
= val
;
5797 else if (!strcmpiW(name
, Version
))
5798 asmname
->version
= val
;
5799 else if (!strcmpiW(name
, Culture
))
5800 asmname
->culture
= val
;
5801 else if (!strcmpiW(name
, PublicKeyToken
))
5802 asmname
->pubkeytoken
= val
;
5806 return ERROR_SUCCESS
;
5809 static void append_str(LPWSTR
*str
, DWORD
*size
, LPCWSTR append
)
5813 *size
= lstrlenW(append
) + 1;
5814 *str
= msi_alloc((*size
) * sizeof(WCHAR
));
5815 lstrcpyW(*str
, append
);
5819 (*size
) += lstrlenW(append
);
5820 *str
= msi_realloc(*str
, (*size
) * sizeof(WCHAR
));
5821 lstrcatW(*str
, append
);
5824 static BOOL
check_assembly_installed(MSIDATABASE
*db
, IAssemblyCache
*cache
,
5827 ASSEMBLY_INFO asminfo
;
5835 static const WCHAR separator
[] = {',',' ',0};
5836 static const WCHAR Version
[] = {'V','e','r','s','i','o','n','=',0};
5837 static const WCHAR Culture
[] = {'C','u','l','t','u','r','e','=',0};
5838 static const WCHAR PublicKeyToken
[] = {
5839 'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
5840 static const WCHAR query
[] = {
5841 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5842 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
5843 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
5844 '=','\'','%','s','\'',0};
5848 ZeroMemory(&name
, sizeof(ASSEMBLY_NAME
));
5849 ZeroMemory(&asminfo
, sizeof(ASSEMBLY_INFO
));
5851 r
= MSI_OpenQuery(db
, &view
, query
, comp
->Component
);
5852 if (r
!= ERROR_SUCCESS
)
5853 return ERROR_SUCCESS
;
5855 MSI_IterateRecords(view
, NULL
, parse_assembly_name
, &name
);
5856 msiobj_release(&view
->hdr
);
5860 ERR("No assembly name specified!\n");
5864 append_str(&disp
, &size
, name
.name
);
5868 append_str(&disp
, &size
, separator
);
5869 append_str(&disp
, &size
, Version
);
5870 append_str(&disp
, &size
, name
.version
);
5875 append_str(&disp
, &size
, separator
);
5876 append_str(&disp
, &size
, Culture
);
5877 append_str(&disp
, &size
, name
.culture
);
5880 if (name
.pubkeytoken
)
5882 append_str(&disp
, &size
, separator
);
5883 append_str(&disp
, &size
, PublicKeyToken
);
5884 append_str(&disp
, &size
, name
.pubkeytoken
);
5887 asminfo
.cbAssemblyInfo
= sizeof(ASSEMBLY_INFO
);
5888 IAssemblyCache_QueryAssemblyInfo(cache
, QUERYASMINFO_FLAG_VALIDATE
,
5890 found
= (asminfo
.dwAssemblyFlags
== ASSEMBLYINFO_FLAG_INSTALLED
);
5894 msi_free(name
.name
);
5895 msi_free(name
.version
);
5896 msi_free(name
.culture
);
5897 msi_free(name
.pubkeytoken
);
5902 static UINT
load_assembly(MSIRECORD
*rec
, LPVOID param
)
5904 ASSEMBLY_LIST
*list
= param
;
5905 MSIASSEMBLY
*assembly
;
5907 assembly
= msi_alloc_zero(sizeof(MSIASSEMBLY
));
5909 return ERROR_OUTOFMEMORY
;
5911 assembly
->component
= get_loaded_component(list
->package
, MSI_RecordGetString(rec
, 1));
5913 if (!assembly
->component
|| !assembly
->component
->Enabled
||
5914 !(assembly
->component
->Action
& (INSTALLSTATE_LOCAL
| INSTALLSTATE_SOURCE
)))
5916 TRACE("Component not set for install, not publishing assembly\n");
5918 return ERROR_SUCCESS
;
5921 assembly
->feature
= find_feature_by_name(list
->package
, MSI_RecordGetString(rec
, 2));
5922 assembly
->file
= msi_find_file(list
->package
, assembly
->component
->KeyPath
);
5924 if (!assembly
->file
)
5926 ERR("File %s not found\n", debugstr_w(assembly
->component
->KeyPath
));
5927 return ERROR_FUNCTION_FAILED
;
5930 assembly
->manifest
= strdupW(MSI_RecordGetString(rec
, 3));
5931 assembly
->application
= strdupW(MSI_RecordGetString(rec
, 4));
5932 assembly
->attributes
= MSI_RecordGetInteger(rec
, 5);
5934 if (assembly
->application
)
5937 DWORD size
= sizeof(version
)/sizeof(WCHAR
);
5939 /* FIXME: we should probably check the manifest file here */
5941 if (!MsiGetFileVersionW(assembly
->file
->TargetPath
, version
, &size
, NULL
, NULL
) &&
5942 (!assembly
->file
->Version
|| strcmpW(version
, assembly
->file
->Version
) >= 0))
5944 assembly
->installed
= TRUE
;
5948 assembly
->installed
= check_assembly_installed(list
->package
->db
,
5950 assembly
->component
);
5952 list_add_head(list
->assemblies
, &assembly
->entry
);
5953 return ERROR_SUCCESS
;
5956 static UINT
load_assemblies(MSIPACKAGE
*package
, struct list
*assemblies
)
5958 IAssemblyCache
*cache
= NULL
;
5964 static const WCHAR query
[] =
5965 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5966 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5968 r
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
5969 if (r
!= ERROR_SUCCESS
)
5970 return ERROR_SUCCESS
;
5972 hr
= pCreateAssemblyCache(&cache
, 0);
5974 return ERROR_FUNCTION_FAILED
;
5976 list
.package
= package
;
5978 list
.assemblies
= assemblies
;
5980 r
= MSI_IterateRecords(view
, NULL
, load_assembly
, &list
);
5981 msiobj_release(&view
->hdr
);
5983 IAssemblyCache_Release(cache
);
5988 static void free_assemblies(struct list
*assemblies
)
5990 struct list
*item
, *cursor
;
5992 LIST_FOR_EACH_SAFE(item
, cursor
, assemblies
)
5994 MSIASSEMBLY
*assembly
= LIST_ENTRY(item
, MSIASSEMBLY
, entry
);
5996 list_remove(&assembly
->entry
);
5997 msi_free(assembly
->application
);
5998 msi_free(assembly
->manifest
);
6003 static BOOL
find_assembly(struct list
*assemblies
, LPCWSTR file
, MSIASSEMBLY
**out
)
6005 MSIASSEMBLY
*assembly
;
6007 LIST_FOR_EACH_ENTRY(assembly
, assemblies
, MSIASSEMBLY
, entry
)
6009 if (!lstrcmpW(assembly
->file
->File
, file
))
6019 static BOOL
installassembly_cb(MSIPACKAGE
*package
, LPCWSTR file
, DWORD action
,
6020 LPWSTR
*path
, DWORD
*attrs
, PVOID user
)
6022 MSIASSEMBLY
*assembly
;
6023 WCHAR temppath
[MAX_PATH
];
6024 struct list
*assemblies
= user
;
6027 if (!find_assembly(assemblies
, file
, &assembly
))
6030 GetTempPathW(MAX_PATH
, temppath
);
6031 PathAddBackslashW(temppath
);
6032 lstrcatW(temppath
, assembly
->file
->FileName
);
6034 if (action
== MSICABEXTRACT_BEGINEXTRACT
)
6036 if (assembly
->installed
)
6039 *path
= strdupW(temppath
);
6040 *attrs
= assembly
->file
->Attributes
;
6042 else if (action
== MSICABEXTRACT_FILEEXTRACTED
)
6044 assembly
->installed
= TRUE
;
6046 r
= install_assembly(package
, assembly
, temppath
);
6047 if (r
!= ERROR_SUCCESS
)
6048 ERR("Failed to install assembly\n");
6054 static UINT
ACTION_MsiPublishAssemblies( MSIPACKAGE
*package
)
6057 struct list assemblies
= LIST_INIT(assemblies
);
6058 MSIASSEMBLY
*assembly
;
6061 if (!init_functionpointers() || !pCreateAssemblyCache
)
6062 return ERROR_FUNCTION_FAILED
;
6064 r
= load_assemblies(package
, &assemblies
);
6065 if (r
!= ERROR_SUCCESS
)
6068 if (list_empty(&assemblies
))
6071 mi
= msi_alloc_zero(sizeof(MSIMEDIAINFO
));
6074 r
= ERROR_OUTOFMEMORY
;
6078 LIST_FOR_EACH_ENTRY(assembly
, &assemblies
, MSIASSEMBLY
, entry
)
6080 if (assembly
->installed
&& !mi
->is_continuous
)
6083 if (assembly
->file
->Sequence
> mi
->last_sequence
|| mi
->is_continuous
||
6084 (assembly
->file
->IsCompressed
&& !mi
->is_extracted
))
6088 r
= ready_media(package
, assembly
->file
, mi
);
6089 if (r
!= ERROR_SUCCESS
)
6091 ERR("Failed to ready media\n");
6096 data
.package
= package
;
6097 data
.cb
= installassembly_cb
;
6098 data
.user
= &assemblies
;
6100 if (assembly
->file
->IsCompressed
&&
6101 !msi_cabextract(package
, mi
, &data
))
6103 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi
->cabinet
));
6104 r
= ERROR_FUNCTION_FAILED
;
6109 if (!assembly
->file
->IsCompressed
)
6111 LPWSTR source
= resolve_file_source(package
, assembly
->file
);
6113 r
= install_assembly(package
, assembly
, source
);
6114 if (r
!= ERROR_SUCCESS
)
6115 ERR("Failed to install assembly\n");
6120 /* FIXME: write Installer assembly reg values */
6124 free_assemblies(&assemblies
);
6128 static UINT
ACTION_ScheduleReboot( MSIPACKAGE
*package
)
6131 package
->need_reboot
= 1;
6132 return ERROR_SUCCESS
;
6135 static UINT
msi_unimplemented_action_stub( MSIPACKAGE
*package
,
6136 LPCSTR action
, LPCWSTR table
)
6138 static const WCHAR query
[] = {
6139 'S','E','L','E','C','T',' ','*',' ',
6140 'F','R','O','M',' ','`','%','s','`',0 };
6141 MSIQUERY
*view
= NULL
;
6145 r
= MSI_OpenQuery( package
->db
, &view
, query
, table
);
6146 if (r
== ERROR_SUCCESS
)
6148 r
= MSI_IterateRecords(view
, &count
, NULL
, package
);
6149 msiobj_release(&view
->hdr
);
6153 FIXME("%s -> %u ignored %s table values\n",
6154 action
, count
, debugstr_w(table
));
6156 return ERROR_SUCCESS
;
6159 static UINT
ACTION_AllocateRegistrySpace( MSIPACKAGE
*package
)
6161 TRACE("%p\n", package
);
6162 return ERROR_SUCCESS
;
6165 static UINT
ACTION_RemoveIniValues( MSIPACKAGE
*package
)
6167 static const WCHAR table
[] =
6168 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6169 return msi_unimplemented_action_stub( package
, "RemoveIniValues", table
);
6172 static UINT
ACTION_PatchFiles( MSIPACKAGE
*package
)
6174 static const WCHAR table
[] = { 'P','a','t','c','h',0 };
6175 return msi_unimplemented_action_stub( package
, "PatchFiles", table
);
6178 static UINT
ACTION_BindImage( MSIPACKAGE
*package
)
6180 static const WCHAR table
[] = { 'B','i','n','d','I','m','a','g','e',0 };
6181 return msi_unimplemented_action_stub( package
, "BindImage", table
);
6184 static UINT
ACTION_IsolateComponents( MSIPACKAGE
*package
)
6186 static const WCHAR table
[] = {
6187 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6188 return msi_unimplemented_action_stub( package
, "IsolateComponents", table
);
6191 static UINT
ACTION_MigrateFeatureStates( MSIPACKAGE
*package
)
6193 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
6194 return msi_unimplemented_action_stub( package
, "MigrateFeatureStates", table
);
6197 static UINT
ACTION_ValidateProductID( MSIPACKAGE
*package
)
6199 static const WCHAR table
[] = {
6200 'P','r','o','d','u','c','t','I','D',0 };
6201 return msi_unimplemented_action_stub( package
, "ValidateProductID", table
);
6204 static UINT
ACTION_RemoveEnvironmentStrings( MSIPACKAGE
*package
)
6206 static const WCHAR table
[] = {
6207 'E','n','v','i','r','o','n','m','e','n','t',0 };
6208 return msi_unimplemented_action_stub( package
, "RemoveEnvironmentStrings", table
);
6211 static UINT
ACTION_MsiUnpublishAssemblies( MSIPACKAGE
*package
)
6213 static const WCHAR table
[] = {
6214 'M','s','i','A','s','s','e','m','b','l','y',0 };
6215 return msi_unimplemented_action_stub( package
, "MsiUnpublishAssemblies", table
);
6218 static UINT
ACTION_UnregisterFonts( MSIPACKAGE
*package
)
6220 static const WCHAR table
[] = { 'F','o','n','t',0 };
6221 return msi_unimplemented_action_stub( package
, "UnregisterFonts", table
);
6224 static UINT
ACTION_RMCCPSearch( MSIPACKAGE
*package
)
6226 static const WCHAR table
[] = { 'C','C','P','S','e','a','r','c','h',0 };
6227 return msi_unimplemented_action_stub( package
, "RMCCPSearch", table
);
6230 static UINT
ACTION_RegisterComPlus( MSIPACKAGE
*package
)
6232 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
6233 return msi_unimplemented_action_stub( package
, "RegisterComPlus", table
);
6236 static UINT
ACTION_UnregisterComPlus( MSIPACKAGE
*package
)
6238 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
6239 return msi_unimplemented_action_stub( package
, "UnregisterComPlus", table
);
6242 static UINT
ACTION_InstallSFPCatalogFile( MSIPACKAGE
*package
)
6244 static const WCHAR table
[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6245 return msi_unimplemented_action_stub( package
, "InstallSFPCatalogFile", table
);
6248 static UINT
ACTION_RemoveDuplicateFiles( MSIPACKAGE
*package
)
6250 static const WCHAR table
[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6251 return msi_unimplemented_action_stub( package
, "RemoveDuplicateFiles", table
);
6254 static UINT
ACTION_RemoveExistingProducts( MSIPACKAGE
*package
)
6256 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
6257 return msi_unimplemented_action_stub( package
, "RemoveExistingProducts", table
);
6260 static UINT
ACTION_RemoveODBC( MSIPACKAGE
*package
)
6262 static const WCHAR table
[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
6263 return msi_unimplemented_action_stub( package
, "RemoveODBC", table
);
6266 static UINT
ACTION_RemoveRegistryValues( MSIPACKAGE
*package
)
6268 static const WCHAR table
[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6269 return msi_unimplemented_action_stub( package
, "RemoveRegistryValues", table
);
6272 static UINT
ACTION_RemoveShortcuts( MSIPACKAGE
*package
)
6274 static const WCHAR table
[] = { 'S','h','o','r','t','c','u','t',0 };
6275 return msi_unimplemented_action_stub( package
, "RemoveShortcuts", table
);
6278 static UINT
ACTION_SetODBCFolders( MSIPACKAGE
*package
)
6280 static const WCHAR table
[] = { 'D','i','r','e','c','t','o','r','y',0 };
6281 return msi_unimplemented_action_stub( package
, "SetODBCFolders", table
);
6284 static UINT
ACTION_UnpublishComponents( MSIPACKAGE
*package
)
6286 static const WCHAR table
[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6287 return msi_unimplemented_action_stub( package
, "UnpublishComponents", table
);
6290 static UINT
ACTION_UnregisterClassInfo( MSIPACKAGE
*package
)
6292 static const WCHAR table
[] = { 'A','p','p','I','d',0 };
6293 return msi_unimplemented_action_stub( package
, "UnregisterClassInfo", table
);
6296 static UINT
ACTION_UnregisterExtensionInfo( MSIPACKAGE
*package
)
6298 static const WCHAR table
[] = { 'E','x','t','e','n','s','i','o','n',0 };
6299 return msi_unimplemented_action_stub( package
, "UnregisterExtensionInfo", table
);
6302 static UINT
ACTION_UnregisterMIMEInfo( MSIPACKAGE
*package
)
6304 static const WCHAR table
[] = { 'M','I','M','E',0 };
6305 return msi_unimplemented_action_stub( package
, "UnregisterMIMEInfo", table
);
6308 static UINT
ACTION_UnregisterProgIdInfo( MSIPACKAGE
*package
)
6310 static const WCHAR table
[] = { 'P','r','o','g','I','d',0 };
6311 return msi_unimplemented_action_stub( package
, "UnregisterProgIdInfo", table
);
6314 static UINT
ACTION_UnregisterTypeLibraries( MSIPACKAGE
*package
)
6316 static const WCHAR table
[] = { 'T','y','p','e','L','i','b',0 };
6317 return msi_unimplemented_action_stub( package
, "UnregisterTypeLibraries", table
);
6320 typedef UINT (*STANDARDACTIONHANDLER
)(MSIPACKAGE
*);
6324 const WCHAR
*action
;
6325 UINT (*handler
)(MSIPACKAGE
*);
6329 { szAllocateRegistrySpace
, ACTION_AllocateRegistrySpace
},
6330 { szAppSearch
, ACTION_AppSearch
},
6331 { szBindImage
, ACTION_BindImage
},
6332 { szCCPSearch
, ACTION_CCPSearch
},
6333 { szCostFinalize
, ACTION_CostFinalize
},
6334 { szCostInitialize
, ACTION_CostInitialize
},
6335 { szCreateFolders
, ACTION_CreateFolders
},
6336 { szCreateShortcuts
, ACTION_CreateShortcuts
},
6337 { szDeleteServices
, ACTION_DeleteServices
},
6338 { szDisableRollback
, NULL
},
6339 { szDuplicateFiles
, ACTION_DuplicateFiles
},
6340 { szExecuteAction
, ACTION_ExecuteAction
},
6341 { szFileCost
, ACTION_FileCost
},
6342 { szFindRelatedProducts
, ACTION_FindRelatedProducts
},
6343 { szForceReboot
, ACTION_ForceReboot
},
6344 { szInstallAdminPackage
, NULL
},
6345 { szInstallExecute
, ACTION_InstallExecute
},
6346 { szInstallExecuteAgain
, ACTION_InstallExecute
},
6347 { szInstallFiles
, ACTION_InstallFiles
},
6348 { szInstallFinalize
, ACTION_InstallFinalize
},
6349 { szInstallInitialize
, ACTION_InstallInitialize
},
6350 { szInstallSFPCatalogFile
, ACTION_InstallSFPCatalogFile
},
6351 { szInstallValidate
, ACTION_InstallValidate
},
6352 { szIsolateComponents
, ACTION_IsolateComponents
},
6353 { szLaunchConditions
, ACTION_LaunchConditions
},
6354 { szMigrateFeatureStates
, ACTION_MigrateFeatureStates
},
6355 { szMoveFiles
, ACTION_MoveFiles
},
6356 { szMsiPublishAssemblies
, ACTION_MsiPublishAssemblies
},
6357 { szMsiUnpublishAssemblies
, ACTION_MsiUnpublishAssemblies
},
6358 { szInstallODBC
, ACTION_InstallODBC
},
6359 { szInstallServices
, ACTION_InstallServices
},
6360 { szPatchFiles
, ACTION_PatchFiles
},
6361 { szProcessComponents
, ACTION_ProcessComponents
},
6362 { szPublishComponents
, ACTION_PublishComponents
},
6363 { szPublishFeatures
, ACTION_PublishFeatures
},
6364 { szPublishProduct
, ACTION_PublishProduct
},
6365 { szRegisterClassInfo
, ACTION_RegisterClassInfo
},
6366 { szRegisterComPlus
, ACTION_RegisterComPlus
},
6367 { szRegisterExtensionInfo
, ACTION_RegisterExtensionInfo
},
6368 { szRegisterFonts
, ACTION_RegisterFonts
},
6369 { szRegisterMIMEInfo
, ACTION_RegisterMIMEInfo
},
6370 { szRegisterProduct
, ACTION_RegisterProduct
},
6371 { szRegisterProgIdInfo
, ACTION_RegisterProgIdInfo
},
6372 { szRegisterTypeLibraries
, ACTION_RegisterTypeLibraries
},
6373 { szRegisterUser
, ACTION_RegisterUser
},
6374 { szRemoveDuplicateFiles
, ACTION_RemoveDuplicateFiles
},
6375 { szRemoveEnvironmentStrings
, ACTION_RemoveEnvironmentStrings
},
6376 { szRemoveExistingProducts
, ACTION_RemoveExistingProducts
},
6377 { szRemoveFiles
, ACTION_RemoveFiles
},
6378 { szRemoveFolders
, ACTION_RemoveFolders
},
6379 { szRemoveIniValues
, ACTION_RemoveIniValues
},
6380 { szRemoveODBC
, ACTION_RemoveODBC
},
6381 { szRemoveRegistryValues
, ACTION_RemoveRegistryValues
},
6382 { szRemoveShortcuts
, ACTION_RemoveShortcuts
},
6383 { szResolveSource
, ACTION_ResolveSource
},
6384 { szRMCCPSearch
, ACTION_RMCCPSearch
},
6385 { szScheduleReboot
, ACTION_ScheduleReboot
},
6386 { szSelfRegModules
, ACTION_SelfRegModules
},
6387 { szSelfUnregModules
, ACTION_SelfUnregModules
},
6388 { szSetODBCFolders
, ACTION_SetODBCFolders
},
6389 { szStartServices
, ACTION_StartServices
},
6390 { szStopServices
, ACTION_StopServices
},
6391 { szUnpublishComponents
, ACTION_UnpublishComponents
},
6392 { szUnpublishFeatures
, ACTION_UnpublishFeatures
},
6393 { szUnregisterClassInfo
, ACTION_UnregisterClassInfo
},
6394 { szUnregisterComPlus
, ACTION_UnregisterComPlus
},
6395 { szUnregisterExtensionInfo
, ACTION_UnregisterExtensionInfo
},
6396 { szUnregisterFonts
, ACTION_UnregisterFonts
},
6397 { szUnregisterMIMEInfo
, ACTION_UnregisterMIMEInfo
},
6398 { szUnregisterProgIdInfo
, ACTION_UnregisterProgIdInfo
},
6399 { szUnregisterTypeLibraries
, ACTION_UnregisterTypeLibraries
},
6400 { szValidateProductID
, ACTION_ValidateProductID
},
6401 { szWriteEnvironmentStrings
, ACTION_WriteEnvironmentStrings
},
6402 { szWriteIniValues
, ACTION_WriteIniValues
},
6403 { szWriteRegistryValues
, ACTION_WriteRegistryValues
},
6407 static BOOL
ACTION_HandleStandardAction(MSIPACKAGE
*package
, LPCWSTR action
,
6408 UINT
* rc
, BOOL force
)
6414 if (!run
&& !package
->script
->CurrentlyScripting
)
6419 if (strcmpW(action
,szInstallFinalize
) == 0 ||
6420 strcmpW(action
,szInstallExecute
) == 0 ||
6421 strcmpW(action
,szInstallExecuteAgain
) == 0)
6426 while (StandardActions
[i
].action
!= NULL
)
6428 if (strcmpW(StandardActions
[i
].action
, action
)==0)
6432 ui_actioninfo(package
, action
, TRUE
, 0);
6433 *rc
= schedule_action(package
,INSTALL_SCRIPT
,action
);
6434 ui_actioninfo(package
, action
, FALSE
, *rc
);
6438 ui_actionstart(package
, action
);
6439 if (StandardActions
[i
].handler
)
6441 *rc
= StandardActions
[i
].handler(package
);
6445 FIXME("unhandled standard action %s\n",debugstr_w(action
));
6446 *rc
= ERROR_SUCCESS
;
6457 UINT
ACTION_PerformAction(MSIPACKAGE
*package
, const WCHAR
*action
, UINT script
, BOOL force
)
6459 UINT rc
= ERROR_SUCCESS
;
6462 TRACE("Performing action (%s)\n", debugstr_w(action
));
6464 handled
= ACTION_HandleStandardAction(package
, action
, &rc
, force
);
6467 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, script
, force
);
6471 WARN("unhandled msi action %s\n", debugstr_w(action
));
6472 rc
= ERROR_FUNCTION_NOT_CALLED
;
6478 UINT
ACTION_PerformUIAction(MSIPACKAGE
*package
, const WCHAR
*action
, UINT script
)
6480 UINT rc
= ERROR_SUCCESS
;
6481 BOOL handled
= FALSE
;
6483 TRACE("Performing action (%s)\n", debugstr_w(action
));
6485 handled
= ACTION_HandleStandardAction(package
, action
, &rc
,TRUE
);
6488 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, script
, FALSE
);
6490 if( !handled
&& ACTION_DialogBox(package
, action
) == ERROR_SUCCESS
)
6495 WARN("unhandled msi action %s\n", debugstr_w(action
));
6496 rc
= ERROR_FUNCTION_NOT_CALLED
;
6502 static UINT
ACTION_PerformActionSequence(MSIPACKAGE
*package
, UINT seq
)
6504 UINT rc
= ERROR_SUCCESS
;
6507 static const WCHAR ExecSeqQuery
[] =
6508 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6509 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
6510 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
6511 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
6512 static const WCHAR UISeqQuery
[] =
6513 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6514 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
6515 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
6516 ' ', '=',' ','%','i',0};
6518 if (needs_ui_sequence(package
))
6519 row
= MSI_QueryGetRecord(package
->db
, UISeqQuery
, seq
);
6521 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
, seq
);
6525 LPCWSTR action
, cond
;
6527 TRACE("Running the actions\n");
6529 /* check conditions */
6530 cond
= MSI_RecordGetString(row
, 2);
6532 /* this is a hack to skip errors in the condition code */
6533 if (MSI_EvaluateConditionW(package
, cond
) == MSICONDITION_FALSE
)
6535 msiobj_release(&row
->hdr
);
6536 return ERROR_SUCCESS
;
6539 action
= MSI_RecordGetString(row
, 1);
6542 ERR("failed to fetch action\n");
6543 msiobj_release(&row
->hdr
);
6544 return ERROR_FUNCTION_FAILED
;
6547 if (needs_ui_sequence(package
))
6548 rc
= ACTION_PerformUIAction(package
, action
, -1);
6550 rc
= ACTION_PerformAction(package
, action
, -1, FALSE
);
6552 msiobj_release(&row
->hdr
);
6558 /****************************************************
6559 * TOP level entry points
6560 *****************************************************/
6562 UINT
MSI_InstallPackage( MSIPACKAGE
*package
, LPCWSTR szPackagePath
,
6563 LPCWSTR szCommandLine
)
6568 static const WCHAR szAction
[] = {'A','C','T','I','O','N',0};
6569 static const WCHAR szInstall
[] = {'I','N','S','T','A','L','L',0};
6571 MSI_SetPropertyW(package
, szAction
, szInstall
);
6573 package
->script
->InWhatSequence
= SEQUENCE_INSTALL
;
6580 dir
= strdupW(szPackagePath
);
6581 p
= strrchrW(dir
, '\\');
6585 file
= szPackagePath
+ (p
- dir
);
6590 dir
= msi_alloc(MAX_PATH
* sizeof(WCHAR
));
6591 GetCurrentDirectoryW(MAX_PATH
, dir
);
6592 lstrcatW(dir
, szBackSlash
);
6593 file
= szPackagePath
;
6596 msi_free( package
->PackagePath
);
6597 package
->PackagePath
= msi_alloc((lstrlenW(dir
) + lstrlenW(file
) + 1) * sizeof(WCHAR
));
6598 if (!package
->PackagePath
)
6601 return ERROR_OUTOFMEMORY
;
6604 lstrcpyW(package
->PackagePath
, dir
);
6605 lstrcatW(package
->PackagePath
, file
);
6608 msi_set_sourcedir_props(package
, FALSE
);
6611 msi_parse_command_line( package
, szCommandLine
, FALSE
);
6613 msi_apply_transforms( package
);
6614 msi_apply_patches( package
);
6616 if (!szCommandLine
&& msi_get_property_int( package
, szInstalled
, 0 ))
6618 TRACE("setting reinstall property\n");
6619 MSI_SetPropertyW( package
, szReinstall
, szAll
);
6622 /* properties may have been added by a transform */
6623 msi_clone_properties( package
);
6624 msi_set_context( package
);
6626 if (needs_ui_sequence( package
))
6628 package
->script
->InWhatSequence
|= SEQUENCE_UI
;
6629 rc
= ACTION_ProcessUISequence(package
);
6630 ui_exists
= ui_sequence_exists(package
);
6631 if (rc
== ERROR_SUCCESS
|| !ui_exists
)
6633 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
6634 rc
= ACTION_ProcessExecSequence(package
, ui_exists
);
6638 rc
= ACTION_ProcessExecSequence(package
, FALSE
);
6640 package
->script
->CurrentlyScripting
= FALSE
;
6642 /* process the ending type action */
6643 if (rc
== ERROR_SUCCESS
)
6644 ACTION_PerformActionSequence(package
, -1);
6645 else if (rc
== ERROR_INSTALL_USEREXIT
)
6646 ACTION_PerformActionSequence(package
, -2);
6647 else if (rc
== ERROR_INSTALL_SUSPEND
)
6648 ACTION_PerformActionSequence(package
, -4);
6650 ACTION_PerformActionSequence(package
, -3);
6652 /* finish up running custom actions */
6653 ACTION_FinishCustomActions(package
);
6655 if (rc
== ERROR_SUCCESS
&& package
->need_reboot
)
6656 return ERROR_SUCCESS_REBOOT_REQUIRED
;