From: Vibhav Pant vibhavp@gmail.com
Support filter expressions consisting of basic comparison operators. --- dlls/cfgmgr32/main.c | 229 +++++++++++++++++++++++++++++++-- dlls/cfgmgr32/tests/cfgmgr32.c | 10 +- 2 files changed, 226 insertions(+), 13 deletions(-)
diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index 9563419465d..b62e8bc284f 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -318,6 +318,212 @@ static BOOL dev_properties_append( DEVPROPERTY **properties, ULONG *props_len, c return TRUE; }
+struct devprop_filter_context +{ + const DEV_OBJECT *obj; + /* If true, don't use the property cache, as no other properties other than in obj->pProperties exist. */ + BOOL all_props; + ULONG props_cache_len; + DEVPROPERTY *props_cache; +}; + +static const char *debugstr_DEVPROPCOMPKEY( const DEVPROPCOMPKEY *key ) +{ + if (!key) return wine_dbg_sprintf("(null)"); + return wine_dbg_sprintf( "{{%s, %#lx}, %d, %s}", debugstr_guid( &key->Key.fmtid ), key->Key.pid, key->Store, + debugstr_w( key->LocaleName ) ); +} + +static HRESULT devprop_filter_context_find_prop( struct devprop_filter_context *ctx, const DEVPROPCOMPKEY *key, + const DEVPROPERTY **prop ) +{ + TRACE( "(%p, %s, %p)\n", ctx, debugstr_DEVPROPCOMPKEY( key ), prop ); + + /* If the object already has some properties, look through them first. */ + *prop = DevFindProperty( &key->Key, key->Store, key->LocaleName, ctx->obj->cPropertyCount, ctx->obj->pProperties ); + if (*prop || ctx->all_props) + return S_OK; + *prop = DevFindProperty( &key->Key, key->Store, key->LocaleName, ctx->props_cache_len, ctx->props_cache ); + if (!*prop) + { + const DEVPROPERTY *props = NULL; + DEVPROPERTY *tmp; + ULONG props_len = 0; + HRESULT hr; + void *buf; + + hr = DevGetObjectProperties( ctx->obj->ObjectType, ctx->obj->pszObjectId, DevQueryFlagNone, 1, key, &props_len, + &props ); + if (FAILED( hr )) + return hr; + /* Even if this property does not exist, we get a property value with Type set to DEVPROP_TYPE_EMPTY. */ + if (!(buf = calloc( 1, props[0].BufferSize ))) + return E_OUTOFMEMORY; + tmp = realloc( ctx->props_cache, (ctx->props_cache_len + 1) * sizeof( *ctx->props_cache ) ); + if (!tmp) + { + free( buf ); + return E_OUTOFMEMORY; + } + + ctx->props_cache = tmp; + tmp = &ctx->props_cache[ctx->props_cache_len++]; + *tmp = props[0]; + tmp->Buffer = buf; + memcpy( tmp->Buffer, props[0].Buffer, tmp->BufferSize ); + DevFreeObjectProperties( props_len, props ); + *prop = tmp; + } + + return S_OK; +} + +static const char *debugstr_DEVPROP_OPERATOR( DEVPROP_OPERATOR op ) +{ + DWORD compare = op & DEVPROP_OPERATOR_MASK_EVAL; + DWORD list = op & DEVPROP_OPERATOR_MASK_LIST; + DWORD modifier = op & DEVPROP_OPERATOR_MASK_MODIFIER; + DWORD logical = op & DEVPROP_OPERATOR_MASK_LOGICAL; + DWORD array = op & DEVPROP_OPERATOR_MASK_ARRAY; + + return wine_dbg_sprintf( "(%#lx|%#lx|%#lx|%#lx|%#lx)", list, array, modifier, compare, logical ); +} + + +static const char *debugstr_DEVPROP_FILTER_EXPRESSION( const DEVPROP_FILTER_EXPRESSION *filter ) +{ + if (!filter) return wine_dbg_sprintf("(null)"); + return wine_dbg_sprintf( "{%s, {%s, %#lx, %lu, %p}}", debugstr_DEVPROP_OPERATOR( filter->Operator ), + debugstr_DEVPROPCOMPKEY( &filter->Property.CompKey ), filter->Property.Type, + filter->Property.BufferSize, filter->Property.Buffer ); +} + +/* Evaluate a filter expression containing comparison operator. */ +static HRESULT devprop_filter_eval_compare( struct devprop_filter_context *ctx, const DEVPROP_FILTER_EXPRESSION *filter ) +{ + const DEVPROPERTY *cmp_prop = &filter->Property; + DEVPROP_OPERATOR op = filter->Operator; + const DEVPROPERTY *prop = NULL; + BOOL ret = FALSE; + HRESULT hr; + + TRACE( "(%p, %s)\n", ctx, debugstr_DEVPROP_FILTER_EXPRESSION( filter ) ); + + if ((op & DEVPROP_OPERATOR_MASK_MODIFIER) & ~(DEVPROP_OPERATOR_MODIFIER_NOT | DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE)) + return E_INVALIDARG; + + switch (filter->Operator & DEVPROP_OPERATOR_MASK_EVAL) + { + case DEVPROP_OPERATOR_EXISTS: + hr = devprop_filter_context_find_prop( ctx, &filter->Property.CompKey, &prop ); + if (FAILED( hr )) + return hr; + ret = prop && prop->Type != DEVPROP_TYPE_EMPTY; + break; + case DEVPROP_OPERATOR_EQUALS: + case DEVPROP_OPERATOR_LESS_THAN: + case DEVPROP_OPERATOR_GREATER_THAN: + case DEVPROP_OPERATOR_LESS_THAN_EQUALS: + case DEVPROP_OPERATOR_GREATER_THAN_EQUALS: + { + int cmp = 0; + + hr = devprop_filter_context_find_prop( ctx, &filter->Property.CompKey, &prop ); + if (FAILED( hr )) + return hr; + if (prop && cmp_prop->Type == prop->Type && cmp_prop->BufferSize == prop->BufferSize) + { + switch (prop->Type) + { + case DEVPROP_TYPE_STRING: + cmp = op & DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE ? wcsicmp( prop->Buffer, cmp_prop->Buffer ) + : wcscmp( prop->Buffer, cmp_prop->Buffer ); + break; + default: + cmp = memcmp( prop->Buffer, cmp_prop->Buffer, prop->BufferSize ); + break; + } + if (op == DEVPROP_OPERATOR_EQUALS) + ret = !cmp; + else if (op & DEVPROP_OPERATOR_EQUALS && !cmp) + ret = TRUE; + else + ret = (op & DEVPROP_OPERATOR_LESS_THAN) ? cmp < 0 : cmp > 0; + } + break; + } + case DEVPROP_OPERATOR_BITWISE_AND: + case DEVPROP_OPERATOR_BITWISE_OR: + case DEVPROP_OPERATOR_BEGINS_WITH: + case DEVPROP_OPERATOR_ENDS_WITH: + case DEVPROP_OPERATOR_CONTAINS: + default: + FIXME( "Unsupported operator: %s", debugstr_DEVPROP_OPERATOR( filter->Operator & DEVPROP_OPERATOR_MASK_EVAL ) ); + return S_OK; + } + + if (op & DEVPROP_OPERATOR_MODIFIER_NOT) + ret = !ret; + return ret ? S_OK : S_FALSE; +} + +static const char *debugstr_DEV_OBJECT( const DEV_OBJECT *obj ) +{ + if (!obj) return wine_dbg_sprintf("(null)"); + return wine_dbg_sprintf( "{%d, %s, %lu, %p}", obj->ObjectType, debugstr_w( obj->pszObjectId ), obj->cPropertyCount, + obj->pProperties ); +} + +/* Return S_OK if the specified filter expressions match the object, S_FALSE if it doesn't. */ +static HRESULT devprop_filter_matches_object( const DEV_OBJECT *obj, BOOL all_props, ULONG filters_len, + const DEVPROP_FILTER_EXPRESSION *filters ) +{ + struct devprop_filter_context ctx = {0}; + HRESULT hr; + ULONG i; + + TRACE( "(%s, %d, %lu, %p)\n", debugstr_DEV_OBJECT( obj ), all_props, filters_len, filters ); + + if (!filters_len) + return S_OK; + + ctx.obj = obj; + ctx.all_props = all_props; + + /* By default, the evaluation is performed by AND-ing all individual filter expressions. */ + for (i = 0; i < filters_len; i++) + { + const DEVPROP_FILTER_EXPRESSION *filter = &filters[i]; + DEVPROP_OPERATOR op = filter->Operator; + + if (op == DEVPROP_OPERATOR_NONE) + { + hr = S_FALSE; + goto done; + } + if (op & (DEVPROP_OPERATOR_MASK_LIST | DEVPROP_OPERATOR_MASK_ARRAY)) + { + FIXME( "Unsupported list/array operator: %s\n", debugstr_DEVPROP_OPERATOR( op ) ); + continue; + } + if (op & DEVPROP_OPERATOR_MASK_LOGICAL) + { + FIXME( "Unsupported logical operator: %s\n", debugstr_DEVPROP_OPERATOR( op ) ); + continue; + } + if (op & DEVPROP_OPERATOR_MASK_EVAL) + { + hr = devprop_filter_eval_compare( &ctx, filter ); + if (FAILED( hr ) || hr == S_FALSE) + goto done; + } + } + +done: + DevFreeObjectProperties( ctx.props_cache_len, ctx.props_cache ); + return hr; +} + static HRESULT stack_push( DEVPROP_OPERATOR **stack, ULONG *len, DEVPROP_OPERATOR op ) { DEVPROP_OPERATOR *tmp; @@ -569,8 +775,9 @@ done:
typedef HRESULT (*enum_device_object_cb)( DEV_OBJECT object, void *context );
-static HRESULT enum_dev_objects( DEV_OBJECT_TYPE type, ULONG props_len, const DEVPROPCOMPKEY *props, - BOOL all_props, enum_device_object_cb callback, void *data ) +static HRESULT enum_dev_objects( DEV_OBJECT_TYPE type, ULONG props_len, const DEVPROPCOMPKEY *props, BOOL all_props, + ULONG filters_len, const DEVPROP_FILTER_EXPRESSION *filters, + enum_device_object_cb callback, void *data ) { HKEY iface_key; HRESULT hr = S_OK; @@ -640,14 +847,21 @@ static HRESULT enum_dev_objects( DEV_OBJECT_TYPE type, ULONG props_len, const DE obj.ObjectType = type; obj.pszObjectId = detail->DevicePath; if (SUCCEEDED( (hr = dev_object_iface_get_props( &obj, set, &iface, props_len, props, all_props )) )) - hr = callback( obj, data ); + { + if (filters_len) + hr = devprop_filter_matches_object( &obj, all_props, filters_len, filters ); + if (hr == S_OK) + hr = callback( obj, data ); + else + DevFreeObjectProperties( obj.cPropertyCount, obj.pProperties ); + } }
if (set != INVALID_HANDLE_VALUE) SetupDiDestroyDeviceInfoList( set ); } RegCloseKey( iface_key ); - return hr; + return SUCCEEDED( hr ) ? S_OK : hr; }
struct objects_list @@ -700,15 +914,14 @@ HRESULT WINAPI DevGetObjectsEx( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_l || (props_len && (flags & DevQueryFlagAllProperties)) || FAILED( devprop_filters_validate( filters_len, filters ) )) return E_INVALIDARG; - if (filters) - FIXME( "Query filters are not supported!\n" ); if (params) FIXME( "Query parameters are not supported!\n" );
*objs = NULL; *objs_len = 0;
- hr = enum_dev_objects( type, props_len, props, !!(flags & DevQueryFlagAllProperties), dev_objects_append, &objects ); + hr = enum_dev_objects( type, props_len, props, !!(flags & DevQueryFlagAllProperties), filters_len, filters, + dev_objects_append, &objects ); if (SUCCEEDED( hr )) { *objs = objects.objects; @@ -1062,7 +1275,7 @@ static void CALLBACK device_query_enum_objects_async( TP_CALLBACK_INSTANCE *inst
if (!ctx->filters) hr = enum_dev_objects( ctx->type, ctx->prop_keys_len, ctx->prop_keys, !!(ctx->flags & DevQueryFlagAllProperties), - device_query_context_add_object, ctx ); + 0, NULL, device_query_context_add_object, ctx );
EnterCriticalSection( &ctx->cs ); if (ctx->state == DevQueryStateClosed) diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index 0af12233a38..a43e83b34f8 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -969,8 +969,8 @@ static void test_DevGetObjects( void ) objects = (DEV_OBJECT *)0xdeadbeef; hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 0, NULL, 1, &filters[0], &len, &objects ); ok( hr == S_OK, "got hr %#lx\n", hr ); - todo_wine ok( !len, "got len %lu\n", len ); - todo_wine ok( !objects, "got objects %p\n", objects ); + ok( !len, "got len %lu\n", len ); + ok( !objects, "got objects %p\n", objects );
filters[1] = valid_filter; /* DEVPROP_OPERATOR_NONE preceeding the next filter expression has the same result. */ @@ -978,8 +978,8 @@ static void test_DevGetObjects( void ) objects = (DEV_OBJECT *)0xdeadbeef; hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 0, NULL, 2, filters, &len, &objects ); ok( hr == S_OK, "got hr %#lx\n", hr ); - todo_wine ok( !len, "got len %lu\n", len ); - todo_wine ok( !objects, "got objects %p\n", objects ); + ok( !len, "got len %lu\n", len ); + ok( !objects, "got objects %p\n", objects );
/* However, filter expressions are still validated. */ filters[1].Property.Buffer = NULL; @@ -1152,7 +1152,7 @@ static void test_DevGetObjects( void ) * unique pair, so there should only be one object. */ if (test_cases[i].object_type == DevObjectTypeDeviceInterface || test_cases[i].object_type == DevObjectTypeDeviceInterfaceDisplay) - todo_wine ok( len2 == 1, "got len2 %lu\n", len2 ); + ok( len2 == 1, "got len2 %lu\n", len2 ); else ok( len2, "got len2 %lu\n", len2 ); for (k = 0; k < len2; k++)