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"
36 #include "wine/unicode.h"
39 #define REG_PROGRESS_VALUE 13200
40 #define COMPONENT_PROGRESS_VALUE 24000
42 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
47 static UINT
ACTION_ProcessExecSequence(MSIPACKAGE
*package
, BOOL UIran
);
48 static UINT
ACTION_ProcessUISequence(MSIPACKAGE
*package
);
49 static UINT
ACTION_PerformActionSequence(MSIPACKAGE
*package
, UINT seq
, BOOL UI
);
50 static BOOL
ACTION_HandleStandardAction(MSIPACKAGE
*package
, LPCWSTR action
, UINT
* rc
, BOOL force
);
53 * consts and values used
55 static const WCHAR c_colon
[] = {'C',':','\\',0};
57 static const WCHAR szCreateFolders
[] =
58 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
59 static const WCHAR szCostFinalize
[] =
60 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
61 const WCHAR szInstallFiles
[] =
62 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
63 const WCHAR szDuplicateFiles
[] =
64 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
65 static const WCHAR szWriteRegistryValues
[] =
66 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
67 'V','a','l','u','e','s',0};
68 static const WCHAR szCostInitialize
[] =
69 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
70 static const WCHAR szFileCost
[] =
71 {'F','i','l','e','C','o','s','t',0};
72 static const WCHAR szInstallInitialize
[] =
73 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
74 static const WCHAR szInstallValidate
[] =
75 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
76 static const WCHAR szLaunchConditions
[] =
77 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
78 static const WCHAR szProcessComponents
[] =
79 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
80 static const WCHAR szRegisterTypeLibraries
[] =
81 {'R','e','g','i','s','t','e','r','T','y','p','e',
82 'L','i','b','r','a','r','i','e','s',0};
83 const WCHAR szRegisterClassInfo
[] =
84 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
85 const WCHAR szRegisterProgIdInfo
[] =
86 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
87 static const WCHAR szCreateShortcuts
[] =
88 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
89 static const WCHAR szPublishProduct
[] =
90 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
91 static const WCHAR szWriteIniValues
[] =
92 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
93 static const WCHAR szSelfRegModules
[] =
94 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
95 static const WCHAR szPublishFeatures
[] =
96 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
97 static const WCHAR szRegisterProduct
[] =
98 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
99 static const WCHAR szInstallExecute
[] =
100 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
101 static const WCHAR szInstallExecuteAgain
[] =
102 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
103 'A','g','a','i','n',0};
104 static const WCHAR szInstallFinalize
[] =
105 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
106 static const WCHAR szForceReboot
[] =
107 {'F','o','r','c','e','R','e','b','o','o','t',0};
108 static const WCHAR szResolveSource
[] =
109 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
110 static const WCHAR szAppSearch
[] =
111 {'A','p','p','S','e','a','r','c','h',0};
112 static const WCHAR szAllocateRegistrySpace
[] =
113 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
114 'S','p','a','c','e',0};
115 static const WCHAR szBindImage
[] =
116 {'B','i','n','d','I','m','a','g','e',0};
117 static const WCHAR szCCPSearch
[] =
118 {'C','C','P','S','e','a','r','c','h',0};
119 static const WCHAR szDeleteServices
[] =
120 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
121 static const WCHAR szDisableRollback
[] =
122 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
123 static const WCHAR szExecuteAction
[] =
124 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
125 const WCHAR szFindRelatedProducts
[] =
126 {'F','i','n','d','R','e','l','a','t','e','d',
127 'P','r','o','d','u','c','t','s',0};
128 static const WCHAR szInstallAdminPackage
[] =
129 {'I','n','s','t','a','l','l','A','d','m','i','n',
130 'P','a','c','k','a','g','e',0};
131 static const WCHAR szInstallSFPCatalogFile
[] =
132 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
134 static const WCHAR szIsolateComponents
[] =
135 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
136 const WCHAR szMigrateFeatureStates
[] =
137 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
138 'S','t','a','t','e','s',0};
139 const WCHAR szMoveFiles
[] =
140 {'M','o','v','e','F','i','l','e','s',0};
141 static const WCHAR szMsiPublishAssemblies
[] =
142 {'M','s','i','P','u','b','l','i','s','h',
143 'A','s','s','e','m','b','l','i','e','s',0};
144 static const WCHAR szMsiUnpublishAssemblies
[] =
145 {'M','s','i','U','n','p','u','b','l','i','s','h',
146 'A','s','s','e','m','b','l','i','e','s',0};
147 static const WCHAR szInstallODBC
[] =
148 {'I','n','s','t','a','l','l','O','D','B','C',0};
149 static const WCHAR szInstallServices
[] =
150 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
151 const WCHAR szPatchFiles
[] =
152 {'P','a','t','c','h','F','i','l','e','s',0};
153 static const WCHAR szPublishComponents
[] =
154 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
155 static const WCHAR szRegisterComPlus
[] =
156 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
157 const WCHAR szRegisterExtensionInfo
[] =
158 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
160 static const WCHAR szRegisterFonts
[] =
161 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
162 const WCHAR szRegisterMIMEInfo
[] =
163 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
164 static const WCHAR szRegisterUser
[] =
165 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
166 const WCHAR szRemoveDuplicateFiles
[] =
167 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
168 'F','i','l','e','s',0};
169 static const WCHAR szRemoveEnvironmentStrings
[] =
170 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
171 'S','t','r','i','n','g','s',0};
172 const WCHAR szRemoveExistingProducts
[] =
173 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
174 'P','r','o','d','u','c','t','s',0};
175 const WCHAR szRemoveFiles
[] =
176 {'R','e','m','o','v','e','F','i','l','e','s',0};
177 static const WCHAR szRemoveFolders
[] =
178 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
179 static const WCHAR szRemoveIniValues
[] =
180 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
181 static const WCHAR szRemoveODBC
[] =
182 {'R','e','m','o','v','e','O','D','B','C',0};
183 static const WCHAR szRemoveRegistryValues
[] =
184 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
185 'V','a','l','u','e','s',0};
186 static const WCHAR szRemoveShortcuts
[] =
187 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
188 static const WCHAR szRMCCPSearch
[] =
189 {'R','M','C','C','P','S','e','a','r','c','h',0};
190 static const WCHAR szScheduleReboot
[] =
191 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
192 static const WCHAR szSelfUnregModules
[] =
193 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
194 static const WCHAR szSetODBCFolders
[] =
195 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
196 static const WCHAR szStartServices
[] =
197 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
198 static const WCHAR szStopServices
[] =
199 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
200 static const WCHAR szUnpublishComponents
[] =
201 {'U','n','p','u','b','l','i','s','h',
202 'C','o','m','p','o','n','e','n','t','s',0};
203 static const WCHAR szUnpublishFeatures
[] =
204 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
205 const WCHAR szUnregisterClassInfo
[] =
206 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
208 static const WCHAR szUnregisterComPlus
[] =
209 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
210 const WCHAR szUnregisterExtensionInfo
[] =
211 {'U','n','r','e','g','i','s','t','e','r',
212 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
213 static const WCHAR szUnregisterFonts
[] =
214 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
215 const WCHAR szUnregisterMIMEInfo
[] =
216 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
217 const WCHAR szUnregisterProgIdInfo
[] =
218 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
220 static const WCHAR szUnregisterTypeLibraries
[] =
221 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
222 'L','i','b','r','a','r','i','e','s',0};
223 static const WCHAR szValidateProductID
[] =
224 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
225 static const WCHAR szWriteEnvironmentStrings
[] =
226 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
227 'S','t','r','i','n','g','s',0};
229 /* action handlers */
230 typedef UINT (*STANDARDACTIONHANDLER
)(MSIPACKAGE
*);
234 STANDARDACTIONHANDLER handler
;
238 /********************************************************
240 ********************************************************/
242 static void ui_actionstart(MSIPACKAGE
*package
, LPCWSTR action
)
244 static const WCHAR Query_t
[] =
245 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
246 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
247 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
248 ' ','\'','%','s','\'',0};
251 row
= MSI_QueryGetRecord( package
->db
, Query_t
, action
);
254 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONSTART
, row
);
255 msiobj_release(&row
->hdr
);
258 static void ui_actioninfo(MSIPACKAGE
*package
, LPCWSTR action
, BOOL start
,
262 static const WCHAR template_s
[]=
263 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
265 static const WCHAR template_e
[]=
266 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
267 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
269 static const WCHAR format
[] =
270 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
274 GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, NULL
, format
, timet
, 0x100);
276 sprintfW(message
,template_s
,timet
,action
);
278 sprintfW(message
,template_e
,timet
,action
,rc
);
280 row
= MSI_CreateRecord(1);
281 MSI_RecordSetStringW(row
,1,message
);
283 MSI_ProcessMessage(package
, INSTALLMESSAGE_INFO
, row
);
284 msiobj_release(&row
->hdr
);
287 UINT
msi_parse_command_line( MSIPACKAGE
*package
, LPCWSTR szCommandLine
)
292 LPWSTR prop
= NULL
, val
= NULL
;
295 return ERROR_SUCCESS
;
307 TRACE("Looking at %s\n",debugstr_w(ptr
));
309 ptr2
= strchrW(ptr
,'=');
312 ERR("command line contains unknown string : %s\n", debugstr_w(ptr
));
319 prop
= msi_alloc((len
+1)*sizeof(WCHAR
));
320 memcpy(prop
,ptr
,len
*sizeof(WCHAR
));
326 while (*ptr
&& (quote
|| (!quote
&& *ptr
!=' ')))
339 val
= msi_alloc((len
+1)*sizeof(WCHAR
));
340 memcpy(val
,ptr2
,len
*sizeof(WCHAR
));
343 if (lstrlenW(prop
) > 0)
345 TRACE("Found commandline property (%s) = (%s)\n",
346 debugstr_w(prop
), debugstr_w(val
));
347 MSI_SetPropertyW(package
,prop
,val
);
353 return ERROR_SUCCESS
;
357 static LPWSTR
* msi_split_string( LPCWSTR str
, WCHAR sep
)
360 LPWSTR p
, *ret
= NULL
;
366 /* count the number of substrings */
367 for ( pc
= str
, count
= 0; pc
; count
++ )
369 pc
= strchrW( pc
, sep
);
374 /* allocate space for an array of substring pointers and the substrings */
375 ret
= msi_alloc( (count
+1) * sizeof (LPWSTR
) +
376 (lstrlenW(str
)+1) * sizeof(WCHAR
) );
380 /* copy the string and set the pointers */
381 p
= (LPWSTR
) &ret
[count
+1];
383 for( count
= 0; (ret
[count
] = p
); count
++ )
385 p
= strchrW( p
, sep
);
393 static UINT
msi_check_transform_applicable( MSIPACKAGE
*package
, IStorage
*patch
)
395 WCHAR szProductCode
[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
396 LPWSTR prod_code
, patch_product
;
399 prod_code
= msi_dup_property( package
, szProductCode
);
400 patch_product
= msi_get_suminfo_product( patch
);
402 TRACE("db = %s patch = %s\n", debugstr_w(prod_code
), debugstr_w(patch_product
));
404 if ( strstrW( patch_product
, prod_code
) )
407 ret
= ERROR_FUNCTION_FAILED
;
409 msi_free( patch_product
);
410 msi_free( prod_code
);
415 static UINT
msi_apply_substorage_transform( MSIPACKAGE
*package
,
416 MSIDATABASE
*patch_db
, LPCWSTR name
)
418 UINT ret
= ERROR_FUNCTION_FAILED
;
419 IStorage
*stg
= NULL
;
422 TRACE("%p %s\n", package
, debugstr_w(name
) );
426 ERR("expected a colon in %s\n", debugstr_w(name
));
427 return ERROR_FUNCTION_FAILED
;
430 r
= IStorage_OpenStorage( patch_db
->storage
, name
, NULL
, STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
433 ret
= msi_check_transform_applicable( package
, stg
);
434 if (ret
== ERROR_SUCCESS
)
435 msi_table_apply_transform( package
->db
, stg
);
437 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name
));
438 IStorage_Release( stg
);
441 ERR("failed to open substorage %s\n", debugstr_w(name
));
443 return ERROR_SUCCESS
;
446 static UINT
msi_check_patch_applicable( MSIPACKAGE
*package
, MSISUMMARYINFO
*si
)
448 static const WCHAR szProdCode
[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
449 LPWSTR guid_list
, *guids
, product_code
;
450 UINT i
, ret
= ERROR_FUNCTION_FAILED
;
452 product_code
= msi_dup_property( package
, szProdCode
);
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_parse_patch_summary( MSIPACKAGE
*package
, MSIDATABASE
*patch_db
)
477 LPWSTR str
, *substorage
;
478 UINT i
, r
= ERROR_SUCCESS
;
480 si
= MSI_GetSummaryInformationW( patch_db
->storage
, 0 );
482 return ERROR_FUNCTION_FAILED
;
484 msi_check_patch_applicable( package
, si
);
486 /* enumerate the substorage */
487 str
= msi_suminfo_dup_string( si
, PID_LASTAUTHOR
);
488 substorage
= msi_split_string( str
, ';' );
489 for ( i
= 0; substorage
&& substorage
[i
] && r
== ERROR_SUCCESS
; i
++ )
490 r
= msi_apply_substorage_transform( package
, patch_db
, substorage
[i
] );
491 msi_free( substorage
);
494 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
496 msiobj_release( &si
->hdr
);
501 static UINT
msi_apply_patch_package( MSIPACKAGE
*package
, LPCWSTR file
)
503 MSIDATABASE
*patch_db
= NULL
;
506 TRACE("%p %s\n", package
, debugstr_w( file
) );
509 * We probably want to make sure we only open a patch collection here.
510 * Patch collections (.msp) and databases (.msi) have different GUIDs
511 * but currently MSI_OpenDatabaseW will accept both.
513 r
= MSI_OpenDatabaseW( file
, MSIDBOPEN_READONLY
, &patch_db
);
514 if ( r
!= ERROR_SUCCESS
)
516 ERR("failed to open patch collection %s\n", debugstr_w( file
) );
520 msi_parse_patch_summary( package
, patch_db
);
523 * There might be a CAB file in the patch package,
524 * so append it to the list of storage to search for streams.
526 append_storage_to_db( package
->db
, patch_db
->storage
);
528 msiobj_release( &patch_db
->hdr
);
530 return ERROR_SUCCESS
;
533 /* get the PATCH property, and apply all the patches it specifies */
534 static UINT
msi_apply_patches( MSIPACKAGE
*package
)
536 static const WCHAR szPatch
[] = { 'P','A','T','C','H',0 };
537 LPWSTR patch_list
, *patches
;
538 UINT i
, r
= ERROR_SUCCESS
;
540 patch_list
= msi_dup_property( package
, szPatch
);
542 TRACE("patches to be applied: %s\n", debugstr_w( patch_list
) );
544 patches
= msi_split_string( patch_list
, ';' );
545 for( i
=0; patches
&& patches
[i
] && r
== ERROR_SUCCESS
; i
++ )
546 r
= msi_apply_patch_package( package
, patches
[i
] );
549 msi_free( patch_list
);
554 static UINT
msi_apply_transforms( MSIPACKAGE
*package
)
556 static const WCHAR szTransforms
[] = {
557 'T','R','A','N','S','F','O','R','M','S',0 };
558 LPWSTR xform_list
, *xforms
;
559 UINT i
, r
= ERROR_SUCCESS
;
561 xform_list
= msi_dup_property( package
, szTransforms
);
562 xforms
= msi_split_string( xform_list
, ';' );
564 for( i
=0; xforms
&& xforms
[i
] && r
== ERROR_SUCCESS
; i
++ )
566 if (xforms
[i
][0] == ':')
567 r
= msi_apply_substorage_transform( package
, package
->db
, xforms
[i
] );
569 r
= MSI_DatabaseApplyTransformW( package
->db
, xforms
[i
], 0 );
573 msi_free( xform_list
);
578 static BOOL
ui_sequence_exists( MSIPACKAGE
*package
)
583 static const WCHAR ExecSeqQuery
[] =
584 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
585 '`','I','n','s','t','a','l','l',
586 'U','I','S','e','q','u','e','n','c','e','`',
587 ' ','W','H','E','R','E',' ',
588 '`','S','e','q','u','e','n','c','e','`',' ',
589 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
590 '`','S','e','q','u','e','n','c','e','`',0};
592 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
593 if (rc
== ERROR_SUCCESS
)
595 msiobj_release(&view
->hdr
);
602 static UINT
msi_set_sourcedir_props(MSIPACKAGE
*package
, BOOL replace
)
605 LPWSTR source
, check
;
608 static const WCHAR szOriginalDatabase
[] =
609 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
611 db
= msi_dup_property( package
, szOriginalDatabase
);
613 return ERROR_OUTOFMEMORY
;
615 p
= strrchrW( db
, '\\' );
618 p
= strrchrW( db
, '/' );
622 return ERROR_SUCCESS
;
627 source
= msi_alloc( len
* sizeof(WCHAR
) );
628 lstrcpynW( source
, db
, len
);
630 check
= msi_dup_property( package
, cszSourceDir
);
631 if (!check
|| replace
)
632 MSI_SetPropertyW( package
, cszSourceDir
, source
);
636 check
= msi_dup_property( package
, cszSOURCEDIR
);
637 if (!check
|| replace
)
638 MSI_SetPropertyW( package
, cszSOURCEDIR
, source
);
644 return ERROR_SUCCESS
;
647 /****************************************************
648 * TOP level entry points
649 *****************************************************/
651 UINT
MSI_InstallPackage( MSIPACKAGE
*package
, LPCWSTR szPackagePath
,
652 LPCWSTR szCommandLine
)
655 BOOL ui
= FALSE
, ui_exists
;
656 static const WCHAR szUILevel
[] = {'U','I','L','e','v','e','l',0};
657 static const WCHAR szAction
[] = {'A','C','T','I','O','N',0};
658 static const WCHAR szInstall
[] = {'I','N','S','T','A','L','L',0};
660 MSI_SetPropertyW(package
, szAction
, szInstall
);
662 package
->script
= msi_alloc_zero(sizeof(MSISCRIPT
));
664 package
->script
->InWhatSequence
= SEQUENCE_INSTALL
;
671 dir
= strdupW(szPackagePath
);
672 p
= strrchrW(dir
, '\\');
676 file
= szPackagePath
+ (p
- dir
);
681 dir
= msi_alloc(MAX_PATH
*sizeof(WCHAR
));
682 GetCurrentDirectoryW(MAX_PATH
, dir
);
683 lstrcatW(dir
, cszbs
);
684 file
= szPackagePath
;
687 msi_free( package
->PackagePath
);
688 package
->PackagePath
= msi_alloc((lstrlenW(dir
) + lstrlenW(file
) + 1) * sizeof(WCHAR
));
689 if (!package
->PackagePath
)
692 return ERROR_OUTOFMEMORY
;
695 lstrcpyW(package
->PackagePath
, dir
);
696 lstrcatW(package
->PackagePath
, file
);
699 msi_set_sourcedir_props(package
, FALSE
);
702 msi_parse_command_line( package
, szCommandLine
);
704 msi_apply_transforms( package
);
705 msi_apply_patches( package
);
707 /* properties may have been added by a transform */
708 msi_clone_properties( package
);
710 if ( (msi_get_property_int(package
, szUILevel
, 0) & INSTALLUILEVEL_MASK
) >= INSTALLUILEVEL_REDUCED
)
712 package
->script
->InWhatSequence
|= SEQUENCE_UI
;
713 rc
= ACTION_ProcessUISequence(package
);
715 ui_exists
= ui_sequence_exists(package
);
716 if (rc
== ERROR_SUCCESS
|| !ui_exists
)
718 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
719 rc
= ACTION_ProcessExecSequence(package
,ui_exists
);
723 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
725 package
->script
->CurrentlyScripting
= FALSE
;
727 /* process the ending type action */
728 if (rc
== ERROR_SUCCESS
)
729 ACTION_PerformActionSequence(package
,-1,ui
);
730 else if (rc
== ERROR_INSTALL_USEREXIT
)
731 ACTION_PerformActionSequence(package
,-2,ui
);
732 else if (rc
== ERROR_INSTALL_SUSPEND
)
733 ACTION_PerformActionSequence(package
,-4,ui
);
735 ACTION_PerformActionSequence(package
,-3,ui
);
737 /* finish up running custom actions */
738 ACTION_FinishCustomActions(package
);
743 static UINT
ACTION_PerformActionSequence(MSIPACKAGE
*package
, UINT seq
, BOOL UI
)
745 UINT rc
= ERROR_SUCCESS
;
747 static const WCHAR ExecSeqQuery
[] =
748 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
749 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
750 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
751 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
753 static const WCHAR UISeqQuery
[] =
754 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
755 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
756 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
757 ' ', '=',' ','%','i',0};
760 row
= MSI_QueryGetRecord(package
->db
, UISeqQuery
, seq
);
762 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
, seq
);
766 LPCWSTR action
, cond
;
768 TRACE("Running the actions\n");
770 /* check conditions */
771 cond
= MSI_RecordGetString(row
,2);
773 /* this is a hack to skip errors in the condition code */
774 if (MSI_EvaluateConditionW(package
, cond
) == MSICONDITION_FALSE
)
777 action
= MSI_RecordGetString(row
,1);
780 ERR("failed to fetch action\n");
781 rc
= ERROR_FUNCTION_FAILED
;
786 rc
= ACTION_PerformUIAction(package
,action
,-1);
788 rc
= ACTION_PerformAction(package
,action
,-1,FALSE
);
790 msiobj_release(&row
->hdr
);
801 } iterate_action_param
;
803 static UINT
ITERATE_Actions(MSIRECORD
*row
, LPVOID param
)
805 iterate_action_param
*iap
= (iterate_action_param
*)param
;
807 LPCWSTR cond
, action
;
809 action
= MSI_RecordGetString(row
,1);
812 ERR("Error is retrieving action name\n");
813 return ERROR_FUNCTION_FAILED
;
816 /* check conditions */
817 cond
= MSI_RecordGetString(row
,2);
819 /* this is a hack to skip errors in the condition code */
820 if (MSI_EvaluateConditionW(iap
->package
, cond
) == MSICONDITION_FALSE
)
822 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action
));
823 return ERROR_SUCCESS
;
827 rc
= ACTION_PerformUIAction(iap
->package
,action
,-1);
829 rc
= ACTION_PerformAction(iap
->package
,action
,-1,FALSE
);
831 msi_dialog_check_messages( NULL
);
833 if (iap
->package
->CurrentInstallState
!= ERROR_SUCCESS
)
834 rc
= iap
->package
->CurrentInstallState
;
836 if (rc
== ERROR_FUNCTION_NOT_CALLED
)
839 if (rc
!= ERROR_SUCCESS
)
840 ERR("Execution halted, action %s returned %i\n", debugstr_w(action
), rc
);
845 UINT
MSI_Sequence( MSIPACKAGE
*package
, LPCWSTR szTable
, INT iSequenceMode
)
849 static const WCHAR query
[] =
850 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
852 ' ','W','H','E','R','E',' ',
853 '`','S','e','q','u','e','n','c','e','`',' ',
854 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
855 '`','S','e','q','u','e','n','c','e','`',0};
856 iterate_action_param iap
;
859 * FIXME: probably should be checking UILevel in the
860 * ACTION_PerformUIAction/ACTION_PerformAction
861 * rather than saving the UI level here. Those
862 * two functions can be merged too.
864 iap
.package
= package
;
867 TRACE("%p %s %i\n", package
, debugstr_w(szTable
), iSequenceMode
);
869 r
= MSI_OpenQuery( package
->db
, &view
, query
, szTable
);
870 if (r
== ERROR_SUCCESS
)
872 r
= MSI_IterateRecords( view
, NULL
, ITERATE_Actions
, &iap
);
873 msiobj_release(&view
->hdr
);
879 static UINT
ACTION_ProcessExecSequence(MSIPACKAGE
*package
, BOOL UIran
)
883 static const WCHAR ExecSeqQuery
[] =
884 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
885 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
886 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
887 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
888 'O','R','D','E','R',' ', 'B','Y',' ',
889 '`','S','e','q','u','e','n','c','e','`',0 };
891 static const WCHAR IVQuery
[] =
892 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
893 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
894 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
895 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
896 ' ','\'', 'I','n','s','t','a','l','l',
897 'V','a','l','i','d','a','t','e','\'', 0};
899 iterate_action_param iap
;
901 iap
.package
= package
;
904 if (package
->script
->ExecuteSequenceRun
)
906 TRACE("Execute Sequence already Run\n");
907 return ERROR_SUCCESS
;
910 package
->script
->ExecuteSequenceRun
= TRUE
;
912 /* get the sequence number */
915 row
= MSI_QueryGetRecord(package
->db
, IVQuery
);
917 return ERROR_FUNCTION_FAILED
;
918 seq
= MSI_RecordGetInteger(row
,1);
919 msiobj_release(&row
->hdr
);
922 rc
= MSI_OpenQuery(package
->db
, &view
, ExecSeqQuery
, seq
);
923 if (rc
== ERROR_SUCCESS
)
925 TRACE("Running the actions\n");
927 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, &iap
);
928 msiobj_release(&view
->hdr
);
934 static UINT
ACTION_ProcessUISequence(MSIPACKAGE
*package
)
938 static const WCHAR ExecSeqQuery
[] =
939 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
940 '`','I','n','s','t','a','l','l',
941 'U','I','S','e','q','u','e','n','c','e','`',
942 ' ','W','H','E','R','E',' ',
943 '`','S','e','q','u','e','n','c','e','`',' ',
944 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
945 '`','S','e','q','u','e','n','c','e','`',0};
946 iterate_action_param iap
;
948 iap
.package
= package
;
951 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
953 if (rc
== ERROR_SUCCESS
)
955 TRACE("Running the actions\n");
957 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, &iap
);
958 msiobj_release(&view
->hdr
);
964 /********************************************************
965 * ACTION helper functions and functions that perform the actions
966 *******************************************************/
967 static BOOL
ACTION_HandleCustomAction( MSIPACKAGE
* package
, LPCWSTR action
,
968 UINT
* rc
, UINT script
, BOOL force
)
973 arc
= ACTION_CustomAction(package
, action
, script
, force
);
975 if (arc
!= ERROR_CALL_NOT_IMPLEMENTED
)
984 * A lot of actions are really important even if they don't do anything
985 * explicit... Lots of properties are set at the beginning of the installation
986 * CostFinalize does a bunch of work to translate the directories and such
988 * But until I get write access to the database that is hard, so I am going to
989 * hack it to see if I can get something to run.
991 UINT
ACTION_PerformAction(MSIPACKAGE
*package
, const WCHAR
*action
, UINT script
, BOOL force
)
993 UINT rc
= ERROR_SUCCESS
;
996 TRACE("Performing action (%s)\n",debugstr_w(action
));
998 handled
= ACTION_HandleStandardAction(package
, action
, &rc
, force
);
1001 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, script
, force
);
1005 WARN("unhandled msi action %s\n",debugstr_w(action
));
1006 rc
= ERROR_FUNCTION_NOT_CALLED
;
1012 UINT
ACTION_PerformUIAction(MSIPACKAGE
*package
, const WCHAR
*action
, UINT script
)
1014 UINT rc
= ERROR_SUCCESS
;
1015 BOOL handled
= FALSE
;
1017 TRACE("Performing action (%s)\n",debugstr_w(action
));
1019 handled
= ACTION_HandleStandardAction(package
, action
, &rc
,TRUE
);
1022 handled
= ACTION_HandleCustomAction(package
, action
, &rc
, script
, FALSE
);
1024 if( !handled
&& ACTION_DialogBox(package
,action
) == ERROR_SUCCESS
)
1029 WARN("unhandled msi action %s\n",debugstr_w(action
));
1030 rc
= ERROR_FUNCTION_NOT_CALLED
;
1038 * Actual Action Handlers
1041 static UINT
ITERATE_CreateFolders(MSIRECORD
*row
, LPVOID param
)
1043 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1049 dir
= MSI_RecordGetString(row
,1);
1052 ERR("Unable to get folder id\n");
1053 return ERROR_SUCCESS
;
1056 full_path
= resolve_folder(package
,dir
,FALSE
,FALSE
,TRUE
,&folder
);
1059 ERR("Unable to resolve folder id %s\n",debugstr_w(dir
));
1060 return ERROR_SUCCESS
;
1063 TRACE("Folder is %s\n",debugstr_w(full_path
));
1066 uirow
= MSI_CreateRecord(1);
1067 MSI_RecordSetStringW(uirow
,1,full_path
);
1068 ui_actiondata(package
,szCreateFolders
,uirow
);
1069 msiobj_release( &uirow
->hdr
);
1071 if (folder
->State
== 0)
1072 create_full_pathW(full_path
);
1076 msi_free(full_path
);
1077 return ERROR_SUCCESS
;
1080 /* FIXME: probably should merge this with the above function */
1081 static UINT
msi_create_directory( MSIPACKAGE
* package
, LPCWSTR dir
)
1083 UINT rc
= ERROR_SUCCESS
;
1085 LPWSTR install_path
;
1087 install_path
= resolve_folder(package
, dir
, FALSE
, FALSE
, TRUE
, &folder
);
1089 return ERROR_FUNCTION_FAILED
;
1091 /* create the path */
1092 if (folder
->State
== 0)
1094 create_full_pathW(install_path
);
1097 msi_free(install_path
);
1102 UINT
msi_create_component_directories( MSIPACKAGE
*package
)
1106 /* create all the folders required by the components are going to install */
1107 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1109 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
1111 msi_create_directory( package
, comp
->Directory
);
1114 return ERROR_SUCCESS
;
1118 * Also we cannot enable/disable components either, so for now I am just going
1119 * to do all the directories for all the components.
1121 static UINT
ACTION_CreateFolders(MSIPACKAGE
*package
)
1123 static const WCHAR ExecSeqQuery
[] =
1124 {'S','E','L','E','C','T',' ',
1125 '`','D','i','r','e','c','t','o','r','y','_','`',
1126 ' ','F','R','O','M',' ',
1127 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1131 /* create all the empty folders specified in the CreateFolder table */
1132 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
1133 if (rc
!= ERROR_SUCCESS
)
1134 return ERROR_SUCCESS
;
1136 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateFolders
, package
);
1137 msiobj_release(&view
->hdr
);
1139 msi_create_component_directories( package
);
1144 static UINT
load_component( MSIRECORD
*row
, LPVOID param
)
1146 MSIPACKAGE
*package
= param
;
1149 comp
= msi_alloc_zero( sizeof(MSICOMPONENT
) );
1151 return ERROR_FUNCTION_FAILED
;
1153 list_add_tail( &package
->components
, &comp
->entry
);
1155 /* fill in the data */
1156 comp
->Component
= msi_dup_record_field( row
, 1 );
1158 TRACE("Loading Component %s\n", debugstr_w(comp
->Component
));
1160 comp
->ComponentId
= msi_dup_record_field( row
, 2 );
1161 comp
->Directory
= msi_dup_record_field( row
, 3 );
1162 comp
->Attributes
= MSI_RecordGetInteger(row
,4);
1163 comp
->Condition
= msi_dup_record_field( row
, 5 );
1164 comp
->KeyPath
= msi_dup_record_field( row
, 6 );
1166 comp
->Installed
= INSTALLSTATE_UNKNOWN
;
1167 msi_component_set_state( comp
, INSTALLSTATE_UNKNOWN
);
1169 return ERROR_SUCCESS
;
1172 static UINT
load_all_components( MSIPACKAGE
*package
)
1174 static const WCHAR query
[] = {
1175 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1176 '`','C','o','m','p','o','n','e','n','t','`',0 };
1180 if (!list_empty(&package
->components
))
1181 return ERROR_SUCCESS
;
1183 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1184 if (r
!= ERROR_SUCCESS
)
1187 r
= MSI_IterateRecords(view
, NULL
, load_component
, package
);
1188 msiobj_release(&view
->hdr
);
1193 MSIPACKAGE
*package
;
1194 MSIFEATURE
*feature
;
1197 static UINT
add_feature_component( MSIFEATURE
*feature
, MSICOMPONENT
*comp
)
1201 cl
= msi_alloc( sizeof (*cl
) );
1203 return ERROR_NOT_ENOUGH_MEMORY
;
1204 cl
->component
= comp
;
1205 list_add_tail( &feature
->Components
, &cl
->entry
);
1207 return ERROR_SUCCESS
;
1210 static UINT
add_feature_child( MSIFEATURE
*parent
, MSIFEATURE
*child
)
1214 fl
= msi_alloc( sizeof(*fl
) );
1216 return ERROR_NOT_ENOUGH_MEMORY
;
1217 fl
->feature
= child
;
1218 list_add_tail( &parent
->Children
, &fl
->entry
);
1220 return ERROR_SUCCESS
;
1223 static UINT
iterate_load_featurecomponents(MSIRECORD
*row
, LPVOID param
)
1225 _ilfs
* ilfs
= (_ilfs
*)param
;
1229 component
= MSI_RecordGetString(row
,1);
1231 /* check to see if the component is already loaded */
1232 comp
= get_loaded_component( ilfs
->package
, component
);
1235 ERR("unknown component %s\n", debugstr_w(component
));
1236 return ERROR_FUNCTION_FAILED
;
1239 add_feature_component( ilfs
->feature
, comp
);
1240 comp
->Enabled
= TRUE
;
1242 return ERROR_SUCCESS
;
1245 static MSIFEATURE
*find_feature_by_name( MSIPACKAGE
*package
, LPCWSTR name
)
1247 MSIFEATURE
*feature
;
1249 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1251 if ( !lstrcmpW( feature
->Feature
, name
) )
1258 static UINT
load_feature(MSIRECORD
* row
, LPVOID param
)
1260 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1261 MSIFEATURE
* feature
;
1262 static const WCHAR Query1
[] =
1263 {'S','E','L','E','C','T',' ',
1264 '`','C','o','m','p','o','n','e','n','t','_','`',
1265 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1266 'C','o','m','p','o','n','e','n','t','s','`',' ',
1267 'W','H','E','R','E',' ',
1268 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1273 /* fill in the data */
1275 feature
= msi_alloc_zero( sizeof (MSIFEATURE
) );
1277 return ERROR_NOT_ENOUGH_MEMORY
;
1279 list_init( &feature
->Children
);
1280 list_init( &feature
->Components
);
1282 feature
->Feature
= msi_dup_record_field( row
, 1 );
1284 TRACE("Loading feature %s\n",debugstr_w(feature
->Feature
));
1286 feature
->Feature_Parent
= msi_dup_record_field( row
, 2 );
1287 feature
->Title
= msi_dup_record_field( row
, 3 );
1288 feature
->Description
= msi_dup_record_field( row
, 4 );
1290 if (!MSI_RecordIsNull(row
,5))
1291 feature
->Display
= MSI_RecordGetInteger(row
,5);
1293 feature
->Level
= MSI_RecordGetInteger(row
,6);
1294 feature
->Directory
= msi_dup_record_field( row
, 7 );
1295 feature
->Attributes
= MSI_RecordGetInteger(row
,8);
1297 feature
->Installed
= INSTALLSTATE_UNKNOWN
;
1298 msi_feature_set_state( feature
, INSTALLSTATE_UNKNOWN
);
1300 list_add_tail( &package
->features
, &feature
->entry
);
1302 /* load feature components */
1304 rc
= MSI_OpenQuery( package
->db
, &view
, Query1
, feature
->Feature
);
1305 if (rc
!= ERROR_SUCCESS
)
1306 return ERROR_SUCCESS
;
1308 ilfs
.package
= package
;
1309 ilfs
.feature
= feature
;
1311 MSI_IterateRecords(view
, NULL
, iterate_load_featurecomponents
, &ilfs
);
1312 msiobj_release(&view
->hdr
);
1314 return ERROR_SUCCESS
;
1317 static UINT
find_feature_children(MSIRECORD
* row
, LPVOID param
)
1319 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1320 MSIFEATURE
*parent
, *child
;
1322 child
= find_feature_by_name( package
, MSI_RecordGetString( row
, 1 ) );
1324 return ERROR_FUNCTION_FAILED
;
1326 if (!child
->Feature_Parent
)
1327 return ERROR_SUCCESS
;
1329 parent
= find_feature_by_name( package
, child
->Feature_Parent
);
1331 return ERROR_FUNCTION_FAILED
;
1333 add_feature_child( parent
, child
);
1334 return ERROR_SUCCESS
;
1337 static UINT
load_all_features( MSIPACKAGE
*package
)
1339 static const WCHAR query
[] = {
1340 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1341 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1342 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1346 if (!list_empty(&package
->features
))
1347 return ERROR_SUCCESS
;
1349 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1350 if (r
!= ERROR_SUCCESS
)
1353 r
= MSI_IterateRecords( view
, NULL
, load_feature
, package
);
1354 if (r
!= ERROR_SUCCESS
)
1357 r
= MSI_IterateRecords( view
, NULL
, find_feature_children
, package
);
1358 msiobj_release( &view
->hdr
);
1363 static LPWSTR
folder_split_path(LPWSTR p
, WCHAR ch
)
1374 static UINT
load_file_hash(MSIPACKAGE
*package
, MSIFILE
*file
)
1376 static const WCHAR query
[] = {
1377 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1378 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1379 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1380 MSIQUERY
*view
= NULL
;
1381 MSIRECORD
*row
= NULL
;
1384 TRACE("%s\n", debugstr_w(file
->File
));
1386 r
= MSI_OpenQuery(package
->db
, &view
, query
, file
->File
);
1387 if (r
!= ERROR_SUCCESS
)
1390 r
= MSI_ViewExecute(view
, NULL
);
1391 if (r
!= ERROR_SUCCESS
)
1394 r
= MSI_ViewFetch(view
, &row
);
1395 if (r
!= ERROR_SUCCESS
)
1398 file
->hash
.dwFileHashInfoSize
= sizeof(MSIFILEHASHINFO
);
1399 file
->hash
.dwData
[0] = MSI_RecordGetInteger(row
, 3);
1400 file
->hash
.dwData
[1] = MSI_RecordGetInteger(row
, 4);
1401 file
->hash
.dwData
[2] = MSI_RecordGetInteger(row
, 5);
1402 file
->hash
.dwData
[3] = MSI_RecordGetInteger(row
, 6);
1405 if (view
) msiobj_release(&view
->hdr
);
1406 if (row
) msiobj_release(&row
->hdr
);
1410 static UINT
load_file(MSIRECORD
*row
, LPVOID param
)
1412 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
1416 /* fill in the data */
1418 file
= msi_alloc_zero( sizeof (MSIFILE
) );
1420 return ERROR_NOT_ENOUGH_MEMORY
;
1422 file
->File
= msi_dup_record_field( row
, 1 );
1424 component
= MSI_RecordGetString( row
, 2 );
1425 file
->Component
= get_loaded_component( package
, component
);
1427 if (!file
->Component
)
1428 ERR("Unfound Component %s\n",debugstr_w(component
));
1430 file
->FileName
= msi_dup_record_field( row
, 3 );
1431 reduce_to_longfilename( file
->FileName
);
1433 file
->ShortName
= msi_dup_record_field( row
, 3 );
1434 file
->LongName
= strdupW( folder_split_path(file
->ShortName
, '|'));
1436 file
->FileSize
= MSI_RecordGetInteger( row
, 4 );
1437 file
->Version
= msi_dup_record_field( row
, 5 );
1438 file
->Language
= msi_dup_record_field( row
, 6 );
1439 file
->Attributes
= MSI_RecordGetInteger( row
, 7 );
1440 file
->Sequence
= MSI_RecordGetInteger( row
, 8 );
1442 file
->state
= msifs_invalid
;
1444 /* if the compressed bits are not set in the file attributes,
1445 * then read the information from the package word count property
1447 if (file
->Attributes
& msidbFileAttributesCompressed
)
1449 file
->IsCompressed
= TRUE
;
1451 else if (file
->Attributes
& msidbFileAttributesNoncompressed
)
1453 file
->IsCompressed
= FALSE
;
1457 file
->IsCompressed
= package
->WordCount
& MSIWORDCOUNT_COMPRESSED
;
1460 load_file_hash(package
, file
);
1462 TRACE("File Loaded (%s)\n",debugstr_w(file
->File
));
1464 list_add_tail( &package
->files
, &file
->entry
);
1466 return ERROR_SUCCESS
;
1469 static UINT
load_all_files(MSIPACKAGE
*package
)
1473 static const WCHAR Query
[] =
1474 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1475 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1476 '`','S','e','q','u','e','n','c','e','`', 0};
1478 if (!list_empty(&package
->files
))
1479 return ERROR_SUCCESS
;
1481 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
1482 if (rc
!= ERROR_SUCCESS
)
1483 return ERROR_SUCCESS
;
1485 rc
= MSI_IterateRecords(view
, NULL
, load_file
, package
);
1486 msiobj_release(&view
->hdr
);
1488 return ERROR_SUCCESS
;
1491 static UINT
load_folder( MSIRECORD
*row
, LPVOID param
)
1493 MSIPACKAGE
*package
= param
;
1494 static const WCHAR szDot
[] = { '.',0 };
1495 static WCHAR szEmpty
[] = { 0 };
1496 LPWSTR p
, tgt_short
, tgt_long
, src_short
, src_long
;
1499 folder
= msi_alloc_zero( sizeof (MSIFOLDER
) );
1501 return ERROR_NOT_ENOUGH_MEMORY
;
1503 folder
->Directory
= msi_dup_record_field( row
, 1 );
1505 TRACE("%s\n", debugstr_w(folder
->Directory
));
1507 p
= msi_dup_record_field(row
, 3);
1509 /* split src and target dir */
1511 src_short
= folder_split_path( p
, ':' );
1513 /* split the long and short paths */
1514 tgt_long
= folder_split_path( tgt_short
, '|' );
1515 src_long
= folder_split_path( src_short
, '|' );
1517 /* check for no-op dirs */
1518 if (!lstrcmpW(szDot
, tgt_short
))
1519 tgt_short
= szEmpty
;
1520 if (!lstrcmpW(szDot
, src_short
))
1521 src_short
= szEmpty
;
1524 tgt_long
= tgt_short
;
1527 src_short
= tgt_short
;
1528 src_long
= tgt_long
;
1532 src_long
= src_short
;
1534 /* FIXME: use the target short path too */
1535 folder
->TargetDefault
= strdupW(tgt_long
);
1536 folder
->SourceShortPath
= strdupW(src_short
);
1537 folder
->SourceLongPath
= strdupW(src_long
);
1540 TRACE("TargetDefault = %s\n",debugstr_w( folder
->TargetDefault
));
1541 TRACE("SourceLong = %s\n", debugstr_w( folder
->SourceLongPath
));
1542 TRACE("SourceShort = %s\n", debugstr_w( folder
->SourceShortPath
));
1544 folder
->Parent
= msi_dup_record_field( row
, 2 );
1546 folder
->Property
= msi_dup_property( package
, folder
->Directory
);
1548 list_add_tail( &package
->folders
, &folder
->entry
);
1550 TRACE("returning %p\n", folder
);
1552 return ERROR_SUCCESS
;
1555 static UINT
load_all_folders( MSIPACKAGE
*package
)
1557 static const WCHAR query
[] = {
1558 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1559 '`','D','i','r','e','c','t','o','r','y','`',0 };
1563 if (!list_empty(&package
->folders
))
1564 return ERROR_SUCCESS
;
1566 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1567 if (r
!= ERROR_SUCCESS
)
1570 r
= MSI_IterateRecords(view
, NULL
, load_folder
, package
);
1571 msiobj_release(&view
->hdr
);
1576 * I am not doing any of the costing functionality yet.
1577 * Mostly looking at doing the Component and Feature loading
1579 * The native MSI does A LOT of modification to tables here. Mostly adding
1580 * a lot of temporary columns to the Feature and Component tables.
1582 * note: Native msi also tracks the short filename. But I am only going to
1583 * track the long ones. Also looking at this directory table
1584 * it appears that the directory table does not get the parents
1585 * resolved base on property only based on their entries in the
1588 static UINT
ACTION_CostInitialize(MSIPACKAGE
*package
)
1590 static const WCHAR szCosting
[] =
1591 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1592 static const WCHAR szZero
[] = { '0', 0 };
1594 MSI_SetPropertyW(package
, szCosting
, szZero
);
1595 MSI_SetPropertyW(package
, cszRootDrive
, c_colon
);
1597 load_all_components( package
);
1598 load_all_features( package
);
1599 load_all_files( package
);
1600 load_all_folders( package
);
1602 return ERROR_SUCCESS
;
1605 static UINT
execute_script(MSIPACKAGE
*package
, UINT script
)
1608 UINT rc
= ERROR_SUCCESS
;
1610 TRACE("Executing Script %i\n",script
);
1612 if (!package
->script
)
1614 ERR("no script!\n");
1615 return ERROR_FUNCTION_FAILED
;
1618 for (i
= 0; i
< package
->script
->ActionCount
[script
]; i
++)
1621 action
= package
->script
->Actions
[script
][i
];
1622 ui_actionstart(package
, action
);
1623 TRACE("Executing Action (%s)\n",debugstr_w(action
));
1624 rc
= ACTION_PerformAction(package
, action
, script
, TRUE
);
1625 if (rc
!= ERROR_SUCCESS
)
1628 msi_free_action_script(package
, script
);
1632 static UINT
ACTION_FileCost(MSIPACKAGE
*package
)
1634 return ERROR_SUCCESS
;
1637 static void ACTION_GetComponentInstallStates(MSIPACKAGE
*package
)
1641 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1645 if (!comp
->ComponentId
)
1648 res
= MsiGetComponentPathW( package
->ProductCode
,
1649 comp
->ComponentId
, NULL
, NULL
);
1651 res
= INSTALLSTATE_ABSENT
;
1652 comp
->Installed
= res
;
1656 /* scan for and update current install states */
1657 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE
*package
)
1660 MSIFEATURE
*feature
;
1662 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1665 INSTALLSTATE res
= INSTALLSTATE_ABSENT
;
1667 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1669 comp
= cl
->component
;
1671 if (!comp
->ComponentId
)
1673 res
= INSTALLSTATE_ABSENT
;
1677 if (res
== INSTALLSTATE_ABSENT
)
1678 res
= comp
->Installed
;
1681 if (res
== comp
->Installed
)
1684 if (res
!= INSTALLSTATE_DEFAULT
&& res
!= INSTALLSTATE_LOCAL
&&
1685 res
!= INSTALLSTATE_SOURCE
)
1687 res
= INSTALLSTATE_INCOMPLETE
;
1691 feature
->Installed
= res
;
1695 static BOOL
process_state_property (MSIPACKAGE
* package
, LPCWSTR property
,
1698 static const WCHAR all
[]={'A','L','L',0};
1700 MSIFEATURE
*feature
;
1702 override
= msi_dup_property( package
, property
);
1706 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1708 if (strcmpiW(override
,all
)==0)
1709 msi_feature_set_state( feature
, state
);
1712 LPWSTR ptr
= override
;
1713 LPWSTR ptr2
= strchrW(override
,',');
1717 if ((ptr2
&& strncmpW(ptr
,feature
->Feature
, ptr2
-ptr
)==0)
1718 || (!ptr2
&& strcmpW(ptr
,feature
->Feature
)==0))
1720 msi_feature_set_state( feature
, state
);
1726 ptr2
= strchrW(ptr
,',');
1738 UINT
MSI_SetFeatureStates(MSIPACKAGE
*package
)
1741 static const WCHAR szlevel
[] =
1742 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1743 static const WCHAR szAddLocal
[] =
1744 {'A','D','D','L','O','C','A','L',0};
1745 static const WCHAR szAddSource
[] =
1746 {'A','D','D','S','O','U','R','C','E',0};
1747 static const WCHAR szRemove
[] =
1748 {'R','E','M','O','V','E',0};
1749 static const WCHAR szReinstall
[] =
1750 {'R','E','I','N','S','T','A','L','L',0};
1751 BOOL override
= FALSE
;
1752 MSICOMPONENT
* component
;
1753 MSIFEATURE
*feature
;
1756 /* I do not know if this is where it should happen.. but */
1758 TRACE("Checking Install Level\n");
1760 install_level
= msi_get_property_int( package
, szlevel
, 1 );
1762 /* ok here is the _real_ rub
1763 * all these activation/deactivation things happen in order and things
1764 * later on the list override things earlier on the list.
1765 * 1) INSTALLLEVEL processing
1775 * 11) FILEADDDEFAULT
1776 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1777 * ignored for all the features. seems strange, especially since it is not
1778 * documented anywhere, but it is how it works.
1780 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1781 * REMOVE are the big ones, since we don't handle administrative installs
1784 override
|= process_state_property(package
,szAddLocal
,INSTALLSTATE_LOCAL
);
1785 override
|= process_state_property(package
,szRemove
,INSTALLSTATE_ABSENT
);
1786 override
|= process_state_property(package
,szAddSource
,INSTALLSTATE_SOURCE
);
1787 override
|= process_state_property(package
,szReinstall
,INSTALLSTATE_LOCAL
);
1791 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1793 BOOL feature_state
= ((feature
->Level
> 0) &&
1794 (feature
->Level
<= install_level
));
1796 if ((feature_state
) && (feature
->Action
== INSTALLSTATE_UNKNOWN
))
1798 if (feature
->Attributes
& msidbFeatureAttributesFavorSource
)
1799 msi_feature_set_state( feature
, INSTALLSTATE_SOURCE
);
1800 else if (feature
->Attributes
& msidbFeatureAttributesFavorAdvertise
)
1801 msi_feature_set_state( feature
, INSTALLSTATE_ADVERTISED
);
1803 msi_feature_set_state( feature
, INSTALLSTATE_LOCAL
);
1807 /* disable child features of unselected parent features */
1808 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1812 if (feature
->Level
> 0 && feature
->Level
<= install_level
)
1815 LIST_FOR_EACH_ENTRY( fl
, &feature
->Children
, FeatureList
, entry
)
1816 msi_feature_set_state( fl
->feature
, INSTALLSTATE_UNKNOWN
);
1821 /* set the Preselected Property */
1822 static const WCHAR szPreselected
[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1823 static const WCHAR szOne
[] = { '1', 0 };
1825 MSI_SetPropertyW(package
,szPreselected
,szOne
);
1829 * now we want to enable or disable components base on feature
1832 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1836 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1837 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
);
1839 /* features with components that have compressed files are made local */
1840 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1842 if (cl
->component
->Enabled
&&
1843 cl
->component
->ForceLocalState
&&
1844 feature
->Action
== INSTALLSTATE_SOURCE
)
1846 msi_feature_set_state( feature
, INSTALLSTATE_LOCAL
);
1851 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1853 component
= cl
->component
;
1855 if (!component
->Enabled
)
1858 switch (feature
->Action
)
1860 case INSTALLSTATE_ABSENT
:
1861 component
->anyAbsent
= 1;
1863 case INSTALLSTATE_ADVERTISED
:
1864 component
->hasAdvertiseFeature
= 1;
1866 case INSTALLSTATE_SOURCE
:
1867 component
->hasSourceFeature
= 1;
1869 case INSTALLSTATE_LOCAL
:
1870 component
->hasLocalFeature
= 1;
1872 case INSTALLSTATE_DEFAULT
:
1873 if (feature
->Attributes
& msidbFeatureAttributesFavorAdvertise
)
1874 component
->hasAdvertiseFeature
= 1;
1875 else if (feature
->Attributes
& msidbFeatureAttributesFavorSource
)
1876 component
->hasSourceFeature
= 1;
1878 component
->hasLocalFeature
= 1;
1886 LIST_FOR_EACH_ENTRY( component
, &package
->components
, MSICOMPONENT
, entry
)
1888 /* if the component isn't enabled, leave it alone */
1889 if (!component
->Enabled
)
1892 /* check if it's local or source */
1893 if (!(component
->Attributes
& msidbComponentAttributesOptional
) &&
1894 (component
->hasLocalFeature
|| component
->hasSourceFeature
))
1896 if ((component
->Attributes
& msidbComponentAttributesSourceOnly
) &&
1897 !component
->ForceLocalState
)
1898 msi_component_set_state( component
, INSTALLSTATE_SOURCE
);
1900 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1904 /* if any feature is local, the component must be local too */
1905 if (component
->hasLocalFeature
)
1907 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1911 if (component
->hasSourceFeature
)
1913 msi_component_set_state( component
, INSTALLSTATE_SOURCE
);
1917 if (component
->hasAdvertiseFeature
)
1919 msi_component_set_state( component
, INSTALLSTATE_ADVERTISED
);
1923 TRACE("nobody wants component %s\n", debugstr_w(component
->Component
));
1924 if (component
->anyAbsent
)
1925 msi_component_set_state(component
, INSTALLSTATE_ABSENT
);
1928 LIST_FOR_EACH_ENTRY( component
, &package
->components
, MSICOMPONENT
, entry
)
1930 if (component
->Action
== INSTALLSTATE_DEFAULT
)
1932 TRACE("%s was default, setting to local\n", debugstr_w(component
->Component
));
1933 msi_component_set_state( component
, INSTALLSTATE_LOCAL
);
1936 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1937 debugstr_w(component
->Component
), component
->Installed
, component
->Action
);
1941 return ERROR_SUCCESS
;
1944 static UINT
ITERATE_CostFinalizeDirectories(MSIRECORD
*row
, LPVOID param
)
1946 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1951 name
= MSI_RecordGetString(row
,1);
1953 f
= get_loaded_folder(package
, name
);
1954 if (!f
) return ERROR_SUCCESS
;
1956 /* reset the ResolvedTarget */
1957 msi_free(f
->ResolvedTarget
);
1958 f
->ResolvedTarget
= NULL
;
1960 /* This helper function now does ALL the work */
1961 TRACE("Dir %s ...\n",debugstr_w(name
));
1962 path
= resolve_folder(package
,name
,FALSE
,TRUE
,TRUE
,NULL
);
1963 TRACE("resolves to %s\n",debugstr_w(path
));
1966 return ERROR_SUCCESS
;
1969 static UINT
ITERATE_CostFinalizeConditions(MSIRECORD
*row
, LPVOID param
)
1971 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
1973 MSIFEATURE
*feature
;
1975 name
= MSI_RecordGetString( row
, 1 );
1977 feature
= get_loaded_feature( package
, name
);
1979 ERR("FAILED to find loaded feature %s\n",debugstr_w(name
));
1983 Condition
= MSI_RecordGetString(row
,3);
1985 if (MSI_EvaluateConditionW(package
,Condition
) == MSICONDITION_TRUE
)
1987 int level
= MSI_RecordGetInteger(row
,2);
1988 TRACE("Reseting feature %s to level %i\n", debugstr_w(name
), level
);
1989 feature
->Level
= level
;
1992 return ERROR_SUCCESS
;
1995 static LPWSTR
msi_get_disk_file_version( LPCWSTR filename
)
1997 static const WCHAR name_fmt
[] =
1998 {'%','u','.','%','u','.','%','u','.','%','u',0};
1999 static WCHAR name
[] = {'\\',0};
2000 VS_FIXEDFILEINFO
*lpVer
;
2001 WCHAR filever
[0x100];
2007 TRACE("%s\n", debugstr_w(filename
));
2009 versize
= GetFileVersionInfoSizeW( filename
, &handle
);
2013 version
= msi_alloc( versize
);
2014 GetFileVersionInfoW( filename
, 0, versize
, version
);
2016 if (!VerQueryValueW( version
, name
, (LPVOID
*)&lpVer
, &sz
))
2018 msi_free( version
);
2022 sprintfW( filever
, name_fmt
,
2023 HIWORD(lpVer
->dwFileVersionMS
),
2024 LOWORD(lpVer
->dwFileVersionMS
),
2025 HIWORD(lpVer
->dwFileVersionLS
),
2026 LOWORD(lpVer
->dwFileVersionLS
));
2028 msi_free( version
);
2030 return strdupW( filever
);
2033 static UINT
msi_check_file_install_states( MSIPACKAGE
*package
)
2035 LPWSTR file_version
;
2038 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2040 MSICOMPONENT
* comp
= file
->Component
;
2046 if (file
->IsCompressed
)
2047 comp
->ForceLocalState
= TRUE
;
2049 /* calculate target */
2050 p
= resolve_folder(package
, comp
->Directory
, FALSE
, FALSE
, TRUE
, NULL
);
2052 msi_free(file
->TargetPath
);
2054 TRACE("file %s is named %s\n",
2055 debugstr_w(file
->File
), debugstr_w(file
->FileName
));
2057 file
->TargetPath
= build_directory_name(2, p
, file
->FileName
);
2061 TRACE("file %s resolves to %s\n",
2062 debugstr_w(file
->File
), debugstr_w(file
->TargetPath
));
2064 /* don't check files of components that aren't installed */
2065 if (comp
->Installed
== INSTALLSTATE_UNKNOWN
||
2066 comp
->Installed
== INSTALLSTATE_ABSENT
)
2068 file
->state
= msifs_missing
; /* assume files are missing */
2072 if (GetFileAttributesW(file
->TargetPath
) == INVALID_FILE_ATTRIBUTES
)
2074 file
->state
= msifs_missing
;
2075 comp
->Cost
+= file
->FileSize
;
2076 comp
->Installed
= INSTALLSTATE_INCOMPLETE
;
2080 if (file
->Version
&&
2081 (file_version
= msi_get_disk_file_version( file
->TargetPath
)))
2083 TRACE("new %s old %s\n", debugstr_w(file
->Version
),
2084 debugstr_w(file_version
));
2085 /* FIXME: seems like a bad way to compare version numbers */
2086 if (lstrcmpiW(file_version
, file
->Version
)<0)
2088 file
->state
= msifs_overwrite
;
2089 comp
->Cost
+= file
->FileSize
;
2090 comp
->Installed
= INSTALLSTATE_INCOMPLETE
;
2093 file
->state
= msifs_present
;
2094 msi_free( file_version
);
2097 file
->state
= msifs_present
;
2100 return ERROR_SUCCESS
;
2104 * A lot is done in this function aside from just the costing.
2105 * The costing needs to be implemented at some point but for now I am going
2106 * to focus on the directory building
2109 static UINT
ACTION_CostFinalize(MSIPACKAGE
*package
)
2111 static const WCHAR ExecSeqQuery
[] =
2112 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2113 '`','D','i','r','e','c','t','o','r','y','`',0};
2114 static const WCHAR ConditionQuery
[] =
2115 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2116 '`','C','o','n','d','i','t','i','o','n','`',0};
2117 static const WCHAR szCosting
[] =
2118 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2119 static const WCHAR szlevel
[] =
2120 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2121 static const WCHAR szOne
[] = { '1', 0 };
2127 if ( 1 == msi_get_property_int( package
, szCosting
, 0 ) )
2128 return ERROR_SUCCESS
;
2130 TRACE("Building Directory properties\n");
2132 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2133 if (rc
== ERROR_SUCCESS
)
2135 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CostFinalizeDirectories
,
2137 msiobj_release(&view
->hdr
);
2140 /* read components states from the registry */
2141 ACTION_GetComponentInstallStates(package
);
2143 TRACE("File calculations\n");
2144 msi_check_file_install_states( package
);
2146 TRACE("Evaluating Condition Table\n");
2148 rc
= MSI_DatabaseOpenViewW(package
->db
, ConditionQuery
, &view
);
2149 if (rc
== ERROR_SUCCESS
)
2151 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CostFinalizeConditions
,
2153 msiobj_release(&view
->hdr
);
2156 TRACE("Enabling or Disabling Components\n");
2157 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2159 if (MSI_EvaluateConditionW(package
, comp
->Condition
) == MSICONDITION_FALSE
)
2161 TRACE("Disabling component %s\n", debugstr_w(comp
->Component
));
2162 comp
->Enabled
= FALSE
;
2166 MSI_SetPropertyW(package
,szCosting
,szOne
);
2167 /* set default run level if not set */
2168 level
= msi_dup_property( package
, szlevel
);
2170 MSI_SetPropertyW(package
,szlevel
, szOne
);
2173 ACTION_UpdateFeatureInstallStates(package
);
2175 return MSI_SetFeatureStates(package
);
2178 /* OK this value is "interpreted" and then formatted based on the
2179 first few characters */
2180 static LPSTR
parse_value(MSIPACKAGE
*package
, LPCWSTR value
, DWORD
*type
,
2185 if (value
[0]=='#' && value
[1]!='#' && value
[1]!='%')
2191 LPWSTR deformated
= NULL
;
2194 deformat_string(package
, &value
[2], &deformated
);
2196 /* binary value type */
2200 *size
= (strlenW(ptr
)/2)+1;
2202 *size
= strlenW(ptr
)/2;
2204 data
= msi_alloc(*size
);
2210 /* if uneven pad with a zero in front */
2216 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2218 TRACE("Uneven byte count\n");
2226 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2229 msi_free(deformated
);
2231 TRACE("Data %i bytes(%i)\n",*size
,count
);
2238 deformat_string(package
, &value
[1], &deformated
);
2241 *size
= sizeof(DWORD
);
2242 data
= msi_alloc(*size
);
2248 if ( (*p
< '0') || (*p
> '9') )
2254 if (deformated
[0] == '-')
2257 TRACE("DWORD %i\n",*(LPDWORD
)data
);
2259 msi_free(deformated
);
2264 static const WCHAR szMulti
[] = {'[','~',']',0};
2273 *type
=REG_EXPAND_SZ
;
2281 if (strstrW(value
,szMulti
))
2282 *type
= REG_MULTI_SZ
;
2284 /* remove initial delimiter */
2285 if (!strncmpW(value
, szMulti
, 3))
2288 *size
= deformat_string(package
, ptr
,(LPWSTR
*)&data
);
2290 /* add double NULL terminator */
2291 if (*type
== REG_MULTI_SZ
)
2293 *size
+= 2 * sizeof(WCHAR
); /* two NULL terminators */
2294 data
= msi_realloc_zero(data
, *size
);
2300 static UINT
ITERATE_WriteRegistryValues(MSIRECORD
*row
, LPVOID param
)
2302 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
2303 static const WCHAR szHCR
[] =
2304 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2305 'R','O','O','T','\\',0};
2306 static const WCHAR szHCU
[] =
2307 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2308 'U','S','E','R','\\',0};
2309 static const WCHAR szHLM
[] =
2310 {'H','K','E','Y','_','L','O','C','A','L','_',
2311 'M','A','C','H','I','N','E','\\',0};
2312 static const WCHAR szHU
[] =
2313 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2315 LPSTR value_data
= NULL
;
2316 HKEY root_key
, hkey
;
2319 LPCWSTR szRoot
, component
, name
, key
, value
;
2324 BOOL check_first
= FALSE
;
2327 ui_progress(package
,2,0,0,0);
2334 component
= MSI_RecordGetString(row
, 6);
2335 comp
= get_loaded_component(package
,component
);
2337 return ERROR_SUCCESS
;
2339 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2341 TRACE("Skipping write due to disabled component %s\n",
2342 debugstr_w(component
));
2344 comp
->Action
= comp
->Installed
;
2346 return ERROR_SUCCESS
;
2349 comp
->Action
= INSTALLSTATE_LOCAL
;
2351 name
= MSI_RecordGetString(row
, 4);
2352 if( MSI_RecordIsNull(row
,5) && name
)
2354 /* null values can have special meanings */
2355 if (name
[0]=='-' && name
[1] == 0)
2356 return ERROR_SUCCESS
;
2357 else if ((name
[0]=='+' && name
[1] == 0) ||
2358 (name
[0] == '*' && name
[1] == 0))
2363 root
= MSI_RecordGetInteger(row
,2);
2364 key
= MSI_RecordGetString(row
, 3);
2366 /* get the root key */
2371 static const WCHAR szALLUSER
[] = {'A','L','L','U','S','E','R','S',0};
2372 LPWSTR all_users
= msi_dup_property( package
, szALLUSER
);
2373 if (all_users
&& all_users
[0] == '1')
2375 root_key
= HKEY_LOCAL_MACHINE
;
2380 root_key
= HKEY_CURRENT_USER
;
2383 msi_free(all_users
);
2386 case 0: root_key
= HKEY_CLASSES_ROOT
;
2389 case 1: root_key
= HKEY_CURRENT_USER
;
2392 case 2: root_key
= HKEY_LOCAL_MACHINE
;
2395 case 3: root_key
= HKEY_USERS
;
2399 ERR("Unknown root %i\n",root
);
2405 return ERROR_SUCCESS
;
2407 deformat_string(package
, key
, &deformated
);
2408 size
= strlenW(deformated
) + strlenW(szRoot
) + 1;
2409 uikey
= msi_alloc(size
*sizeof(WCHAR
));
2410 strcpyW(uikey
,szRoot
);
2411 strcatW(uikey
,deformated
);
2413 if (RegCreateKeyW( root_key
, deformated
, &hkey
))
2415 ERR("Could not create key %s\n",debugstr_w(deformated
));
2416 msi_free(deformated
);
2418 return ERROR_SUCCESS
;
2420 msi_free(deformated
);
2422 value
= MSI_RecordGetString(row
,5);
2424 value_data
= parse_value(package
, value
, &type
, &size
);
2427 static const WCHAR szEmpty
[] = {0};
2428 value_data
= (LPSTR
)strdupW(szEmpty
);
2429 size
= sizeof(szEmpty
);
2433 deformat_string(package
, name
, &deformated
);
2437 TRACE("Setting value %s of %s\n",debugstr_w(deformated
),
2439 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
)value_data
, size
);
2444 rc
= RegQueryValueExW(hkey
, deformated
, NULL
, NULL
, NULL
, &sz
);
2445 if (rc
== ERROR_SUCCESS
|| rc
== ERROR_MORE_DATA
)
2447 TRACE("value %s of %s checked already exists\n",
2448 debugstr_w(deformated
), debugstr_w(uikey
));
2452 TRACE("Checked and setting value %s of %s\n",
2453 debugstr_w(deformated
), debugstr_w(uikey
));
2454 if (deformated
|| size
)
2455 RegSetValueExW(hkey
, deformated
, 0, type
, (LPBYTE
) value_data
, size
);
2460 uirow
= MSI_CreateRecord(3);
2461 MSI_RecordSetStringW(uirow
,2,deformated
);
2462 MSI_RecordSetStringW(uirow
,1,uikey
);
2465 MSI_RecordSetStringW(uirow
,3,(LPWSTR
)value_data
);
2467 MSI_RecordSetStringW(uirow
,3,value
);
2469 ui_actiondata(package
,szWriteRegistryValues
,uirow
);
2470 msiobj_release( &uirow
->hdr
);
2472 msi_free(value_data
);
2473 msi_free(deformated
);
2476 return ERROR_SUCCESS
;
2479 static UINT
ACTION_WriteRegistryValues(MSIPACKAGE
*package
)
2483 static const WCHAR ExecSeqQuery
[] =
2484 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2485 '`','R','e','g','i','s','t','r','y','`',0 };
2487 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2488 if (rc
!= ERROR_SUCCESS
)
2489 return ERROR_SUCCESS
;
2491 /* increment progress bar each time action data is sent */
2492 ui_progress(package
,1,REG_PROGRESS_VALUE
,1,0);
2494 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteRegistryValues
, package
);
2496 msiobj_release(&view
->hdr
);
2500 static UINT
ACTION_InstallInitialize(MSIPACKAGE
*package
)
2502 package
->script
->CurrentlyScripting
= TRUE
;
2504 return ERROR_SUCCESS
;
2508 static UINT
ACTION_InstallValidate(MSIPACKAGE
*package
)
2513 static const WCHAR q1
[]=
2514 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2515 '`','R','e','g','i','s','t','r','y','`',0};
2518 MSIFEATURE
*feature
;
2521 TRACE("InstallValidate\n");
2523 rc
= MSI_DatabaseOpenViewW(package
->db
, q1
, &view
);
2524 if (rc
== ERROR_SUCCESS
)
2526 MSI_IterateRecords( view
, &progress
, NULL
, package
);
2527 msiobj_release( &view
->hdr
);
2528 total
+= progress
* REG_PROGRESS_VALUE
;
2531 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2532 total
+= COMPONENT_PROGRESS_VALUE
;
2534 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2535 total
+= file
->FileSize
;
2537 ui_progress(package
,0,total
,0,0);
2539 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2541 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2542 debugstr_w(feature
->Feature
), feature
->Installed
, feature
->Action
,
2543 feature
->ActionRequest
);
2546 return ERROR_SUCCESS
;
2549 static UINT
ITERATE_LaunchConditions(MSIRECORD
*row
, LPVOID param
)
2551 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
2552 LPCWSTR cond
= NULL
;
2553 LPCWSTR message
= NULL
;
2556 static const WCHAR title
[]=
2557 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2559 cond
= MSI_RecordGetString(row
,1);
2561 r
= MSI_EvaluateConditionW(package
,cond
);
2562 if (r
== MSICONDITION_FALSE
)
2564 if ((gUILevel
& INSTALLUILEVEL_MASK
) != INSTALLUILEVEL_NONE
)
2567 message
= MSI_RecordGetString(row
,2);
2568 deformat_string(package
,message
,&deformated
);
2569 MessageBoxW(NULL
,deformated
,title
,MB_OK
);
2570 msi_free(deformated
);
2573 return ERROR_INSTALL_FAILURE
;
2576 return ERROR_SUCCESS
;
2579 static UINT
ACTION_LaunchConditions(MSIPACKAGE
*package
)
2582 MSIQUERY
* view
= NULL
;
2583 static const WCHAR ExecSeqQuery
[] =
2584 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2585 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2587 TRACE("Checking launch conditions\n");
2589 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
2590 if (rc
!= ERROR_SUCCESS
)
2591 return ERROR_SUCCESS
;
2593 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_LaunchConditions
, package
);
2594 msiobj_release(&view
->hdr
);
2599 static LPWSTR
resolve_keypath( MSIPACKAGE
* package
, MSICOMPONENT
*cmp
)
2603 return resolve_folder(package
,cmp
->Directory
,FALSE
,FALSE
,TRUE
,NULL
);
2605 if (cmp
->Attributes
& msidbComponentAttributesRegistryKeyPath
)
2607 MSIRECORD
* row
= 0;
2609 LPWSTR deformated
,buffer
,deformated_name
;
2611 static const WCHAR ExecSeqQuery
[] =
2612 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2613 '`','R','e','g','i','s','t','r','y','`',' ',
2614 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2615 ' ','=',' ' ,'\'','%','s','\'',0 };
2616 static const WCHAR fmt
[]={'%','0','2','i',':','\\','%','s','\\',0};
2617 static const WCHAR fmt2
[]=
2618 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2620 row
= MSI_QueryGetRecord(package
->db
, ExecSeqQuery
,cmp
->KeyPath
);
2624 root
= MSI_RecordGetInteger(row
,2);
2625 key
= MSI_RecordGetString(row
, 3);
2626 name
= MSI_RecordGetString(row
, 4);
2627 deformat_string(package
, key
, &deformated
);
2628 deformat_string(package
, name
, &deformated_name
);
2630 len
= strlenW(deformated
) + 6;
2631 if (deformated_name
)
2632 len
+=strlenW(deformated_name
);
2634 buffer
= msi_alloc( len
*sizeof(WCHAR
));
2636 if (deformated_name
)
2637 sprintfW(buffer
,fmt2
,root
,deformated
,deformated_name
);
2639 sprintfW(buffer
,fmt
,root
,deformated
);
2641 msi_free(deformated
);
2642 msi_free(deformated_name
);
2643 msiobj_release(&row
->hdr
);
2647 else if (cmp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2649 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2654 MSIFILE
*file
= get_loaded_file( package
, cmp
->KeyPath
);
2657 return strdupW( file
->TargetPath
);
2662 static HKEY
openSharedDLLsKey(void)
2665 static const WCHAR path
[] =
2666 {'S','o','f','t','w','a','r','e','\\',
2667 'M','i','c','r','o','s','o','f','t','\\',
2668 'W','i','n','d','o','w','s','\\',
2669 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2670 'S','h','a','r','e','d','D','L','L','s',0};
2672 RegCreateKeyW(HKEY_LOCAL_MACHINE
,path
,&hkey
);
2676 static UINT
ACTION_GetSharedDLLsCount(LPCWSTR dll
)
2681 DWORD sz
= sizeof(count
);
2684 hkey
= openSharedDLLsKey();
2685 rc
= RegQueryValueExW(hkey
, dll
, NULL
, &type
, (LPBYTE
)&count
, &sz
);
2686 if (rc
!= ERROR_SUCCESS
)
2692 static UINT
ACTION_WriteSharedDLLsCount(LPCWSTR path
, UINT count
)
2696 hkey
= openSharedDLLsKey();
2698 msi_reg_set_val_dword( hkey
, path
, count
);
2700 RegDeleteValueW(hkey
,path
);
2706 * Return TRUE if the count should be written out and FALSE if not
2708 static void ACTION_RefCountComponent( MSIPACKAGE
* package
, MSICOMPONENT
*comp
)
2710 MSIFEATURE
*feature
;
2714 /* only refcount DLLs */
2715 if (comp
->KeyPath
== NULL
||
2716 comp
->Attributes
& msidbComponentAttributesRegistryKeyPath
||
2717 comp
->Attributes
& msidbComponentAttributesODBCDataSource
)
2721 count
= ACTION_GetSharedDLLsCount( comp
->FullKeypath
);
2722 write
= (count
> 0);
2724 if (comp
->Attributes
& msidbComponentAttributesSharedDllRefCount
)
2728 /* increment counts */
2729 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2733 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
))
2736 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2738 if ( cl
->component
== comp
)
2743 /* decrement counts */
2744 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
2748 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ABSENT
))
2751 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
2753 if ( cl
->component
== comp
)
2758 /* ref count all the files in the component */
2763 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2765 if (file
->Component
== comp
)
2766 ACTION_WriteSharedDLLsCount( file
->TargetPath
, count
);
2770 /* add a count for permanent */
2771 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2774 comp
->RefCount
= count
;
2777 ACTION_WriteSharedDLLsCount( comp
->FullKeypath
, comp
->RefCount
);
2781 * Ok further analysis makes me think that this work is
2782 * actually done in the PublishComponents and PublishFeatures
2783 * step, and not here. It appears like the keypath and all that is
2784 * resolved in this step, however actually written in the Publish steps.
2785 * But we will leave it here for now because it is unclear
2787 static UINT
ACTION_ProcessComponents(MSIPACKAGE
*package
)
2789 WCHAR squished_pc
[GUID_SIZE
];
2790 WCHAR squished_cc
[GUID_SIZE
];
2793 HKEY hkey
=0,hkey2
=0;
2797 /* writes the Component and Features values to the registry */
2799 rc
= MSIREG_OpenComponents(&hkey
);
2800 if (rc
!= ERROR_SUCCESS
)
2803 squash_guid(package
->ProductCode
,squished_pc
);
2804 ui_progress(package
,1,COMPONENT_PROGRESS_VALUE
,1,0);
2806 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2810 ui_progress(package
,2,0,0,0);
2811 if (!comp
->ComponentId
)
2814 squash_guid(comp
->ComponentId
,squished_cc
);
2816 msi_free(comp
->FullKeypath
);
2817 comp
->FullKeypath
= resolve_keypath( package
, comp
);
2819 /* do the refcounting */
2820 ACTION_RefCountComponent( package
, comp
);
2822 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2823 debugstr_w(comp
->Component
),
2824 debugstr_w(squished_cc
),
2825 debugstr_w(comp
->FullKeypath
),
2828 * Write the keypath out if the component is to be registered
2829 * and delete the key if the component is to be unregistered
2831 if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2833 rc
= RegCreateKeyW(hkey
,squished_cc
,&hkey2
);
2834 if (rc
!= ERROR_SUCCESS
)
2837 if (!comp
->FullKeypath
)
2840 msi_reg_set_val_str( hkey2
, squished_pc
, comp
->FullKeypath
);
2842 if (comp
->Attributes
& msidbComponentAttributesPermanent
)
2844 static const WCHAR szPermKey
[] =
2845 { '0','0','0','0','0','0','0','0','0','0','0','0',
2846 '0','0','0','0','0','0','0','0','0','0','0','0',
2847 '0','0','0','0','0','0','0','0',0 };
2849 msi_reg_set_val_str( hkey2
, szPermKey
, comp
->FullKeypath
);
2854 rc
= MSIREG_OpenUserDataComponentKey(comp
->ComponentId
, &hkey2
, TRUE
);
2855 if (rc
!= ERROR_SUCCESS
)
2858 msi_reg_set_val_str(hkey2
, squished_pc
, comp
->FullKeypath
);
2861 else if (ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ABSENT
))
2865 rc
= RegOpenKeyW(hkey
,squished_cc
,&hkey2
);
2866 if (rc
!= ERROR_SUCCESS
)
2869 RegDeleteValueW(hkey2
,squished_pc
);
2871 /* if the key is empty delete it */
2872 res
= RegEnumKeyExW(hkey2
,0,NULL
,0,0,NULL
,0,NULL
);
2874 if (res
== ERROR_NO_MORE_ITEMS
)
2875 RegDeleteKeyW(hkey
,squished_cc
);
2877 MSIREG_DeleteUserDataComponentKey(comp
->ComponentId
);
2881 uirow
= MSI_CreateRecord(3);
2882 MSI_RecordSetStringW(uirow
,1,package
->ProductCode
);
2883 MSI_RecordSetStringW(uirow
,2,comp
->ComponentId
);
2884 MSI_RecordSetStringW(uirow
,3,comp
->FullKeypath
);
2885 ui_actiondata(package
,szProcessComponents
,uirow
);
2886 msiobj_release( &uirow
->hdr
);
2900 static BOOL CALLBACK
Typelib_EnumResNameProc( HMODULE hModule
, LPCWSTR lpszType
,
2901 LPWSTR lpszName
, LONG_PTR lParam
)
2904 typelib_struct
*tl_struct
= (typelib_struct
*) lParam
;
2905 static const WCHAR fmt
[] = {'%','s','\\','%','i',0};
2909 if (!IS_INTRESOURCE(lpszName
))
2911 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName
));
2915 sz
= strlenW(tl_struct
->source
)+4;
2916 sz
*= sizeof(WCHAR
);
2918 if ((INT_PTR
)lpszName
== 1)
2919 tl_struct
->path
= strdupW(tl_struct
->source
);
2922 tl_struct
->path
= msi_alloc(sz
);
2923 sprintfW(tl_struct
->path
,fmt
,tl_struct
->source
, lpszName
);
2926 TRACE("trying %s\n", debugstr_w(tl_struct
->path
));
2927 res
= LoadTypeLib(tl_struct
->path
,&tl_struct
->ptLib
);
2928 if (!SUCCEEDED(res
))
2930 msi_free(tl_struct
->path
);
2931 tl_struct
->path
= NULL
;
2936 ITypeLib_GetLibAttr(tl_struct
->ptLib
, &attr
);
2937 if (IsEqualGUID(&(tl_struct
->clsid
),&(attr
->guid
)))
2939 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2943 msi_free(tl_struct
->path
);
2944 tl_struct
->path
= NULL
;
2946 ITypeLib_ReleaseTLibAttr(tl_struct
->ptLib
, attr
);
2947 ITypeLib_Release(tl_struct
->ptLib
);
2952 static UINT
ITERATE_RegisterTypeLibraries(MSIRECORD
*row
, LPVOID param
)
2954 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
2958 typelib_struct tl_struct
;
2960 static const WCHAR szTYPELIB
[] = {'T','Y','P','E','L','I','B',0};
2962 component
= MSI_RecordGetString(row
,3);
2963 comp
= get_loaded_component(package
,component
);
2965 return ERROR_SUCCESS
;
2967 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
2969 TRACE("Skipping typelib reg due to disabled component\n");
2971 comp
->Action
= comp
->Installed
;
2973 return ERROR_SUCCESS
;
2976 comp
->Action
= INSTALLSTATE_LOCAL
;
2978 file
= get_loaded_file( package
, comp
->KeyPath
);
2980 return ERROR_SUCCESS
;
2982 module
= LoadLibraryExW( file
->TargetPath
, NULL
, LOAD_LIBRARY_AS_DATAFILE
);
2986 guid
= MSI_RecordGetString(row
,1);
2987 CLSIDFromString((LPWSTR
)guid
, &tl_struct
.clsid
);
2988 tl_struct
.source
= strdupW( file
->TargetPath
);
2989 tl_struct
.path
= NULL
;
2991 EnumResourceNamesW(module
, szTYPELIB
, Typelib_EnumResNameProc
,
2992 (LONG_PTR
)&tl_struct
);
3000 helpid
= MSI_RecordGetString(row
,6);
3003 help
= resolve_folder(package
,helpid
,FALSE
,FALSE
,TRUE
,NULL
);
3004 res
= RegisterTypeLib(tl_struct
.ptLib
,tl_struct
.path
,help
);
3007 if (!SUCCEEDED(res
))
3008 ERR("Failed to register type library %s\n",
3009 debugstr_w(tl_struct
.path
));
3012 ui_actiondata(package
,szRegisterTypeLibraries
,row
);
3014 TRACE("Registered %s\n", debugstr_w(tl_struct
.path
));
3017 ITypeLib_Release(tl_struct
.ptLib
);
3018 msi_free(tl_struct
.path
);
3021 ERR("Failed to load type library %s\n",
3022 debugstr_w(tl_struct
.source
));
3024 FreeLibrary(module
);
3025 msi_free(tl_struct
.source
);
3028 ERR("Could not load file! %s\n", debugstr_w(file
->TargetPath
));
3030 return ERROR_SUCCESS
;
3033 static UINT
ACTION_RegisterTypeLibraries(MSIPACKAGE
*package
)
3036 * OK this is a bit confusing.. I am given a _Component key and I believe
3037 * that the file that is being registered as a type library is the "key file
3038 * of that component" which I interpret to mean "The file in the KeyPath of
3043 static const WCHAR Query
[] =
3044 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3045 '`','T','y','p','e','L','i','b','`',0};
3047 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3048 if (rc
!= ERROR_SUCCESS
)
3049 return ERROR_SUCCESS
;
3051 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_RegisterTypeLibraries
, package
);
3052 msiobj_release(&view
->hdr
);
3056 static UINT
ITERATE_CreateShortcuts(MSIRECORD
*row
, LPVOID param
)
3058 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3059 LPWSTR target_file
, target_folder
, filename
;
3060 LPCWSTR buffer
, extension
;
3062 static const WCHAR szlnk
[]={'.','l','n','k',0};
3063 IShellLinkW
*sl
= NULL
;
3064 IPersistFile
*pf
= NULL
;
3067 buffer
= MSI_RecordGetString(row
,4);
3068 comp
= get_loaded_component(package
,buffer
);
3070 return ERROR_SUCCESS
;
3072 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3074 TRACE("Skipping shortcut creation due to disabled component\n");
3076 comp
->Action
= comp
->Installed
;
3078 return ERROR_SUCCESS
;
3081 comp
->Action
= INSTALLSTATE_LOCAL
;
3083 ui_actiondata(package
,szCreateShortcuts
,row
);
3085 res
= CoCreateInstance( &CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
,
3086 &IID_IShellLinkW
, (LPVOID
*) &sl
);
3090 ERR("CLSID_ShellLink not available\n");
3094 res
= IShellLinkW_QueryInterface( sl
, &IID_IPersistFile
,(LPVOID
*) &pf
);
3097 ERR("QueryInterface(IID_IPersistFile) failed\n");
3101 buffer
= MSI_RecordGetString(row
,2);
3102 target_folder
= resolve_folder(package
, buffer
,FALSE
,FALSE
,TRUE
,NULL
);
3104 /* may be needed because of a bug somewhere else */
3105 create_full_pathW(target_folder
);
3107 filename
= msi_dup_record_field( row
, 3 );
3108 reduce_to_longfilename(filename
);
3110 extension
= strchrW(filename
,'.');
3111 if (!extension
|| strcmpiW(extension
,szlnk
))
3113 int len
= strlenW(filename
);
3114 filename
= msi_realloc(filename
, len
* sizeof(WCHAR
) + sizeof(szlnk
));
3115 memcpy(filename
+ len
, szlnk
, sizeof(szlnk
));
3117 target_file
= build_directory_name(2, target_folder
, filename
);
3118 msi_free(target_folder
);
3121 buffer
= MSI_RecordGetString(row
,5);
3122 if (strchrW(buffer
,'['))
3125 deformat_string(package
,buffer
,&deformated
);
3126 IShellLinkW_SetPath(sl
,deformated
);
3127 msi_free(deformated
);
3131 FIXME("poorly handled shortcut format, advertised shortcut\n");
3132 IShellLinkW_SetPath(sl
,comp
->FullKeypath
);
3135 if (!MSI_RecordIsNull(row
,6))
3138 buffer
= MSI_RecordGetString(row
,6);
3139 deformat_string(package
,buffer
,&deformated
);
3140 IShellLinkW_SetArguments(sl
,deformated
);
3141 msi_free(deformated
);
3144 if (!MSI_RecordIsNull(row
,7))
3146 buffer
= MSI_RecordGetString(row
,7);
3147 IShellLinkW_SetDescription(sl
,buffer
);
3150 if (!MSI_RecordIsNull(row
,8))
3151 IShellLinkW_SetHotkey(sl
,MSI_RecordGetInteger(row
,8));
3153 if (!MSI_RecordIsNull(row
,9))
3158 buffer
= MSI_RecordGetString(row
,9);
3160 Path
= build_icon_path(package
,buffer
);
3161 index
= MSI_RecordGetInteger(row
,10);
3163 /* no value means 0 */
3164 if (index
== MSI_NULL_INTEGER
)
3167 IShellLinkW_SetIconLocation(sl
,Path
,index
);
3171 if (!MSI_RecordIsNull(row
,11))
3172 IShellLinkW_SetShowCmd(sl
,MSI_RecordGetInteger(row
,11));
3174 if (!MSI_RecordIsNull(row
,12))
3177 buffer
= MSI_RecordGetString(row
,12);
3178 Path
= resolve_folder(package
, buffer
, FALSE
, FALSE
, TRUE
, NULL
);
3180 IShellLinkW_SetWorkingDirectory(sl
,Path
);
3184 TRACE("Writing shortcut to %s\n",debugstr_w(target_file
));
3185 IPersistFile_Save(pf
,target_file
,FALSE
);
3187 msi_free(target_file
);
3191 IPersistFile_Release( pf
);
3193 IShellLinkW_Release( sl
);
3195 return ERROR_SUCCESS
;
3198 static UINT
ACTION_CreateShortcuts(MSIPACKAGE
*package
)
3203 static const WCHAR Query
[] =
3204 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3205 '`','S','h','o','r','t','c','u','t','`',0};
3207 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3208 if (rc
!= ERROR_SUCCESS
)
3209 return ERROR_SUCCESS
;
3211 res
= CoInitialize( NULL
);
3214 ERR("CoInitialize failed\n");
3215 return ERROR_FUNCTION_FAILED
;
3218 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateShortcuts
, package
);
3219 msiobj_release(&view
->hdr
);
3226 static UINT
ITERATE_PublishProduct(MSIRECORD
*row
, LPVOID param
)
3228 MSIPACKAGE
* package
= (MSIPACKAGE
*)param
;
3237 FileName
= MSI_RecordGetString(row
,1);
3240 ERR("Unable to get FileName\n");
3241 return ERROR_SUCCESS
;
3244 FilePath
= build_icon_path(package
,FileName
);
3246 TRACE("Creating icon file at %s\n",debugstr_w(FilePath
));
3248 the_file
= CreateFileW(FilePath
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
,
3249 FILE_ATTRIBUTE_NORMAL
, NULL
);
3251 if (the_file
== INVALID_HANDLE_VALUE
)
3253 ERR("Unable to create file %s\n",debugstr_w(FilePath
));
3255 return ERROR_SUCCESS
;
3262 rc
= MSI_RecordReadStream(row
,2,buffer
,&sz
);
3263 if (rc
!= ERROR_SUCCESS
)
3265 ERR("Failed to get stream\n");
3266 CloseHandle(the_file
);
3267 DeleteFileW(FilePath
);
3270 WriteFile(the_file
,buffer
,sz
,&write
,NULL
);
3271 } while (sz
== 1024);
3275 CloseHandle(the_file
);
3277 uirow
= MSI_CreateRecord(1);
3278 MSI_RecordSetStringW(uirow
,1,FileName
);
3279 ui_actiondata(package
,szPublishProduct
,uirow
);
3280 msiobj_release( &uirow
->hdr
);
3282 return ERROR_SUCCESS
;
3285 static BOOL
msi_check_publish(MSIPACKAGE
*package
)
3287 MSIFEATURE
*feature
;
3289 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3291 if (feature
->ActionRequest
== INSTALLSTATE_LOCAL
)
3299 * 99% of the work done here is only done for
3300 * advertised installs. However this is where the
3301 * Icon table is processed and written out
3302 * so that is what I am going to do here.
3304 static UINT
ACTION_PublishProduct(MSIPACKAGE
*package
)
3309 MSISOURCELISTINFO
*info
;
3311 static const WCHAR Query
[]=
3312 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3313 '`','I','c','o','n','`',0};
3314 /* for registry stuff */
3317 HKEY hudkey
=0, props
=0;
3319 static const WCHAR szProductLanguage
[] =
3320 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3321 static const WCHAR szARPProductIcon
[] =
3322 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3323 static const WCHAR szProductVersion
[] =
3324 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3325 static const WCHAR szSourceList
[] =
3326 {'S','o','u','r','c','e','L','i','s','t',0};
3327 static const WCHAR szEmpty
[] = {0};
3331 MSIHANDLE hDb
, hSumInfo
;
3333 /* FIXME: also need to publish if the product is in advertise mode */
3334 if (!msi_check_publish(package
))
3335 return ERROR_SUCCESS
;
3337 /* write out icon files */
3339 rc
= MSI_DatabaseOpenViewW(package
->db
, Query
, &view
);
3340 if (rc
== ERROR_SUCCESS
)
3342 MSI_IterateRecords(view
, NULL
, ITERATE_PublishProduct
, package
);
3343 msiobj_release(&view
->hdr
);
3346 /* ok there is a lot more done here but i need to figure out what */
3348 if (package
->Context
== MSIINSTALLCONTEXT_MACHINE
)
3350 rc
= MSIREG_OpenLocalClassesProductKey(package
->ProductCode
, &hukey
, TRUE
);
3351 if (rc
!= ERROR_SUCCESS
)
3354 rc
= MSIREG_OpenLocalSystemInstallProps(package
->ProductCode
, &props
, TRUE
);
3355 if (rc
!= ERROR_SUCCESS
)
3360 rc
= MSIREG_OpenProductsKey(package
->ProductCode
,&hkey
,TRUE
);
3361 if (rc
!= ERROR_SUCCESS
)
3364 rc
= MSIREG_OpenUserProductsKey(package
->ProductCode
,&hukey
,TRUE
);
3365 if (rc
!= ERROR_SUCCESS
)
3368 rc
= MSIREG_OpenCurrentUserInstallProps(package
->ProductCode
, &props
, TRUE
);
3369 if (rc
!= ERROR_SUCCESS
)
3373 rc
= RegCreateKeyW(hukey
, szSourceList
, &source
);
3374 if (rc
!= ERROR_SUCCESS
)
3377 RegCloseKey(source
);
3379 rc
= MSIREG_OpenUserDataProductKey(package
->ProductCode
,&hudkey
,TRUE
);
3380 if (rc
!= ERROR_SUCCESS
)
3383 buffer
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTNAMEW
);
3384 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PRODUCTNAMEW
, buffer
);
3387 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
3388 msi_reg_set_val_dword( hukey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
3390 packname
= strrchrW( package
->PackagePath
, '\\' ) + 1;
3391 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PACKAGENAMEW
, packname
);
3394 msi_reg_set_val_dword( hukey
, INSTALLPROPERTY_AUTHORIZED_LUA_APPW
, 0 );
3395 msi_reg_set_val_dword( props
, INSTALLPROPERTY_INSTANCETYPEW
, 0 );
3397 buffer
= msi_dup_property( package
, szARPProductIcon
);
3400 LPWSTR path
= build_icon_path(package
,buffer
);
3401 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PRODUCTICONW
, path
);
3406 buffer
= msi_dup_property( package
, szProductVersion
);
3409 DWORD verdword
= msi_version_str_to_dword(buffer
);
3410 msi_reg_set_val_dword( hukey
, INSTALLPROPERTY_VERSIONW
, verdword
);
3414 buffer
= strrchrW( package
->PackagePath
, '\\') + 1;
3415 rc
= MsiSourceListSetInfoW( package
->ProductCode
, NULL
,
3416 package
->Context
, MSICODE_PRODUCT
,
3417 INSTALLPROPERTY_PACKAGENAMEW
, buffer
);
3418 if (rc
!= ERROR_SUCCESS
)
3421 rc
= MsiSourceListSetInfoW( package
->ProductCode
, NULL
,
3422 package
->Context
, MSICODE_PRODUCT
,
3423 INSTALLPROPERTY_MEDIAPACKAGEPATHW
, szEmpty
);
3424 if (rc
!= ERROR_SUCCESS
)
3427 rc
= MsiSourceListSetInfoW( package
->ProductCode
, NULL
,
3428 package
->Context
, MSICODE_PRODUCT
,
3429 INSTALLPROPERTY_DISKPROMPTW
, szEmpty
);
3430 if (rc
!= ERROR_SUCCESS
)
3433 /* FIXME: Need to write more keys to the user registry */
3435 hDb
= alloc_msihandle( &package
->db
->hdr
);
3437 rc
= ERROR_NOT_ENOUGH_MEMORY
;
3440 rc
= MsiGetSummaryInformationW(hDb
, NULL
, 0, &hSumInfo
);
3441 MsiCloseHandle(hDb
);
3442 if (rc
== ERROR_SUCCESS
)
3444 WCHAR guidbuffer
[0x200];
3446 rc
= MsiSummaryInfoGetPropertyW(hSumInfo
, 9, NULL
, NULL
, NULL
,
3448 if (rc
== ERROR_SUCCESS
)
3450 /* for now we only care about the first guid */
3451 LPWSTR ptr
= strchrW(guidbuffer
,';');
3453 msi_reg_set_val_str( hukey
, INSTALLPROPERTY_PACKAGECODEW
, guidbuffer
);
3457 ERR("Unable to query Revision_Number...\n");
3460 MsiCloseHandle(hSumInfo
);
3464 ERR("Unable to open Summary Information\n");
3468 /* publish the SourceList info */
3469 LIST_FOR_EACH_ENTRY(info
, &package
->sourcelist_info
, MSISOURCELISTINFO
, entry
)
3471 if (!lstrcmpW(info
->property
, INSTALLPROPERTY_LASTUSEDSOURCEW
))
3472 msi_set_last_used_source(package
->ProductCode
, NULL
, info
->context
,
3473 info
->options
, info
->value
);
3475 MsiSourceListSetInfoW(package
->ProductCode
, NULL
,
3476 info
->context
, info
->options
,
3477 info
->property
, info
->value
);
3480 LIST_FOR_EACH_ENTRY(disk
, &package
->sourcelist_media
, MSIMEDIADISK
, entry
)
3482 MsiSourceListAddMediaDiskW(package
->ProductCode
, NULL
,
3483 disk
->context
, disk
->options
,
3484 disk
->disk_id
, disk
->volume_label
, disk
->disk_prompt
);
3490 RegCloseKey(hudkey
);
3496 static UINT
ITERATE_WriteIniValues(MSIRECORD
*row
, LPVOID param
)
3498 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3499 LPCWSTR component
,section
,key
,value
,identifier
,filename
,dirproperty
;
3500 LPWSTR deformated_section
, deformated_key
, deformated_value
;
3501 LPWSTR folder
, fullname
= NULL
;
3505 static const WCHAR szWindowsFolder
[] =
3506 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3508 component
= MSI_RecordGetString(row
, 8);
3509 comp
= get_loaded_component(package
,component
);
3511 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
))
3513 TRACE("Skipping ini file due to disabled component %s\n",
3514 debugstr_w(component
));
3516 comp
->Action
= comp
->Installed
;
3518 return ERROR_SUCCESS
;
3521 comp
->Action
= INSTALLSTATE_LOCAL
;
3523 identifier
= MSI_RecordGetString(row
,1);
3524 filename
= MSI_RecordGetString(row
,2);
3525 dirproperty
= MSI_RecordGetString(row
,3);
3526 section
= MSI_RecordGetString(row
,4);
3527 key
= MSI_RecordGetString(row
,5);
3528 value
= MSI_RecordGetString(row
,6);
3529 action
= MSI_RecordGetInteger(row
,7);
3531 deformat_string(package
,section
,&deformated_section
);
3532 deformat_string(package
,key
,&deformated_key
);
3533 deformat_string(package
,value
,&deformated_value
);
3537 folder
= resolve_folder(package
, dirproperty
, FALSE
, FALSE
, TRUE
, NULL
);
3539 folder
= msi_dup_property( package
, dirproperty
);
3542 folder
= msi_dup_property( package
, szWindowsFolder
);
3546 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty
));
3550 fullname
= build_directory_name(2, folder
, filename
);
3554 TRACE("Adding value %s to section %s in %s\n",
3555 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3556 debugstr_w(fullname
));
3557 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3558 deformated_value
, fullname
);
3560 else if (action
== 1)
3563 GetPrivateProfileStringW(deformated_section
, deformated_key
, NULL
,
3564 returned
, 10, fullname
);
3565 if (returned
[0] == 0)
3567 TRACE("Adding value %s to section %s in %s\n",
3568 debugstr_w(deformated_key
), debugstr_w(deformated_section
),
3569 debugstr_w(fullname
));
3571 WritePrivateProfileStringW(deformated_section
, deformated_key
,
3572 deformated_value
, fullname
);
3575 else if (action
== 3)
3576 FIXME("Append to existing section not yet implemented\n");
3578 uirow
= MSI_CreateRecord(4);
3579 MSI_RecordSetStringW(uirow
,1,identifier
);
3580 MSI_RecordSetStringW(uirow
,2,deformated_section
);
3581 MSI_RecordSetStringW(uirow
,3,deformated_key
);
3582 MSI_RecordSetStringW(uirow
,4,deformated_value
);
3583 ui_actiondata(package
,szWriteIniValues
,uirow
);
3584 msiobj_release( &uirow
->hdr
);
3588 msi_free(deformated_key
);
3589 msi_free(deformated_value
);
3590 msi_free(deformated_section
);
3591 return ERROR_SUCCESS
;
3594 static UINT
ACTION_WriteIniValues(MSIPACKAGE
*package
)
3598 static const WCHAR ExecSeqQuery
[] =
3599 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3600 '`','I','n','i','F','i','l','e','`',0};
3602 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3603 if (rc
!= ERROR_SUCCESS
)
3605 TRACE("no IniFile table\n");
3606 return ERROR_SUCCESS
;
3609 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteIniValues
, package
);
3610 msiobj_release(&view
->hdr
);
3614 static UINT
ITERATE_SelfRegModules(MSIRECORD
*row
, LPVOID param
)
3616 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
3621 static const WCHAR ExeStr
[] =
3622 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3623 static const WCHAR close
[] = {'\"',0};
3625 PROCESS_INFORMATION info
;
3630 memset(&si
,0,sizeof(STARTUPINFOW
));
3632 filename
= MSI_RecordGetString(row
,1);
3633 file
= get_loaded_file( package
, filename
);
3637 ERR("Unable to find file id %s\n",debugstr_w(filename
));
3638 return ERROR_SUCCESS
;
3641 len
= strlenW(ExeStr
) + strlenW( file
->TargetPath
) + 2;
3643 FullName
= msi_alloc(len
*sizeof(WCHAR
));
3644 strcpyW(FullName
,ExeStr
);
3645 strcatW( FullName
, file
->TargetPath
);
3646 strcatW(FullName
,close
);
3648 TRACE("Registering %s\n",debugstr_w(FullName
));
3649 brc
= CreateProcessW(NULL
, FullName
, NULL
, NULL
, FALSE
, 0, NULL
, c_colon
,
3653 msi_dialog_check_messages(info
.hProcess
);
3658 uirow
= MSI_CreateRecord( 2 );
3659 uipath
= strdupW( file
->TargetPath
);
3660 p
= strrchrW(uipath
,'\\');
3663 MSI_RecordSetStringW( uirow
, 1, &p
[1] );
3664 MSI_RecordSetStringW( uirow
, 2, uipath
);
3665 ui_actiondata( package
, szSelfRegModules
, uirow
);
3666 msiobj_release( &uirow
->hdr
);
3668 /* FIXME: call ui_progress? */
3670 return ERROR_SUCCESS
;
3673 static UINT
ACTION_SelfRegModules(MSIPACKAGE
*package
)
3677 static const WCHAR ExecSeqQuery
[] =
3678 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3679 '`','S','e','l','f','R','e','g','`',0};
3681 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
3682 if (rc
!= ERROR_SUCCESS
)
3684 TRACE("no SelfReg table\n");
3685 return ERROR_SUCCESS
;
3688 MSI_IterateRecords(view
, NULL
, ITERATE_SelfRegModules
, package
);
3689 msiobj_release(&view
->hdr
);
3691 return ERROR_SUCCESS
;
3694 static UINT
ACTION_PublishFeatures(MSIPACKAGE
*package
)
3696 MSIFEATURE
*feature
;
3702 if (!msi_check_publish(package
))
3703 return ERROR_SUCCESS
;
3705 rc
= MSIREG_OpenFeaturesKey(package
->ProductCode
,&hkey
,TRUE
);
3706 if (rc
!= ERROR_SUCCESS
)
3709 rc
= MSIREG_OpenUserFeaturesKey(package
->ProductCode
,&hukey
,TRUE
);
3710 if (rc
!= ERROR_SUCCESS
)
3713 rc
= MSIREG_OpenUserDataFeaturesKey(package
->ProductCode
, &userdata
, TRUE
);
3714 if (rc
!= ERROR_SUCCESS
)
3717 /* here the guids are base 85 encoded */
3718 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
3724 BOOL absent
= FALSE
;
3727 if (!ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_LOCAL
) &&
3728 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_SOURCE
) &&
3729 !ACTION_VerifyFeatureForAction( feature
, INSTALLSTATE_ADVERTISED
))
3733 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3737 if (feature
->Feature_Parent
)
3738 size
+= strlenW( feature
->Feature_Parent
)+2;
3740 data
= msi_alloc(size
* sizeof(WCHAR
));
3743 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
3745 MSICOMPONENT
* component
= cl
->component
;
3749 if (component
->ComponentId
)
3751 TRACE("From %s\n",debugstr_w(component
->ComponentId
));
3752 CLSIDFromString(component
->ComponentId
, &clsid
);
3753 encode_base85_guid(&clsid
,buf
);
3754 TRACE("to %s\n",debugstr_w(buf
));
3759 if (feature
->Feature_Parent
)
3761 static const WCHAR sep
[] = {'\2',0};
3763 strcatW(data
,feature
->Feature_Parent
);
3766 msi_reg_set_val_str( hkey
, feature
->Feature
, data
);
3767 msi_reg_set_val_str( userdata
, feature
->Feature
, data
);
3771 if (feature
->Feature_Parent
)
3772 size
= strlenW(feature
->Feature_Parent
)*sizeof(WCHAR
);
3775 static const WCHAR emptyW
[] = {0};
3776 size
+= sizeof(WCHAR
);
3777 RegSetValueExW(hukey
,feature
->Feature
,0,REG_SZ
,
3778 (LPBYTE
)(feature
->Feature_Parent
? feature
->Feature_Parent
: emptyW
),size
);
3782 size
+= 2*sizeof(WCHAR
);
3783 data
= msi_alloc(size
);
3786 if (feature
->Feature_Parent
)
3787 strcpyW( &data
[1], feature
->Feature_Parent
);
3788 RegSetValueExW(hukey
,feature
->Feature
,0,REG_SZ
,
3794 uirow
= MSI_CreateRecord( 1 );
3795 MSI_RecordSetStringW( uirow
, 1, feature
->Feature
);
3796 ui_actiondata( package
, szPublishFeatures
, uirow
);
3797 msiobj_release( &uirow
->hdr
);
3798 /* FIXME: call ui_progress? */
3807 static UINT
msi_unpublish_feature(MSIPACKAGE
*package
, MSIFEATURE
*feature
)
3812 TRACE("unpublishing feature %s\n", debugstr_w(feature
->Feature
));
3814 r
= MSIREG_OpenUserFeaturesKey(package
->ProductCode
, &hkey
, FALSE
);
3815 if (r
== ERROR_SUCCESS
)
3817 RegDeleteValueW(hkey
, feature
->Feature
);
3821 r
= MSIREG_OpenUserDataFeaturesKey(package
->ProductCode
, &hkey
, FALSE
);
3822 if (r
== ERROR_SUCCESS
)
3824 RegDeleteValueW(hkey
, feature
->Feature
);
3828 return ERROR_SUCCESS
;
3831 static BOOL
msi_check_unpublish(MSIPACKAGE
*package
)
3833 MSIFEATURE
*feature
;
3835 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3837 if (feature
->ActionRequest
!= INSTALLSTATE_ABSENT
)
3844 static UINT
ACTION_UnpublishFeatures(MSIPACKAGE
*package
)
3846 MSIFEATURE
*feature
;
3848 if (!msi_check_unpublish(package
))
3849 return ERROR_SUCCESS
;
3851 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
3853 msi_unpublish_feature(package
, feature
);
3856 return ERROR_SUCCESS
;
3859 static UINT
msi_get_local_package_name( LPWSTR path
)
3861 static const WCHAR szInstaller
[] = {
3862 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3863 static const WCHAR fmt
[] = { '%','x','.','m','s','i',0};
3867 time
= GetTickCount();
3868 GetWindowsDirectoryW( path
, MAX_PATH
);
3869 lstrcatW( path
, szInstaller
);
3870 CreateDirectoryW( path
, NULL
);
3872 len
= lstrlenW(path
);
3873 for (i
=0; i
<0x10000; i
++)
3875 snprintfW( &path
[len
], MAX_PATH
- len
, fmt
, (time
+i
)&0xffff );
3876 handle
= CreateFileW( path
, GENERIC_WRITE
, 0, NULL
,
3877 CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, 0 );
3878 if (handle
!= INVALID_HANDLE_VALUE
)
3880 CloseHandle(handle
);
3883 if (GetLastError() != ERROR_FILE_EXISTS
&&
3884 GetLastError() != ERROR_SHARING_VIOLATION
)
3885 return ERROR_FUNCTION_FAILED
;
3888 return ERROR_SUCCESS
;
3891 static UINT
msi_make_package_local( MSIPACKAGE
*package
, HKEY hkey
)
3893 WCHAR packagefile
[MAX_PATH
];
3897 r
= msi_get_local_package_name( packagefile
);
3898 if (r
!= ERROR_SUCCESS
)
3901 TRACE("Copying to local package %s\n",debugstr_w(packagefile
));
3903 r
= CopyFileW( package
->db
->path
, packagefile
, FALSE
);
3907 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3908 debugstr_w(package
->db
->path
), debugstr_w(packagefile
), GetLastError());
3909 return ERROR_FUNCTION_FAILED
;
3912 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_LOCALPACKAGEW
, packagefile
);
3914 r
= MSIREG_OpenCurrentUserInstallProps(package
->ProductCode
, &props
, TRUE
);
3915 if (r
!= ERROR_SUCCESS
)
3918 msi_reg_set_val_str(props
, INSTALLPROPERTY_LOCALPACKAGEW
, packagefile
);
3920 return ERROR_SUCCESS
;
3923 static UINT
msi_write_uninstall_property_vals( MSIPACKAGE
*package
, HKEY hkey
)
3925 LPWSTR prop
, val
, key
;
3926 static const LPCSTR propval
[] = {
3927 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3928 "ARPCONTACT", "Contact",
3929 "ARPCOMMENTS", "Comments",
3930 "ProductName", "DisplayName",
3931 "ProductVersion", "DisplayVersion",
3932 "ARPHELPLINK", "HelpLink",
3933 "ARPHELPTELEPHONE", "HelpTelephone",
3934 "ARPINSTALLLOCATION", "InstallLocation",
3935 "SourceDir", "InstallSource",
3936 "Manufacturer", "Publisher",
3937 "ARPREADME", "Readme",
3939 "ARPURLINFOABOUT", "URLInfoAbout",
3940 "ARPURLUPDATEINFO", "URLUpdateInfo",
3943 const LPCSTR
*p
= propval
;
3947 prop
= strdupAtoW( *p
++ );
3948 key
= strdupAtoW( *p
++ );
3949 val
= msi_dup_property( package
, prop
);
3950 msi_reg_set_val_str( hkey
, key
, val
);
3955 return ERROR_SUCCESS
;
3958 static UINT
ACTION_RegisterProduct(MSIPACKAGE
*package
)
3961 HKEY hudkey
=0, props
=0;
3962 LPWSTR buffer
= NULL
;
3965 static const WCHAR szWindowsInstaller
[] =
3966 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3967 static const WCHAR szUpgradeCode
[] =
3968 {'U','p','g','r','a','d','e','C','o','d','e',0};
3969 static const WCHAR modpath_fmt
[] =
3970 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3971 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3972 static const WCHAR szModifyPath
[] =
3973 {'M','o','d','i','f','y','P','a','t','h',0};
3974 static const WCHAR szUninstallString
[] =
3975 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3976 static const WCHAR szEstimatedSize
[] =
3977 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3978 static const WCHAR szProductLanguage
[] =
3979 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3980 static const WCHAR szProductVersion
[] =
3981 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3982 static const WCHAR szProductName
[] =
3983 {'P','r','o','d','u','c','t','N','a','m','e',0};
3984 static const WCHAR szDisplayName
[] =
3985 {'D','i','s','p','l','a','y','N','a','m','e',0};
3986 static const WCHAR szDisplayVersion
[] =
3987 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
3988 static const WCHAR szManufacturer
[] =
3989 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
3992 static const WCHAR date_fmt
[] = {'%','i','%','0','2','i','%','0','2','i',0};
3993 LPWSTR upgrade_code
;
3996 /* FIXME: also need to publish if the product is in advertise mode */
3997 if (!msi_check_publish(package
))
3998 return ERROR_SUCCESS
;
4000 rc
= MSIREG_OpenUninstallKey(package
->ProductCode
,&hkey
,TRUE
);
4001 if (rc
!= ERROR_SUCCESS
)
4004 if (package
->Context
== MSIINSTALLCONTEXT_MACHINE
)
4006 rc
= MSIREG_OpenLocalSystemInstallProps(package
->ProductCode
, &props
, TRUE
);
4007 if (rc
!= ERROR_SUCCESS
)
4012 rc
= MSIREG_OpenCurrentUserInstallProps(package
->ProductCode
, &props
, TRUE
);
4013 if (rc
!= ERROR_SUCCESS
)
4017 /* dump all the info i can grab */
4018 /* FIXME: Flesh out more information */
4020 msi_write_uninstall_property_vals( package
, hkey
);
4022 msi_reg_set_val_dword( hkey
, szWindowsInstaller
, 1 );
4024 msi_make_package_local( package
, hkey
);
4026 /* do ModifyPath and UninstallString */
4027 size
= deformat_string(package
,modpath_fmt
,&buffer
);
4028 RegSetValueExW(hkey
,szModifyPath
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
4029 RegSetValueExW(hkey
,szUninstallString
,0,REG_EXPAND_SZ
,(LPBYTE
)buffer
,size
);
4032 /* FIXME: Write real Estimated Size when we have it */
4033 msi_reg_set_val_dword( hkey
, szEstimatedSize
, 0 );
4035 buffer
= msi_dup_property( package
, szProductName
);
4036 msi_reg_set_val_str( props
, szDisplayName
, buffer
);
4039 buffer
= msi_dup_property( package
, cszSourceDir
);
4040 msi_reg_set_val_str( props
, INSTALLPROPERTY_INSTALLSOURCEW
, buffer
);
4043 buffer
= msi_dup_property( package
, szManufacturer
);
4044 msi_reg_set_val_str( props
, INSTALLPROPERTY_PUBLISHERW
, buffer
);
4047 GetLocalTime(&systime
);
4048 sprintfW(szDate
,date_fmt
,systime
.wYear
,systime
.wMonth
,systime
.wDay
);
4049 msi_reg_set_val_str( hkey
, INSTALLPROPERTY_INSTALLDATEW
, szDate
);
4050 msi_reg_set_val_str( props
, INSTALLPROPERTY_INSTALLDATEW
, szDate
);
4052 langid
= msi_get_property_int( package
, szProductLanguage
, 0 );
4053 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_LANGUAGEW
, langid
);
4055 buffer
= msi_dup_property( package
, szProductVersion
);
4056 msi_reg_set_val_str( props
, szDisplayVersion
, buffer
);
4059 DWORD verdword
= msi_version_str_to_dword(buffer
);
4061 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONW
, verdword
);
4062 msi_reg_set_val_dword( props
, INSTALLPROPERTY_VERSIONW
, verdword
);
4063 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMAJORW
, verdword
>>24 );
4064 msi_reg_set_val_dword( props
, INSTALLPROPERTY_VERSIONMAJORW
, verdword
>>24 );
4065 msi_reg_set_val_dword( hkey
, INSTALLPROPERTY_VERSIONMINORW
, (verdword
>>16)&0x00FF );
4066 msi_reg_set_val_dword( props
, INSTALLPROPERTY_VERSIONMINORW
, (verdword
>>16)&0x00FF );
4070 /* Handle Upgrade Codes */
4071 upgrade_code
= msi_dup_property( package
, szUpgradeCode
);
4076 MSIREG_OpenUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
4077 squash_guid(package
->ProductCode
,squashed
);
4078 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
4080 MSIREG_OpenUserUpgradeCodesKey(upgrade_code
, &hkey2
, TRUE
);
4081 squash_guid(package
->ProductCode
,squashed
);
4082 msi_reg_set_val_str( hkey2
, squashed
, NULL
);
4085 msi_free(upgrade_code
);
4090 rc
= MSIREG_OpenUserDataProductKey(package
->ProductCode
, &hudkey
, TRUE
);
4091 if (rc
!= ERROR_SUCCESS
)
4094 RegCloseKey(hudkey
);
4096 msi_reg_set_val_dword( props
, szWindowsInstaller
, 1 );
4099 return ERROR_SUCCESS
;
4102 static UINT
ACTION_InstallExecute(MSIPACKAGE
*package
)
4104 return execute_script(package
,INSTALL_SCRIPT
);
4107 static UINT
msi_unpublish_product(MSIPACKAGE
*package
)
4109 LPWSTR remove
= NULL
;
4110 LPWSTR
*features
= NULL
;
4111 BOOL full_uninstall
= TRUE
;
4112 MSIFEATURE
*feature
;
4114 static const WCHAR szRemove
[] = {'R','E','M','O','V','E',0};
4115 static const WCHAR szAll
[] = {'A','L','L',0};
4117 remove
= msi_dup_property(package
, szRemove
);
4119 return ERROR_SUCCESS
;
4121 features
= msi_split_string(remove
, ',');
4125 ERR("REMOVE feature list is empty!\n");
4126 return ERROR_FUNCTION_FAILED
;
4129 if (!lstrcmpW(features
[0], szAll
))
4130 full_uninstall
= TRUE
;
4133 LIST_FOR_EACH_ENTRY(feature
, &package
->features
, MSIFEATURE
, entry
)
4135 if (feature
->Action
!= INSTALLSTATE_ABSENT
)
4136 full_uninstall
= FALSE
;
4140 if (!full_uninstall
)
4143 MSIREG_DeleteProductKey(package
->ProductCode
);
4144 MSIREG_DeleteUserProductKey(package
->ProductCode
);
4145 MSIREG_DeleteUserDataProductKey(package
->ProductCode
);
4146 MSIREG_DeleteUserFeaturesKey(package
->ProductCode
);
4147 MSIREG_DeleteUninstallKey(package
->ProductCode
);
4152 return ERROR_SUCCESS
;
4155 static UINT
ACTION_InstallFinalize(MSIPACKAGE
*package
)
4159 rc
= msi_unpublish_product(package
);
4160 if (rc
!= ERROR_SUCCESS
)
4163 /* turn off scheduling */
4164 package
->script
->CurrentlyScripting
= FALSE
;
4166 /* first do the same as an InstallExecute */
4167 rc
= ACTION_InstallExecute(package
);
4168 if (rc
!= ERROR_SUCCESS
)
4171 /* then handle Commit Actions */
4172 rc
= execute_script(package
,COMMIT_SCRIPT
);
4177 UINT
ACTION_ForceReboot(MSIPACKAGE
*package
)
4179 static const WCHAR RunOnce
[] = {
4180 'S','o','f','t','w','a','r','e','\\',
4181 'M','i','c','r','o','s','o','f','t','\\',
4182 'W','i','n','d','o','w','s','\\',
4183 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4184 'R','u','n','O','n','c','e',0};
4185 static const WCHAR InstallRunOnce
[] = {
4186 'S','o','f','t','w','a','r','e','\\',
4187 'M','i','c','r','o','s','o','f','t','\\',
4188 'W','i','n','d','o','w','s','\\',
4189 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4190 'I','n','s','t','a','l','l','e','r','\\',
4191 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4193 static const WCHAR msiexec_fmt
[] = {
4195 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4196 '\"','%','s','\"',0};
4197 static const WCHAR install_fmt
[] = {
4198 '/','I',' ','\"','%','s','\"',' ',
4199 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4200 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4201 WCHAR buffer
[256], sysdir
[MAX_PATH
];
4203 WCHAR squished_pc
[100];
4205 squash_guid(package
->ProductCode
,squished_pc
);
4207 GetSystemDirectoryW(sysdir
, sizeof(sysdir
)/sizeof(sysdir
[0]));
4208 RegCreateKeyW(HKEY_LOCAL_MACHINE
,RunOnce
,&hkey
);
4209 snprintfW(buffer
,sizeof(buffer
)/sizeof(buffer
[0]),msiexec_fmt
,sysdir
,
4212 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
4215 TRACE("Reboot command %s\n",debugstr_w(buffer
));
4217 RegCreateKeyW(HKEY_LOCAL_MACHINE
,InstallRunOnce
,&hkey
);
4218 sprintfW(buffer
,install_fmt
,package
->ProductCode
,squished_pc
);
4220 msi_reg_set_val_str( hkey
, squished_pc
, buffer
);
4223 return ERROR_INSTALL_SUSPEND
;
4226 static UINT
ACTION_ResolveSource(MSIPACKAGE
* package
)
4232 * We are currently doing what should be done here in the top level Install
4233 * however for Administrative and uninstalls this step will be needed
4235 if (!package
->PackagePath
)
4236 return ERROR_SUCCESS
;
4238 msi_set_sourcedir_props(package
, TRUE
);
4240 attrib
= GetFileAttributesW(package
->db
->path
);
4241 if (attrib
== INVALID_FILE_ATTRIBUTES
)
4247 rc
= MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
4248 package
->Context
, MSICODE_PRODUCT
,
4249 INSTALLPROPERTY_DISKPROMPTW
,NULL
,&size
);
4250 if (rc
== ERROR_MORE_DATA
)
4252 prompt
= msi_alloc(size
* sizeof(WCHAR
));
4253 MsiSourceListGetInfoW(package
->ProductCode
, NULL
,
4254 package
->Context
, MSICODE_PRODUCT
,
4255 INSTALLPROPERTY_DISKPROMPTW
,prompt
,&size
);
4258 prompt
= strdupW(package
->db
->path
);
4260 msg
= generate_error_string(package
,1302,1,prompt
);
4261 while(attrib
== INVALID_FILE_ATTRIBUTES
)
4263 rc
= MessageBoxW(NULL
,msg
,NULL
,MB_OKCANCEL
);
4266 rc
= ERROR_INSTALL_USEREXIT
;
4269 attrib
= GetFileAttributesW(package
->db
->path
);
4275 return ERROR_SUCCESS
;
4280 static UINT
ACTION_RegisterUser(MSIPACKAGE
*package
)
4287 static const WCHAR szPropKeys
[][80] =
4289 {'P','r','o','d','u','c','t','I','D',0},
4290 {'U','S','E','R','N','A','M','E',0},
4291 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4295 static const WCHAR szRegKeys
[][80] =
4297 {'P','r','o','d','u','c','t','I','D',0},
4298 {'R','e','g','O','w','n','e','r',0},
4299 {'R','e','g','C','o','m','p','a','n','y',0},
4303 if (msi_check_unpublish(package
))
4305 MSIREG_DeleteUserDataProductKey(package
->ProductCode
);
4306 return ERROR_SUCCESS
;
4309 productid
= msi_dup_property( package
, INSTALLPROPERTY_PRODUCTIDW
);
4311 return ERROR_SUCCESS
;
4313 rc
= MSIREG_OpenCurrentUserInstallProps(package
->ProductCode
, &hkey
, TRUE
);
4314 if (rc
!= ERROR_SUCCESS
)
4317 for( i
= 0; szPropKeys
[i
][0]; i
++ )
4319 buffer
= msi_dup_property( package
, szPropKeys
[i
] );
4320 msi_reg_set_val_str( hkey
, szRegKeys
[i
], buffer
);
4325 msi_free(productid
);
4328 /* FIXME: call ui_actiondata */
4334 static UINT
ACTION_ExecuteAction(MSIPACKAGE
*package
)
4338 package
->script
->InWhatSequence
|= SEQUENCE_EXEC
;
4339 rc
= ACTION_ProcessExecSequence(package
,FALSE
);
4344 static UINT
ITERATE_PublishComponent(MSIRECORD
*rec
, LPVOID param
)
4346 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4347 LPCWSTR compgroupid
=NULL
;
4348 LPCWSTR feature
=NULL
;
4349 LPCWSTR text
= NULL
;
4350 LPCWSTR qualifier
= NULL
;
4351 LPCWSTR component
= NULL
;
4352 LPWSTR advertise
= NULL
;
4353 LPWSTR output
= NULL
;
4355 UINT rc
= ERROR_SUCCESS
;
4360 component
= MSI_RecordGetString(rec
,3);
4361 comp
= get_loaded_component(package
,component
);
4363 if (!ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_LOCAL
) &&
4364 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_SOURCE
) &&
4365 !ACTION_VerifyComponentForAction( comp
, INSTALLSTATE_ADVERTISED
))
4367 TRACE("Skipping: Component %s not scheduled for install\n",
4368 debugstr_w(component
));
4370 return ERROR_SUCCESS
;
4373 compgroupid
= MSI_RecordGetString(rec
,1);
4374 qualifier
= MSI_RecordGetString(rec
,2);
4376 rc
= MSIREG_OpenUserComponentsKey(compgroupid
, &hkey
, TRUE
);
4377 if (rc
!= ERROR_SUCCESS
)
4380 text
= MSI_RecordGetString(rec
,4);
4381 feature
= MSI_RecordGetString(rec
,5);
4383 advertise
= create_component_advertise_string(package
, comp
, feature
);
4385 sz
= strlenW(advertise
);
4388 sz
+= lstrlenW(text
);
4391 sz
*= sizeof(WCHAR
);
4393 output
= msi_alloc_zero(sz
);
4394 strcpyW(output
,advertise
);
4395 msi_free(advertise
);
4398 strcatW(output
,text
);
4400 msi_reg_set_val_multi_str( hkey
, qualifier
, output
);
4407 uirow
= MSI_CreateRecord( 2 );
4408 MSI_RecordSetStringW( uirow
, 1, compgroupid
);
4409 MSI_RecordSetStringW( uirow
, 2, qualifier
);
4410 ui_actiondata( package
, szPublishComponents
, uirow
);
4411 msiobj_release( &uirow
->hdr
);
4412 /* FIXME: call ui_progress? */
4418 * At present I am ignorning the advertised components part of this and only
4419 * focusing on the qualified component sets
4421 static UINT
ACTION_PublishComponents(MSIPACKAGE
*package
)
4425 static const WCHAR ExecSeqQuery
[] =
4426 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4427 '`','P','u','b','l','i','s','h',
4428 'C','o','m','p','o','n','e','n','t','`',0};
4430 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4431 if (rc
!= ERROR_SUCCESS
)
4432 return ERROR_SUCCESS
;
4434 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_PublishComponent
, package
);
4435 msiobj_release(&view
->hdr
);
4440 static UINT
ITERATE_InstallService(MSIRECORD
*rec
, LPVOID param
)
4442 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4445 SC_HANDLE hscm
, service
= NULL
;
4446 LPCWSTR comp
, depends
, pass
;
4447 LPWSTR name
= NULL
, disp
= NULL
;
4448 LPCWSTR load_order
, serv_name
, key
;
4449 DWORD serv_type
, start_type
;
4452 static const WCHAR query
[] =
4453 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4454 '`','C','o','m','p','o','n','e','n','t','`',' ',
4455 'W','H','E','R','E',' ',
4456 '`','C','o','m','p','o','n','e','n','t','`',' ',
4457 '=','\'','%','s','\'',0};
4459 hscm
= OpenSCManagerW(NULL
, SERVICES_ACTIVE_DATABASEW
, GENERIC_WRITE
);
4462 ERR("Failed to open the SC Manager!\n");
4466 start_type
= MSI_RecordGetInteger(rec
, 5);
4467 if (start_type
== SERVICE_BOOT_START
|| start_type
== SERVICE_SYSTEM_START
)
4470 depends
= MSI_RecordGetString(rec
, 8);
4471 if (depends
&& *depends
)
4472 FIXME("Dependency list unhandled!\n");
4474 deformat_string(package
, MSI_RecordGetString(rec
, 2), &name
);
4475 deformat_string(package
, MSI_RecordGetString(rec
, 3), &disp
);
4476 serv_type
= MSI_RecordGetInteger(rec
, 4);
4477 err_control
= MSI_RecordGetInteger(rec
, 6);
4478 load_order
= MSI_RecordGetString(rec
, 7);
4479 serv_name
= MSI_RecordGetString(rec
, 9);
4480 pass
= MSI_RecordGetString(rec
, 10);
4481 comp
= MSI_RecordGetString(rec
, 12);
4483 /* fetch the service path */
4484 row
= MSI_QueryGetRecord(package
->db
, query
, comp
);
4487 ERR("Control query failed!\n");
4491 key
= MSI_RecordGetString(row
, 6);
4493 file
= get_loaded_file(package
, key
);
4494 msiobj_release(&row
->hdr
);
4497 ERR("Failed to load the service file\n");
4501 service
= CreateServiceW(hscm
, name
, disp
, GENERIC_ALL
, serv_type
,
4502 start_type
, err_control
, file
->TargetPath
,
4503 load_order
, NULL
, NULL
, serv_name
, pass
);
4506 if (GetLastError() != ERROR_SERVICE_EXISTS
)
4507 ERR("Failed to create service %s: %d\n", debugstr_w(name
), GetLastError());
4511 CloseServiceHandle(service
);
4512 CloseServiceHandle(hscm
);
4516 return ERROR_SUCCESS
;
4519 static UINT
ACTION_InstallServices( MSIPACKAGE
*package
)
4523 static const WCHAR ExecSeqQuery
[] =
4524 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4525 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4527 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
4528 if (rc
!= ERROR_SUCCESS
)
4529 return ERROR_SUCCESS
;
4531 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallService
, package
);
4532 msiobj_release(&view
->hdr
);
4537 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4538 static LPCWSTR
*msi_service_args_to_vector(LPWSTR args
, DWORD
*numargs
)
4540 LPCWSTR
*vector
, *temp_vector
;
4544 static const WCHAR separator
[] = {'[','~',']',0};
4547 sep_len
= sizeof(separator
) / sizeof(WCHAR
) - 1;
4552 vector
= msi_alloc(sizeof(LPWSTR
));
4560 vector
[*numargs
- 1] = p
;
4562 if ((q
= strstrW(p
, separator
)))
4566 temp_vector
= msi_realloc(vector
, (*numargs
+ 1) * sizeof(LPWSTR
));
4572 vector
= temp_vector
;
4581 static UINT
ITERATE_StartService(MSIRECORD
*rec
, LPVOID param
)
4583 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4585 SC_HANDLE scm
, service
= NULL
;
4586 LPCWSTR name
, *vector
= NULL
;
4588 DWORD event
, numargs
;
4589 UINT r
= ERROR_FUNCTION_FAILED
;
4591 comp
= get_loaded_component(package
, MSI_RecordGetString(rec
, 6));
4592 if (!comp
|| comp
->Action
== INSTALLSTATE_UNKNOWN
|| comp
->Action
== INSTALLSTATE_ABSENT
)
4593 return ERROR_SUCCESS
;
4595 name
= MSI_RecordGetString(rec
, 2);
4596 event
= MSI_RecordGetInteger(rec
, 3);
4597 args
= strdupW(MSI_RecordGetString(rec
, 4));
4599 if (!(event
& msidbServiceControlEventStart
))
4600 return ERROR_SUCCESS
;
4602 scm
= OpenSCManagerW(NULL
, NULL
, SC_MANAGER_CONNECT
);
4605 ERR("Failed to open the service control manager\n");
4609 service
= OpenServiceW(scm
, name
, SERVICE_START
);
4612 ERR("Failed to open service %s\n", debugstr_w(name
));
4616 vector
= msi_service_args_to_vector(args
, &numargs
);
4618 if (!StartServiceW(service
, numargs
, vector
))
4620 ERR("Failed to start service %s\n", debugstr_w(name
));
4627 CloseServiceHandle(service
);
4628 CloseServiceHandle(scm
);
4635 static UINT
ACTION_StartServices( MSIPACKAGE
*package
)
4640 static const WCHAR query
[] = {
4641 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4642 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4644 rc
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
4645 if (rc
!= ERROR_SUCCESS
)
4646 return ERROR_SUCCESS
;
4648 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_StartService
, package
);
4649 msiobj_release(&view
->hdr
);
4654 static BOOL
stop_service_dependents(SC_HANDLE scm
, SC_HANDLE service
)
4656 DWORD i
, needed
, count
;
4657 ENUM_SERVICE_STATUSW
*dependencies
;
4661 if (EnumDependentServicesW(service
, SERVICE_ACTIVE
, NULL
,
4662 0, &needed
, &count
))
4665 if (GetLastError() != ERROR_MORE_DATA
)
4668 dependencies
= msi_alloc(needed
);
4672 if (!EnumDependentServicesW(service
, SERVICE_ACTIVE
, dependencies
,
4673 needed
, &needed
, &count
))
4676 for (i
= 0; i
< count
; i
++)
4678 depserv
= OpenServiceW(scm
, dependencies
[i
].lpServiceName
,
4679 SERVICE_STOP
| SERVICE_QUERY_STATUS
);
4683 if (!ControlService(depserv
, SERVICE_CONTROL_STOP
, &ss
))
4690 msi_free(dependencies
);
4694 static UINT
ITERATE_StopService(MSIRECORD
*rec
, LPVOID param
)
4696 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4698 SERVICE_STATUS status
;
4699 SERVICE_STATUS_PROCESS ssp
;
4700 SC_HANDLE scm
= NULL
, service
= NULL
;
4702 DWORD event
, needed
;
4704 event
= MSI_RecordGetInteger(rec
, 3);
4705 if (!(event
& msidbServiceControlEventStop
))
4706 return ERROR_SUCCESS
;
4708 comp
= get_loaded_component(package
, MSI_RecordGetString(rec
, 6));
4709 if (!comp
|| comp
->Action
== INSTALLSTATE_UNKNOWN
|| comp
->Action
== INSTALLSTATE_ABSENT
)
4710 return ERROR_SUCCESS
;
4712 deformat_string(package
, MSI_RecordGetString(rec
, 2), &name
);
4713 deformat_string(package
, MSI_RecordGetString(rec
, 4), &args
);
4714 args
= strdupW(MSI_RecordGetString(rec
, 4));
4716 scm
= OpenSCManagerW(NULL
, NULL
, SC_MANAGER_ALL_ACCESS
);
4719 WARN("Failed to open the SCM: %d\n", GetLastError());
4723 service
= OpenServiceW(scm
, name
,
4725 SERVICE_QUERY_STATUS
|
4726 SERVICE_ENUMERATE_DEPENDENTS
);
4729 WARN("Failed to open service (%s): %d\n",
4730 debugstr_w(name
), GetLastError());
4734 if (!QueryServiceStatusEx(service
, SC_STATUS_PROCESS_INFO
, (LPBYTE
)&ssp
,
4735 sizeof(SERVICE_STATUS_PROCESS
), &needed
))
4737 WARN("Failed to query service status (%s): %d\n",
4738 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
);
4756 return ERROR_SUCCESS
;
4759 static UINT
ACTION_StopServices( MSIPACKAGE
*package
)
4764 static const WCHAR query
[] = {
4765 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4766 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4768 rc
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
4769 if (rc
!= ERROR_SUCCESS
)
4770 return ERROR_SUCCESS
;
4772 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_StopService
, package
);
4773 msiobj_release(&view
->hdr
);
4778 static MSIFILE
*msi_find_file( MSIPACKAGE
*package
, LPCWSTR filename
)
4782 LIST_FOR_EACH_ENTRY(file
, &package
->files
, MSIFILE
, entry
)
4784 if (!lstrcmpW(file
->File
, filename
))
4791 static UINT
ITERATE_InstallODBCDriver( MSIRECORD
*rec
, LPVOID param
)
4793 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4794 LPWSTR driver
, driver_path
, ptr
;
4795 WCHAR outpath
[MAX_PATH
];
4796 MSIFILE
*driver_file
, *setup_file
;
4799 UINT r
= ERROR_SUCCESS
;
4801 static const WCHAR driver_fmt
[] = {
4802 'D','r','i','v','e','r','=','%','s',0};
4803 static const WCHAR setup_fmt
[] = {
4804 'S','e','t','u','p','=','%','s',0};
4805 static const WCHAR usage_fmt
[] = {
4806 'F','i','l','e','U','s','a','g','e','=','1',0};
4808 desc
= MSI_RecordGetString(rec
, 3);
4810 driver_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 4));
4811 setup_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 5));
4813 if (!driver_file
|| !setup_file
)
4815 ERR("ODBC Driver entry not found!\n");
4816 return ERROR_FUNCTION_FAILED
;
4819 len
= lstrlenW(desc
) + lstrlenW(driver_fmt
) + lstrlenW(driver_file
->FileName
) +
4820 lstrlenW(setup_fmt
) + lstrlenW(setup_file
->FileName
) +
4821 lstrlenW(usage_fmt
) + 1;
4822 driver
= msi_alloc(len
* sizeof(WCHAR
));
4824 return ERROR_OUTOFMEMORY
;
4827 lstrcpyW(ptr
, desc
);
4828 ptr
+= lstrlenW(ptr
) + 1;
4830 sprintfW(ptr
, driver_fmt
, driver_file
->FileName
);
4831 ptr
+= lstrlenW(ptr
) + 1;
4833 sprintfW(ptr
, setup_fmt
, setup_file
->FileName
);
4834 ptr
+= lstrlenW(ptr
) + 1;
4836 lstrcpyW(ptr
, usage_fmt
);
4837 ptr
+= lstrlenW(ptr
) + 1;
4840 driver_path
= strdupW(driver_file
->TargetPath
);
4841 ptr
= strrchrW(driver_path
, '\\');
4842 if (ptr
) *ptr
= '\0';
4844 if (!SQLInstallDriverExW(driver
, driver_path
, outpath
, MAX_PATH
,
4845 NULL
, ODBC_INSTALL_COMPLETE
, &usage
))
4847 ERR("Failed to install SQL driver!\n");
4848 r
= ERROR_FUNCTION_FAILED
;
4852 msi_free(driver_path
);
4857 static UINT
ITERATE_InstallODBCTranslator( MSIRECORD
*rec
, LPVOID param
)
4859 MSIPACKAGE
*package
= (MSIPACKAGE
*)param
;
4860 LPWSTR translator
, translator_path
, ptr
;
4861 WCHAR outpath
[MAX_PATH
];
4862 MSIFILE
*translator_file
, *setup_file
;
4865 UINT r
= ERROR_SUCCESS
;
4867 static const WCHAR translator_fmt
[] = {
4868 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4869 static const WCHAR setup_fmt
[] = {
4870 'S','e','t','u','p','=','%','s',0};
4872 desc
= MSI_RecordGetString(rec
, 3);
4874 translator_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 4));
4875 setup_file
= msi_find_file(package
, MSI_RecordGetString(rec
, 5));
4877 if (!translator_file
|| !setup_file
)
4879 ERR("ODBC Translator entry not found!\n");
4880 return ERROR_FUNCTION_FAILED
;
4883 len
= lstrlenW(desc
) + lstrlenW(translator_fmt
) + lstrlenW(translator_file
->FileName
) +
4884 lstrlenW(setup_fmt
) + lstrlenW(setup_file
->FileName
) + 1;
4885 translator
= msi_alloc(len
* sizeof(WCHAR
));
4887 return ERROR_OUTOFMEMORY
;
4890 lstrcpyW(ptr
, desc
);
4891 ptr
+= lstrlenW(ptr
) + 1;
4893 sprintfW(ptr
, translator_fmt
, translator_file
->FileName
);
4894 ptr
+= lstrlenW(ptr
) + 1;
4896 sprintfW(ptr
, setup_fmt
, setup_file
->FileName
);
4897 ptr
+= lstrlenW(ptr
) + 1;
4900 translator_path
= strdupW(translator_file
->TargetPath
);
4901 ptr
= strrchrW(translator_path
, '\\');
4902 if (ptr
) *ptr
= '\0';
4904 if (!SQLInstallTranslatorExW(translator
, translator_path
, outpath
, MAX_PATH
,
4905 NULL
, ODBC_INSTALL_COMPLETE
, &usage
))
4907 ERR("Failed to install SQL translator!\n");
4908 r
= ERROR_FUNCTION_FAILED
;
4911 msi_free(translator
);
4912 msi_free(translator_path
);
4917 static UINT
ITERATE_InstallODBCDataSource( MSIRECORD
*rec
, LPVOID param
)
4920 LPCWSTR desc
, driver
;
4921 WORD request
= ODBC_ADD_SYS_DSN
;
4924 UINT r
= ERROR_SUCCESS
;
4926 static const WCHAR attrs_fmt
[] = {
4927 'D','S','N','=','%','s',0 };
4929 desc
= MSI_RecordGetString(rec
, 3);
4930 driver
= MSI_RecordGetString(rec
, 4);
4931 registration
= MSI_RecordGetInteger(rec
, 5);
4933 if (registration
== msidbODBCDataSourceRegistrationPerMachine
) request
= ODBC_ADD_SYS_DSN
;
4934 else if (registration
== msidbODBCDataSourceRegistrationPerUser
) request
= ODBC_ADD_DSN
;
4936 len
= lstrlenW(attrs_fmt
) + lstrlenW(desc
) + 1 + 1;
4937 attrs
= msi_alloc(len
* sizeof(WCHAR
));
4939 return ERROR_OUTOFMEMORY
;
4941 sprintfW(attrs
, attrs_fmt
, desc
);
4942 attrs
[len
- 1] = '\0';
4944 if (!SQLConfigDataSourceW(NULL
, request
, driver
, attrs
))
4946 ERR("Failed to install SQL data source!\n");
4947 r
= ERROR_FUNCTION_FAILED
;
4955 static UINT
ACTION_InstallODBC( MSIPACKAGE
*package
)
4960 static const WCHAR driver_query
[] = {
4961 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4962 'O','D','B','C','D','r','i','v','e','r',0 };
4964 static const WCHAR translator_query
[] = {
4965 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4966 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4968 static const WCHAR source_query
[] = {
4969 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4970 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4972 rc
= MSI_DatabaseOpenViewW(package
->db
, driver_query
, &view
);
4973 if (rc
!= ERROR_SUCCESS
)
4974 return ERROR_SUCCESS
;
4976 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCDriver
, package
);
4977 msiobj_release(&view
->hdr
);
4979 rc
= MSI_DatabaseOpenViewW(package
->db
, translator_query
, &view
);
4980 if (rc
!= ERROR_SUCCESS
)
4981 return ERROR_SUCCESS
;
4983 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCTranslator
, package
);
4984 msiobj_release(&view
->hdr
);
4986 rc
= MSI_DatabaseOpenViewW(package
->db
, source_query
, &view
);
4987 if (rc
!= ERROR_SUCCESS
)
4988 return ERROR_SUCCESS
;
4990 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_InstallODBCDataSource
, package
);
4991 msiobj_release(&view
->hdr
);
4996 #define ENV_ACT_SETALWAYS 0x1
4997 #define ENV_ACT_SETABSENT 0x2
4998 #define ENV_ACT_REMOVE 0x4
4999 #define ENV_ACT_REMOVEMATCH 0x8
5001 #define ENV_MOD_MACHINE 0x20000000
5002 #define ENV_MOD_APPEND 0x40000000
5003 #define ENV_MOD_PREFIX 0x80000000
5004 #define ENV_MOD_MASK 0xC0000000
5006 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5008 static LONG
env_set_flags( LPCWSTR
*name
, LPCWSTR
*value
, DWORD
*flags
)
5010 LPCWSTR cptr
= *name
;
5011 LPCWSTR ptr
= *value
;
5013 static const WCHAR prefix
[] = {'[','~',']',0};
5014 static const int prefix_len
= 3;
5020 *flags
|= ENV_ACT_SETALWAYS
;
5021 else if (*cptr
== '+')
5022 *flags
|= ENV_ACT_SETABSENT
;
5023 else if (*cptr
== '-')
5024 *flags
|= ENV_ACT_REMOVE
;
5025 else if (*cptr
== '!')
5026 *flags
|= ENV_ACT_REMOVEMATCH
;
5027 else if (*cptr
== '*')
5028 *flags
|= ENV_MOD_MACHINE
;
5038 ERR("Missing environment variable\n");
5039 return ERROR_FUNCTION_FAILED
;
5042 if (!strncmpW(ptr
, prefix
, prefix_len
))
5044 *flags
|= ENV_MOD_APPEND
;
5045 *value
+= lstrlenW(prefix
);
5047 else if (lstrlenW(*value
) >= prefix_len
)
5049 ptr
+= lstrlenW(ptr
) - prefix_len
;
5050 if (!lstrcmpW(ptr
, prefix
))
5052 *flags
|= ENV_MOD_PREFIX
;
5053 /* the "[~]" will be removed by deformat_string */;
5058 check_flag_combo(*flags
, ENV_ACT_SETALWAYS
| ENV_ACT_SETABSENT
) ||
5059 check_flag_combo(*flags
, ENV_ACT_REMOVEMATCH
| ENV_ACT_SETABSENT
) ||
5060 check_flag_combo(*flags
, ENV_ACT_REMOVEMATCH
| ENV_ACT_SETALWAYS
) ||
5061 check_flag_combo(*flags
, ENV_ACT_SETABSENT
| ENV_MOD_MASK
))
5063 ERR("Invalid flags: %08x\n", *flags
);
5064 return ERROR_FUNCTION_FAILED
;
5067 return ERROR_SUCCESS
;
5070 static UINT
ITERATE_WriteEnvironmentString( MSIRECORD
*rec
, LPVOID param
)
5072 MSIPACKAGE
*package
= param
;
5073 LPCWSTR name
, value
;
5074 LPWSTR data
= NULL
, newval
= NULL
;
5075 LPWSTR deformatted
= NULL
, ptr
;
5076 DWORD flags
, type
, size
;
5078 HKEY env
= NULL
, root
;
5079 LPCWSTR environment
;
5081 static const WCHAR user_env
[] =
5082 {'E','n','v','i','r','o','n','m','e','n','t',0};
5083 static const WCHAR machine_env
[] =
5084 {'S','y','s','t','e','m','\\',
5085 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5086 'C','o','n','t','r','o','l','\\',
5087 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5088 'E','n','v','i','r','o','n','m','e','n','t',0};
5089 static const WCHAR semicolon
[] = {';',0};
5091 name
= MSI_RecordGetString(rec
, 2);
5092 value
= MSI_RecordGetString(rec
, 3);
5094 res
= env_set_flags(&name
, &value
, &flags
);
5095 if (res
!= ERROR_SUCCESS
)
5098 deformat_string(package
, value
, &deformatted
);
5101 res
= ERROR_OUTOFMEMORY
;
5105 value
= deformatted
;
5107 if (flags
& ENV_MOD_MACHINE
)
5109 environment
= machine_env
;
5110 root
= HKEY_LOCAL_MACHINE
;
5114 environment
= user_env
;
5115 root
= HKEY_CURRENT_USER
;
5118 res
= RegCreateKeyExW(root
, environment
, 0, NULL
, 0,
5119 KEY_ALL_ACCESS
, NULL
, &env
, NULL
);
5120 if (res
!= ERROR_SUCCESS
)
5123 if (flags
& ENV_ACT_REMOVE
)
5124 FIXME("Not removing environment variable on uninstall!\n");
5127 res
= RegQueryValueExW(env
, name
, NULL
, &type
, NULL
, &size
);
5128 if ((res
!= ERROR_SUCCESS
&& res
!= ERROR_FILE_NOT_FOUND
) ||
5129 (res
== ERROR_SUCCESS
&& type
!= REG_SZ
&& type
!= REG_EXPAND_SZ
))
5132 if (res
!= ERROR_FILE_NOT_FOUND
)
5134 if (flags
& ENV_ACT_SETABSENT
)
5136 res
= ERROR_SUCCESS
;
5140 data
= msi_alloc(size
);
5144 return ERROR_OUTOFMEMORY
;
5147 res
= RegQueryValueExW(env
, name
, NULL
, &type
, (LPVOID
)data
, &size
);
5148 if (res
!= ERROR_SUCCESS
)
5151 if (flags
& ENV_ACT_REMOVEMATCH
&& (!value
|| !lstrcmpW(data
, value
)))
5153 res
= RegDeleteKeyW(env
, name
);
5157 size
= (lstrlenW(value
) + 1 + size
) * sizeof(WCHAR
);
5158 newval
= msi_alloc(size
);
5162 res
= ERROR_OUTOFMEMORY
;
5166 if (!(flags
& ENV_MOD_MASK
))
5167 lstrcpyW(newval
, value
);
5170 if (flags
& ENV_MOD_PREFIX
)
5172 lstrcpyW(newval
, value
);
5173 lstrcatW(newval
, semicolon
);
5174 ptr
= newval
+ lstrlenW(value
) + 1;
5177 lstrcpyW(ptr
, data
);
5179 if (flags
& ENV_MOD_APPEND
)
5181 lstrcatW(newval
, semicolon
);
5182 lstrcatW(newval
, value
);
5188 size
= (lstrlenW(value
) + 1) * sizeof(WCHAR
);
5189 newval
= msi_alloc(size
);
5192 res
= ERROR_OUTOFMEMORY
;
5196 lstrcpyW(newval
, value
);
5199 TRACE("setting %s to %s\n", debugstr_w(name
), debugstr_w(newval
));
5200 res
= RegSetValueExW(env
, name
, 0, type
, (LPVOID
)newval
, size
);
5203 if (env
) RegCloseKey(env
);
5204 msi_free(deformatted
);
5210 static UINT
ACTION_WriteEnvironmentStrings( MSIPACKAGE
*package
)
5214 static const WCHAR ExecSeqQuery
[] =
5215 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5216 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5217 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
5218 if (rc
!= ERROR_SUCCESS
)
5219 return ERROR_SUCCESS
;
5221 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteEnvironmentString
, package
);
5222 msiobj_release(&view
->hdr
);
5227 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5238 static BOOL
msi_move_file(LPCWSTR source
, LPCWSTR dest
, int options
)
5242 if (GetFileAttributesW(source
) == FILE_ATTRIBUTE_DIRECTORY
||
5243 GetFileAttributesW(dest
) == FILE_ATTRIBUTE_DIRECTORY
)
5245 WARN("Source or dest is directory, not moving\n");
5249 if (options
== msidbMoveFileOptionsMove
)
5251 TRACE("moving %s -> %s\n", debugstr_w(source
), debugstr_w(dest
));
5252 ret
= MoveFileExW(source
, dest
, MOVEFILE_REPLACE_EXISTING
);
5255 WARN("MoveFile failed: %d\n", GetLastError());
5261 TRACE("copying %s -> %s\n", debugstr_w(source
), debugstr_w(dest
));
5262 ret
= CopyFileW(source
, dest
, FALSE
);
5265 WARN("CopyFile failed: %d\n", GetLastError());
5273 static LPWSTR
wildcard_to_file(LPWSTR wildcard
, LPWSTR filename
)
5276 DWORD dirlen
, pathlen
;
5278 ptr
= strrchrW(wildcard
, '\\');
5279 dirlen
= ptr
- wildcard
+ 1;
5281 pathlen
= dirlen
+ lstrlenW(filename
) + 1;
5282 path
= msi_alloc(pathlen
* sizeof(WCHAR
));
5284 lstrcpynW(path
, wildcard
, dirlen
+ 1);
5285 lstrcatW(path
, filename
);
5290 static void free_file_entry(FILE_LIST
*file
)
5292 msi_free(file
->source
);
5293 msi_free(file
->dest
);
5297 static void free_list(FILE_LIST
*list
)
5299 while (!list_empty(&list
->entry
))
5301 FILE_LIST
*file
= LIST_ENTRY(list_head(&list
->entry
), FILE_LIST
, entry
);
5303 list_remove(&file
->entry
);
5304 free_file_entry(file
);
5308 static BOOL
add_wildcard(FILE_LIST
*files
, LPWSTR source
, LPWSTR dest
)
5310 FILE_LIST
*new, *file
;
5311 LPWSTR ptr
, filename
;
5314 new = msi_alloc_zero(sizeof(FILE_LIST
));
5318 new->source
= strdupW(source
);
5319 ptr
= strrchrW(dest
, '\\') + 1;
5320 filename
= strrchrW(new->source
, '\\') + 1;
5322 new->sourcename
= filename
;
5325 new->destname
= ptr
;
5327 new->destname
= new->sourcename
;
5329 size
= (ptr
- dest
) + lstrlenW(filename
) + 1;
5330 new->dest
= msi_alloc(size
* sizeof(WCHAR
));
5333 free_file_entry(new);
5337 lstrcpynW(new->dest
, dest
, ptr
- dest
+ 1);
5338 lstrcatW(new->dest
, filename
);
5340 if (list_empty(&files
->entry
))
5342 list_add_head(&files
->entry
, &new->entry
);
5346 LIST_FOR_EACH_ENTRY(file
, &files
->entry
, FILE_LIST
, entry
)
5348 if (lstrcmpW(source
, file
->source
) < 0)
5350 list_add_before(&file
->entry
, &new->entry
);
5355 list_add_after(&file
->entry
, &new->entry
);
5359 static BOOL
move_files_wildcard(LPWSTR source
, LPWSTR dest
, int options
)
5361 WIN32_FIND_DATAW wfd
;
5365 FILE_LIST files
, *file
;
5368 hfile
= FindFirstFileW(source
, &wfd
);
5369 if (hfile
== INVALID_HANDLE_VALUE
) return FALSE
;
5371 list_init(&files
.entry
);
5373 for (res
= TRUE
; res
; res
= FindNextFileW(hfile
, &wfd
))
5375 if (is_dot_dir(wfd
.cFileName
)) continue;
5377 path
= wildcard_to_file(source
, wfd
.cFileName
);
5384 add_wildcard(&files
, path
, dest
);
5388 /* no files match the wildcard */
5389 if (list_empty(&files
.entry
))
5392 /* only the first wildcard match gets renamed to dest */
5393 file
= LIST_ENTRY(list_head(&files
.entry
), FILE_LIST
, entry
);
5394 size
= (strrchrW(file
->dest
, '\\') - file
->dest
) + lstrlenW(file
->destname
) + 2;
5395 file
->dest
= msi_realloc(file
->dest
, size
* sizeof(WCHAR
));
5402 lstrcpyW(strrchrW(file
->dest
, '\\') + 1, file
->destname
);
5404 while (!list_empty(&files
.entry
))
5406 file
= LIST_ENTRY(list_head(&files
.entry
), FILE_LIST
, entry
);
5408 msi_move_file((LPCWSTR
)file
->source
, (LPCWSTR
)file
->dest
, options
);
5410 list_remove(&file
->entry
);
5411 free_file_entry(file
);
5422 static UINT
ITERATE_MoveFiles( MSIRECORD
*rec
, LPVOID param
)
5424 MSIPACKAGE
*package
= param
;
5426 LPCWSTR sourcename
, destname
;
5427 LPWSTR sourcedir
= NULL
, destdir
= NULL
;
5428 LPWSTR source
= NULL
, dest
= NULL
;
5431 BOOL ret
, wildcards
;
5433 static const WCHAR backslash
[] = {'\\',0};
5435 comp
= get_loaded_component(package
, MSI_RecordGetString(rec
, 2));
5436 if (!comp
|| !comp
->Enabled
||
5437 !(comp
->Action
& (INSTALLSTATE_LOCAL
| INSTALLSTATE_SOURCE
)))
5439 TRACE("Component not set for install, not moving file\n");
5440 return ERROR_SUCCESS
;
5443 sourcename
= MSI_RecordGetString(rec
, 3);
5444 destname
= MSI_RecordGetString(rec
, 4);
5445 options
= MSI_RecordGetInteger(rec
, 7);
5447 sourcedir
= msi_dup_property(package
, MSI_RecordGetString(rec
, 5));
5451 destdir
= msi_dup_property(package
, MSI_RecordGetString(rec
, 6));
5457 if (GetFileAttributesW(sourcedir
) == INVALID_FILE_ATTRIBUTES
)
5460 source
= strdupW(sourcedir
);
5466 size
= lstrlenW(sourcedir
) + lstrlenW(sourcename
) + 2;
5467 source
= msi_alloc(size
* sizeof(WCHAR
));
5471 lstrcpyW(source
, sourcedir
);
5472 if (source
[lstrlenW(source
) - 1] != '\\')
5473 lstrcatW(source
, backslash
);
5474 lstrcatW(source
, sourcename
);
5477 wildcards
= strchrW(source
, '*') || strchrW(source
, '?');
5479 if (!destname
&& !wildcards
)
5481 destname
= strdupW(sourcename
);
5488 size
= lstrlenW(destname
);
5490 size
+= lstrlenW(destdir
) + 2;
5491 dest
= msi_alloc(size
* sizeof(WCHAR
));
5495 lstrcpyW(dest
, destdir
);
5496 if (dest
[lstrlenW(dest
) - 1] != '\\')
5497 lstrcatW(dest
, backslash
);
5500 lstrcatW(dest
, destname
);
5502 if (GetFileAttributesW(destdir
) == INVALID_FILE_ATTRIBUTES
)
5504 ret
= CreateDirectoryW(destdir
, NULL
);
5507 WARN("CreateDirectory failed: %d\n", GetLastError());
5508 return ERROR_SUCCESS
;
5513 msi_move_file(source
, dest
, options
);
5515 move_files_wildcard(source
, dest
, options
);
5518 msi_free(sourcedir
);
5523 return ERROR_SUCCESS
;
5526 static UINT
ACTION_MoveFiles( MSIPACKAGE
*package
)
5531 static const WCHAR ExecSeqQuery
[] =
5532 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5533 '`','M','o','v','e','F','i','l','e','`',0};
5535 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
5536 if (rc
!= ERROR_SUCCESS
)
5537 return ERROR_SUCCESS
;
5539 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_MoveFiles
, package
);
5540 msiobj_release(&view
->hdr
);
5545 static UINT
msi_unimplemented_action_stub( MSIPACKAGE
*package
,
5546 LPCSTR action
, LPCWSTR table
)
5548 static const WCHAR query
[] = {
5549 'S','E','L','E','C','T',' ','*',' ',
5550 'F','R','O','M',' ','`','%','s','`',0 };
5551 MSIQUERY
*view
= NULL
;
5555 r
= MSI_OpenQuery( package
->db
, &view
, query
, table
);
5556 if (r
== ERROR_SUCCESS
)
5558 r
= MSI_IterateRecords(view
, &count
, NULL
, package
);
5559 msiobj_release(&view
->hdr
);
5563 FIXME("%s -> %u ignored %s table values\n",
5564 action
, count
, debugstr_w(table
));
5566 return ERROR_SUCCESS
;
5569 static UINT
ACTION_AllocateRegistrySpace( MSIPACKAGE
*package
)
5571 TRACE("%p\n", package
);
5572 return ERROR_SUCCESS
;
5575 static UINT
ACTION_RemoveIniValues( MSIPACKAGE
*package
)
5577 static const WCHAR table
[] =
5578 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5579 return msi_unimplemented_action_stub( package
, "RemoveIniValues", table
);
5582 static UINT
ACTION_PatchFiles( MSIPACKAGE
*package
)
5584 static const WCHAR table
[] = { 'P','a','t','c','h',0 };
5585 return msi_unimplemented_action_stub( package
, "PatchFiles", table
);
5588 static UINT
ACTION_BindImage( MSIPACKAGE
*package
)
5590 static const WCHAR table
[] = { 'B','i','n','d','I','m','a','g','e',0 };
5591 return msi_unimplemented_action_stub( package
, "BindImage", table
);
5594 static UINT
ACTION_IsolateComponents( MSIPACKAGE
*package
)
5596 static const WCHAR table
[] = {
5597 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5598 return msi_unimplemented_action_stub( package
, "IsolateComponents", table
);
5601 static UINT
ACTION_MigrateFeatureStates( MSIPACKAGE
*package
)
5603 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
5604 return msi_unimplemented_action_stub( package
, "MigrateFeatureStates", table
);
5607 static UINT
ACTION_SelfUnregModules( MSIPACKAGE
*package
)
5609 static const WCHAR table
[] = { 'S','e','l','f','R','e','g',0 };
5610 return msi_unimplemented_action_stub( package
, "SelfUnregModules", table
);
5613 static UINT
ACTION_DeleteServices( MSIPACKAGE
*package
)
5615 static const WCHAR table
[] = {
5616 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5617 return msi_unimplemented_action_stub( package
, "DeleteServices", table
);
5619 static UINT
ACTION_ValidateProductID( MSIPACKAGE
*package
)
5621 static const WCHAR table
[] = {
5622 'P','r','o','d','u','c','t','I','D',0 };
5623 return msi_unimplemented_action_stub( package
, "ValidateProductID", table
);
5626 static UINT
ACTION_RemoveEnvironmentStrings( MSIPACKAGE
*package
)
5628 static const WCHAR table
[] = {
5629 'E','n','v','i','r','o','n','m','e','n','t',0 };
5630 return msi_unimplemented_action_stub( package
, "RemoveEnvironmentStrings", table
);
5633 static UINT
ACTION_MsiPublishAssemblies( MSIPACKAGE
*package
)
5635 static const WCHAR table
[] = {
5636 'M','s','i','A','s','s','e','m','b','l','y',0 };
5637 return msi_unimplemented_action_stub( package
, "MsiPublishAssemblies", table
);
5640 static UINT
ACTION_MsiUnpublishAssemblies( MSIPACKAGE
*package
)
5642 static const WCHAR table
[] = {
5643 'M','s','i','A','s','s','e','m','b','l','y',0 };
5644 return msi_unimplemented_action_stub( package
, "MsiUnpublishAssemblies", table
);
5647 static UINT
ACTION_UnregisterFonts( MSIPACKAGE
*package
)
5649 static const WCHAR table
[] = { 'F','o','n','t',0 };
5650 return msi_unimplemented_action_stub( package
, "UnregisterFonts", table
);
5653 static UINT
ACTION_RMCCPSearch( MSIPACKAGE
*package
)
5655 static const WCHAR table
[] = { 'C','C','P','S','e','a','r','c','h',0 };
5656 return msi_unimplemented_action_stub( package
, "RMCCPSearch", table
);
5659 static UINT
ACTION_RegisterComPlus( MSIPACKAGE
*package
)
5661 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
5662 return msi_unimplemented_action_stub( package
, "RegisterComPlus", table
);
5665 static UINT
ACTION_UnregisterComPlus( MSIPACKAGE
*package
)
5667 static const WCHAR table
[] = { 'C','o','m','p','l','u','s',0 };
5668 return msi_unimplemented_action_stub( package
, "UnregisterComPlus", table
);
5671 static UINT
ACTION_InstallSFPCatalogFile( MSIPACKAGE
*package
)
5673 static const WCHAR table
[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5674 return msi_unimplemented_action_stub( package
, "InstallSFPCatalogFile", table
);
5677 static UINT
ACTION_RemoveDuplicateFiles( MSIPACKAGE
*package
)
5679 static const WCHAR table
[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5680 return msi_unimplemented_action_stub( package
, "RemoveDuplicateFiles", table
);
5683 static UINT
ACTION_RemoveExistingProducts( MSIPACKAGE
*package
)
5685 static const WCHAR table
[] = { 'U','p','g','r','a','d','e',0 };
5686 return msi_unimplemented_action_stub( package
, "RemoveExistingProducts", table
);
5689 static UINT
ACTION_RemoveFolders( MSIPACKAGE
*package
)
5691 static const WCHAR table
[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5692 return msi_unimplemented_action_stub( package
, "RemoveFolders", table
);
5695 static UINT
ACTION_RemoveODBC( MSIPACKAGE
*package
)
5697 static const WCHAR table
[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5698 return msi_unimplemented_action_stub( package
, "RemoveODBC", table
);
5701 static UINT
ACTION_RemoveRegistryValues( MSIPACKAGE
*package
)
5703 static const WCHAR table
[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5704 return msi_unimplemented_action_stub( package
, "RemoveRegistryValues", table
);
5707 static UINT
ACTION_RemoveShortcuts( MSIPACKAGE
*package
)
5709 static const WCHAR table
[] = { 'S','h','o','r','t','c','u','t',0 };
5710 return msi_unimplemented_action_stub( package
, "RemoveShortcuts", table
);
5713 static UINT
ACTION_UnpublishComponents( MSIPACKAGE
*package
)
5715 static const WCHAR table
[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5716 return msi_unimplemented_action_stub( package
, "UnpublishComponents", table
);
5719 static UINT
ACTION_UnregisterClassInfo( MSIPACKAGE
*package
)
5721 static const WCHAR table
[] = { 'A','p','p','I','d',0 };
5722 return msi_unimplemented_action_stub( package
, "UnregisterClassInfo", table
);
5725 static UINT
ACTION_UnregisterExtensionInfo( MSIPACKAGE
*package
)
5727 static const WCHAR table
[] = { 'E','x','t','e','n','s','i','o','n',0 };
5728 return msi_unimplemented_action_stub( package
, "UnregisterExtensionInfo", table
);
5731 static UINT
ACTION_UnregisterMIMEInfo( MSIPACKAGE
*package
)
5733 static const WCHAR table
[] = { 'M','I','M','E',0 };
5734 return msi_unimplemented_action_stub( package
, "UnregisterMIMEInfo", table
);
5737 static UINT
ACTION_UnregisterProgIdInfo( MSIPACKAGE
*package
)
5739 static const WCHAR table
[] = { 'P','r','o','g','I','d',0 };
5740 return msi_unimplemented_action_stub( package
, "UnregisterProgIdInfo", table
);
5743 static UINT
ACTION_UnregisterTypeLibraries( MSIPACKAGE
*package
)
5745 static const WCHAR table
[] = { 'T','y','p','e','L','i','b',0 };
5746 return msi_unimplemented_action_stub( package
, "UnregisterTypeLibraries", table
);
5749 static const struct _actions StandardActions
[] = {
5750 { szAllocateRegistrySpace
, ACTION_AllocateRegistrySpace
},
5751 { szAppSearch
, ACTION_AppSearch
},
5752 { szBindImage
, ACTION_BindImage
},
5753 { szCCPSearch
, ACTION_CCPSearch
},
5754 { szCostFinalize
, ACTION_CostFinalize
},
5755 { szCostInitialize
, ACTION_CostInitialize
},
5756 { szCreateFolders
, ACTION_CreateFolders
},
5757 { szCreateShortcuts
, ACTION_CreateShortcuts
},
5758 { szDeleteServices
, ACTION_DeleteServices
},
5759 { szDisableRollback
, NULL
},
5760 { szDuplicateFiles
, ACTION_DuplicateFiles
},
5761 { szExecuteAction
, ACTION_ExecuteAction
},
5762 { szFileCost
, ACTION_FileCost
},
5763 { szFindRelatedProducts
, ACTION_FindRelatedProducts
},
5764 { szForceReboot
, ACTION_ForceReboot
},
5765 { szInstallAdminPackage
, NULL
},
5766 { szInstallExecute
, ACTION_InstallExecute
},
5767 { szInstallExecuteAgain
, ACTION_InstallExecute
},
5768 { szInstallFiles
, ACTION_InstallFiles
},
5769 { szInstallFinalize
, ACTION_InstallFinalize
},
5770 { szInstallInitialize
, ACTION_InstallInitialize
},
5771 { szInstallSFPCatalogFile
, ACTION_InstallSFPCatalogFile
},
5772 { szInstallValidate
, ACTION_InstallValidate
},
5773 { szIsolateComponents
, ACTION_IsolateComponents
},
5774 { szLaunchConditions
, ACTION_LaunchConditions
},
5775 { szMigrateFeatureStates
, ACTION_MigrateFeatureStates
},
5776 { szMoveFiles
, ACTION_MoveFiles
},
5777 { szMsiPublishAssemblies
, ACTION_MsiPublishAssemblies
},
5778 { szMsiUnpublishAssemblies
, ACTION_MsiUnpublishAssemblies
},
5779 { szInstallODBC
, ACTION_InstallODBC
},
5780 { szInstallServices
, ACTION_InstallServices
},
5781 { szPatchFiles
, ACTION_PatchFiles
},
5782 { szProcessComponents
, ACTION_ProcessComponents
},
5783 { szPublishComponents
, ACTION_PublishComponents
},
5784 { szPublishFeatures
, ACTION_PublishFeatures
},
5785 { szPublishProduct
, ACTION_PublishProduct
},
5786 { szRegisterClassInfo
, ACTION_RegisterClassInfo
},
5787 { szRegisterComPlus
, ACTION_RegisterComPlus
},
5788 { szRegisterExtensionInfo
, ACTION_RegisterExtensionInfo
},
5789 { szRegisterFonts
, ACTION_RegisterFonts
},
5790 { szRegisterMIMEInfo
, ACTION_RegisterMIMEInfo
},
5791 { szRegisterProduct
, ACTION_RegisterProduct
},
5792 { szRegisterProgIdInfo
, ACTION_RegisterProgIdInfo
},
5793 { szRegisterTypeLibraries
, ACTION_RegisterTypeLibraries
},
5794 { szRegisterUser
, ACTION_RegisterUser
},
5795 { szRemoveDuplicateFiles
, ACTION_RemoveDuplicateFiles
},
5796 { szRemoveEnvironmentStrings
, ACTION_RemoveEnvironmentStrings
},
5797 { szRemoveExistingProducts
, ACTION_RemoveExistingProducts
},
5798 { szRemoveFiles
, ACTION_RemoveFiles
},
5799 { szRemoveFolders
, ACTION_RemoveFolders
},
5800 { szRemoveIniValues
, ACTION_RemoveIniValues
},
5801 { szRemoveODBC
, ACTION_RemoveODBC
},
5802 { szRemoveRegistryValues
, ACTION_RemoveRegistryValues
},
5803 { szRemoveShortcuts
, ACTION_RemoveShortcuts
},
5804 { szResolveSource
, ACTION_ResolveSource
},
5805 { szRMCCPSearch
, ACTION_RMCCPSearch
},
5806 { szScheduleReboot
, NULL
},
5807 { szSelfRegModules
, ACTION_SelfRegModules
},
5808 { szSelfUnregModules
, ACTION_SelfUnregModules
},
5809 { szSetODBCFolders
, NULL
},
5810 { szStartServices
, ACTION_StartServices
},
5811 { szStopServices
, ACTION_StopServices
},
5812 { szUnpublishComponents
, ACTION_UnpublishComponents
},
5813 { szUnpublishFeatures
, ACTION_UnpublishFeatures
},
5814 { szUnregisterClassInfo
, ACTION_UnregisterClassInfo
},
5815 { szUnregisterComPlus
, ACTION_UnregisterComPlus
},
5816 { szUnregisterExtensionInfo
, ACTION_UnregisterExtensionInfo
},
5817 { szUnregisterFonts
, ACTION_UnregisterFonts
},
5818 { szUnregisterMIMEInfo
, ACTION_UnregisterMIMEInfo
},
5819 { szUnregisterProgIdInfo
, ACTION_UnregisterProgIdInfo
},
5820 { szUnregisterTypeLibraries
, ACTION_UnregisterTypeLibraries
},
5821 { szValidateProductID
, ACTION_ValidateProductID
},
5822 { szWriteEnvironmentStrings
, ACTION_WriteEnvironmentStrings
},
5823 { szWriteIniValues
, ACTION_WriteIniValues
},
5824 { szWriteRegistryValues
, ACTION_WriteRegistryValues
},
5828 static BOOL
ACTION_HandleStandardAction(MSIPACKAGE
*package
, LPCWSTR action
,
5829 UINT
* rc
, BOOL force
)
5835 if (!run
&& !package
->script
->CurrentlyScripting
)
5840 if (strcmpW(action
,szInstallFinalize
) == 0 ||
5841 strcmpW(action
,szInstallExecute
) == 0 ||
5842 strcmpW(action
,szInstallExecuteAgain
) == 0)
5847 while (StandardActions
[i
].action
!= NULL
)
5849 if (strcmpW(StandardActions
[i
].action
, action
)==0)
5853 ui_actioninfo(package
, action
, TRUE
, 0);
5854 *rc
= schedule_action(package
,INSTALL_SCRIPT
,action
);
5855 ui_actioninfo(package
, action
, FALSE
, *rc
);
5859 ui_actionstart(package
, action
);
5860 if (StandardActions
[i
].handler
)
5862 *rc
= StandardActions
[i
].handler(package
);
5866 FIXME("unhandled standard action %s\n",debugstr_w(action
));
5867 *rc
= ERROR_SUCCESS
;