From: Vibhav Pant vibhavp@gmail.com
Support filter expressions consisting of basic comparison operators. --- dlls/cfgmgr32/main.c | 250 ++++++++++++++++++++++++++++++--- dlls/cfgmgr32/tests/cfgmgr32.c | 10 +- 2 files changed, 238 insertions(+), 22 deletions(-)
diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index d11b1fd5f96..656d1988503 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -318,6 +318,173 @@ static BOOL dev_properties_append( DEVPROPERTY **properties, ULONG *props_len, c return TRUE; }
+static const char *debugstr_DEVPROPKEY( const DEVPROPKEY *key ) +{ + if (!key) return "(null)"; + return wine_dbg_sprintf( "{%s, %04lx}", debugstr_guid( &key->fmtid ), key->pid ); +} + +static const char *debugstr_DEVPROPCOMPKEY( const DEVPROPCOMPKEY *key ) +{ + if (!key) return "(null)"; + return wine_dbg_sprintf( "{%s, %d, %s}", debugstr_DEVPROPKEY( &key->Key ), key->Store, + debugstr_w( key->LocaleName ) ); +} + +static const char *debugstr_DEV_OBJECT( const DEV_OBJECT *obj ) +{ + if (!obj) return "(null)"; + return wine_dbg_sprintf( "{%d, %s, %lu, %p}", obj->ObjectType, debugstr_w( obj->pszObjectId ), obj->cPropertyCount, + obj->pProperties ); +} + +static int devproperty_compare( const void *p1, const void *p2 ) +{ + const DEVPROPCOMPKEY *key1 = &((DEVPROPERTY *)p1)->CompKey; + const DEVPROPCOMPKEY *key2 = &((DEVPROPERTY *)p2)->CompKey; + int cmp = memcmp( key1, key2, offsetof( DEVPROPCOMPKEY, LocaleName )); + + if (cmp) + return cmp; + if (key1->LocaleName == key2->LocaleName) + return 0; + if (!key1->LocaleName) + return -1; + if (!key2->LocaleName) + return 1; + return wcsicmp( key1->LocaleName, key2->LocaleName ); +} + +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 "(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( const DEV_OBJECT *obj, const DEVPROP_FILTER_EXPRESSION *filter ) +{ + const DEVPROPERTY *cmp_prop = &filter->Property; + DEVPROP_OPERATOR op = filter->Operator; + const DEVPROPERTY *prop = NULL; + BOOL ret = FALSE; + + TRACE( "(%s, %s)\n", debugstr_DEV_OBJECT( obj ), 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: + prop = bsearch( &filter->Property.CompKey, obj->pProperties, obj->cPropertyCount, sizeof( *obj->pProperties ), + devproperty_compare ); + 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; + + prop = bsearch( &filter->Property.CompKey, obj->pProperties, obj->cPropertyCount, sizeof( *obj->pProperties ), + devproperty_compare ); + 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; +} + +/* 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, ULONG filters_len, + const DEVPROP_FILTER_EXPRESSION *filters ) +{ + HRESULT hr = S_OK; + ULONG i; + + TRACE( "(%s, %lu, %p)\n", debugstr_DEV_OBJECT( obj ), filters_len, filters ); + + if (!filters_len) + return S_OK; + + /* 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; + break; + } + 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( obj, filter ); + if (FAILED( hr ) || hr == S_FALSE) + break; + } + } + + return hr; +} + static HRESULT stack_push( DEVPROP_OPERATOR **stack, ULONG *len, DEVPROP_OPERATOR op ) { DEVPROP_OPERATOR *tmp; @@ -488,7 +655,7 @@ static HRESULT devprop_filters_validate( ULONG filters_len, const DEVPROP_FILTER }
static HRESULT dev_object_iface_get_props( DEV_OBJECT *obj, HDEVINFO set, SP_DEVICE_INTERFACE_DATA *iface_data, - ULONG props_len, const DEVPROPCOMPKEY *props, BOOL all_props ) + ULONG props_len, const DEVPROPCOMPKEY *props, BOOL all_props, BOOL sort ) { DEVPROPKEY *all_keys = NULL; DWORD keys_len = 0, i = 0; @@ -570,13 +737,51 @@ done: obj->cPropertyCount = 0; obj->pProperties = NULL; } + else if (sort) /* Sort properties by DEVPROPCOMPKEY for faster filter evaluation. */ + qsort( (DEVPROPERTY *)obj->pProperties, obj->cPropertyCount, sizeof( *obj->pProperties ), devproperty_compare ); return hr; }
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 void dev_object_remove_unwanted_props( DEV_OBJECT *obj, ULONG keys_len, const DEVPROPCOMPKEY *keys_wanted ) +{ + DEVPROPERTY *props = (DEVPROPERTY *)obj->pProperties; + ULONG i = 0, j; + + if (!keys_len) + { + DevFreeObjectProperties( obj->cPropertyCount, obj->pProperties ); + obj->cPropertyCount = 0; + obj->pProperties = NULL; + } + + while (i < obj->cPropertyCount) + { + BOOL found = FALSE; + + for (j = 0; j < keys_len; j++) + { + if (IsEqualDevPropCompKey( props[i].CompKey, keys_wanted[j] )) + { + found = TRUE; + break; + } + } + if (!found) + { + free( obj->pProperties[i].Buffer ); + props[i] = props[obj->cPropertyCount - 1]; + obj->cPropertyCount--; + } + else + i++; + } +} + +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; @@ -645,15 +850,33 @@ 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 we're also filtering objects, get all properties for this object. Once the filters have been + * evaluated, free properties that have not been requested, and set cPropertyCount to props_len. */ + if (filters_len) + hr = dev_object_iface_get_props( &obj, set, &iface, 0, NULL, TRUE, TRUE ); + else + hr = dev_object_iface_get_props( &obj, set, &iface, props_len, props, all_props, FALSE ); + if (SUCCEEDED( hr )) + { + if (filters_len) + { + hr = devprop_filter_matches_object( &obj, filters_len, filters ); + /* Shrink pProperties to only the desired ones, unless DevQueryFlagAllProperties is set. */ + if (!all_props) + dev_object_remove_unwanted_props( &obj, props_len, props ); + } + 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 @@ -706,15 +929,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; @@ -1068,7 +1290,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) @@ -1266,7 +1488,7 @@ HRESULT WINAPI DevGetObjectPropertiesEx( DEV_OBJECT_TYPE type, const WCHAR *id, return HRESULT_FROM_WIN32(err == ERROR_NO_SUCH_DEVICE_INTERFACE ? ERROR_FILE_NOT_FOUND : err); }
- hr = dev_object_iface_get_props( &obj, set, &iface, props_len, props, !!(flags & DevQueryFlagAllProperties) ); + hr = dev_object_iface_get_props( &obj, set, &iface, props_len, props, !!(flags & DevQueryFlagAllProperties), FALSE ); *buf = obj.pProperties; *buf_len = obj.cPropertyCount; break; @@ -1279,12 +1501,6 @@ HRESULT WINAPI DevGetObjectPropertiesEx( DEV_OBJECT_TYPE type, const WCHAR *id, return hr; }
-static const char *debugstr_DEVPROPKEY( const DEVPROPKEY *key ) -{ - if (!key) return "(null)"; - return wine_dbg_sprintf( "{%s, %04lx}", debugstr_guid( &key->fmtid ), key->pid ); -} - const DEVPROPERTY *WINAPI DevFindProperty( const DEVPROPKEY *key, DEVPROPSTORE store, const WCHAR *locale, ULONG props_len, const DEVPROPERTY *props ) { diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index ff6e04e35b2..1e69417d356 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -971,8 +971,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. */ @@ -980,8 +980,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; @@ -1181,7 +1181,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++)