diff --git a/dlls/msi/action.c b/dlls/msi/action.c index a1729d3fcd3..037d509f131 100644 --- a/dlls/msi/action.c +++ b/dlls/msi/action.c @@ -1163,6 +1163,19 @@ static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp ) return ERROR_SUCCESS; } +static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child ) +{ + FeatureList *fl; + + fl = msi_alloc( sizeof(*fl) ); + if ( !fl ) + return ERROR_NOT_ENOUGH_MEMORY; + fl->feature = child; + list_add_tail( &parent->Children, &fl->entry ); + + return ERROR_SUCCESS; +} + static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param) { _ilfs* ilfs= (_ilfs*)param; @@ -1185,6 +1198,19 @@ static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param) return ERROR_SUCCESS; } +static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name ) +{ + MSIFEATURE *feature; + + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + if ( !lstrcmpW( feature->Feature, name ) ) + return feature; + } + + return NULL; +} + static UINT load_feature(MSIRECORD * row, LPVOID param) { MSIPACKAGE* package = (MSIPACKAGE*)param; @@ -1206,6 +1232,7 @@ static UINT load_feature(MSIRECORD * row, LPVOID param) if (!feature) return ERROR_NOT_ENOUGH_MEMORY; + list_init( &feature->Children ); list_init( &feature->Components ); feature->Feature = msi_dup_record_field( row, 1 ); @@ -1244,6 +1271,26 @@ static UINT load_feature(MSIRECORD * row, LPVOID param) return ERROR_SUCCESS; } +static UINT find_feature_children(MSIRECORD * row, LPVOID param) +{ + MSIPACKAGE* package = (MSIPACKAGE*)param; + MSIFEATURE *parent, *child; + + child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) ); + if (!child) + return ERROR_FUNCTION_FAILED; + + if (!child->Feature_Parent) + return ERROR_SUCCESS; + + parent = find_feature_by_name( package, child->Feature_Parent ); + if (!parent) + return ERROR_FUNCTION_FAILED; + + add_feature_child( parent, child ); + return ERROR_SUCCESS; +} + static UINT load_all_features( MSIPACKAGE *package ) { static const WCHAR query[] = { @@ -1261,7 +1308,12 @@ static UINT load_all_features( MSIPACKAGE *package ) return r; r = MSI_IterateRecords( view, NULL, load_feature, package ); + if (r != ERROR_SUCCESS) + return r; + + r = MSI_IterateRecords( view, NULL, find_feature_children, package ); msiobj_release( &view->hdr ); + return r; } @@ -1542,7 +1594,7 @@ static void ACTION_UpdateInstallStates(MSIPACKAGE *package) LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) { ComponentList *cl; - INSTALLSTATE res = -10; + INSTALLSTATE res = INSTALLSTATE_ABSENT; LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) { @@ -1554,7 +1606,7 @@ static void ACTION_UpdateInstallStates(MSIPACKAGE *package) break; } - if (res == -10) + if (res == INSTALLSTATE_ABSENT) res = comp->Installed; else { @@ -1693,6 +1745,21 @@ UINT MSI_SetFeatureStates(MSIPACKAGE *package) } } } + + /* disable child features of unselected parent features */ + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + FeatureList *fl; + + if (feature->Level > 0 && feature->Level <= install_level) + continue; + + LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry ) + { + fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN; + fl->feature->Action = INSTALLSTATE_UNKNOWN; + } + } } else { diff --git a/dlls/msi/action.h b/dlls/msi/action.h index 785c4e3fda2..ae7de8284c4 100644 --- a/dlls/msi/action.h +++ b/dlls/msi/action.h @@ -39,6 +39,7 @@ typedef struct tagMSIFEATURE INSTALLSTATE ActionRequest; INSTALLSTATE Action; + struct list Children; struct list Components; INT Cost; @@ -74,6 +75,12 @@ typedef struct tagComponentList MSICOMPONENT *component; } ComponentList; +typedef struct tagFeatureList +{ + struct list entry; + MSIFEATURE *feature; +} FeatureList; + typedef struct tagMSIFOLDER { struct list entry; diff --git a/dlls/msi/helpers.c b/dlls/msi/helpers.c index 8c957a2b78f..50fc9925222 100644 --- a/dlls/msi/helpers.c +++ b/dlls/msi/helpers.c @@ -404,6 +404,13 @@ static void free_feature( MSIFEATURE *feature ) { struct list *item, *cursor; + LIST_FOR_EACH_SAFE( item, cursor, &feature->Children ) + { + FeatureList *fl = LIST_ENTRY( item, FeatureList, entry ); + list_remove( &fl->entry ); + msi_free( fl ); + } + LIST_FOR_EACH_SAFE( item, cursor, &feature->Components ) { ComponentList *cl = LIST_ENTRY( item, ComponentList, entry ); diff --git a/dlls/msi/tests/package.c b/dlls/msi/tests/package.c index 4e9d40a0f35..cd55f890a59 100644 --- a/dlls/msi/tests/package.c +++ b/dlls/msi/tests/package.c @@ -2366,6 +2366,14 @@ static void test_featureparents(void) r = add_feature_entry( hdb, "'orion', '', '', '', 2, 1, '', 0" ); ok( r == ERROR_SUCCESS, "cannot add feature: %d\n", r ); + /* disabled because of install level */ + r = add_feature_entry( hdb, "'waters', '', '', '', 15, 101, '', 9" ); + ok( r == ERROR_SUCCESS, "cannot add feature: %d\n", r ); + + /* child feature of disabled feature */ + r = add_feature_entry( hdb, "'bayer', 'waters', '', '', 14, 1, '', 9" ); + ok( r == ERROR_SUCCESS, "cannot add feature: %d\n", r ); + /* msidbFeatureAttributesFavorLocal:msidbComponentAttributesLocalOnly */ r = add_component_entry( hdb, "'leo', '', 'TARGETDIR', 0, '', 'leo_file'" ); ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); @@ -2506,6 +2514,20 @@ static void test_featureparents(void) ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action); + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "waters", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "bayer", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "leo", &state, &action);