From: Vibhav Pant vibhavp@gmail.com
--- dlls/windows.devices.enumeration/Makefile.in | 2 + dlls/windows.devices.enumeration/aqs.c | 331 ++++++++++++++++++ dlls/windows.devices.enumeration/aqs.h | 155 ++++++++ dlls/windows.devices.enumeration/aqs.y | 175 +++++++++ dlls/windows.devices.enumeration/main.c | 14 +- dlls/windows.devices.enumeration/private.h | 1 + .../tests/devices.c | 25 +- 7 files changed, 688 insertions(+), 15 deletions(-) create mode 100644 dlls/windows.devices.enumeration/aqs.c create mode 100644 dlls/windows.devices.enumeration/aqs.h create mode 100644 dlls/windows.devices.enumeration/aqs.y
diff --git a/dlls/windows.devices.enumeration/Makefile.in b/dlls/windows.devices.enumeration/Makefile.in index 0a204835ae1..8ca6eb7dedb 100644 --- a/dlls/windows.devices.enumeration/Makefile.in +++ b/dlls/windows.devices.enumeration/Makefile.in @@ -5,6 +5,8 @@ SOURCES = \ access.c \ async.c \ async_private.idl \ + aqs.c \ + aqs.y \ classes.idl \ event_handlers.c \ information.c \ diff --git a/dlls/windows.devices.enumeration/aqs.c b/dlls/windows.devices.enumeration/aqs.c new file mode 100644 index 00000000000..b44fec831ec --- /dev/null +++ b/dlls/windows.devices.enumeration/aqs.c @@ -0,0 +1,331 @@ +/* Advanced Query Syntax parser + * + * Copyright 2025 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdarg.h> + +#include <windef.h> +#include <devpropdef.h> +#include <devfiltertypes.h> +#include <propvarutil.h> + +#include <wine/debug.h> + +#include "aqs.h" +#include "aqs.tab.h" + +WINE_DEFAULT_DEBUG_CHANNEL( aqs ); + +static BOOL is_idchar( WCHAR chr ) +{ + static const WCHAR legal[] = { 5, '#', '-', '.', '_', '{', '}' }; + int i; + + if (iswdigit(chr) || iswalpha(chr) || (chr >= 125 && chr <= 255)) return TRUE; + for (i = 0; i < ARRAY_SIZE( legal ); i++) if (chr == legal[i]) return TRUE; + return FALSE; +} + +static int keyword_type( const WCHAR *str, unsigned int len ) +{ + if (!wcsncmp( str, L"AND", len )) + return TK_AND; + if (!wcsncmp( str, L"NOT", len )) + return TK_NOT; + if (!wcsncmp( str, L"OR", len )) + return TK_OR; + if (!wcsnicmp( str, L"System.StructuredQueryType.Boolean#False", len )) + return TK_FALSE; + if (!wcsnicmp( str, L"System.StructuredQueryType.Boolean#True", len )) + return TK_TRUE; + + return TK_ID; +} + +int get_token( const WCHAR *str, aqs_token_kind_t *token ) +{ + int i; + + switch (str[0]) + { + case '\0': + *token = AQS_EOF; + return 0; + case ' ': + case '\t': + case '\r': + case '\n': + for (i = 1; iswspace( str[i] ); i++) /*nothing */; + *token = TK_WHITESPACE; + return i; + case '(': + *token = TK_LEFTPAREN; + return 1; + case ')': + *token = TK_RIGHTPAREN; + return 1; + case '[': + if (str[1] != ']') break; /* illegal */ + *token = TK_NULL; + return 2; + case ':': + *token = TK_COLON; + return 1; + case '=': + *token = TK_EQUAL; + return 1; + case L'\u2260': /* ≠, NOT EQUALS TO */ + *token = TK_NOTEQUAL; + return 1; + case '-': + if (iswdigit( str[1] )) + { + *token = TK_MINUS; + return 1; + } + *token = TK_NOTEQUAL; + /* Both -- and - are used as not-equals operators. */ + return str[1] == '-' ? 2 : 1; + case '<': + switch (str[1]) + { + case '=': + *token = TK_LTE; + return 2; + case '>': + *token = TK_NOTEQUAL; + return 2; + default: + *token = TK_LT; + return 1; + } + case L'\u2264': /* ≤, LESS-THAN OR EQUAL TO */ + *token = TK_LTE; + return 1; + case '>': + if (str[1] == '=') + { + *token = TK_GTE; + return 2; + } + *token = TK_GT; + return 1; + case L'\u2265': /* ≥, GREATER-THAN OR EQUAL TO */ + *token = TK_GTE; + return 1; + case '~': + *token = TK_TILDE; + return 1; + case '!': + *token = TK_EXCLAM; + return 1; + case '$': + *token = TK_DOLLAR; + return 1; + case '"': + /* lookup for end double quote, skipping any "" escaped double quotes */ + for (i = 1; str[i]; i++) if (str[i] == '"' && str[++i] != '"') break; + if (i == 1 || str[i - 1] != '"') break; /* illegal */ + *token = TK_STRING; + return i; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + *token = TK_INTEGER; + for (i = 1; iswdigit( str[i] ); i++) /* nothing */; + return i; + default: + if (!is_idchar( str[0] )) break; + for (i = 1; is_idchar( str[i] ); i++) /* nothing */; + *token = keyword_type( str, i ); + return i; + } + *token = AQS_UNDEF; + return 0; +} + +UINT aqs_lex( void *p, struct aqs_parser *parser ) +{ + aqs_token_kind_t token = -1; + struct string *str = p; + + do + { + parser->idx += parser->len; + if ((parser->len = get_token( &parser->query[parser->idx], &token ))) + { + str->data = &parser->query[parser->idx]; + str->len = parser->len; + } + } while (token == TK_WHITESPACE); + return token; +} + +HRESULT aqs_parse_query( const WCHAR *str ) +{ + struct aqs_parser parser = {0}; + HRESULT hr; + int ret; + + if (!wcslen( str )) return S_OK; + + parser.query = str; + if (FAILED(hr = CoCreateInstance( &CLSID_PropertySystem, NULL, CLSCTX_INPROC_SERVER, &IID_IPropertySystem, (void **)&parser.propsys ))) + return hr; + aqs_debug = TRACE_ON( aqs ); + ret = aqs_parse( &parser ); + if (!ret) + FIXME( "semi-stub!\n" ); + else + hr = FAILED(parser.error) ? parser.error : E_INVALIDARG; + + IPropertySystem_Release( parser.propsys ); + return hr; +} + +HRESULT get_integer( struct aqs_parser *parser, PROPVARIANT *val ) +{ + const WCHAR *str = &parser->query[parser->idx]; + int i, num = 0; + + for (i = 0; i < parser->len; i++) + num = (str[i] - '0') + num * 10; + val->vt = VT_UI4; + val->lVal = num; + return S_OK; +} + +HRESULT get_string( struct aqs_parser *parser, const struct string *p, BOOL id, PROPVARIANT *val ) +{ + const WCHAR *str = p->data; + SIZE_T len = p->len; + WCHAR *buf; + + if (!id) + { + str++; + len -= 2; + } + if (!(buf = CoTaskMemAlloc((len + 1) * sizeof( WCHAR )))) return (parser->error = E_OUTOFMEMORY); + memcpy( buf, str, len * sizeof( WCHAR ) ); + buf[len] = 0; + val->vt = VT_LPWSTR; + val->pwszVal = buf; + return S_OK; +} + +void get_boolean( struct aqs_parser *parser, BOOL b, PROPVARIANT *val ) +{ + val->vt = VT_BOOL; + val->boolVal = b ? VARIANT_TRUE : VARIANT_FALSE; +} + +HRESULT get_boolean_expr( struct aqs_parser *parser, enum operator_boolean op, struct aqs_expr *lhs, + struct aqs_expr *rhs, struct aqs_expr **ret_expr ) +{ + struct aqs_expr *expr; + + if (!(expr = calloc( 1, sizeof( *expr ) ))) + { + parser->error = E_OUTOFMEMORY; + return E_OUTOFMEMORY; + } + + expr->op_type = OP_TYPE_BOOLEAN; + expr->boolean.op = op; + expr->boolean.lhs = lhs; + expr->boolean.rhs = rhs; + *ret_expr = expr; + return S_OK; +} + +HRESULT get_compare_expr( struct aqs_parser *parser, enum operator_compare op, const WCHAR *prop_name, + PROPVARIANT *val, struct aqs_expr **ret_expr ) +{ + IPropertyDescription *prop_desc = NULL; + struct aqs_expr *expr; + HRESULT hr; + + if (!(expr = calloc( 1, sizeof( *expr )))) goto fail; + if (FAILED(hr = IPropertySystem_GetPropertyDescriptionByName( parser->propsys, prop_name, &IID_IPropertyDescription, (void **)&prop_desc ))) + { + parser->error = hr == TYPE_E_ELEMENTNOTFOUND ? E_INVALIDARG : hr; + goto fail; + } + if (FAILED(parser->error = IPropertyDescription_GetPropertyKey( prop_desc, (PROPERTYKEY *)&expr->compare.prop_key ))) goto fail; + if (FAILED(parser->error = IPropertyDescription_GetPropertyType( prop_desc, &expr->compare.prop_type ))) goto fail; + if (FAILED(parser->error = PropVariantCopy( &expr->compare.val, val ))) goto fail; + + PropVariantClear( val ); + expr->op_type = OP_TYPE_COMPARE; + expr->compare.op = op; + parser->expr = expr; + *ret_expr = expr; + return S_OK; + +fail: + PropVariantClear( val ); + if (prop_desc) IPropertyDescription_Release( prop_desc ); + free( expr ); + return parser->error; +} + +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 char *debugstr_expr( const struct aqs_expr *expr ) +{ + static const char *bool_op[] = { "OP_AND", "OP_OR", "OP_NOT" }; + static const char *compare_op[] = { "OP_EQ", "OP_NEQ", "OP_GT", "OP_GTE", "OP_LT", "OP_LTE", "OP_STARTS_WITH", "OP_ENDS_WITH", "OP_CONTAINS", + "OP_NOT_CONTAINS", "OP_MATCH_WILDCARD" }; + + switch (expr->op_type) + { + case OP_TYPE_BOOLEAN: + if (expr->boolean.op < ARRAY_SIZE( bool_op )) + return wine_dbg_sprintf( "{%s %p %p}", bool_op[expr->boolean.op], expr->boolean.lhs, + expr->boolean.rhs ); + return wine_dbg_sprintf( "{(unknown %d) %p %p}", expr->boolean.op, expr->boolean.lhs, expr->boolean.rhs ); + case OP_TYPE_COMPARE: + { + const struct expr_compare *cmp = &expr->compare; + if (expr->compare.op < ARRAY_SIZE( compare_op )) + return wine_dbg_sprintf( "{%s %s %u %s}", compare_op[cmp->op], debugstr_DEVPROPKEY( &cmp->prop_key ), cmp->prop_type, debugstr_propvar( &cmp->val ) ); + return wine_dbg_sprintf( "{(unknown %d) %s %u %s}", cmp->op, debugstr_DEVPROPKEY( &cmp->prop_key ), cmp->prop_type, debugstr_propvar( &cmp->val ) ); + } + default: + return wine_dbg_sprintf( "{(unknown %d)}", expr->op_type ); + } +} + +void free_expr( struct aqs_expr *expr ) +{ + switch (expr->op_type) + { + case OP_TYPE_BOOLEAN: + free_expr( expr->boolean.lhs ); + free_expr( expr->boolean.rhs ); + break; + case OP_TYPE_COMPARE: + PropVariantClear( &expr->compare.val ); + break; + } + free( expr ); +} diff --git a/dlls/windows.devices.enumeration/aqs.h b/dlls/windows.devices.enumeration/aqs.h new file mode 100644 index 00000000000..0ec35cf2e06 --- /dev/null +++ b/dlls/windows.devices.enumeration/aqs.h @@ -0,0 +1,155 @@ +/* Advanced Query Syntax parser + * + * Copyright 2025 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <devpropdef.h> +#include <devfiltertypes.h> +#include <wine/list.h> +#include <wine/debug.h> + +#include "private.h" + +struct string +{ + const WCHAR *data; + int len; +}; + +struct aqs_parser +{ + const WCHAR *query; + int idx; + int len; + HRESULT error; + + IPropertySystem *propsys; + struct aqs_expr *expr; +}; + +enum operator_boolean +{ + OP_AND, + OP_OR, + OP_NOT +}; + +enum operator_compare +{ + OP_EQ, + OP_NEQ, + OP_GT, + OP_GTE, + OP_LT, + OP_LTE, + OP_STARTS_WITH, + OP_ENDS_WITH, + OP_CONTAINS, + OP_NOT_CONTAINS, + OP_MATCH_WILDCARD +}; + +enum operator_type +{ + OP_TYPE_BOOLEAN, + OP_TYPE_COMPARE, +}; + +struct aqs_expr; +struct expr_boolean +{ + enum operator_boolean op; + struct aqs_expr *lhs; + struct aqs_expr *rhs; +}; + +enum propval_type +{ + PROPVAL_TYPE_INTEGER, + PROPVAL_TYPE_STRING, + PROPVAL_TYPE_BOOLEAN, + PROPVAL_TYPE_NULL, +}; + +struct expr_compare +{ + enum operator_compare op; + DEVPROPKEY prop_key; + VARTYPE prop_type; + PROPVARIANT val; +}; + +struct aqs_expr +{ + enum operator_type op_type; + union { + struct expr_boolean boolean; + struct expr_compare compare; + }; +}; + +extern HRESULT aqs_parse_query( const WCHAR *str ); + +extern UINT aqs_lex( void *val, struct aqs_parser *parser ); + +extern HRESULT get_integer( struct aqs_parser *parser, PROPVARIANT *val ); +extern HRESULT get_string( struct aqs_parser *parser, const struct string *str, BOOL id, PROPVARIANT *val ); +extern void get_boolean( struct aqs_parser *parser, BOOL b, PROPVARIANT *val ); + +extern HRESULT get_boolean_expr( struct aqs_parser *parser, enum operator_boolean op, struct aqs_expr *lhs, + struct aqs_expr *rhs, struct aqs_expr **expr ); +extern HRESULT get_compare_expr( struct aqs_parser *parser, enum operator_compare op, const WCHAR *prop_name, + PROPVARIANT *val, struct aqs_expr **expr ); + +extern void free_expr( struct aqs_expr *expr ); + +extern const char *debugstr_expr( const struct aqs_expr *expr ); +static inline const char *debugstr_propvar(const PROPVARIANT *v) +{ + if (!v) + return "(null)"; + + switch (v->vt) + { + case VT_EMPTY: + return wine_dbg_sprintf("%p {VT_EMPTY}", v); + case VT_NULL: + return wine_dbg_sprintf("%p {VT_NULL}", v); + case VT_BOOL: + return wine_dbg_sprintf("%p {VT_BOOL %d}", v, !!v->boolVal); + case VT_UI4: + return wine_dbg_sprintf("%p {VT_UI4: %ld}", v, v->ulVal); + case VT_UI8: + return wine_dbg_sprintf("%p {VT_UI8: %s}", v, wine_dbgstr_longlong(v->uhVal.QuadPart)); + case VT_I8: + return wine_dbg_sprintf("%p {VT_I8: %s}", v, wine_dbgstr_longlong(v->hVal.QuadPart)); + case VT_R4: + return wine_dbg_sprintf("%p {VT_R4: %.8e}", v, v->fltVal); + case VT_R8: + return wine_dbg_sprintf("%p {VT_R8: %lf}", v, v->dblVal); + case VT_CLSID: + return wine_dbg_sprintf("%p {VT_CLSID: %s}", v, wine_dbgstr_guid(v->puuid)); + case VT_LPWSTR: + return wine_dbg_sprintf("%p {VT_LPWSTR: %s}", v, wine_dbgstr_w(v->pwszVal)); + case VT_VECTOR | VT_UI1: + return wine_dbg_sprintf("%p {VT_VECTOR|VT_UI1: %p}", v, v->caub.pElems); + case VT_UNKNOWN: + return wine_dbg_sprintf("%p {VT_UNKNOWN: %p}", v, v->punkVal); + default: + return wine_dbg_sprintf("%p {vt %#x}", v, v->vt); + } +} diff --git a/dlls/windows.devices.enumeration/aqs.y b/dlls/windows.devices.enumeration/aqs.y new file mode 100644 index 00000000000..6abedfebff9 --- /dev/null +++ b/dlls/windows.devices.enumeration/aqs.y @@ -0,0 +1,175 @@ +%{ + +/* Advanced Query Syntax parser + * + * Copyright 2025 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdarg.h> +#include <stdlib.h> + +#include <windef.h> +#include <winbase.h> + +#include <wine/debug.h> + +#include "aqs.h" + +WINE_DEFAULT_DEBUG_CHANNEL( aqs ); + +#define YYFPRINTF(file, ...) TRACE(__VA_ARGS__) + +static const PROPVARIANT propval_empty = { VT_EMPTY }; + +static int aqs_error( struct aqs_parser *parser, const char *str ) +{ + if (TRACE_ON( aqs)) ERR( "%s\n", str ); + return 0; +} + +#define GET_COMPARE_EXPR( ctx, op, prop_vt, val_vt, out ) \ + if (FAILED(get_compare_expr( ctx, op, (prop_vt)->pwszVal, val_vt, out ))) YYABORT +%} + +%lex-param { struct aqs_parser *ctx } +%parse-param { struct aqs_parser *ctx } +%define parse.error verbose +%define api.prefix {aqs_} +%define api.pure + +%union +{ + struct string str; + PROPVARIANT propval; + struct aqs_expr *expr; +} + +%token TK_LEFTPAREN TK_RIGHTPAREN TK_NULL +%token TK_INTEGER TK_WHITESPACE TK_ILLEGAL TK_MINUS +%token TK_TRUE TK_FALSE +%token <str> TK_STRING TK_ID + +%type <propval> id string number boolean null +%type <propval> propval +%type <expr> expr query + +%left TK_AND TK_OR TK_NOT TK_COLON TK_EQUAL TK_NOTEQUAL TK_LT TK_LTE TK_GT TK_GTE TK_TILDE TK_EXCLAM TK_DOLLAR + +%destructor { PropVariantClear( &$$ ); } propval +%destructor { free_expr( $$ ); } expr + +%debug + +%printer { TRACE( "%s", debugstr_wn( $$.data, $$.len ) ); } TK_STRING TK_ID +%printer { TRACE( "%s", debugstr_propvar( &$$ ) ); } id string +%printer { TRACE( "%s", debugstr_propvar( &$$ ) ); } number +%printer { TRACE( "%s", debugstr_propvar( &$$ ) ); } propval +%printer { TRACE( "%s", debugstr_expr( $$ ) ); } expr + +%% + +query: expr { ctx->expr = $1; } + ; +expr: + TK_LEFTPAREN expr TK_RIGHTPAREN + { + $$ = $2; +#if YYBISON >= 30704 + (void)yysymbol_name; /* avoid unused function warning */ +#endif + (void)yynerrs; /* avoid unused variable warning */ + } + | expr TK_AND expr + { + if (FAILED(get_boolean_expr( ctx, OP_AND, $1, $3, &$$ ))) + YYABORT; + } + | expr TK_OR expr + { + if (FAILED(get_boolean_expr( ctx, OP_OR, $1, $3, &$$ ))) + YYABORT; + } + | TK_NOT expr + { + if (FAILED(get_boolean_expr( ctx, OP_NOT, $2, NULL, &$$ ))) + YYABORT; + } + | expr expr + { + if (FAILED(get_boolean_expr( ctx, OP_AND, $1, $2, &$$ ))) + YYABORT; + } + | id TK_COLON propval { GET_COMPARE_EXPR( ctx, OP_EQ, &$1, &$3, &$$ ); } + | id TK_COLON TK_EQUAL propval { GET_COMPARE_EXPR( ctx, OP_EQ, &$1, &$4, &$$ ); } + | id TK_COLON TK_NOT propval { GET_COMPARE_EXPR( ctx, OP_NEQ, &$1, &$4, &$$ ); } + | id TK_COLON TK_NOTEQUAL propval { GET_COMPARE_EXPR( ctx, OP_NEQ, &$1, &$4, &$$ ); } + | id TK_COLON TK_LT propval { GET_COMPARE_EXPR( ctx, OP_LT, &$1, &$4, &$$ ); } + | id TK_COLON TK_LTE propval { GET_COMPARE_EXPR( ctx, OP_LTE, &$1, &$4, &$$ ); } + | id TK_COLON TK_GT propval { GET_COMPARE_EXPR( ctx, OP_GT, &$1, &$4, &$$ ); } + | id TK_COLON TK_GTE propval { GET_COMPARE_EXPR( ctx, OP_GTE, &$1, &$4, &$$ ); } + /* String operators */ + | id TK_TILDE TK_LT propval { GET_COMPARE_EXPR( ctx, OP_STARTS_WITH, &$1, &$4, &$$ ); } + | id TK_TILDE TK_GT propval { GET_COMPARE_EXPR( ctx, OP_ENDS_WITH, &$1, &$4, &$$ ); } + | id TK_TILDE TK_EQUAL propval { GET_COMPARE_EXPR( ctx, OP_CONTAINS, &$1, &$4, &$$ ); } + | id TK_TILDE TK_TILDE propval { GET_COMPARE_EXPR( ctx, OP_CONTAINS, &$1, &$4, &$$ ); } + | id TK_TILDE TK_EXCLAM propval { GET_COMPARE_EXPR( ctx, OP_NOT_CONTAINS, &$1, &$4, &$$ ); } + | id TK_TILDE propval { GET_COMPARE_EXPR( ctx, OP_MATCH_WILDCARD, &$1, &$3, &$$ ); } + | id TK_DOLLAR TK_EQUAL propval { GET_COMPARE_EXPR( ctx, OP_EQ, &$1, &$4, &$$ ); } + | id TK_DOLLAR TK_DOLLAR propval { GET_COMPARE_EXPR( ctx, OP_EQ, &$1, &$4, &$$ ); } + | id TK_DOLLAR TK_LT propval { GET_COMPARE_EXPR( ctx, OP_STARTS_WITH, &$1, &$4, &$$ ); } +propval: + id | string | number | boolean | null + ; +id: + TK_ID + { + if (FAILED(get_string( ctx, &$1, TRUE, &$$ ))) + YYABORT; + } + ; +string: + TK_STRING + { + if (FAILED(get_string( ctx, &$1, FALSE, &$$ ))) + YYABORT; + } + ; +number: + TK_INTEGER + { + if (FAILED(get_integer( ctx, &$$ ))) + YYABORT; + } + ; +boolean: + TK_TRUE + { + get_boolean( ctx, TRUE, &$$ ); + } + ; + | TK_FALSE + { + get_boolean( ctx, FALSE, &$$ ); + } + ; +null: + TK_NULL + { + $$ = propval_empty; + } + ; +%% diff --git a/dlls/windows.devices.enumeration/main.c b/dlls/windows.devices.enumeration/main.c index f8462c8faed..ca5af3f2100 100644 --- a/dlls/windows.devices.enumeration/main.c +++ b/dlls/windows.devices.enumeration/main.c @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#define COBJMACROS #include <assert.h>
#include "initguid.h" @@ -26,6 +27,7 @@ #include "devpropdef.h" #include "devfiltertypes.h" #include "devquery.h" +#include "aqs.h"
#include "wine/debug.h"
@@ -575,8 +577,7 @@ static HRESULT WINAPI device_statics_FindAllAsync( IDeviceInformationStatics *if IAsyncOperation_DeviceInformationCollection **op ) { TRACE( "iface %p, op %p\n", iface, op ); - return async_operation_inspectable_create( &IID_IAsyncOperation_DeviceInformationCollection, (IUnknown *)iface, NULL, - find_all_async, (IAsyncOperation_IInspectable **)op ); + return IDeviceInformationStatics_FindAllAsyncAqsFilter( iface, NULL, op ); }
static HRESULT WINAPI device_statics_FindAllAsyncDeviceClass( IDeviceInformationStatics *iface, DeviceClass class, @@ -589,8 +590,13 @@ static HRESULT WINAPI device_statics_FindAllAsyncDeviceClass( IDeviceInformation static HRESULT WINAPI device_statics_FindAllAsyncAqsFilter( IDeviceInformationStatics *iface, HSTRING filter, IAsyncOperation_DeviceInformationCollection **op ) { - FIXME( "iface %p, aqs %p, op %p stub!\n", iface, debugstr_hstring(filter), op ); - return E_NOTIMPL; + 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, + find_all_async, (IAsyncOperation_IInspectable **)op ); }
static HRESULT WINAPI device_statics_FindAllAsyncAqsFilterAndAdditionalProperties( IDeviceInformationStatics *iface, HSTRING filter, diff --git a/dlls/windows.devices.enumeration/private.h b/dlls/windows.devices.enumeration/private.h index ad15b7916ca..0ce5624b21c 100644 --- a/dlls/windows.devices.enumeration/private.h +++ b/dlls/windows.devices.enumeration/private.h @@ -28,6 +28,7 @@ #include "winbase.h" #include "winstring.h" #include "objbase.h" +#include "propsys.h"
#include "activation.h"
diff --git a/dlls/windows.devices.enumeration/tests/devices.c b/dlls/windows.devices.enumeration/tests/devices.c index 840af19e2a1..8fbfd9b50af 100644 --- a/dlls/windows.devices.enumeration/tests/devices.c +++ b/dlls/windows.devices.enumeration/tests/devices.c @@ -544,8 +544,11 @@ struct test_case_filter BOOL no_results; };
-#define test_FindAllAsyncAqsFilter( statics, test_cases ) test_FindAllAsyncAqsFilter_( statics, #test_cases, test_cases, ARRAY_SIZE( test_cases ) ) -static void test_FindAllAsyncAqsFilter_( IDeviceInformationStatics *statics, const char *name, const struct test_case_filter *test_cases, SIZE_T len ) +#define test_FindAllAsyncAqsFilter( statics, test_cases, todo_hr, todo_results ) \ + test_FindAllAsyncAqsFilter_( statics, #test_cases, test_cases, ARRAY_SIZE( test_cases ), todo_hr, todo_results ) + +static void test_FindAllAsyncAqsFilter_( IDeviceInformationStatics *statics, const char *name, const struct test_case_filter *test_cases, SIZE_T len, + BOOL todo_hr, BOOL todo_results ) { SIZE_T i;
@@ -561,7 +564,7 @@ static void test_FindAllAsyncAqsFilter_( IDeviceInformationStatics *statics, con hr = WindowsCreateString( test_cases[i].filter, wcslen( test_cases[i].filter ), &str ); ok( hr == S_OK, "got hr %#lx\n", hr ); hr = IDeviceInformationStatics_FindAllAsyncAqsFilter( statics, str, &devices_async ); - todo_wine ok( hr == test_cases[i].hr, "gor hr %#lx != %#lx\n", hr, test_cases[i].hr ); + todo_wine_if( todo_hr ) ok( hr == test_cases[i].hr, "gor hr %#lx != %#lx\n", hr, test_cases[i].hr ); WindowsDeleteString( str ); if (FAILED( hr ) || FAILED( test_cases[i].hr )) { @@ -573,7 +576,7 @@ static void test_FindAllAsyncAqsFilter_( IDeviceInformationStatics *statics, con
hr = IVectorView_DeviceInformation_get_Size( devices, &size ); ok( hr == S_OK, "got hr %#lx\n", hr ); - ok( test_cases[i].no_results == !size, "got size %I32u\n", size ); + todo_wine_if( todo_results ) ok( test_cases[i].no_results == !size, "got size %I32u\n", size ); IAsyncOperation_DeviceInformationCollection_Release( devices_async ); IVectorView_DeviceInformation_Release( devices );
@@ -661,13 +664,13 @@ static void test_DeviceInformation_filters( void ) return; }
- test_FindAllAsyncAqsFilter( statics, simple ); - test_FindAllAsyncAqsFilter( statics, case_insensitive ); - test_FindAllAsyncAqsFilter( statics, no_results ); - test_FindAllAsyncAqsFilter( statics, invalid_comparand_type ); - test_FindAllAsyncAqsFilter( statics, invalid_empty ); - test_FindAllAsyncAqsFilter( statics, invalid_operator ); - test_FindAllAsyncAqsFilter( statics, invalid_operand ); + 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, invalid_empty, FALSE, FALSE ); + test_FindAllAsyncAqsFilter( statics, invalid_operator, FALSE, FALSE ); + test_FindAllAsyncAqsFilter( statics, invalid_operand, FALSE, FALSE );
IDeviceInformationStatics_Release( statics ); }