From: Vibhav Pant vibhavp@gmail.com
--- dlls/windows.devices.enumeration/Makefile.in | 2 +- dlls/windows.devices.enumeration/aqs.c | 210 +++++++++++++++++- dlls/windows.devices.enumeration/aqs.h | 3 +- dlls/windows.devices.enumeration/main.c | 91 +++++++- .../tests/devices.c | 4 +- 5 files changed, 300 insertions(+), 10 deletions(-)
diff --git a/dlls/windows.devices.enumeration/Makefile.in b/dlls/windows.devices.enumeration/Makefile.in index 8ca6eb7dedb..525c55d353b 100644 --- a/dlls/windows.devices.enumeration/Makefile.in +++ b/dlls/windows.devices.enumeration/Makefile.in @@ -1,5 +1,5 @@ MODULE = windows.devices.enumeration.dll -IMPORTS = cfgmgr32 combase uuid +IMPORTS = cfgmgr32 combase propsys uuid
SOURCES = \ access.c \ diff --git a/dlls/windows.devices.enumeration/aqs.c b/dlls/windows.devices.enumeration/aqs.c index f557e98e25c..e8bcff07ef6 100644 --- a/dlls/windows.devices.enumeration/aqs.c +++ b/dlls/windows.devices.enumeration/aqs.c @@ -199,12 +199,16 @@ UINT aqs_lex( void *p, struct aqs_parser *parser ) return token; }
-HRESULT aqs_parse_query( const WCHAR *str ) +static HRESULT aqs_expr_to_filters( const struct aqs_expr *expr, DEVPROP_FILTER_EXPRESSION **filters, ULONG *filters_len ); + +HRESULT aqs_parse_query( const WCHAR *str, DEVPROP_FILTER_EXPRESSION **filters, ULONG *filters_len ) { struct aqs_parser parser = {0}; HRESULT hr; int ret;
+ *filters = NULL; + *filters_len = 0; if (!wcslen( str )) return S_OK;
parser.query = str; @@ -212,8 +216,11 @@ HRESULT aqs_parse_query( const WCHAR *str ) return hr; aqs_debug = TRACE_ON( aqs ); ret = aqs_parse( &parser ); - if (!ret) - FIXME( "semi-stub!\n" ); + if (!ret && parser.expr) + { + hr = aqs_expr_to_filters( parser.expr, filters, filters_len ); + free_expr( parser.expr ); + } else hr = FAILED(parser.error) ? parser.error : E_INVALIDARG;
@@ -319,3 +326,200 @@ void free_expr( struct aqs_expr *expr ) } free( expr ); } + +static HRESULT propval_to_devprop( const PROPVARIANT *comparand_val, IPropertyDescription *desc, DEVPROPERTY *devprop ) +{ + union + { + BYTE byte; + UINT16 int16; + UINT32 int32; + UINT64 int64; + GUID guid; + DEVPROP_BOOLEAN boolean; + } devprop_basic_val = {0}; + PROPVARIANT tmp = {0}; + VARTYPE prop_vt; + HRESULT hr; + + if(FAILED(hr = IPropertyDescription_GetPropertyKey( desc, (PROPERTYKEY *)&devprop->CompKey.Key))) return hr; + if (FAILED(hr = IPropertyDescription_GetPropertyType( desc, &prop_vt ))) return hr; + if (comparand_val->vt != VT_EMPTY) + { + if (FAILED(hr = PropVariantChangeType( &tmp, comparand_val, 0, prop_vt ))) + return (hr == E_FAIL || hr == E_NOTIMPL) ? E_INVALIDARG : hr; + switch (prop_vt) + { + case VT_CLSID: + devprop->Type = DEVPROP_TYPE_GUID; + devprop_basic_val.guid = *tmp.puuid; + devprop->BufferSize = sizeof( devprop_basic_val.guid ); + break; + case VT_I1: + case VT_UI1: + devprop->Type = prop_vt == VT_I1 ? DEVPROP_TYPE_SBYTE : DEVPROP_TYPE_BYTE; + devprop_basic_val.byte = tmp.bVal; + devprop->BufferSize = sizeof( devprop_basic_val.byte ); + break; + case VT_BOOL: + devprop->Type = DEVPROP_TYPE_BOOLEAN; + devprop_basic_val.boolean = tmp.boolVal ? DEVPROP_TRUE : DEVPROP_FALSE; + devprop->BufferSize = sizeof( devprop_basic_val.boolean ); + break; + case VT_I2: + case VT_UI2: + devprop->Type = prop_vt == VT_I2 ? DEVPROP_TYPE_INT16 : DEVPROP_TYPE_UINT16; + devprop_basic_val.int16 = tmp.uiVal; + devprop->BufferSize = sizeof( devprop_basic_val.int16 ); + break; + case VT_I4: + case VT_UI4: + devprop->Type = prop_vt == VT_I4 ? DEVPROP_TYPE_INT32 : DEVPROP_TYPE_UINT32; + devprop_basic_val.int32 = tmp.ulVal; + devprop->BufferSize = sizeof( devprop_basic_val.int32 ); + break; + case VT_I8: + case VT_UI8: + devprop->Type = prop_vt == VT_I8 ? DEVPROP_TYPE_INT64 : DEVPROP_TYPE_UINT64; + devprop_basic_val.int64 = tmp.uhVal.QuadPart; + devprop->BufferSize = sizeof( devprop_basic_val.int64 ); + break; + case VT_LPWSTR: + devprop->Type = DEVPROP_TYPE_STRING; + devprop->BufferSize = (wcslen( tmp.pwszVal ) + 1) * sizeof( WCHAR ); + break; + default: + FIXME( "Unsupported property VARTYPE %d, treating comparand as string.\n", prop_vt ); + PropVariantClear( &tmp ); + if (FAILED(hr = PropVariantChangeType( &tmp, comparand_val, 0, VT_LPWSTR ))) + return (hr == E_FAIL || hr == E_NOTIMPL) ? E_INVALIDARG : hr; + devprop->Type = DEVPROP_TYPE_STRING; + devprop->BufferSize = (wcslen( tmp.pwszVal ) + 1) * sizeof( WCHAR ); + break; + } + } + else + { + devprop->Type = DEVPROP_TYPE_EMPTY; + devprop->BufferSize = 0; + } + + devprop->CompKey.Store = DEVPROP_STORE_SYSTEM; + devprop->CompKey.LocaleName = NULL; + devprop->Buffer = NULL; + if (devprop->BufferSize && !(devprop->Buffer = calloc( 1, devprop->BufferSize ))) + { + PropVariantClear( &tmp ); + return E_OUTOFMEMORY; + } + switch (devprop->Type) + { + case DEVPROP_TYPE_STRING: + wcscpy( devprop->Buffer, tmp.pwszVal ); + break; + case DEVPROP_TYPE_EMPTY: + break; + default: + memcpy( devprop->Buffer, &devprop_basic_val, devprop->BufferSize ); + break; + } + PropVariantClear( &tmp ); + return S_OK; +} + +static HRESULT filters_append_op( DEVPROP_FILTER_EXPRESSION **filters, ULONG *len, ULONG len_hint, DEVPROP_OPERATOR op ) +{ + DEVPROP_FILTER_EXPRESSION *tmp; + + if (!(tmp = realloc( *filters, sizeof( *tmp ) * (len_hint ? len_hint : (*len + 1)) ))) + return E_OUTOFMEMORY; + + *filters = tmp; + tmp = &(*filters)[*len]; + memset( tmp, 0, sizeof( *tmp ) ); + tmp->Operator = op; + *len += 1; + return S_OK; +} + +static HRESULT devprop_filters_append_expr( const struct aqs_expr *expr, DEVPROP_FILTER_EXPRESSION **filters, ULONG *len ) +{ + static const DEVPROP_OPERATOR boolean_ops[] = { + DEVPROP_OPERATOR_AND_OPEN, + DEVPROP_OPERATOR_OR_OPEN, + DEVPROP_OPERATOR_NOT_OPEN, + }; + static const DEVPROP_OPERATOR compare_ops[] = { + DEVPROP_OPERATOR_EQUALS, + DEVPROP_OPERATOR_NOT_EQUALS, + DEVPROP_OPERATOR_GREATER_THAN, + DEVPROP_OPERATOR_GREATER_THAN_EQUALS, + DEVPROP_OPERATOR_LESS_THAN, + DEVPROP_OPERATOR_LESS_THAN_EQUALS, + DEVPROP_OPERATOR_BEGINS_WITH_IGNORE_CASE, + DEVPROP_OPERATOR_ENDS_WITH_IGNORE_CASE, + DEVPROP_OPERATOR_CONTAINS_IGNORE_CASE, + DEVPROP_OPERATOR_MODIFIER_NOT | DEVPROP_OPERATOR_CONTAINS_IGNORE_CASE, + DEVPROP_OPERATOR_CONTAINS, + }; + HRESULT hr = S_OK; + + if (!expr) return S_OK; + + switch (expr->op_type) + { + case OP_TYPE_BOOLEAN: + { + const struct expr_boolean *boolean = &expr->u.boolean; + DEVPROP_OPERATOR open = boolean_ops[boolean->op]; + DEVPROP_OPERATOR close = open + (DEVPROP_OPERATOR_AND_CLOSE - DEVPROP_OPERATOR_AND_OPEN); + + /* We'll append at least two filters, _OPEN and _CLOSE. */ + if (FAILED(hr = filters_append_op( filters, len, *len + 2, open ))) return hr; + if (FAILED(hr = devprop_filters_append_expr( boolean->lhs, filters, len ))) return hr; + if (FAILED(hr = devprop_filters_append_expr( boolean->rhs, filters, len ))) return hr; + hr = filters_append_op( filters, len, 0, close ); + break; + } + case OP_TYPE_COMPARE: + { + const struct expr_compare *compare = &expr->u.compare; + DEVPROP_OPERATOR op; + + if (compare->op == OP_MATCH_WILDCARD) FIXME( "Wildcard matching is not supported yet, will compare verbatim.\n" ); + + if ((compare->op == OP_EQ || compare->op == OP_NEQ) && compare->val.vt == VT_EMPTY) + op = (compare->op == OP_EQ) ? DEVPROP_OPERATOR_NOT_EXISTS : DEVPROP_OPERATOR_EXISTS; + else + op = compare_ops[compare->op]; + if (FAILED(hr = filters_append_op( filters, len, 0, op ))) return hr; + hr = propval_to_devprop( &compare->val, compare->prop_desc, &(*filters)[*len - 1].Property); + break; + } + } + return hr; +} + +static HRESULT aqs_expr_to_filters( const struct aqs_expr *expr, DEVPROP_FILTER_EXPRESSION **filters, ULONG *filters_len ) +{ + HRESULT hr; + + *filters = NULL; + *filters_len = 0; + if (FAILED(hr = devprop_filters_append_expr( expr, filters, filters_len ))) + { + free_devprop_filters( *filters, *filters_len ); + *filters = NULL; + *filters_len = 0; + } + return hr; +} + +void free_devprop_filters( DEVPROP_FILTER_EXPRESSION *filters, ULONG filters_len ) +{ + ULONG i; + + for (i = 0; i < filters_len; i++) + free( filters[i].Property.Buffer ); + free( filters ); +} diff --git a/dlls/windows.devices.enumeration/aqs.h b/dlls/windows.devices.enumeration/aqs.h index 5f229b2eb6c..5dcc3872e84 100644 --- a/dlls/windows.devices.enumeration/aqs.h +++ b/dlls/windows.devices.enumeration/aqs.h @@ -100,7 +100,7 @@ struct aqs_expr } u; };
-extern HRESULT aqs_parse_query( const WCHAR *str ); +extern HRESULT aqs_parse_query( const WCHAR *str, DEVPROP_FILTER_EXPRESSION **filters, ULONG *filters_len );
extern UINT aqs_lex( void *val, struct aqs_parser *parser );
@@ -113,3 +113,4 @@ extern HRESULT get_compare_expr( struct aqs_parser *parser, enum operator_compar const PROPVARIANT *val, struct aqs_expr **expr );
extern void free_expr( struct aqs_expr *expr ); +extern void free_devprop_filters( DEVPROP_FILTER_EXPRESSION *filters, ULONG filters_len ); diff --git a/dlls/windows.devices.enumeration/main.c b/dlls/windows.devices.enumeration/main.c index ca5af3f2100..667d85e134b 100644 --- a/dlls/windows.devices.enumeration/main.c +++ b/dlls/windows.devices.enumeration/main.c @@ -531,6 +531,81 @@ static HRESULT WINAPI device_statics_CreateFromIdAsyncAdditionalProperties( IDev return E_NOTIMPL; }
+struct devquery_params +{ + IUnknown IUnknown_iface; + DEVPROP_FILTER_EXPRESSION *filters; + ULONG filters_len; + LONG ref; +}; + +static inline struct devquery_params *impl_from_IUnknown( IUnknown *iface ) +{ + return CONTAINING_RECORD( iface, struct devquery_params, IUnknown_iface ); +} + +static HRESULT WINAPI devquery_params_QueryInterface( IUnknown *iface, REFIID iid, void **out ) +{ + TRACE( "iface %p, iid %s, out %p\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown )) + { + IUnknown_AddRef(( iface )); + *out = iface; + return S_OK; + } + + *out = NULL; + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + return S_OK; +} + +static ULONG WINAPI devquery_params_AddRef( IUnknown *iface ) +{ + struct devquery_params *impl = impl_from_IUnknown( iface ); + + TRACE( "iface %p\n", iface ); + return InterlockedIncrement( &impl->ref ); +} + +static ULONG WINAPI devquery_params_Release( IUnknown *iface ) +{ + struct devquery_params *impl = impl_from_IUnknown( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + + TRACE( "iface %p\n", iface ); + + if (!ref) + { + free_devprop_filters( impl->filters, impl->filters_len ); + free( impl ); + } + return ref; +} + +static const IUnknownVtbl devquery_params_vtbl = +{ + /* IUnknown */ + devquery_params_QueryInterface, + devquery_params_AddRef, + devquery_params_Release, +}; + +static HRESULT create_devquery_params( DEVPROP_FILTER_EXPRESSION *filters, ULONG filters_len, IUnknown **out ) +{ + struct devquery_params *impl; + + *out = NULL; + if (!(impl = calloc( 1, sizeof( *impl ) ))) return E_OUTOFMEMORY; + + impl->IUnknown_iface.lpVtbl = &devquery_params_vtbl; + impl->ref = 1; + impl->filters = filters; + impl->filters_len = filters_len; + *out = &impl->IUnknown_iface; + return S_OK; +} + static HRESULT find_all_async( IUnknown *invoker, IUnknown *param, PROPVARIANT *result ) { static const struct vector_iids iids = @@ -541,6 +616,7 @@ static HRESULT find_all_async( IUnknown *invoker, IUnknown *param, PROPVARIANT * .iterator = &IID_IIterator_DeviceInformation, }; IVectorView_DeviceInformation *view; + struct devquery_params *params; IVector_IInspectable *vector; const DEV_OBJECT *objects; ULONG len, i; @@ -548,8 +624,9 @@ static HRESULT find_all_async( IUnknown *invoker, IUnknown *param, PROPVARIANT *
TRACE( "invoker %p, param %p, result %p\n", invoker, param, result );
+ params = impl_from_IUnknown( param ); if (FAILED(hr = vector_create( &iids, (void *)&vector ))) return hr; - if (FAILED(hr = DevGetObjects( DevObjectTypeDeviceInterfaceDisplay, DevQueryFlagNone, 0, NULL, 0, NULL, &len, &objects ))) + if (FAILED(hr = DevGetObjects( DevObjectTypeDeviceInterfaceDisplay, DevQueryFlagNone, 0, NULL, params->filters_len, params->filters, &len, &objects ))) { IVector_IInspectable_Release( vector ); return hr; @@ -590,12 +667,20 @@ static HRESULT WINAPI device_statics_FindAllAsyncDeviceClass( IDeviceInformation static HRESULT WINAPI device_statics_FindAllAsyncAqsFilter( IDeviceInformationStatics *iface, HSTRING filter, IAsyncOperation_DeviceInformationCollection **op ) { + DEVPROP_FILTER_EXPRESSION *filters; + ULONG filters_len; + IUnknown *params; HRESULT hr;
TRACE( "iface %p, aqs %p, op %p\n", iface, debugstr_hstring(filter), op );
- if (FAILED(hr = aqs_parse_query(WindowsGetStringRawBuffer( filter, NULL )))) return hr; - return async_operation_inspectable_create( &IID_IAsyncOperation_DeviceInformationCollection, (IUnknown *)iface, NULL, + if (FAILED(hr = aqs_parse_query(WindowsGetStringRawBuffer( filter, NULL ), &filters, &filters_len ))) return hr; + if (FAILED(hr = create_devquery_params( filters, filters_len, ¶ms ))) + { + free_devprop_filters( filters, filters_len ); + return hr; + } + return async_operation_inspectable_create( &IID_IAsyncOperation_DeviceInformationCollection, (IUnknown *)iface, (IUnknown *)params, find_all_async, (IAsyncOperation_IInspectable **)op ); }
diff --git a/dlls/windows.devices.enumeration/tests/devices.c b/dlls/windows.devices.enumeration/tests/devices.c index 8fbfd9b50af..59567fd71bb 100644 --- a/dlls/windows.devices.enumeration/tests/devices.c +++ b/dlls/windows.devices.enumeration/tests/devices.c @@ -666,8 +666,8 @@ static void test_DeviceInformation_filters( void )
test_FindAllAsyncAqsFilter( statics, simple, FALSE, FALSE ); test_FindAllAsyncAqsFilter( statics, case_insensitive, TRUE, FALSE ); - test_FindAllAsyncAqsFilter( statics, no_results, FALSE, TRUE ); - test_FindAllAsyncAqsFilter( statics, invalid_comparand_type, TRUE, FALSE ); + test_FindAllAsyncAqsFilter( statics, no_results, FALSE, FALSE ); + test_FindAllAsyncAqsFilter( statics, invalid_comparand_type, FALSE, FALSE ); test_FindAllAsyncAqsFilter( statics, invalid_empty, FALSE, FALSE ); test_FindAllAsyncAqsFilter( statics, invalid_operator, FALSE, FALSE ); test_FindAllAsyncAqsFilter( statics, invalid_operand, FALSE, FALSE );