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
27 #define REG_PROGRESS_VALUE 13200
28 #define COMPONENT_PROGRESS_VALUE 24000
30 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
32 static const WCHAR szCreateFolders
[] =
33 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
34 static const WCHAR szCostFinalize
[] =
35 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
36 static const WCHAR szWriteRegistryValues
[] =
37 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
38 static const WCHAR szFileCost
[] =
39 {'F','i','l','e','C','o','s','t',0};
40 static const WCHAR szInstallInitialize
[] =
41 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
42 static const WCHAR szInstallValidate
[] =
43 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
44 static const WCHAR szLaunchConditions
[] =
45 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
46 static const WCHAR szProcessComponents
[] =
47 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
48 static const WCHAR szRegisterTypeLibraries
[] =
49 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
50 static const WCHAR szCreateShortcuts
[] =
51 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
52 static const WCHAR szPublishProduct
[] =
53 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
54 static const WCHAR szWriteIniValues
[] =
55 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
56 static const WCHAR szSelfRegModules
[] =
57 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
58 static const WCHAR szPublishFeatures
[] =
59 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
60 static const WCHAR szRegisterProduct
[] =
61 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
62 static const WCHAR szInstallExecute
[] =
63 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
64 static const WCHAR szInstallExecuteAgain
[] =
65 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
66 static const WCHAR szInstallFinalize
[] =
67 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
68 static const WCHAR szForceReboot
[] =
69 {'F','o','r','c','e','R','e','b','o','o','t',0};
70 static const WCHAR szResolveSource
[] =
71 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
72 static const WCHAR szAllocateRegistrySpace
[] =
73 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
74 static const WCHAR szBindImage
[] =
75 {'B','i','n','d','I','m','a','g','e',0};
76 static const WCHAR szDeleteServices
[] =
77 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
78 static const WCHAR szDisableRollback
[] =
79 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
80 static const WCHAR szExecuteAction
[] =
81 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
82 static const WCHAR szInstallAdminPackage
[] =
83 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
84 static const WCHAR szInstallSFPCatalogFile
[] =
85 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
86 static const WCHAR szIsolateComponents
[] =
87 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
88 static const WCHAR szMigrateFeatureStates
[] =
89 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
90 static const WCHAR szMsiUnpublishAssemblies
[] =
91 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
92 static const WCHAR szInstallODBC
[] =
93 {'I','n','s','t','a','l','l','O','D','B','C',0};
94 static const WCHAR szInstallServices
[] =
95 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
96 static const WCHAR szPublishComponents
[] =
97 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
98 static const WCHAR szRegisterComPlus
[] =
99 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
100 static const WCHAR szRegisterUser
[] =
101 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
102 static const WCHAR szRemoveEnvironmentStrings
[] =
103 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
104 static const WCHAR szRemoveExistingProducts
[] =
105 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
106 static const WCHAR szRemoveFolders
[] =
107 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
108 static const WCHAR szRemoveIniValues
[] =
109 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
110 static const WCHAR szRemoveODBC
[] =
111 {'R','e','m','o','v','e','O','D','B','C',0};
112 static const WCHAR szRemoveRegistryValues
[] =
113 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
114 static const WCHAR szRemoveShortcuts
[] =
115 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
116 static const WCHAR szRMCCPSearch
[] =
117 {'R','M','C','C','P','S','e','a','r','c','h',0};
118 static const WCHAR szScheduleReboot
[] =
119 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
120 static const WCHAR szSelfUnregModules
[] =
121 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
122 static const WCHAR szSetODBCFolders
[] =
123 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
124 static const WCHAR szStartServices
[] =
125 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
126 static const WCHAR szStopServices
[] =
127 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
128 static const WCHAR szUnpublishComponents
[] =
129 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
130 static const WCHAR szUnpublishFeatures
[] =
131 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
132 static const WCHAR szUnpublishProduct
[] =
133 {'U','n','p','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
134 static const WCHAR szUnregisterComPlus
[] =
135 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
136 static const WCHAR szUnregisterTypeLibraries
[] =
137 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
138 static const WCHAR szValidateProductID
[] =
139 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
140 static const WCHAR szWriteEnvironmentStrings
[] =
141 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
150 static INT
ui_actionstart(MSIPACKAGE
*package
, LPCWSTR action
, LPCWSTR description
, LPCWSTR
template)
152 WCHAR query
[] = {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
153 '`','A','c','t','i','o','n','T','e','x','t','`',' ','W','H','E','R','E',' ',
154 '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
155 MSIRECORD
*row
, *textrow
;
158 textrow
= MSI_QueryGetRecord(package
->db
, query
, action
);
161 description
= MSI_RecordGetString(textrow
, 2);
162 template = MSI_RecordGetString(textrow
, 3);
165 row
= MSI_CreateRecord(3);
167 MSI_RecordSetStringW(row
, 1, action
);
168 MSI_RecordSetStringW(row
, 2, description
);
169 MSI_RecordSetStringW(row
, 3, template);
170 rc
= MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONSTART
, row
);
171 if (textrow
) msiobj_release(&textrow
->hdr
);
172 msiobj_release(&row
->hdr
);
176 static void ui_actioninfo(MSIPACKAGE
*package
, LPCWSTR action
, BOOL start
,
182 template = msi_get_error_message(package
->db
, start
? MSIERR_INFO_ACTIONSTART
: MSIERR_INFO_ACTIONENDED
);
184 row
= MSI_CreateRecord(2);
186 MSI_RecordSetStringW(row
, 0, template);
187 MSI_RecordSetStringW(row
, 1, action
);
188 MSI_RecordSetInteger(row
, 2, start
? package
->LastActionResult
: rc
);
189 MSI_ProcessMessage(package
, INSTALLMESSAGE_INFO
, row
);
190 msiobj_release(&row
->hdr
);
192 if (!start
) package
->LastActionResult
= rc
;
202 static int parse_prop( const WCHAR
*str
, WCHAR
*value
, int *quotes
)
204 enum parse_state state
= state_quote
;
207 BOOL ignore
, in_quotes
= FALSE
;
208 int count
= 0, len
= 0;
210 for (p
= str
; *p
; p
++)
215 case state_whitespace
:
225 if (in_quotes
&& p
[1] != '\"') count
--;
241 if (in_quotes
) count
--;
245 state
= state_whitespace
;
246 if (!count
) goto done
;
251 if (count
) in_quotes
= TRUE
;
261 if (in_quotes
&& p
[1] != '\"') count
--;
265 state
= state_whitespace
;
266 if (!count
|| (count
> 1 && !len
)) goto done
;
272 if (count
) in_quotes
= TRUE
;
280 if (!ignore
) *out
++ = *p
;
281 if (!count
) in_quotes
= FALSE
;
285 if (!len
) *value
= 0;
292 static void remove_quotes( WCHAR
*str
)
295 int len
= strlenW( str
);
297 while ((p
= strchrW( p
, '"' )))
299 memmove( p
, p
+ 1, (len
- (p
- str
)) * sizeof(WCHAR
) );
304 UINT
msi_parse_command_line( MSIPACKAGE
*package
, LPCWSTR szCommandLine
,
314 return ERROR_SUCCESS
;
319 while (*ptr
== ' ') ptr
++;
322 ptr2
= strchrW( ptr
, '=' );
323 if (!ptr2
) return ERROR_INVALID_COMMAND_LINE
;
326 if (!len
) return ERROR_INVALID_COMMAND_LINE
;
328 while (ptr
[len
- 1] == ' ') len
--;
330 prop
= msi_alloc( (len
+ 1) * sizeof(WCHAR
) );
331 memcpy( prop
, ptr
, len
* sizeof(WCHAR
) );
333 if (!preserve_case
) struprW( prop
);
336 while (*ptr2
== ' ') ptr2
++;
339 val
= msi_alloc( (strlenW( ptr2
) + 1) * sizeof(WCHAR
) );
340 len
= parse_prop( ptr2
, val
, &num_quotes
);
343 WARN("unbalanced quotes\n");
346 return ERROR_INVALID_COMMAND_LINE
;
348 remove_quotes( val
);
349 TRACE("Found commandline property %s = %s\n", debugstr_w(prop
), debugstr_w(val
));
351 r
= msi_set_property( package
->db
, prop
, val
, -1 );
352 if (r
== ERROR_SUCCESS
&& !strcmpW( prop
, szSourceDir
))
353 msi_reset_folders( package
, TRUE
);
361 return ERROR_SUCCESS
;
364 WCHAR
**msi_split_string( const WCHAR
*str
, WCHAR sep
)
367 LPWSTR p
, *ret
= NULL
;
373 /* count the number of substrings */
374 for ( pc
= str
, count
= 0; pc
; count
++ )
376 pc
= strchrW( pc
, sep
);
381 /* allocate space for an array of substring pointers and the substrings */
382 ret
= msi_alloc( (count
+1) * sizeof (LPWSTR
) +
383 (lstrlenW(str
)+1) * sizeof(WCHAR
) );
387 /* copy the string and set the pointers */
388 p
= (LPWSTR
) &ret
[count
+1];
390 for( count
= 0; (ret
[count
] = p
); count
++ )
392 p
= strchrW( p
, sep
);
400 static BOOL
ui_sequence_exists( MSIPACKAGE
*package
)
402 static const WCHAR query
[] = {
403 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
404 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
405 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',0};
409 if (!(MSI_DatabaseOpenViewW( package
->db
, query
, &view
)))
411 MSI_IterateRecords( view
, &count
, NULL
, package
);
412 msiobj_release( &view
->hdr
);
417 UINT
msi_set_sourcedir_props(MSIPACKAGE
*package
, BOOL replace
)
419 WCHAR
*source
, *check
, *p
, *db
;
422 if (!(db
= msi_dup_property( package
->db
, szOriginalDatabase
)))
423 return ERROR_OUTOFMEMORY
;
425 if (!(p
= strrchrW( db
, '\\' )) && !(p
= strrchrW( db
, '/' )))
428 return ERROR_SUCCESS
;
431 source
= msi_alloc( len
* sizeof(WCHAR
) );
432 lstrcpynW( source
, db
, len
);
435 check
= msi_dup_property( package
->db
, szSourceDir
);
436 if (!check
|| replace
)
438 UINT r
= msi_set_property( package
->db
, szSourceDir
, source
, -1 );
439 if (r
== ERROR_SUCCESS
)
440 msi_reset_folders( package
, TRUE
);
444 check
= msi_dup_property( package
->db
, szSOURCEDIR
);
445 if (!check
|| replace
)
446 msi_set_property( package
->db
, szSOURCEDIR
, source
, -1 );
451 return ERROR_SUCCESS
;
454 static BOOL
needs_ui_sequence(MSIPACKAGE
*package
)
456 return (package
->ui_level
& INSTALLUILEVEL_MASK
) >= INSTALLUILEVEL_REDUCED
;
459 UINT
msi_set_context(MSIPACKAGE
*package
)
461 UINT r
= msi_locate_product( package
->ProductCode
, &package
->Context
);
462 if (r
!= ERROR_SUCCESS
)
464 int num
= msi_get_property_int( package
->db
, szAllUsers
, 0 );
465 if (num
== 1 || num
== 2)
466 package
->Context
= MSIINSTALLCONTEXT_MACHINE
;
468 package
->Context
= MSIINSTALLCONTEXT_USERUNMANAGED
;
470 return ERROR_SUCCESS
;
473 static UINT
ITERATE_Actions(MSIRECORD
*row
, LPVOID param
)
476 LPCWSTR cond
, action
;
477 MSIPACKAGE
*package
= param
;
479 action
= MSI_RecordGetString(row
,1);
482 ERR("Error is retrieving action name\n");
483 return ERROR_FUNCTION_FAILED
;
486 /* check conditions */
487 cond
= MSI_RecordGetString(row
,2);
489 /* this is a hack to skip errors in the condition code */
490 if (MSI_EvaluateConditionW(package
, cond
) == MSICONDITION_FALSE
)
492 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action
));
493 return ERROR_SUCCESS
;
496 if (needs_ui_sequence(package
))
497 rc
= ACTION_PerformUIAction(package
, action
, SCRIPT_NONE
);
499 rc
= ACTION_PerformAction(package
, action
, SCRIPT_NONE
);
501 msi_dialog_check_messages( NULL
);
503 if (rc
== ERROR_FUNCTION_NOT_CALLED
)
506 if (rc
!= ERROR_SUCCESS
)
507 ERR("Execution halted, action %s returned %i\n", debugstr_w(action
), rc
);
509 if (package
->need_reboot_now
)
511 TRACE("action %s asked for immediate reboot, suspending installation\n",
513 rc
= ACTION_ForceReboot( package
);
518 UINT
MSI_Sequence( MSIPACKAGE
*package
, LPCWSTR table
)
520 static const WCHAR query
[] = {
521 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
522 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
523 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
524 '`','S','e','q','u','e','n','c','e','`',0};
528 TRACE("%p %s\n", package
, debugstr_w(table
));
530 r
= MSI_OpenQuery( package
->db
, &view
, query
, table
);
531 if (r
== ERROR_SUCCESS
)
533 r
= MSI_IterateRecords( view
, NULL
, ITERATE_Actions
, package
);
534 msiobj_release(&view
->hdr
);
539 static UINT
ACTION_ProcessExecSequence(MSIPACKAGE
*package
, BOOL UIran
)
541 static const WCHAR query
[] = {
542 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
543 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
544 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
545 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
546 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
547 static const WCHAR query_validate
[] = {
548 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
549 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
550 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
551 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
552 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
557 if (package
->script
->ExecuteSequenceRun
)
559 TRACE("Execute Sequence already Run\n");
560 return ERROR_SUCCESS
;
563 package
->script
->ExecuteSequenceRun
= TRUE
;
565 /* get the sequence number */
568 MSIRECORD
*row
= MSI_QueryGetRecord(package
->db
, query_validate
);
569 if (!row
) return ERROR_FUNCTION_FAILED
;
570 seq
= MSI_RecordGetInteger(row
,1);
571 msiobj_release(&row
->hdr
);
573 rc
= MSI_OpenQuery(package
->db
, &view
, query
, seq
);
574 if (rc
== ERROR_SUCCESS
)
576 TRACE("Running the actions\n");
578 msi_set_property( package
->db
, szSourceDir
, NULL
, -1 );
579 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, package
);
580 msiobj_release(&view
->hdr
);
585 static UINT
ACTION_ProcessUISequence(MSIPACKAGE
*package
)
587 static const WCHAR query
[] = {
588 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
589 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
590 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
591 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
595 rc
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
596 if (rc
== ERROR_SUCCESS
)
598 TRACE("Running the actions\n");
599 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_Actions
, package
);
600 msiobj_release(&view
->hdr
);
605 /********************************************************
606 * ACTION helper functions and functions that perform the actions
607 *******************************************************/
608 static UINT
ACTION_HandleCustomAction(MSIPACKAGE
*package
, LPCWSTR action
, UINT script
)
613 uirc
= ui_actionstart(package
, action
, NULL
, NULL
);
614 if (uirc
== IDCANCEL
)
615 return ERROR_INSTALL_USEREXIT
;
616 ui_actioninfo(package
, action
, TRUE
, 0);
617 arc
= ACTION_CustomAction( package
, action
, script
);
620 if (arc
== ERROR_FUNCTION_NOT_CALLED
&& needs_ui_sequence(package
))
622 uirc
= ACTION_ShowDialog(package
, action
);
626 return ERROR_SUCCESS
; /* stop immediately */
627 case 0: arc
= ERROR_FUNCTION_NOT_CALLED
; break;
628 case 1: arc
= ERROR_SUCCESS
; break;
629 case 2: arc
= ERROR_INSTALL_USEREXIT
; break;
630 case 3: arc
= ERROR_INSTALL_FAILURE
; break;
631 case 4: arc
= ERROR_INSTALL_SUSPEND
; break;
632 case 5: arc
= ERROR_MORE_DATA
; break;
633 case 6: arc
= ERROR_INVALID_HANDLE_STATE
; break;
634 case 7: arc
= ERROR_INVALID_DATA
; break;
635 case 8: arc
= ERROR_INSTALL_ALREADY_RUNNING
; break;
636 case 9: arc
= ERROR_INSTALL_PACKAGE_REJECTED
; break;
637 default: arc
= ERROR_FUNCTION_FAILED
; break;
641 ui_actioninfo(package
, action
, FALSE
, uirc
);
646 MSICOMPONENT
*msi_get_loaded_component( MSIPACKAGE
*package
, const WCHAR
*Component
)
650 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
652 if (!strcmpW( Component
, comp
->Component
)) return comp
;
657 MSIFEATURE
*msi_get_loaded_feature(MSIPACKAGE
* package
, const WCHAR
*Feature
)
661 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
663 if (!strcmpW( Feature
, feature
->Feature
)) return feature
;
668 MSIFILE
*msi_get_loaded_file( MSIPACKAGE
*package
, const WCHAR
*key
)
672 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
674 if (!strcmpW( key
, file
->File
)) return file
;
679 MSIFOLDER
*msi_get_loaded_folder( MSIPACKAGE
*package
, const WCHAR
*dir
)
683 LIST_FOR_EACH_ENTRY( folder
, &package
->folders
, MSIFOLDER
, entry
)
685 if (!strcmpW( dir
, folder
->Directory
)) return folder
;
691 * Recursively create all directories in the path.
692 * shamelessly stolen from setupapi/queue.c
694 BOOL
msi_create_full_path( const WCHAR
*path
)
700 new_path
= msi_alloc( (strlenW( path
) + 1) * sizeof(WCHAR
) );
701 strcpyW( new_path
, path
);
703 while ((len
= strlenW( new_path
)) && new_path
[len
- 1] == '\\')
704 new_path
[len
- 1] = 0;
706 while (!CreateDirectoryW( new_path
, NULL
))
709 DWORD last_error
= GetLastError();
710 if (last_error
== ERROR_ALREADY_EXISTS
) break;
711 if (last_error
!= ERROR_PATH_NOT_FOUND
)
716 if (!(slash
= strrchrW( new_path
, '\\' )))
721 len
= slash
- new_path
;
723 if (!msi_create_full_path( new_path
))
728 new_path
[len
] = '\\';
730 msi_free( new_path
);
734 void msi_ui_progress( MSIPACKAGE
*package
, int a
, int b
, int c
, int d
)
738 row
= MSI_CreateRecord( 4 );
739 MSI_RecordSetInteger( row
, 1, a
);
740 MSI_RecordSetInteger( row
, 2, b
);
741 MSI_RecordSetInteger( row
, 3, c
);
742 MSI_RecordSetInteger( row
, 4, d
);
743 MSI_ProcessMessage( package
, INSTALLMESSAGE_PROGRESS
, row
);
744 msiobj_release( &row
->hdr
);
746 msi_dialog_check_messages( NULL
);
749 INSTALLSTATE
msi_get_component_action( MSIPACKAGE
*package
, MSICOMPONENT
*comp
)
753 TRACE("component is disabled: %s\n", debugstr_w(comp
->Component
));
754 return INSTALLSTATE_UNKNOWN
;
756 if (package
->need_rollback
) return comp
->Installed
;
757 if (comp
->num_clients
> 0 && comp
->ActionRequest
== INSTALLSTATE_ABSENT
)
759 TRACE("%s has %u clients left\n", debugstr_w(comp
->Component
), comp
->num_clients
);
760 return INSTALLSTATE_UNKNOWN
;
762 return comp
->ActionRequest
;
765 INSTALLSTATE
msi_get_feature_action( MSIPACKAGE
*package
, MSIFEATURE
*feature
)
767 if (package
->need_rollback
) return feature
->Installed
;
768 return feature
->ActionRequest
;
771 static UINT
ITERATE_CreateFolders(MSIRECORD
*row
, LPVOID param
)
773 MSIPACKAGE
*package
= param
;
774 LPCWSTR dir
, component
, full_path
;
779 component
= MSI_RecordGetString(row
, 2);
781 return ERROR_SUCCESS
;
783 comp
= msi_get_loaded_component(package
, component
);
785 return ERROR_SUCCESS
;
787 comp
->Action
= msi_get_component_action( package
, comp
);
788 if (comp
->Action
!= INSTALLSTATE_LOCAL
)
790 TRACE("component not scheduled for installation: %s\n", debugstr_w(component
));
791 return ERROR_SUCCESS
;
794 dir
= MSI_RecordGetString(row
,1);
797 ERR("Unable to get folder id\n");
798 return ERROR_SUCCESS
;
801 uirow
= MSI_CreateRecord(1);
802 MSI_RecordSetStringW(uirow
, 1, dir
);
803 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONDATA
, uirow
);
804 msiobj_release(&uirow
->hdr
);
806 full_path
= msi_get_target_folder( package
, dir
);
809 ERR("Unable to retrieve folder %s\n", debugstr_w(dir
));
810 return ERROR_SUCCESS
;
812 TRACE("folder is %s\n", debugstr_w(full_path
));
814 folder
= msi_get_loaded_folder( package
, dir
);
815 if (folder
->State
== FOLDER_STATE_UNINITIALIZED
) msi_create_full_path( full_path
);
816 folder
->State
= FOLDER_STATE_CREATED
;
817 return ERROR_SUCCESS
;
820 static UINT
ACTION_CreateFolders(MSIPACKAGE
*package
)
822 static const WCHAR query
[] = {
823 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
824 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
828 rc
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
829 if (rc
!= ERROR_SUCCESS
)
830 return ERROR_SUCCESS
;
832 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_CreateFolders
, package
);
833 msiobj_release(&view
->hdr
);
837 static void remove_persistent_folder( MSIFOLDER
*folder
)
841 LIST_FOR_EACH_ENTRY( fl
, &folder
->children
, FolderList
, entry
)
843 remove_persistent_folder( fl
->folder
);
845 if (folder
->persistent
&& folder
->State
!= FOLDER_STATE_REMOVED
)
847 if (RemoveDirectoryW( folder
->ResolvedTarget
)) folder
->State
= FOLDER_STATE_REMOVED
;
851 static UINT
ITERATE_RemoveFolders( MSIRECORD
*row
, LPVOID param
)
853 MSIPACKAGE
*package
= param
;
854 LPCWSTR dir
, component
, full_path
;
859 component
= MSI_RecordGetString(row
, 2);
861 return ERROR_SUCCESS
;
863 comp
= msi_get_loaded_component(package
, component
);
865 return ERROR_SUCCESS
;
867 comp
->Action
= msi_get_component_action( package
, comp
);
868 if (comp
->Action
!= INSTALLSTATE_ABSENT
)
870 TRACE("component not scheduled for removal %s\n", debugstr_w(component
));
871 return ERROR_SUCCESS
;
874 dir
= MSI_RecordGetString( row
, 1 );
877 ERR("Unable to get folder id\n");
878 return ERROR_SUCCESS
;
881 full_path
= msi_get_target_folder( package
, dir
);
884 ERR("Unable to resolve folder %s\n", debugstr_w(dir
));
885 return ERROR_SUCCESS
;
887 TRACE("folder is %s\n", debugstr_w(full_path
));
889 uirow
= MSI_CreateRecord( 1 );
890 MSI_RecordSetStringW( uirow
, 1, dir
);
891 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONDATA
, uirow
);
892 msiobj_release( &uirow
->hdr
);
894 folder
= msi_get_loaded_folder( package
, dir
);
895 remove_persistent_folder( folder
);
896 return ERROR_SUCCESS
;
899 static UINT
ACTION_RemoveFolders( MSIPACKAGE
*package
)
901 static const WCHAR query
[] = {
902 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
903 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
907 rc
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
908 if (rc
!= ERROR_SUCCESS
)
909 return ERROR_SUCCESS
;
911 rc
= MSI_IterateRecords( view
, NULL
, ITERATE_RemoveFolders
, package
);
912 msiobj_release( &view
->hdr
);
916 static UINT
load_component( MSIRECORD
*row
, LPVOID param
)
918 MSIPACKAGE
*package
= param
;
921 comp
= msi_alloc_zero( sizeof(MSICOMPONENT
) );
923 return ERROR_FUNCTION_FAILED
;
925 list_add_tail( &package
->components
, &comp
->entry
);
927 /* fill in the data */
928 comp
->Component
= msi_dup_record_field( row
, 1 );
930 TRACE("Loading Component %s\n", debugstr_w(comp
->Component
));
932 comp
->ComponentId
= msi_dup_record_field( row
, 2 );
933 comp
->Directory
= msi_dup_record_field( row
, 3 );
934 comp
->Attributes
= MSI_RecordGetInteger(row
,4);
935 comp
->Condition
= msi_dup_record_field( row
, 5 );
936 comp
->KeyPath
= msi_dup_record_field( row
, 6 );
938 comp
->Installed
= INSTALLSTATE_UNKNOWN
;
939 comp
->Action
= INSTALLSTATE_UNKNOWN
;
940 comp
->ActionRequest
= INSTALLSTATE_UNKNOWN
;
942 comp
->assembly
= msi_load_assembly( package
, comp
);
943 return ERROR_SUCCESS
;
946 UINT
msi_load_all_components( MSIPACKAGE
*package
)
948 static const WCHAR query
[] = {
949 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
950 '`','C','o','m','p','o','n','e','n','t','`',0};
954 if (!list_empty(&package
->components
))
955 return ERROR_SUCCESS
;
957 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
958 if (r
!= ERROR_SUCCESS
)
961 if (!msi_init_assembly_caches( package
))
963 ERR("can't initialize assembly caches\n");
964 msiobj_release( &view
->hdr
);
965 return ERROR_FUNCTION_FAILED
;
968 r
= MSI_IterateRecords(view
, NULL
, load_component
, package
);
969 msiobj_release(&view
->hdr
);
978 static UINT
add_feature_component( MSIFEATURE
*feature
, MSICOMPONENT
*comp
)
982 cl
= msi_alloc( sizeof (*cl
) );
984 return ERROR_NOT_ENOUGH_MEMORY
;
985 cl
->component
= comp
;
986 list_add_tail( &feature
->Components
, &cl
->entry
);
988 return ERROR_SUCCESS
;
991 static UINT
add_feature_child( MSIFEATURE
*parent
, MSIFEATURE
*child
)
995 fl
= msi_alloc( sizeof(*fl
) );
997 return ERROR_NOT_ENOUGH_MEMORY
;
999 list_add_tail( &parent
->Children
, &fl
->entry
);
1001 return ERROR_SUCCESS
;
1004 static UINT
iterate_load_featurecomponents(MSIRECORD
*row
, LPVOID param
)
1006 _ilfs
* ilfs
= param
;
1010 component
= MSI_RecordGetString(row
,1);
1012 /* check to see if the component is already loaded */
1013 comp
= msi_get_loaded_component( ilfs
->package
, component
);
1016 WARN("ignoring unknown component %s\n", debugstr_w(component
));
1017 return ERROR_SUCCESS
;
1019 add_feature_component( ilfs
->feature
, comp
);
1020 comp
->Enabled
= TRUE
;
1022 return ERROR_SUCCESS
;
1025 static UINT
load_feature(MSIRECORD
* row
, LPVOID param
)
1027 static const WCHAR query
[] = {
1028 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1029 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1030 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1031 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1032 MSIPACKAGE
*package
= param
;
1033 MSIFEATURE
*feature
;
1038 /* fill in the data */
1040 feature
= msi_alloc_zero( sizeof (MSIFEATURE
) );
1042 return ERROR_NOT_ENOUGH_MEMORY
;
1044 list_init( &feature
->Children
);
1045 list_init( &feature
->Components
);
1047 feature
->Feature
= msi_dup_record_field( row
, 1 );
1049 TRACE("Loading feature %s\n",debugstr_w(feature
->Feature
));
1051 feature
->Feature_Parent
= msi_dup_record_field( row
, 2 );
1052 feature
->Title
= msi_dup_record_field( row
, 3 );
1053 feature
->Description
= msi_dup_record_field( row
, 4 );
1055 if (!MSI_RecordIsNull(row
,5))
1056 feature
->Display
= MSI_RecordGetInteger(row
,5);
1058 feature
->Level
= MSI_RecordGetInteger(row
,6);
1059 feature
->Directory
= msi_dup_record_field( row
, 7 );
1060 feature
->Attributes
= MSI_RecordGetInteger(row
,8);
1062 feature
->Installed
= INSTALLSTATE_UNKNOWN
;
1063 feature
->Action
= INSTALLSTATE_UNKNOWN
;
1064 feature
->ActionRequest
= INSTALLSTATE_UNKNOWN
;
1066 list_add_tail( &package
->features
, &feature
->entry
);
1068 /* load feature components */
1070 rc
= MSI_OpenQuery( package
->db
, &view
, query
, feature
->Feature
);
1071 if (rc
!= ERROR_SUCCESS
)
1072 return ERROR_SUCCESS
;
1074 ilfs
.package
= package
;
1075 ilfs
.feature
= feature
;
1077 rc
= MSI_IterateRecords(view
, NULL
, iterate_load_featurecomponents
, &ilfs
);
1078 msiobj_release(&view
->hdr
);
1082 static UINT
find_feature_children(MSIRECORD
* row
, LPVOID param
)
1084 MSIPACKAGE
*package
= param
;
1085 MSIFEATURE
*parent
, *child
;
1087 child
= msi_get_loaded_feature( package
, MSI_RecordGetString( row
, 1 ) );
1089 return ERROR_FUNCTION_FAILED
;
1091 if (!child
->Feature_Parent
)
1092 return ERROR_SUCCESS
;
1094 parent
= msi_get_loaded_feature( package
, child
->Feature_Parent
);
1096 return ERROR_FUNCTION_FAILED
;
1098 add_feature_child( parent
, child
);
1099 return ERROR_SUCCESS
;
1102 UINT
msi_load_all_features( MSIPACKAGE
*package
)
1104 static const WCHAR query
[] = {
1105 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1106 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1107 '`','D','i','s','p','l','a','y','`',0};
1111 if (!list_empty(&package
->features
))
1112 return ERROR_SUCCESS
;
1114 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1115 if (r
!= ERROR_SUCCESS
)
1118 r
= MSI_IterateRecords( view
, NULL
, load_feature
, package
);
1119 if (r
!= ERROR_SUCCESS
)
1121 msiobj_release( &view
->hdr
);
1124 r
= MSI_IterateRecords( view
, NULL
, find_feature_children
, package
);
1125 msiobj_release( &view
->hdr
);
1129 static LPWSTR
folder_split_path(LPWSTR p
, WCHAR ch
)
1140 static UINT
load_file_hash(MSIPACKAGE
*package
, MSIFILE
*file
)
1142 static const WCHAR query
[] = {
1143 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1144 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1145 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1146 MSIQUERY
*view
= NULL
;
1147 MSIRECORD
*row
= NULL
;
1150 TRACE("%s\n", debugstr_w(file
->File
));
1152 r
= MSI_OpenQuery(package
->db
, &view
, query
, file
->File
);
1153 if (r
!= ERROR_SUCCESS
)
1156 r
= MSI_ViewExecute(view
, NULL
);
1157 if (r
!= ERROR_SUCCESS
)
1160 r
= MSI_ViewFetch(view
, &row
);
1161 if (r
!= ERROR_SUCCESS
)
1164 file
->hash
.dwFileHashInfoSize
= sizeof(MSIFILEHASHINFO
);
1165 file
->hash
.dwData
[0] = MSI_RecordGetInteger(row
, 3);
1166 file
->hash
.dwData
[1] = MSI_RecordGetInteger(row
, 4);
1167 file
->hash
.dwData
[2] = MSI_RecordGetInteger(row
, 5);
1168 file
->hash
.dwData
[3] = MSI_RecordGetInteger(row
, 6);
1171 if (view
) msiobj_release(&view
->hdr
);
1172 if (row
) msiobj_release(&row
->hdr
);
1176 static UINT
load_file_disk_id( MSIPACKAGE
*package
, MSIFILE
*file
)
1179 static const WCHAR query
[] = {
1180 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1181 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1182 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1184 row
= MSI_QueryGetRecord( package
->db
, query
, file
->Sequence
);
1187 WARN("query failed\n");
1188 return ERROR_FUNCTION_FAILED
;
1191 file
->disk_id
= MSI_RecordGetInteger( row
, 1 );
1192 msiobj_release( &row
->hdr
);
1193 return ERROR_SUCCESS
;
1196 static UINT
load_file(MSIRECORD
*row
, LPVOID param
)
1198 MSIPACKAGE
* package
= param
;
1202 /* fill in the data */
1204 file
= msi_alloc_zero( sizeof (MSIFILE
) );
1206 return ERROR_NOT_ENOUGH_MEMORY
;
1208 file
->File
= msi_dup_record_field( row
, 1 );
1210 component
= MSI_RecordGetString( row
, 2 );
1211 file
->Component
= msi_get_loaded_component( package
, component
);
1213 if (!file
->Component
)
1215 WARN("Component not found: %s\n", debugstr_w(component
));
1216 msi_free(file
->File
);
1218 return ERROR_SUCCESS
;
1221 file
->FileName
= msi_dup_record_field( row
, 3 );
1222 msi_reduce_to_long_filename( file
->FileName
);
1224 file
->ShortName
= msi_dup_record_field( row
, 3 );
1225 file
->LongName
= strdupW( folder_split_path(file
->ShortName
, '|'));
1227 file
->FileSize
= MSI_RecordGetInteger( row
, 4 );
1228 file
->Version
= msi_dup_record_field( row
, 5 );
1229 file
->Language
= msi_dup_record_field( row
, 6 );
1230 file
->Attributes
= MSI_RecordGetInteger( row
, 7 );
1231 file
->Sequence
= MSI_RecordGetInteger( row
, 8 );
1233 file
->state
= msifs_invalid
;
1235 /* if the compressed bits are not set in the file attributes,
1236 * then read the information from the package word count property
1238 if (package
->WordCount
& msidbSumInfoSourceTypeAdminImage
)
1240 file
->IsCompressed
= FALSE
;
1242 else if (file
->Attributes
&
1243 (msidbFileAttributesCompressed
| msidbFileAttributesPatchAdded
))
1245 file
->IsCompressed
= TRUE
;
1247 else if (file
->Attributes
& msidbFileAttributesNoncompressed
)
1249 file
->IsCompressed
= FALSE
;
1253 file
->IsCompressed
= package
->WordCount
& msidbSumInfoSourceTypeCompressed
;
1256 load_file_hash(package
, file
);
1257 load_file_disk_id(package
, file
);
1259 TRACE("File loaded (file %s sequence %u)\n", debugstr_w(file
->File
), file
->Sequence
);
1261 list_add_tail( &package
->files
, &file
->entry
);
1263 return ERROR_SUCCESS
;
1266 static UINT
load_all_files(MSIPACKAGE
*package
)
1268 static const WCHAR query
[] = {
1269 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1270 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1271 '`','S','e','q','u','e','n','c','e','`', 0};
1275 if (!list_empty(&package
->files
))
1276 return ERROR_SUCCESS
;
1278 rc
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
1279 if (rc
!= ERROR_SUCCESS
)
1280 return ERROR_SUCCESS
;
1282 rc
= MSI_IterateRecords(view
, NULL
, load_file
, package
);
1283 msiobj_release(&view
->hdr
);
1287 static UINT
load_media( MSIRECORD
*row
, LPVOID param
)
1289 MSIPACKAGE
*package
= param
;
1290 UINT disk_id
= MSI_RecordGetInteger( row
, 1 );
1291 const WCHAR
*cabinet
= MSI_RecordGetString( row
, 4 );
1293 /* FIXME: load external cabinets and directory sources too */
1294 if (!cabinet
|| cabinet
[0] != '#' || disk_id
>= MSI_INITIAL_MEDIA_TRANSFORM_DISKID
)
1295 return ERROR_SUCCESS
;
1297 return msi_add_cabinet_stream( package
, disk_id
, package
->db
->storage
, cabinet
);
1300 static UINT
load_all_media( MSIPACKAGE
*package
)
1302 static const WCHAR query
[] = {
1303 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1304 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1305 '`','D','i','s','k','I','d','`',0};
1309 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1310 if (r
!= ERROR_SUCCESS
)
1311 return ERROR_SUCCESS
;
1313 r
= MSI_IterateRecords( view
, NULL
, load_media
, package
);
1314 msiobj_release( &view
->hdr
);
1318 static UINT
load_patch_disk_id( MSIPACKAGE
*package
, MSIFILEPATCH
*patch
)
1320 static const WCHAR query
[] =
1321 {'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1322 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1323 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','u',0};
1326 if (!(rec
= MSI_QueryGetRecord( package
->db
, query
, patch
->Sequence
)))
1328 WARN("query failed\n");
1329 return ERROR_FUNCTION_FAILED
;
1332 patch
->disk_id
= MSI_RecordGetInteger( rec
, 1 );
1333 msiobj_release( &rec
->hdr
);
1334 return ERROR_SUCCESS
;
1337 static UINT
load_patch(MSIRECORD
*row
, LPVOID param
)
1339 MSIPACKAGE
*package
= param
;
1340 MSIFILEPATCH
*patch
;
1341 const WCHAR
*file_key
;
1343 patch
= msi_alloc_zero( sizeof (MSIFILEPATCH
) );
1345 return ERROR_NOT_ENOUGH_MEMORY
;
1347 file_key
= MSI_RecordGetString( row
, 1 );
1348 patch
->File
= msi_get_loaded_file( package
, file_key
);
1351 ERR("Failed to find target for patch in File table\n");
1353 return ERROR_FUNCTION_FAILED
;
1356 patch
->Sequence
= MSI_RecordGetInteger( row
, 2 );
1357 patch
->PatchSize
= MSI_RecordGetInteger( row
, 3 );
1358 patch
->Attributes
= MSI_RecordGetInteger( row
, 4 );
1361 * Header field - for patch validation.
1362 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1365 load_patch_disk_id( package
, patch
);
1367 TRACE("Patch loaded (file %s sequence %u)\n", debugstr_w(patch
->File
->File
), patch
->Sequence
);
1369 list_add_tail( &package
->filepatches
, &patch
->entry
);
1371 return ERROR_SUCCESS
;
1374 static UINT
load_all_patches(MSIPACKAGE
*package
)
1376 static const WCHAR query
[] = {
1377 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1378 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1379 '`','S','e','q','u','e','n','c','e','`',0};
1383 if (!list_empty(&package
->filepatches
))
1384 return ERROR_SUCCESS
;
1386 rc
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
1387 if (rc
!= ERROR_SUCCESS
)
1388 return ERROR_SUCCESS
;
1390 rc
= MSI_IterateRecords(view
, NULL
, load_patch
, package
);
1391 msiobj_release(&view
->hdr
);
1395 static UINT
load_folder_persistence( MSIPACKAGE
*package
, MSIFOLDER
*folder
)
1397 static const WCHAR query
[] = {
1398 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1399 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1400 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1403 folder
->persistent
= FALSE
;
1404 if (!MSI_OpenQuery( package
->db
, &view
, query
, folder
->Directory
))
1406 if (!MSI_ViewExecute( view
, NULL
))
1409 if (!MSI_ViewFetch( view
, &rec
))
1411 TRACE("directory %s is persistent\n", debugstr_w(folder
->Directory
));
1412 folder
->persistent
= TRUE
;
1413 msiobj_release( &rec
->hdr
);
1416 msiobj_release( &view
->hdr
);
1418 return ERROR_SUCCESS
;
1421 static UINT
load_folder( MSIRECORD
*row
, LPVOID param
)
1423 MSIPACKAGE
*package
= param
;
1424 static WCHAR szEmpty
[] = { 0 };
1425 LPWSTR p
, tgt_short
, tgt_long
, src_short
, src_long
;
1428 if (!(folder
= msi_alloc_zero( sizeof(*folder
) ))) return ERROR_NOT_ENOUGH_MEMORY
;
1429 list_init( &folder
->children
);
1430 folder
->Directory
= msi_dup_record_field( row
, 1 );
1431 folder
->Parent
= msi_dup_record_field( row
, 2 );
1432 p
= msi_dup_record_field(row
, 3);
1434 TRACE("%s\n", debugstr_w(folder
->Directory
));
1436 /* split src and target dir */
1438 src_short
= folder_split_path( p
, ':' );
1440 /* split the long and short paths */
1441 tgt_long
= folder_split_path( tgt_short
, '|' );
1442 src_long
= folder_split_path( src_short
, '|' );
1444 /* check for no-op dirs */
1445 if (tgt_short
&& !strcmpW( szDot
, tgt_short
))
1446 tgt_short
= szEmpty
;
1447 if (src_short
&& !strcmpW( szDot
, src_short
))
1448 src_short
= szEmpty
;
1451 tgt_long
= tgt_short
;
1454 src_short
= tgt_short
;
1455 src_long
= tgt_long
;
1459 src_long
= src_short
;
1461 /* FIXME: use the target short path too */
1462 folder
->TargetDefault
= strdupW(tgt_long
);
1463 folder
->SourceShortPath
= strdupW(src_short
);
1464 folder
->SourceLongPath
= strdupW(src_long
);
1467 TRACE("TargetDefault = %s\n",debugstr_w( folder
->TargetDefault
));
1468 TRACE("SourceLong = %s\n", debugstr_w( folder
->SourceLongPath
));
1469 TRACE("SourceShort = %s\n", debugstr_w( folder
->SourceShortPath
));
1471 load_folder_persistence( package
, folder
);
1473 list_add_tail( &package
->folders
, &folder
->entry
);
1474 return ERROR_SUCCESS
;
1477 static UINT
add_folder_child( MSIFOLDER
*parent
, MSIFOLDER
*child
)
1481 if (!(fl
= msi_alloc( sizeof(*fl
) ))) return ERROR_NOT_ENOUGH_MEMORY
;
1483 list_add_tail( &parent
->children
, &fl
->entry
);
1484 return ERROR_SUCCESS
;
1487 static UINT
find_folder_children( MSIRECORD
*row
, LPVOID param
)
1489 MSIPACKAGE
*package
= param
;
1490 MSIFOLDER
*parent
, *child
;
1492 if (!(child
= msi_get_loaded_folder( package
, MSI_RecordGetString( row
, 1 ) )))
1493 return ERROR_FUNCTION_FAILED
;
1495 if (!child
->Parent
) return ERROR_SUCCESS
;
1497 if (!(parent
= msi_get_loaded_folder( package
, child
->Parent
)))
1498 return ERROR_FUNCTION_FAILED
;
1500 return add_folder_child( parent
, child
);
1503 static UINT
load_all_folders( MSIPACKAGE
*package
)
1505 static const WCHAR query
[] = {
1506 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1507 '`','D','i','r','e','c','t','o','r','y','`',0};
1511 if (!list_empty(&package
->folders
))
1512 return ERROR_SUCCESS
;
1514 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
1515 if (r
!= ERROR_SUCCESS
)
1518 r
= MSI_IterateRecords( view
, NULL
, load_folder
, package
);
1519 if (r
!= ERROR_SUCCESS
)
1521 msiobj_release( &view
->hdr
);
1524 r
= MSI_IterateRecords( view
, NULL
, find_folder_children
, package
);
1525 msiobj_release( &view
->hdr
);
1529 static UINT
ACTION_CostInitialize(MSIPACKAGE
*package
)
1531 msi_set_property( package
->db
, szCostingComplete
, szZero
, -1 );
1532 msi_set_property( package
->db
, szRootDrive
, szCRoot
, -1 );
1534 load_all_folders( package
);
1535 msi_load_all_components( package
);
1536 msi_load_all_features( package
);
1537 load_all_files( package
);
1538 load_all_patches( package
);
1539 load_all_media( package
);
1541 return ERROR_SUCCESS
;
1544 static UINT
execute_script_action( MSIPACKAGE
*package
, UINT script
, UINT index
)
1546 const WCHAR
*action
= package
->script
->Actions
[script
][index
];
1547 ui_actionstart( package
, action
, NULL
, NULL
);
1548 TRACE("executing %s\n", debugstr_w(action
));
1549 return ACTION_PerformAction( package
, action
, script
);
1552 static UINT
execute_script( MSIPACKAGE
*package
, UINT script
)
1554 UINT i
, rc
= ERROR_SUCCESS
;
1556 TRACE("executing script %u\n", script
);
1558 if (!package
->script
)
1560 ERR("no script!\n");
1561 return ERROR_FUNCTION_FAILED
;
1563 if (script
== SCRIPT_ROLLBACK
)
1565 for (i
= package
->script
->ActionCount
[script
]; i
> 0; i
--)
1567 rc
= execute_script_action( package
, script
, i
- 1 );
1568 if (rc
!= ERROR_SUCCESS
) break;
1573 for (i
= 0; i
< package
->script
->ActionCount
[script
]; i
++)
1575 rc
= execute_script_action( package
, script
, i
);
1576 if (rc
!= ERROR_SUCCESS
) break;
1579 msi_free_action_script(package
, script
);
1583 static UINT
ACTION_FileCost(MSIPACKAGE
*package
)
1585 return ERROR_SUCCESS
;
1588 static void get_client_counts( MSIPACKAGE
*package
)
1593 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
1595 if (!comp
->ComponentId
) continue;
1597 if (MSIREG_OpenUserDataComponentKey( comp
->ComponentId
, szLocalSid
, &hkey
, FALSE
) &&
1598 MSIREG_OpenUserDataComponentKey( comp
->ComponentId
, NULL
, &hkey
, FALSE
))
1600 comp
->num_clients
= 0;
1603 RegQueryInfoKeyW( hkey
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, (DWORD
*)&comp
->num_clients
,
1604 NULL
, NULL
, NULL
, NULL
);
1605 RegCloseKey( hkey
);
1609 static void ACTION_GetComponentInstallStates(MSIPACKAGE
*package
)
1614 LIST_FOR_EACH_ENTRY(comp
, &package
->components
, MSICOMPONENT
, entry
)
1616 if (!comp
->ComponentId
) continue;
1618 r
= MsiQueryComponentStateW( package
->ProductCode
, NULL
,
1619 MSIINSTALLCONTEXT_USERMANAGED
, comp
->ComponentId
,
1621 if (r
== ERROR_SUCCESS
) continue;
1623 r
= MsiQueryComponentStateW( package
->ProductCode
, NULL
,
1624 MSIINSTALLCONTEXT_USERUNMANAGED
, comp
->ComponentId
,
1626 if (r
== ERROR_SUCCESS
) continue;
1628 r
= MsiQueryComponentStateW( package
->ProductCode
, NULL
,
1629 MSIINSTALLCONTEXT_MACHINE
, comp
->ComponentId
,
1631 if (r
== ERROR_SUCCESS
) continue;
1633 comp
->Installed
= INSTALLSTATE_ABSENT
;
1637 static void ACTION_GetFeatureInstallStates(MSIPACKAGE
*package
)
1639 MSIFEATURE
*feature
;
1641 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1643 INSTALLSTATE state
= MsiQueryFeatureStateW( package
->ProductCode
, feature
->Feature
);
1645 if (state
== INSTALLSTATE_UNKNOWN
|| state
== INSTALLSTATE_INVALIDARG
)
1646 feature
->Installed
= INSTALLSTATE_ABSENT
;
1648 feature
->Installed
= state
;
1652 static inline BOOL
is_feature_selected( MSIFEATURE
*feature
, INT level
)
1654 return (feature
->Level
> 0 && feature
->Level
<= level
);
1657 static BOOL
process_state_property(MSIPACKAGE
* package
, int level
,
1658 LPCWSTR property
, INSTALLSTATE state
)
1661 MSIFEATURE
*feature
;
1662 BOOL remove
= !strcmpW(property
, szRemove
);
1663 BOOL reinstall
= !strcmpW(property
, szReinstall
);
1665 override
= msi_dup_property( package
->db
, property
);
1669 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1671 if (feature
->Level
<= 0)
1675 state
= (feature
->Installed
== INSTALLSTATE_ABSENT
? INSTALLSTATE_UNKNOWN
: feature
->Installed
);
1677 state
= (feature
->Installed
== INSTALLSTATE_ABSENT
? INSTALLSTATE_UNKNOWN
: INSTALLSTATE_ABSENT
);
1679 if (!strcmpiW( override
, szAll
))
1681 feature
->Action
= state
;
1682 feature
->ActionRequest
= state
;
1686 LPWSTR ptr
= override
;
1687 LPWSTR ptr2
= strchrW(override
,',');
1691 int len
= ptr2
- ptr
;
1693 if ((ptr2
&& strlenW(feature
->Feature
) == len
&& !strncmpW(ptr
, feature
->Feature
, len
))
1694 || (!ptr2
&& !strcmpW(ptr
, feature
->Feature
)))
1696 feature
->Action
= state
;
1697 feature
->ActionRequest
= state
;
1703 ptr2
= strchrW(ptr
,',');
1714 static BOOL
process_overrides( MSIPACKAGE
*package
, int level
)
1716 static const WCHAR szAddLocal
[] =
1717 {'A','D','D','L','O','C','A','L',0};
1718 static const WCHAR szAddSource
[] =
1719 {'A','D','D','S','O','U','R','C','E',0};
1720 static const WCHAR szAdvertise
[] =
1721 {'A','D','V','E','R','T','I','S','E',0};
1724 /* all these activation/deactivation things happen in order and things
1725 * later on the list override things earlier on the list.
1727 * 0 INSTALLLEVEL processing
1740 ret
|= process_state_property( package
, level
, szAddLocal
, INSTALLSTATE_LOCAL
);
1741 ret
|= process_state_property( package
, level
, szRemove
, INSTALLSTATE_ABSENT
);
1742 ret
|= process_state_property( package
, level
, szAddSource
, INSTALLSTATE_SOURCE
);
1743 ret
|= process_state_property( package
, level
, szReinstall
, INSTALLSTATE_UNKNOWN
);
1744 ret
|= process_state_property( package
, level
, szAdvertise
, INSTALLSTATE_ADVERTISED
);
1746 if (ret
&& !package
->full_reinstall
)
1747 msi_set_property( package
->db
, szPreselected
, szOne
, -1 );
1752 static void disable_children( MSIFEATURE
*feature
, int level
)
1756 LIST_FOR_EACH_ENTRY( fl
, &feature
->Children
, FeatureList
, entry
)
1758 if (!is_feature_selected( feature
, level
))
1760 TRACE("child %s (level %d request %d) follows disabled parent %s (level %d request %d)\n",
1761 debugstr_w(fl
->feature
->Feature
), fl
->feature
->Level
, fl
->feature
->ActionRequest
,
1762 debugstr_w(feature
->Feature
), feature
->Level
, feature
->ActionRequest
);
1764 fl
->feature
->Level
= feature
->Level
;
1765 fl
->feature
->Action
= INSTALLSTATE_UNKNOWN
;
1766 fl
->feature
->ActionRequest
= INSTALLSTATE_UNKNOWN
;
1768 disable_children( fl
->feature
, level
);
1772 static void follow_parent( MSIFEATURE
*feature
)
1776 LIST_FOR_EACH_ENTRY( fl
, &feature
->Children
, FeatureList
, entry
)
1778 if (fl
->feature
->Attributes
& msidbFeatureAttributesFollowParent
)
1780 TRACE("child %s (level %d request %d) follows parent %s (level %d request %d)\n",
1781 debugstr_w(fl
->feature
->Feature
), fl
->feature
->Level
, fl
->feature
->ActionRequest
,
1782 debugstr_w(feature
->Feature
), feature
->Level
, feature
->ActionRequest
);
1784 fl
->feature
->Action
= feature
->Action
;
1785 fl
->feature
->ActionRequest
= feature
->ActionRequest
;
1787 follow_parent( fl
->feature
);
1791 UINT
MSI_SetFeatureStates(MSIPACKAGE
*package
)
1794 MSICOMPONENT
* component
;
1795 MSIFEATURE
*feature
;
1797 TRACE("Checking Install Level\n");
1799 level
= msi_get_property_int(package
->db
, szInstallLevel
, 1);
1801 if (!msi_get_property_int( package
->db
, szPreselected
, 0 ))
1803 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1805 if (!is_feature_selected( feature
, level
)) continue;
1807 if (feature
->ActionRequest
== INSTALLSTATE_UNKNOWN
)
1809 if (feature
->Attributes
& msidbFeatureAttributesFavorSource
)
1811 feature
->Action
= INSTALLSTATE_SOURCE
;
1812 feature
->ActionRequest
= INSTALLSTATE_SOURCE
;
1814 else if (feature
->Attributes
& msidbFeatureAttributesFavorAdvertise
)
1816 feature
->Action
= INSTALLSTATE_ADVERTISED
;
1817 feature
->ActionRequest
= INSTALLSTATE_ADVERTISED
;
1821 feature
->Action
= INSTALLSTATE_LOCAL
;
1822 feature
->ActionRequest
= INSTALLSTATE_LOCAL
;
1826 /* disable child features of unselected parent or follow parent */
1827 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1829 if (feature
->Feature_Parent
) continue;
1830 disable_children( feature
, level
);
1831 follow_parent( feature
);
1834 else /* preselected */
1836 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1838 if (!is_feature_selected( feature
, level
)) continue;
1840 if (feature
->ActionRequest
== INSTALLSTATE_UNKNOWN
)
1842 if (feature
->Installed
== INSTALLSTATE_ABSENT
)
1844 feature
->Action
= INSTALLSTATE_UNKNOWN
;
1845 feature
->ActionRequest
= INSTALLSTATE_UNKNOWN
;
1849 feature
->Action
= feature
->Installed
;
1850 feature
->ActionRequest
= feature
->Installed
;
1854 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1856 if (feature
->Feature_Parent
) continue;
1857 disable_children( feature
, level
);
1858 follow_parent( feature
);
1862 /* now we want to set component state based based on feature state */
1863 LIST_FOR_EACH_ENTRY( feature
, &package
->features
, MSIFEATURE
, entry
)
1867 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1868 debugstr_w(feature
->Feature
), feature
->Level
, feature
->Installed
,
1869 feature
->ActionRequest
, feature
->Action
);
1871 /* features with components that have compressed files are made local */
1872 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1874 if (cl
->component
->ForceLocalState
&&
1875 feature
->ActionRequest
== INSTALLSTATE_SOURCE
)
1877 feature
->Action
= INSTALLSTATE_LOCAL
;
1878 feature
->ActionRequest
= INSTALLSTATE_LOCAL
;
1883 LIST_FOR_EACH_ENTRY( cl
, &feature
->Components
, ComponentList
, entry
)
1885 component
= cl
->component
;
1887 switch (feature
->ActionRequest
)
1889 case INSTALLSTATE_ABSENT
:
1890 component
->anyAbsent
= 1;
1892 case INSTALLSTATE_ADVERTISED
:
1893 component
->hasAdvertisedFeature
= 1;
1895 case INSTALLSTATE_SOURCE
:
1896 component
->hasSourceFeature
= 1;
1898 case INSTALLSTATE_LOCAL
:
1899 component
->hasLocalFeature
= 1;
1901 case INSTALLSTATE_DEFAULT
:
1902 if (feature
->Attributes
& msidbFeatureAttributesFavorAdvertise
)
1903 component
->hasAdvertisedFeature
= 1;
1904 else if (feature
->Attributes
& msidbFeatureAttributesFavorSource
)
1905 component
->hasSourceFeature
= 1;
1907 component
->hasLocalFeature
= 1;
1915 LIST_FOR_EACH_ENTRY( component
, &package
->components
, MSICOMPONENT
, entry
)
1917 /* check if it's local or source */
1918 if (!(component
->Attributes
& msidbComponentAttributesOptional
) &&
1919 (component
->hasLocalFeature
|| component
->hasSourceFeature
))
1921 if ((component
->Attributes
& msidbComponentAttributesSourceOnly
) &&
1922 !component
->ForceLocalState
)
1924 component
->Action
= INSTALLSTATE_SOURCE
;
1925 component
->ActionRequest
= INSTALLSTATE_SOURCE
;
1929 component
->Action
= INSTALLSTATE_LOCAL
;
1930 component
->ActionRequest
= INSTALLSTATE_LOCAL
;
1935 /* if any feature is local, the component must be local too */
1936 if (component
->hasLocalFeature
)
1938 component
->Action
= INSTALLSTATE_LOCAL
;
1939 component
->ActionRequest
= INSTALLSTATE_LOCAL
;
1942 if (component
->hasSourceFeature
)
1944 component
->Action
= INSTALLSTATE_SOURCE
;
1945 component
->ActionRequest
= INSTALLSTATE_SOURCE
;
1948 if (component
->hasAdvertisedFeature
)
1950 component
->Action
= INSTALLSTATE_ADVERTISED
;
1951 component
->ActionRequest
= INSTALLSTATE_ADVERTISED
;
1954 TRACE("nobody wants component %s\n", debugstr_w(component
->Component
));
1955 if (component
->anyAbsent
&& component
->ComponentId
)
1957 component
->Action
= INSTALLSTATE_ABSENT
;
1958 component
->ActionRequest
= INSTALLSTATE_ABSENT
;
1962 LIST_FOR_EACH_ENTRY( component
, &package
->components
, MSICOMPONENT
, entry
)
1964 if (component
->ActionRequest
== INSTALLSTATE_DEFAULT
)
1966 TRACE("%s was default, setting to local\n", debugstr_w(component
->Component
));
1967 component
->Action
= INSTALLSTATE_LOCAL
;
1968 component
->ActionRequest
= INSTALLSTATE_LOCAL
;
1971 if (component
->ActionRequest
== INSTALLSTATE_SOURCE
&&
1972 component
->Installed
== INSTALLSTATE_SOURCE
&&
1973 component
->hasSourceFeature
)
1975 component
->Action
= INSTALLSTATE_UNKNOWN
;
1976 component
->ActionRequest
= INSTALLSTATE_UNKNOWN
;
1979 TRACE("component %s (installed %d request %d action %d)\n",
1980 debugstr_w(component
->Component
), component
->Installed
, component
->ActionRequest
, component
->Action
);
1982 if (component
->Action
== INSTALLSTATE_LOCAL
|| component
->Action
== INSTALLSTATE_SOURCE
)
1983 component
->num_clients
++;
1984 else if (component
->Action
== INSTALLSTATE_ABSENT
)
1985 component
->num_clients
--;
1988 return ERROR_SUCCESS
;
1991 static UINT
ITERATE_CostFinalizeConditions(MSIRECORD
*row
, LPVOID param
)
1993 MSIPACKAGE
*package
= param
;
1995 MSIFEATURE
*feature
;
1997 name
= MSI_RecordGetString( row
, 1 );
1999 feature
= msi_get_loaded_feature( package
, name
);
2001 ERR("FAILED to find loaded feature %s\n",debugstr_w(name
));
2005 Condition
= MSI_RecordGetString(row
,3);
2007 if (MSI_EvaluateConditionW(package
,Condition
) == MSICONDITION_TRUE
)
2009 int level
= MSI_RecordGetInteger(row
,2);
2010 TRACE("Resetting feature %s to level %i\n", debugstr_w(name
), level
);
2011 feature
->Level
= level
;
2014 return ERROR_SUCCESS
;
2017 VS_FIXEDFILEINFO
*msi_get_disk_file_version( LPCWSTR filename
)
2019 static const WCHAR name
[] = {'\\',0};
2020 VS_FIXEDFILEINFO
*ptr
, *ret
;
2022 DWORD versize
, handle
;
2025 versize
= GetFileVersionInfoSizeW( filename
, &handle
);
2029 version
= msi_alloc( versize
);
2033 GetFileVersionInfoW( filename
, 0, versize
, version
);
2035 if (!VerQueryValueW( version
, name
, (LPVOID
*)&ptr
, &sz
))
2037 msi_free( version
);
2041 ret
= msi_alloc( sz
);
2042 memcpy( ret
, ptr
, sz
);
2044 msi_free( version
);
2048 int msi_compare_file_versions( VS_FIXEDFILEINFO
*fi
, const WCHAR
*version
)
2052 msi_parse_version_string( version
, &ms
, &ls
);
2054 if (fi
->dwFileVersionMS
> ms
) return 1;
2055 else if (fi
->dwFileVersionMS
< ms
) return -1;
2056 else if (fi
->dwFileVersionLS
> ls
) return 1;
2057 else if (fi
->dwFileVersionLS
< ls
) return -1;
2061 int msi_compare_font_versions( const WCHAR
*ver1
, const WCHAR
*ver2
)
2065 msi_parse_version_string( ver1
, &ms1
, NULL
);
2066 msi_parse_version_string( ver2
, &ms2
, NULL
);
2068 if (ms1
> ms2
) return 1;
2069 else if (ms1
< ms2
) return -1;
2073 DWORD
msi_get_disk_file_size( LPCWSTR filename
)
2078 file
= CreateFileW( filename
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
2079 if (file
== INVALID_HANDLE_VALUE
)
2080 return INVALID_FILE_SIZE
;
2082 size
= GetFileSize( file
, NULL
);
2083 CloseHandle( file
);
2087 BOOL
msi_file_hash_matches( MSIFILE
*file
)
2090 MSIFILEHASHINFO hash
;
2092 hash
.dwFileHashInfoSize
= sizeof(MSIFILEHASHINFO
);
2093 r
= msi_get_filehash( file
->TargetPath
, &hash
);
2094 if (r
!= ERROR_SUCCESS
)
2097 return !memcmp( &hash
, &file
->hash
, sizeof(MSIFILEHASHINFO
) );
2100 static WCHAR
*create_temp_dir( MSIDATABASE
*db
)
2105 if (!db
->tempfolder
)
2107 WCHAR tmp
[MAX_PATH
];
2108 UINT len
= sizeof(tmp
)/sizeof(tmp
[0]);
2110 if (msi_get_property( db
, szTempFolder
, tmp
, &len
) ||
2111 GetFileAttributesW( tmp
) != FILE_ATTRIBUTE_DIRECTORY
)
2113 GetTempPathW( MAX_PATH
, tmp
);
2115 if (!(db
->tempfolder
= strdupW( tmp
))) return NULL
;
2118 if ((ret
= msi_alloc( (strlenW( db
->tempfolder
) + 20) * sizeof(WCHAR
) )))
2122 if (!GetTempFileNameW( db
->tempfolder
, szMsi
, ++id
, ret
))
2127 if (CreateDirectoryW( ret
, NULL
)) break;
2135 * msi_build_directory_name()
2137 * This function is to save messing round with directory names
2138 * It handles adding backslashes between path segments,
2139 * and can add \ at the end of the directory name if told to.
2141 * It takes a variable number of arguments.
2142 * It always allocates a new string for the result, so make sure
2143 * to free the return value when finished with it.
2145 * The first arg is the number of path segments that follow.
2146 * The arguments following count are a list of path segments.
2147 * A path segment may be NULL.
2149 * Path segments will be added with a \ separating them.
2150 * A \ will not be added after the last segment, however if the
2151 * last segment is NULL, then the last character will be a \
2153 WCHAR
*msi_build_directory_name( DWORD count
, ... )
2159 va_start( va
, count
);
2160 for (i
= 0; i
< count
; i
++)
2162 const WCHAR
*str
= va_arg( va
, const WCHAR
* );
2163 if (str
) sz
+= strlenW( str
) + 1;
2167 dir
= msi_alloc( sz
* sizeof(WCHAR
) );
2170 va_start( va
, count
);
2171 for (i
= 0; i
< count
; i
++)
2173 const WCHAR
*str
= va_arg( va
, const WCHAR
* );
2175 strcatW( dir
, str
);
2176 if ( i
+ 1 != count
&& dir
[0] && dir
[strlenW( dir
) - 1] != '\\') strcatW( dir
, szBackSlash
);
2182 BOOL
msi_is_global_assembly( MSICOMPONENT
*comp
)
2184 return comp
->assembly
&& !comp
->assembly
->application
;
2187 static void set_target_path( MSIPACKAGE
*package
, MSIFILE
*file
)
2189 msi_free( file
->TargetPath
);
2190 if (msi_is_global_assembly( file
->Component
))
2192 MSIASSEMBLY
*assembly
= file
->Component
->assembly
;
2194 if (!assembly
->tempdir
) assembly
->tempdir
= create_temp_dir( package
->db
);
2195 file
->TargetPath
= msi_build_directory_name( 2, assembly
->tempdir
, file
->FileName
);
2199 const WCHAR
*dir
= msi_get_target_folder( package
, file
->Component
->Directory
);
2200 file
->TargetPath
= msi_build_directory_name( 2, dir
, file
->FileName
);
2203 TRACE("file %s resolves to %s\n", debugstr_w(file
->File
), debugstr_w(file
->TargetPath
));
2206 static UINT
calculate_file_cost( MSIPACKAGE
*package
)
2208 VS_FIXEDFILEINFO
*file_version
;
2209 WCHAR
*font_version
;
2212 LIST_FOR_EACH_ENTRY( file
, &package
->files
, MSIFILE
, entry
)
2214 MSICOMPONENT
*comp
= file
->Component
;
2217 if (!comp
->Enabled
) continue;
2219 if (file
->IsCompressed
)
2220 comp
->ForceLocalState
= TRUE
;
2222 set_target_path( package
, file
);
2224 if ((comp
->assembly
&& !comp
->assembly
->installed
) ||
2225 GetFileAttributesW(file
->TargetPath
) == INVALID_FILE_ATTRIBUTES
)
2227 comp
->Cost
+= file
->FileSize
;
2230 file_size
= msi_get_disk_file_size( file
->TargetPath
);
2231 TRACE("%s (size %u)\n", debugstr_w(file
->TargetPath
), file_size
);
2235 if ((file_version
= msi_get_disk_file_version( file
->TargetPath
)))
2237 if (msi_compare_file_versions( file_version
, file
->Version
) < 0)
2239 comp
->Cost
+= file
->FileSize
- file_size
;
2241 msi_free( file_version
);
2244 else if ((font_version
= msi_font_version_from_file( file
->TargetPath
)))
2246 if (msi_compare_font_versions( font_version
, file
->Version
) < 0)
2248 comp
->Cost
+= file
->FileSize
- file_size
;
2250 msi_free( font_version
);
2254 if (file_size
!= file
->FileSize
)
2256 comp
->Cost
+= file
->FileSize
- file_size
;
2259 return ERROR_SUCCESS
;
2262 WCHAR
*msi_normalize_path( const WCHAR
*in
)
2264 const WCHAR
*p
= in
;
2266 int n
, len
= strlenW( in
) + 2;
2268 if (!(q
= ret
= msi_alloc( len
* sizeof(WCHAR
) ))) return NULL
;
2273 /* copy until the end of the string or a space */
2274 while (*p
!= ' ' && (*q
= *p
))
2277 /* reduce many backslashes to one */
2278 if (*p
!= '\\' || *q
!= '\\')
2282 /* quit at the end of the string */
2286 /* count the number of spaces */
2291 /* if it's leading or trailing space, skip it */
2292 if ( len
== 0 || p
[-1] == '\\' || p
[n
] == '\\' )
2294 else /* copy n spaces */
2295 while (n
&& (*q
++ = *p
++)) n
--;
2297 while (q
- ret
> 0 && q
[-1] == ' ') q
--;
2298 if (q
- ret
> 0 && q
[-1] != '\\')
2306 static WCHAR
*get_install_location( MSIPACKAGE
*package
)
2311 if (!package
->ProductCode
) return NULL
;
2312 if (MSIREG_OpenInstallProps( package
->ProductCode
, package
->Context
, NULL
, &hkey
, FALSE
)) return NULL
;
2313 if ((path
= msi_reg_get_val_str( hkey
, szInstallLocation
)) && !path
[0])
2318 RegCloseKey( hkey
);
2322 void msi_resolve_target_folder( MSIPACKAGE
*package
, const WCHAR
*name
, BOOL load_prop
)
2325 MSIFOLDER
*folder
, *parent
, *child
;
2326 WCHAR
*path
, *normalized_path
;
2328 TRACE("resolving %s\n", debugstr_w(name
));
2330 if (!(folder
= msi_get_loaded_folder( package
, name
))) return;
2332 if (!strcmpW( folder
->Directory
, szTargetDir
)) /* special resolving for target root dir */
2334 if (!(path
= get_install_location( package
)) &&
2335 (!load_prop
|| !(path
= msi_dup_property( package
->db
, szTargetDir
))))
2337 path
= msi_dup_property( package
->db
, szRootDrive
);
2340 else if (!load_prop
|| !(path
= msi_dup_property( package
->db
, folder
->Directory
)))
2342 if (folder
->Parent
&& strcmpW( folder
->Directory
, folder
->Parent
))
2344 parent
= msi_get_loaded_folder( package
, folder
->Parent
);
2345 path
= msi_build_directory_name( 3, parent
->ResolvedTarget
, folder
->TargetDefault
, NULL
);
2348 path
= msi_build_directory_name( 2, folder
->TargetDefault
, NULL
);
2350 normalized_path
= msi_normalize_path( path
);
2352 if (folder
->ResolvedTarget
&& !strcmpiW( normalized_path
, folder
->ResolvedTarget
))
2354 TRACE("%s already resolved to %s\n", debugstr_w(name
), debugstr_w(folder
->ResolvedTarget
));
2355 msi_free( normalized_path
);
2358 msi_set_property( package
->db
, folder
->Directory
, normalized_path
, -1 );
2359 msi_free( folder
->ResolvedTarget
);
2360 folder
->ResolvedTarget
= normalized_path
;
2362 LIST_FOR_EACH_ENTRY( fl
, &folder
->children
, FolderList
, entry
)
2365 msi_resolve_target_folder( package
, child
->Directory
, load_prop
);
2367 TRACE("%s resolves to %s\n", debugstr_w(name
), debugstr_w(folder
->ResolvedTarget
));
2370 static ULONGLONG
get_volume_space_required( MSIPACKAGE
*package
)
2375 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2377 if (comp
->Action
== INSTALLSTATE_LOCAL
) ret
+= comp
->Cost
;
2382 static UINT
ACTION_CostFinalize(MSIPACKAGE
*package
)
2384 static const WCHAR query
[] =
2385 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2386 '`','C','o','n','d','i','t','i','o','n','`',0};
2387 static const WCHAR szOutOfDiskSpace
[] =
2388 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2389 static const WCHAR szPrimaryFolder
[] =
2390 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2391 static const WCHAR szPrimaryVolumePath
[] =
2392 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2393 static const WCHAR szPrimaryVolumeSpaceAvailable
[] =
2394 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2395 'A','v','a','i','l','a','b','l','e',0};
2396 static const WCHAR szPrimaryVolumeSpaceRequired
[] =
2397 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2398 'R','e','q','u','i','r','e','d',0};
2399 static const WCHAR szPrimaryVolumeSpaceRemaining
[] =
2400 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2401 'R','e','m','a','i','n','i','n','g',0};
2402 static const WCHAR szOutOfNoRbDiskSpace
[] =
2403 {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0};
2406 WCHAR
*level
, *primary_key
, *primary_folder
;
2409 TRACE("Building directory properties\n");
2410 msi_resolve_target_folder( package
, szTargetDir
, TRUE
);
2412 TRACE("Evaluating component conditions\n");
2413 LIST_FOR_EACH_ENTRY( comp
, &package
->components
, MSICOMPONENT
, entry
)
2415 if (MSI_EvaluateConditionW( package
, comp
->Condition
) == MSICONDITION_FALSE
)
2417 TRACE("Disabling component %s\n", debugstr_w(comp
->Component
));
2418 comp
->Enabled
= FALSE
;
2421 comp
->Enabled
= TRUE
;
2423 get_client_counts( package
);
2425 /* read components states from the registry */
2426 ACTION_GetComponentInstallStates(package
);
2427 ACTION_GetFeatureInstallStates(package
);
2429 if (!process_overrides( package
, msi_get_property_int( package
->db
, szInstallLevel
, 1 ) ))
2431 TRACE("Evaluating feature conditions\n");
2433 rc
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
2434 if (rc
== ERROR_SUCCESS
)
2436 rc
= MSI_IterateRecords( view
, NULL
, ITERATE_CostFinalizeConditions
, package
);
2437 msiobj_release( &view
->hdr
);
2438 if (rc
!= ERROR_SUCCESS
)
2443 TRACE("Calculating file cost\n");
2444 calculate_file_cost( package
);
2446 msi_set_property( package
->db
, szCostingComplete
, szOne
, -1 );
2447 /* set default run level if not set */
2448 level
= msi_dup_property( package
->db
, szInstallLevel
);
2449 if (!level
) msi_set_property( package
->db
, szInstallLevel
, szOne
, -1 );
2452 if ((rc
= MSI_SetFeatureStates( package
))) return rc
;
2454 if ((primary_key
= msi_dup_property( package
->db
, szPrimaryFolder
)))
2456 if ((primary_folder
= msi_dup_property( package
->db
, primary_key
)))
2458 if (((primary_folder
[0] >= 'A' && primary_folder
[0] <= 'Z') ||
2459 (primary_folder
[0] >= 'a' && primary_folder
[0] <= 'z')) && primary_folder
[1] == ':')
2461 static const WCHAR fmtW
[] = {'%','l','u',0};
2462 ULARGE_INTEGER free
;
2466 primary_folder
[2] = 0;
2467 if (GetDiskFreeSpaceExW( primary_folder
, &free
, NULL
, NULL
))
2469 sprintfW( buf
, fmtW
, free
.QuadPart
/ 512 );
2470 msi_set_property( package
->db
, szPrimaryVolumeSpaceAvailable
, buf
, -1 );
2472 required
= get_volume_space_required( package
);
2473 sprintfW( buf
, fmtW
, required
/ 512 );
2474 msi_set_property( package
->db
, szPrimaryVolumeSpaceRequired
, buf
, -1 );
2476 sprintfW( buf
, fmtW
, (free
.QuadPart
- required
) / 512 );
2477 msi_set_property( package
->db
, szPrimaryVolumeSpaceRemaining
, buf
, -1 );
2478 msi_set_property( package
->db
, szPrimaryVolumePath
, primary_folder
, 2 );
2480 msi_free( primary_folder
);
2482 msi_free( primary_key
);
2485 /* FIXME: check volume disk space */
2486 msi_set_property( package
->db
, szOutOfDiskSpace
, szZero
, -1 );
2487 msi_set_property( package
->db
, szOutOfNoRbDiskSpace
, szZero
, -1 );
2489 return ERROR_SUCCESS
;
2492 static BYTE
*parse_value( MSIPACKAGE
*package
, const WCHAR
*value
, DWORD len
, DWORD
*type
, DWORD
*size
)
2498 *size
= sizeof(WCHAR
);
2500 if ((data
= msi_alloc( *size
))) *(WCHAR
*)data
= 0;
2503 if (value
[0]=='#' && value
[1]!='#' && value
[1]!='%')
2509 LPWSTR deformated
= NULL
;
2512 deformat_string(package
, &value
[2], &deformated
);
2514 /* binary value type */
2518 *size
= (strlenW(ptr
)/2)+1;
2520 *size
= strlenW(ptr
)/2;
2522 data
= msi_alloc(*size
);
2528 /* if uneven pad with a zero in front */
2534 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2536 TRACE("Uneven byte count\n");
2544 data
[count
] = (BYTE
)strtol(byte
,NULL
,0);
2547 msi_free(deformated
);
2549 TRACE("Data %i bytes(%i)\n",*size
,count
);
2556 deformat_string(package
, &value
[1], &deformated
);
2559 *size
= sizeof(DWORD
);
2560 data
= msi_alloc(*size
);
2566 if ( (*p
< '0') || (*p
> '9') )
2572 if (deformated
[0] == '-')
2575 TRACE("DWORD %i\n",*(LPDWORD
)data
);
2577 msi_free(deformated
);
2582 const WCHAR
*ptr
= value
;
2585 if (value
[0] == '#')
2588 if (value
[1] == '%')
2591 *type
= REG_EXPAND_SZ
;
2594 data
= (BYTE
*)msi_strdupW( ptr
, len
);
2595 if (len
> strlenW( (const WCHAR
*)data
)) *type
= REG_MULTI_SZ
;
2596 *size
= (len
+ 1) * sizeof(WCHAR
);
2601 static const WCHAR
*get_root_key( MSIPACKAGE
*package
, INT root
, HKEY
*root_key
)
2608 if (msi_get_property_int( package
->db
, szAllUsers
, 0 ))
2610 *root_key
= HKEY_LOCAL_MACHINE
;
2615 *root_key
= HKEY_CURRENT_USER
;
2620 *root_key
= HKEY_CLASSES_ROOT
;
2624 *root_key
= HKEY_CURRENT_USER
;
2628 *root_key
= HKEY_LOCAL_MACHINE
;
2632 *root_key
= HKEY_USERS
;
2636 ERR("Unknown root %i\n", root
);
2643 static inline REGSAM
get_registry_view( const MSICOMPONENT
*comp
)
2646 if (is_wow64
|| is_64bit
)
2647 view
|= (comp
->Attributes
& msidbComponentAttributes64bit
) ? KEY_WOW64_64KEY
: KEY_WOW64_32KEY
;
2651 static HKEY
open_key( const MSICOMPONENT
*comp
, HKEY root
, const WCHAR
*path
, BOOL create
, REGSAM access
)
2653 WCHAR
*subkey
, *p
, *q
;
2654 HKEY hkey
, ret
= NULL
;
2657 access
|= get_registry_view( comp
);
2659 if (!(subkey
= strdupW( path
))) return NULL
;
2661 if ((q
= strchrW( p
, '\\' ))) *q
= 0;
2663 res
= RegCreateKeyExW( root
, subkey
, 0, NULL
, 0, access
, NULL
, &hkey
, NULL
);
2665 res
= RegOpenKeyExW( root
, subkey
, 0, access
, &hkey
);
2668 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey
), res
);
2674 ret
= open_key( comp
, hkey
, q
+ 1, create
, access
);
2675 RegCloseKey( hkey
);
2682 static BOOL
is_special_entry( const WCHAR
*name
)
2684 return (name
&& (name
[0] == '*' || name
[0] == '+') && !name
[1]);
2687 static WCHAR
**split_multi_string_values( const WCHAR
*str
, DWORD len
, DWORD
*count
)
2689 const WCHAR
*p
= str
;
2694 if (!str
) return NULL
;
2695 while ((p
- str
) < len
)
2697 p
+= strlenW( p
) + 1;
2700 if (!(ret
= msi_alloc( *count
* sizeof(WCHAR
*) ))) return NULL
;
2702 while ((p
- str
) < len
)
2704 if (!(ret
[i
] = strdupW( p
)))
2706 for (; i
>= 0; i
--) msi_free( ret
[i
] );
2710 p
+= strlenW( p
) + 1;
2716 static WCHAR
*flatten_multi_string_values( WCHAR
**left
, DWORD left_count
,
2717 WCHAR
**right
, DWORD right_count
, DWORD
*size
)
2722 *size
= sizeof(WCHAR
);
2723 for (i
= 0; i
< left_count
; i
++) *size
+= (strlenW( left
[i
] ) + 1) * sizeof(WCHAR
);
2724 for (i
= 0; i
< right_count
; i
++) *size
+= (strlenW( right
[i
] ) + 1) * sizeof(WCHAR
);
2726 if (!(ret
= p
= msi_alloc( *size
))) return NULL
;
2728 for (i
= 0; i
< left_count
; i
++)
2730 strcpyW( p
, left
[i
] );
2731 p
+= strlenW( p
) + 1;
2733 for (i
= 0; i
< right_count
; i
++)
2735 strcpyW( p
, right
[i
] );
2736 p
+= strlenW( p
) + 1;
2742 static DWORD
remove_duplicate_values( WCHAR
**old
, DWORD old_count
,
2743 WCHAR
**new, DWORD new_count
)
2745 DWORD ret
= old_count
;
2746 unsigned int i
, j
, k
;
2748 for (i
= 0; i
< new_count
; i
++)
2750 for (j
= 0; j
< old_count
; j
++)
2752 if (old
[j
] && !strcmpW( new[i
], old
[j
] ))
2755 for (k
= j
; k
< old_count
- 1; k
++) { old
[k
] = old
[k
+ 1]; }
2771 static WCHAR
*join_multi_string_values( enum join_op op
, WCHAR
**old
, DWORD old_count
,
2772 WCHAR
**new, DWORD new_count
, DWORD
*size
)
2776 case JOIN_OP_APPEND
:
2777 old_count
= remove_duplicate_values( old
, old_count
, new, new_count
);
2778 return flatten_multi_string_values( old
, old_count
, new, new_count
, size
);
2780 case JOIN_OP_PREPEND
:
2781 old_count
= remove_duplicate_values( old
, old_count
, new, new_count
);
2782 return flatten_multi_string_values( new, new_count
, old
, old_count
, size
);
2784 case JOIN_OP_REPLACE
:
2785 return flatten_multi_string_values( new, new_count
, NULL
, 0, size
);
2788 ERR("unhandled join op %u\n", op
);
2793 static BYTE
*build_multi_string_value( BYTE
*old_value
, DWORD old_size
,
2794 BYTE
*new_value
, DWORD new_size
, DWORD
*size
)
2796 DWORD i
, old_len
= 0, new_len
= 0, old_count
= 0, new_count
= 0;
2797 const WCHAR
*new_ptr
= NULL
, *old_ptr
= NULL
;
2798 enum join_op op
= JOIN_OP_REPLACE
;
2799 WCHAR
**old
= NULL
, **new = NULL
;
2802 if (new_size
/ sizeof(WCHAR
) - 1 > 1)
2804 new_ptr
= (const WCHAR
*)new_value
;
2805 new_len
= new_size
/ sizeof(WCHAR
) - 1;
2807 if (!new_ptr
[0] && new_ptr
[new_len
- 1])
2809 op
= JOIN_OP_APPEND
;
2813 else if (new_ptr
[0] && !new_ptr
[new_len
- 1])
2815 op
= JOIN_OP_PREPEND
;
2818 else if (new_len
> 2 && !new_ptr
[0] && !new_ptr
[new_len
- 1])
2820 op
= JOIN_OP_REPLACE
;
2824 new = split_multi_string_values( new_ptr
, new_len
, &new_count
);
2826 if (old_size
/ sizeof(WCHAR
) - 1 > 1)
2828 old_ptr
= (const WCHAR
*)old_value
;
2829 old_len
= old_size
/ sizeof(WCHAR
) - 1;
2830 old
= split_multi_string_values( old_ptr
, old_len
, &old_count
);
2832 ret
= (BYTE
*)join_multi_string_values( op
, old
, old_count
, new, new_count
, size
);
2833 for (i
= 0; i
< old_count
; i
++) msi_free( old
[i
] );
2834 for (i
= 0; i
< new_count
; i
++) msi_free( new[i
] );
2840 static BYTE
*reg_get_value( HKEY hkey
, const WCHAR
*name
, DWORD
*type
, DWORD
*size
)
2843 if (RegQueryValueExW( hkey
, name
, NULL
, NULL
, NULL
, size
)) return NULL
;
2844 if (!(ret
= msi_alloc( *size
))) return NULL
;
2845 RegQueryValueExW( hkey
, name
, NULL
, type
, ret
, size
);
2849 static UINT
ITERATE_WriteRegistryValues(MSIRECORD
*row
, LPVOID param
)
2851 MSIPACKAGE
*package
= param
;
2852 BYTE
*new_value
, *old_value
= NULL
;
2853 HKEY root_key
, hkey
;
2854 DWORD type
, old_type
, new_size
, old_size
= 0;
2855 LPWSTR deformated
, uikey
;
2856 const WCHAR
*szRoot
, *component
, *name
, *key
, *str
;
2860 BOOL check_first
= FALSE
;
2863 msi_ui_progress( package
, 2, REG_PROGRESS_VALUE
, 0, 0 );
2865 component
= MSI_RecordGetString(row
, 6);
2866 comp
= msi_get_loaded_component(package
,component
);
2868 return ERROR_SUCCESS
;
2870 comp
->Action
= msi_get_component_action( package
, comp
);
2871 if (comp
->Action
!= INSTALLSTATE_LOCAL
)
2873 TRACE("component not scheduled for installation %s\n", debugstr_w(component
));
2874 return ERROR_SUCCESS
;
2877 name
= MSI_RecordGetString(row
, 4);
2878 if( MSI_RecordIsNull(row
,5) && name
)
2880 /* null values can have special meanings */
2881 if (name
[0]=='-' && name
[1] == 0)
2882 return ERROR_SUCCESS
;
2883 if ((name
[0] == '+' || name
[0] == '*') && !name
[1])
2887 root
= MSI_RecordGetInteger(row
,2);
2888 key
= MSI_RecordGetString(row
, 3);
2890 szRoot
= get_root_key( package
, root
, &root_key
);
2892 return ERROR_SUCCESS
;
2894 deformat_string(package
, key
, &deformated
);
2895 uikey
= msi_alloc( (strlenW(deformated
) + strlenW(szRoot
) + 1) * sizeof(WCHAR
) );
2896 strcpyW(uikey
,szRoot
);
2897 strcatW(uikey
,deformated
);
2899 if (!(hkey
= open_key( comp
, root_key
, deformated
, TRUE
, KEY_QUERY_VALUE
| KEY_SET_VALUE
)))
2901 ERR("Could not create key %s\n", debugstr_w(deformated
));
2903 msi_free(deformated
);
2904 return ERROR_FUNCTION_FAILED
;
2906 msi_free( deformated
);
2907 str
= msi_record_get_string( row
, 5, NULL
);
2908 len
= deformat_string( package
, str
, &deformated
);
2909 new_value
= parse_value( package
, deformated
, len
, &type
, &new_size
);
2911 msi_free( deformated
);
2912 deformat_string(package
, name
, &deformated
);
2914 if (!is_special_entry( name
))
2916 old_value
= reg_get_value( hkey
, deformated
, &old_type
, &old_size
);
2917 if (type
== REG_MULTI_SZ
)
2920 if (old_value
&& old_type
!= REG_MULTI_SZ
)
2922 msi_free( old_value
);
2926 new = build_multi_string_value( old_value
, old_size
, new_value
, new_size
, &new_size
);
2927 msi_free( new_value
);
2932 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated
), debugstr_w(uikey
), type
);
2933 RegSetValueExW( hkey
, deformated
, 0, type
, new_value
, new_size
);
2935 else if (!old_value
)
2937 if (deformated
|| new_size
)
2939 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated
), debugstr_w(uikey
), type
);
2940 RegSetValueExW( hkey
, deformated
, 0, type
, new_value
, new_size
);
2943 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated
), debugstr_w(uikey
));
2947 uirow
= MSI_CreateRecord(3);
2948 MSI_RecordSetStringW(uirow
,2,deformated
);
2949 MSI_RecordSetStringW(uirow
,1,uikey
);
2950 if (type
== REG_SZ
|| type
== REG_EXPAND_SZ
)
2951 MSI_RecordSetStringW(uirow
, 3, (LPWSTR
)new_value
);
2952 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONDATA
, uirow
);
2953 msiobj_release( &uirow
->hdr
);
2955 msi_free(new_value
);
2956 msi_free(old_value
);
2957 msi_free(deformated
);
2960 return ERROR_SUCCESS
;
2963 static UINT
ACTION_WriteRegistryValues(MSIPACKAGE
*package
)
2965 static const WCHAR query
[] = {
2966 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2967 '`','R','e','g','i','s','t','r','y','`',0};
2971 rc
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
2972 if (rc
!= ERROR_SUCCESS
)
2973 return ERROR_SUCCESS
;
2975 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_WriteRegistryValues
, package
);
2976 msiobj_release(&view
->hdr
);
2980 static void delete_key( const MSICOMPONENT
*comp
, HKEY root
, const WCHAR
*path
)
2987 access
|= get_registry_view( comp
);
2989 if (!(subkey
= strdupW( path
))) return;
2992 if ((p
= strrchrW( subkey
, '\\' )))
2995 if (!p
[1]) continue; /* trailing backslash */
2996 hkey
= open_key( comp
, root
, subkey
, FALSE
, access
| READ_CONTROL
);
2998 res
= RegDeleteKeyExW( hkey
, p
+ 1, access
, 0 );
2999 RegCloseKey( hkey
);
3002 res
= RegDeleteKeyExW( root
, subkey
, access
, 0 );
3005 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey
), res
);
3012 static void delete_value( const MSICOMPONENT
*comp
, HKEY root
, const WCHAR
*path
, const WCHAR
*value
)
3016 DWORD num_subkeys
, num_values
;
3018 if ((hkey
= open_key( comp
, root
, path
, FALSE
, KEY_SET_VALUE
| KEY_QUERY_VALUE
)))
3020 if ((res
= RegDeleteValueW( hkey
, value
)))
3021 TRACE("failed to delete value %s (%d)\n", debugstr_w(value
), res
);
3023 res
= RegQueryInfoKeyW( hkey
, NULL
, NULL
, NULL
, &num_subkeys
, NULL
, NULL
, &num_values
,
3024 NULL
, NULL
, NULL
, NULL
);
3025 RegCloseKey( hkey
);
3026 if (!res
&& !num_subkeys
&& !num_values
)
3028 TRACE("removing empty key %s\n", debugstr_w(path
));
3029 delete_key( comp
, root
, path
);
3034 static void delete_tree( const MSICOMPONENT
*comp
, HKEY root
, const WCHAR
*path
)
3039 if (!(hkey
= open_key( comp
, root
, path
, FALSE
, KEY_ALL_ACCESS
))) return;
3040 res
= RegDeleteTreeW( hkey
, NULL
);
3041 if (res
) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path
), res
);
3042 delete_key( comp
, root
, path
);
3043 RegCloseKey( hkey
);
3046 static UINT
ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD
*row
, LPVOID param
)
3048 MSIPACKAGE
*package
= param
;
3049 LPCWSTR component
, name
, key_str
, root_key_str
;
3050 LPWSTR deformated_key
, deformated_name
, ui_key_str
;
3053 BOOL delete_key
= FALSE
;
3058 msi_ui_progress( package
, 2, REG_PROGRESS_VALUE
, 0, 0 );
3060 component
= MSI_RecordGetString( row
, 6 );
3061 comp
= msi_get_loaded_component( package
, component
);
3063 return ERROR_SUCCESS
;
3065 comp
->Action
= msi_get_component_action( package
, comp
);
3066 if (comp
->Action
!= INSTALLSTATE_ABSENT
)
3068 TRACE("component not scheduled for removal %s\n", debugstr_w(component
));
3069 return ERROR_SUCCESS
;
3072 name
= MSI_RecordGetString( row
, 4 );
3073 if (MSI_RecordIsNull( row
, 5 ) && name
)
3075 if (name
[0] == '+' && !name
[1])
3076 return ERROR_SUCCESS
;
3077 if ((name
[0] == '-' || name
[0] == '*') && !name
[1])
3084 root
= MSI_RecordGetInteger( row
, 2 );
3085 key_str
= MSI_RecordGetString( row
, 3 );
3087 root_key_str
= get_root_key( package
, root
, &hkey_root
);
3089 return ERROR_SUCCESS
;
3091 deformat_string( package
, key_str
, &deformated_key
);
3092 size
= strlenW( deformated_key
) + strlenW( root_key_str
) + 1;
3093 ui_key_str
= msi_alloc( size
* sizeof(WCHAR
) );
3094 strcpyW( ui_key_str
, root_key_str
);
3095 strcatW( ui_key_str
, deformated_key
);
3097 deformat_string( package
, name
, &deformated_name
);
3099 if (delete_key
) delete_tree( comp
, hkey_root
, deformated_key
);
3100 else delete_value( comp
, hkey_root
, deformated_key
, deformated_name
);
3101 msi_free( deformated_key
);
3103 uirow
= MSI_CreateRecord( 2 );
3104 MSI_RecordSetStringW( uirow
, 1, ui_key_str
);
3105 MSI_RecordSetStringW( uirow
, 2, deformated_name
);
3106 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONDATA
, uirow
);
3107 msiobj_release( &uirow
->hdr
);
3109 msi_free( ui_key_str
);
3110 msi_free( deformated_name
);
3111 return ERROR_SUCCESS
;
3114 static UINT
ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD
*row
, LPVOID param
)
3116 MSIPACKAGE
*package
= param
;
3117 LPCWSTR component
, name
, key_str
, root_key_str
;
3118 LPWSTR deformated_key
, deformated_name
, ui_key_str
;
3121 BOOL delete_key
= FALSE
;
3126 component
= MSI_RecordGetString( row
, 5 );
3127 comp
= msi_get_loaded_component( package
, component
);
3129 return ERROR_SUCCESS
;
3131 comp
->Action
= msi_get_component_action( package
, comp
);
3132 if (comp
->Action
!= INSTALLSTATE_LOCAL
)
3134 TRACE("component not scheduled for installation %s\n", debugstr_w(component
));
3135 return ERROR_SUCCESS
;
3138 if ((name
= MSI_RecordGetString( row
, 4 )))
3140 if (name
[0] == '-' && !name
[1])
3147 root
= MSI_RecordGetInteger( row
, 2 );
3148 key_str
= MSI_RecordGetString( row
, 3 );
3150 root_key_str
= get_root_key( package
, root
, &hkey_root
);
3152 return ERROR_SUCCESS
;
3154 deformat_string( package
, key_str
, &deformated_key
);
3155 size
= strlenW( deformated_key
) + strlenW( root_key_str
) + 1;
3156 ui_key_str
= msi_alloc( size
* sizeof(WCHAR
) );
3157 strcpyW( ui_key_str
, root_key_str
);
3158 strcatW( ui_key_str
, deformated_key
);
3160 deformat_string( package
, name
, &deformated_name
);
3162 if (delete_key
) delete_tree( comp
, hkey_root
, deformated_key
);
3163 else delete_value( comp
, hkey_root
, deformated_key
, deformated_name
);
3164 msi_free( deformated_key
);
3166 uirow
= MSI_CreateRecord( 2 );
3167 MSI_RecordSetStringW( uirow
, 1, ui_key_str
);
3168 MSI_RecordSetStringW( uirow
, 2, deformated_name
);
3169 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONDATA
, uirow
);
3170 msiobj_release( &uirow
->hdr
);
3172 msi_free( ui_key_str
);
3173 msi_free( deformated_name
);
3174 return ERROR_SUCCESS
;
3177 static UINT
ACTION_RemoveRegistryValues( MSIPACKAGE
*package
)
3179 static const WCHAR registry_query
[] = {
3180 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3181 '`','R','e','g','i','s','t','r','y','`',0};
3182 static const WCHAR remove_registry_query
[] = {
3183 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3184 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3188 rc
= MSI_DatabaseOpenViewW( package
->db
, registry_query
, &view
);
3189 if (rc
== ERROR_SUCCESS
)
3191 rc
= MSI_IterateRecords( view
, NULL
, ITERATE_RemoveRegistryValuesOnUninstall
, package
);
3192 msiobj_release( &view
->hdr
);
3193 if (rc
!= ERROR_SUCCESS
)
3196 rc
= MSI_DatabaseOpenViewW( package
->db
, remove_registry_query
, &view
);
3197 if (rc
== ERROR_SUCCESS
)
3199 rc
= MSI_IterateRecords( view
, NULL
, ITERATE_RemoveRegistryValuesOnInstall
, package
);
3200 msiobj_release( &view
->hdr
);
3201 if (rc
!= ERROR_SUCCESS
)
3204 return ERROR_SUCCESS
;
3207 static UINT
ACTION_InstallInitialize(MSIPACKAGE
*package
)
3209 return ERROR_SUCCESS
;
3213 static UINT
ACTION_InstallValidate(MSIPACKAGE
*package
)
3215 static const WCHAR query
[]= {
3216 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3217 '`','R','e','g','i','s','t','r','y','`',0