Support filter expressions consisting of basic comparison operators in DevGetObjects(Ex).
-- v2: cfgmgr32: Implement support for basic filter expressions in DevGetObjects. cfgmgr32: Validate DEVPROP_FILTER_EXPRESSION values passed to Dev{GetObjects, CreateObjectQueryEx}. cfgmgr32/tests: Add some tests for calling DevGetObjects with filters.
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/cfgmgr32.spec | 1 + dlls/cfgmgr32/main.c | 14 ++++++ dlls/cfgmgr32/tests/cfgmgr32.c | 79 ++++++++++++++++++++++++++++------ 3 files changed, 82 insertions(+), 12 deletions(-)
diff --git a/dlls/cfgmgr32/cfgmgr32.spec b/dlls/cfgmgr32/cfgmgr32.spec index 2d4d6b23bec..f55f244d17f 100644 --- a/dlls/cfgmgr32/cfgmgr32.spec +++ b/dlls/cfgmgr32/cfgmgr32.spec @@ -197,3 +197,4 @@ @ stdcall DevGetObjectPropertiesEx(long ptr long long ptr long ptr ptr ptr) @ stdcall DevGetObjects(long long long ptr long ptr ptr ptr) @ stdcall DevGetObjectsEx(long long long ptr long ptr long ptr ptr ptr) +@ stdcall DevFindProperty(ptr long wstr long ptr) diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index 5a1418a0f3c..a9a899cfdb8 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -1107,6 +1107,20 @@ HRESULT WINAPI DevGetObjectPropertiesEx( DEV_OBJECT_TYPE type, const WCHAR *id, return hr; }
+static const char *debugstr_DEVPROPERTY( const DEVPROPKEY *key ) +{ + if (!key) return wine_dbg_sprintf("(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 ) +{ + FIXME( "(%s, %d, %s, %lu, %p): stub!\n", debugstr_DEVPROPERTY( key ), store, debugstr_w( locale ), props_len, + props ); + return NULL; +} + void WINAPI DevFreeObjectProperties( ULONG len, const DEVPROPERTY *props ) { DEVPROPERTY *properties = (DEVPROPERTY *)props; diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index f328da04200..d76678fa66b 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -127,6 +127,7 @@ void (WINAPI *pDevFreeObjects)(ULONG, const DEV_OBJECT*); HRESULT (WINAPI *pDevGetObjectProperties)(DEV_OBJECT_TYPE, const WCHAR *, ULONG, ULONG, const DEVPROPCOMPKEY *, ULONG *, const DEVPROPERTY **); void (WINAPI *pDevFreeObjectProperties)(ULONG, const DEVPROPERTY *); +const DEVPROPERTY* (WINAPI *pDevFindProperty)(const DEVPROPKEY *, DEVPROPSTORE, PCWSTR, ULONG, const DEVPROPERTY *);
DEFINE_DEVPROPKEY(DEVPROPKEY_GPU_LUID, 0x60b193cb, 0x5276, 0x4d0f, 0x96, 0xfc, 0xf1, 0x73, 0xab, 0xad, 0x3e, 0xc6, 2);
@@ -584,18 +585,22 @@ static const char *debugstr_DEVPROP_val( const DEVPROPERTY *prop ) } }
+static const char *debugstr_DEVPROPKEY( const DEVPROPKEY *key ) +{ + return wine_dbg_sprintf( "{%s, %04lx}", debugstr_guid( &key->fmtid ), key->pid ); +} + static void test_DevGetObjectProperties( DEV_OBJECT_TYPE type, const WCHAR *id, const DEVPROPERTY *exp_props, ULONG props_len ) { - static const DEVPROPKEY dummy_propkey = { { 0xdeadbeef, 0xdead, 0xbeef, { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef } }, 0x1 }; - static const DEVPROPCOMPKEY dummy_propcompkey = { dummy_propkey, DEVPROP_STORE_SYSTEM, NULL }; + static const DEVPROPCOMPKEY dummy_propcompkey = { DEVPKEY_dummy, DEVPROP_STORE_SYSTEM, NULL }; ULONG buf_len, rem_props = props_len, i; const DEVPROPERTY *buf; DEVPROPCOMPKEY *keys; HRESULT hr;
- if (!pDevGetObjectProperties || !pDevFreeObjectProperties) + if (!pDevGetObjectProperties || !pDevFreeObjectProperties || !pDevFindProperty) { - win_skip( "Functions unavailable, skipping test. (%p %p)\n", pDevGetObjects, pDevFreeObjects ); + win_skip( "Functions unavailable, skipping test. (%p %p %p)\n", pDevGetObjects, pDevFreeObjects, pDevFindProperty ); return; }
@@ -626,13 +631,16 @@ static void test_DevGetObjectProperties( DEV_OBJECT_TYPE type, const WCHAR *id, { if (IsEqualDevPropKey( exp_props[i].CompKey.Key, buf[j].CompKey.Key ) && rem_props) { - winetest_push_context( "{%s,%#lx}", debugstr_guid( &exp_props[i].CompKey.Key.fmtid ), - exp_props[i].CompKey.Key.pid ); + const DEVPROPERTY *found_prop; + + winetest_push_context( "%s", debugstr_DEVPROPKEY( &exp_props[i].CompKey.Key ) ); /* ItemNameDisplay for software devices has different values for properties obtained from DevGetObjects * and DevGetObjectProperties. */ if (!IsEqualDevPropKey(PKEY_ItemNameDisplay, buf[j].CompKey.Key)) ok( dev_property_val_equal( &exp_props[i], &buf[j] ), "%s != %s\n", debugstr_DEVPROP_val( &buf[j] ), debugstr_DEVPROP_val( &exp_props[i] ) ); + found_prop = pDevFindProperty( &exp_props[i].CompKey.Key, DEVPROP_STORE_SYSTEM, NULL, buf_len, buf ); + todo_wine ok( found_prop == &buf[i], "got found_prop %p != %p\n", found_prop, &buf[i] ); winetest_pop_context(); rem_props--; } @@ -664,7 +672,13 @@ static void test_DevGetObjectProperties( DEV_OBJECT_TYPE type, const WCHAR *id, { if (IsEqualDevPropKey( exp_props[i].CompKey.Key, buf[j].CompKey.Key )) { + const DEVPROPERTY *found_prop; + + winetest_push_context( "%s", debugstr_DEVPROPKEY( &exp_props[i].CompKey.Key ) ); rem_props--; + found_prop = pDevFindProperty( &exp_props[i].CompKey.Key, DEVPROP_STORE_SYSTEM, NULL, buf_len, buf ); + todo_wine ok( found_prop == &buf[j], "got found_prop %p != %p\n", found_prop, &buf[j] ); + winetest_pop_context(); break; } } @@ -680,9 +694,13 @@ static void test_DevGetObjectProperties( DEV_OBJECT_TYPE type, const WCHAR *id, ok( buf_len == 1, "got buf_len %lu\n", buf_len ); if (buf) { - ok( IsEqualDevPropKey( buf[0].CompKey.Key, dummy_propkey ), "got propkey {%s, %#lx}\n", + const DEVPROPERTY *found_prop; + + ok( IsEqualDevPropKey( buf[0].CompKey.Key, DEVPKEY_dummy ), "got propkey {%s, %#lx}\n", debugstr_guid( &buf[0].CompKey.Key.fmtid ), buf[0].CompKey.Key.pid ); ok( buf[0].Type == DEVPROP_TYPE_EMPTY, "got Type %#lx\n", buf[0].Type ); + found_prop = pDevFindProperty( &DEVPKEY_dummy, DEVPROP_STORE_SYSTEM, NULL, buf_len, buf ); + todo_wine ok( found_prop == &buf[0], "got found_prop %p != %p\n", found_prop, &buf[0] ); pDevFreeObjectProperties( buf_len, buf ); } free( keys ); @@ -710,6 +728,7 @@ static void test_dev_object_iface_props( int line, const DEV_OBJECT *obj, const { SP_INTERFACE_DEVICE_DATA iface_data = {0}; DEVPROPTYPE type = DEVPROP_TYPE_EMPTY; + const DEVPROPERTY *found_prop; ULONG size = 0; CONFIGRET ret; BYTE *buf; @@ -751,6 +770,10 @@ static void test_dev_object_iface_props( int line, const DEV_OBJECT *obj, const break; } } + + found_prop = pDevFindProperty( &property->CompKey.Key, DEVPROP_STORE_SYSTEM, NULL, + obj->cPropertyCount, obj->pProperties ); + todo_wine ok( found_prop == property, "got found_prop %p != %p\n", found_prop, property ); free( buf ); winetest_pop_context(); break; @@ -792,9 +815,9 @@ static void test_DevGetObjects( void ) HRESULT hr; ULONG i, len = 0;
- if (!pDevGetObjects || !pDevFreeObjects) + if (!pDevGetObjects || !pDevFreeObjects || !pDevFindProperty) { - win_skip("Functions unavailable, skipping test. (%p %p)\n", pDevGetObjects, pDevFreeObjects); + win_skip("Functions unavailable, skipping test. (%p %p %p)\n", pDevGetObjects, pDevFreeObjects, pDevFindProperty); return; }
@@ -885,14 +908,24 @@ static void test_DevGetObjects( void ) for (k = 0; k < len; k++) { const DEV_OBJECT *obj = &objects[k]; + const DEVPROPERTY *found_prop;
winetest_push_context( "objects[%lu]", k ); ok( obj->cPropertyCount == 1, "got cPropertyCount %lu != 1\n", obj->cPropertyCount ); ok( !!obj->pProperties, "got pProperties %p\n", obj->pProperties ); if (obj->pProperties) + { ok( IsEqualDevPropKey( obj->pProperties[0].CompKey.Key, prop->key ), "got property {%s, %#lx} != {%s, %#lx}\n", debugstr_guid( &obj->pProperties[0].CompKey.Key.fmtid ), obj->pProperties[0].CompKey.Key.pid, debugstr_guid( &prop->key.fmtid ), prop->key.pid ); + found_prop = pDevFindProperty( &prop->key, DEVPROP_STORE_SYSTEM, NULL, obj->cPropertyCount, obj->pProperties ); + todo_wine ok( found_prop == &obj->pProperties[0], "got found_prop %p != %p\n", found_prop, &obj->pProperties[0] ); + } + /* Search for a property not in obj->pProperties, we should get NULL, as we haven't requested this + * property in the DevGetObjects call. */ + found_prop = pDevFindProperty( &DEVPKEY_dummy, DEVPROP_STORE_SYSTEM, NULL, obj->cPropertyCount, obj->pProperties ); + ok( !found_prop, "got found_prop %p\n", found_prop ); + winetest_pop_context(); } pDevFreeObjects( len, objects ); @@ -918,11 +951,14 @@ static void test_DevGetObjects( void ) ok( !!obj->pProperties, "got pProperties %p\n", obj->pProperties ); if (obj->pProperties) { - ok( IsEqualDevPropKey( obj->pProperties[0].CompKey.Key, DEVPKEY_dummy ), - "got property {%s, %#lx} != {%s, %#lx}\n", debugstr_guid( &obj->pProperties[0].CompKey.Key.fmtid ), - obj->pProperties[0].CompKey.Key.pid, debugstr_guid( &DEVPKEY_dummy.fmtid ), DEVPKEY_dummy.pid ); + const DEVPROPERTY *found_prop; + + ok( IsEqualDevPropKey( obj->pProperties[0].CompKey.Key, DEVPKEY_dummy ), "got property %s != %s\n", + debugstr_DEVPROPKEY( &obj->pProperties[0].CompKey.Key ), debugstr_DEVPROPKEY( &DEVPKEY_dummy ) ); ok( obj->pProperties[0].Type == DEVPROP_TYPE_EMPTY, "got Type %#lx != %#x", obj->pProperties[0].Type, DEVPROP_TYPE_EMPTY ); + found_prop = pDevFindProperty( &DEVPKEY_dummy, DEVPROP_STORE_SYSTEM, NULL, obj->cPropertyCount, obj->pProperties ); + todo_wine ok( found_prop == &obj->pProperties[0], "got found_prop %p != %p\n", found_prop, &obj->pProperties[0] ); } winetest_pop_context(); } @@ -1115,6 +1151,23 @@ static void test_DevGetObjectProperties_invalid( void ) ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); }
+static void test_DevFindProperty_invalid( void ) +{ + const DEVPROPERTY *prop; + + if (!pDevFindProperty) + { + win_skip( "Functions unavailable, skipping test. (%p)\n", pDevFindProperty ); + return; + } + + prop = pDevFindProperty( &DEVPKEY_dummy, DEVPROP_STORE_SYSTEM, NULL, 0, NULL ); + ok( !prop, "got prop %p\n", prop ); + + prop = pDevFindProperty( &DEVPKEY_dummy, DEVPROP_STORE_SYSTEM, NULL, 0, (DEVPROPERTY *)0xdeadbeef ); + ok( !prop, "got prop %p\n", prop ); +} + START_TEST(cfgmgr32) { HMODULE mod = GetModuleHandleA("cfgmgr32.dll"); @@ -1124,6 +1177,7 @@ START_TEST(cfgmgr32) pDevFreeObjects = (void *)GetProcAddress(mod, "DevFreeObjects"); pDevGetObjectProperties = (void *)GetProcAddress(mod, "DevGetObjectProperties"); pDevFreeObjectProperties = (void *)GetProcAddress(mod, "DevFreeObjectProperties"); + pDevFindProperty = (void *)GetProcAddress(mod, "DevFindProperty");
test_CM_MapCrToWin32Err(); test_CM_Get_Device_ID_List(); @@ -1132,4 +1186,5 @@ START_TEST(cfgmgr32) test_DevGetObjects(); test_DevCreateObjectQuery(); test_DevGetObjectProperties_invalid(); + test_DevFindProperty_invalid(); }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/main.c | 13 +++++++++++-- dlls/cfgmgr32/tests/cfgmgr32.c | 12 ++++++------ include/devpropdef.h | 9 +++++++++ 3 files changed, 26 insertions(+), 8 deletions(-)
diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index a9a899cfdb8..053a657d381 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -1109,15 +1109,24 @@ HRESULT WINAPI DevGetObjectPropertiesEx( DEV_OBJECT_TYPE type, const WCHAR *id,
static const char *debugstr_DEVPROPERTY( const DEVPROPKEY *key ) { - if (!key) return wine_dbg_sprintf("(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 ) { - FIXME( "(%s, %d, %s, %lu, %p): stub!\n", debugstr_DEVPROPERTY( key ), store, debugstr_w( locale ), props_len, + /* Windows does not validate parameters here. */ + const DEVPROPCOMPKEY comp_key = { *key, store, locale }; + ULONG i; + + TRACE( "(%s, %d, %s, %lu, %p)\n", debugstr_DEVPROPERTY( key ), store, debugstr_w( locale ), props_len, props ); + + for (i = 0; i < props_len; i++) + { + if (IsEqualDevPropCompKey( comp_key, props[i].CompKey )) + return &props[i]; + } return NULL; }
diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index d76678fa66b..f77c15ec0ab 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -640,7 +640,7 @@ static void test_DevGetObjectProperties( DEV_OBJECT_TYPE type, const WCHAR *id, ok( dev_property_val_equal( &exp_props[i], &buf[j] ), "%s != %s\n", debugstr_DEVPROP_val( &buf[j] ), debugstr_DEVPROP_val( &exp_props[i] ) ); found_prop = pDevFindProperty( &exp_props[i].CompKey.Key, DEVPROP_STORE_SYSTEM, NULL, buf_len, buf ); - todo_wine ok( found_prop == &buf[i], "got found_prop %p != %p\n", found_prop, &buf[i] ); + ok( found_prop == &buf[i], "got found_prop %p != %p\n", found_prop, &buf[i] ); winetest_pop_context(); rem_props--; } @@ -677,7 +677,7 @@ static void test_DevGetObjectProperties( DEV_OBJECT_TYPE type, const WCHAR *id, winetest_push_context( "%s", debugstr_DEVPROPKEY( &exp_props[i].CompKey.Key ) ); rem_props--; found_prop = pDevFindProperty( &exp_props[i].CompKey.Key, DEVPROP_STORE_SYSTEM, NULL, buf_len, buf ); - todo_wine ok( found_prop == &buf[j], "got found_prop %p != %p\n", found_prop, &buf[j] ); + ok( found_prop == &buf[j], "got found_prop %p != %p\n", found_prop, &buf[j] ); winetest_pop_context(); break; } @@ -700,7 +700,7 @@ static void test_DevGetObjectProperties( DEV_OBJECT_TYPE type, const WCHAR *id, debugstr_guid( &buf[0].CompKey.Key.fmtid ), buf[0].CompKey.Key.pid ); ok( buf[0].Type == DEVPROP_TYPE_EMPTY, "got Type %#lx\n", buf[0].Type ); found_prop = pDevFindProperty( &DEVPKEY_dummy, DEVPROP_STORE_SYSTEM, NULL, buf_len, buf ); - todo_wine ok( found_prop == &buf[0], "got found_prop %p != %p\n", found_prop, &buf[0] ); + ok( found_prop == &buf[0], "got found_prop %p != %p\n", found_prop, &buf[0] ); pDevFreeObjectProperties( buf_len, buf ); } free( keys ); @@ -773,7 +773,7 @@ static void test_dev_object_iface_props( int line, const DEV_OBJECT *obj, const
found_prop = pDevFindProperty( &property->CompKey.Key, DEVPROP_STORE_SYSTEM, NULL, obj->cPropertyCount, obj->pProperties ); - todo_wine ok( found_prop == property, "got found_prop %p != %p\n", found_prop, property ); + ok( found_prop == property, "got found_prop %p != %p\n", found_prop, property ); free( buf ); winetest_pop_context(); break; @@ -919,7 +919,7 @@ static void test_DevGetObjects( void ) debugstr_guid( &obj->pProperties[0].CompKey.Key.fmtid ), obj->pProperties[0].CompKey.Key.pid, debugstr_guid( &prop->key.fmtid ), prop->key.pid ); found_prop = pDevFindProperty( &prop->key, DEVPROP_STORE_SYSTEM, NULL, obj->cPropertyCount, obj->pProperties ); - todo_wine ok( found_prop == &obj->pProperties[0], "got found_prop %p != %p\n", found_prop, &obj->pProperties[0] ); + ok( found_prop == &obj->pProperties[0], "got found_prop %p != %p\n", found_prop, &obj->pProperties[0] ); } /* Search for a property not in obj->pProperties, we should get NULL, as we haven't requested this * property in the DevGetObjects call. */ @@ -958,7 +958,7 @@ static void test_DevGetObjects( void ) ok( obj->pProperties[0].Type == DEVPROP_TYPE_EMPTY, "got Type %#lx != %#x", obj->pProperties[0].Type, DEVPROP_TYPE_EMPTY ); found_prop = pDevFindProperty( &DEVPKEY_dummy, DEVPROP_STORE_SYSTEM, NULL, obj->cPropertyCount, obj->pProperties ); - todo_wine ok( found_prop == &obj->pProperties[0], "got found_prop %p != %p\n", found_prop, &obj->pProperties[0] ); + ok( found_prop == &obj->pProperties[0], "got found_prop %p != %p\n", found_prop, &obj->pProperties[0] ); } winetest_pop_context(); } diff --git a/include/devpropdef.h b/include/devpropdef.h index de82648e661..b654425dd91 100644 --- a/include/devpropdef.h +++ b/include/devpropdef.h @@ -114,6 +114,15 @@ typedef struct _DEVPROPCOMPKEY PCWSTR LocaleName; } DEVPROPCOMPKEY, *PDEVPROPCOMPKEY;
+#ifndef IsEqualLocaleName +#define IsEqualLocaleName(a,b) ((a) == (b) || ((a) && (b) && !wcsicmp((a),(b)))) +#endif + +#ifndef IsEqualDevPropCompKey +#define IsEqualDevPropCompKey(a,b) (IsEqualDevPropKey((a).Key,(b).Key) && (a).Store == (b).Store && \ + IsEqualLocaleName((a).LocaleName,(b).LocaleName)) +#endif + typedef struct _DEVPROPERTY { DEVPROPCOMPKEY CompKey;
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/tests/cfgmgr32.c | 300 ++++++++++++++++++++++++++++++++- 1 file changed, 299 insertions(+), 1 deletion(-)
diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index f77c15ec0ab..b537ed44dae 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -784,6 +784,17 @@ static void test_dev_object_iface_props( int line, const DEV_OBJECT *obj, const SetupDiDestroyDeviceInfoList( set ); }
+static void filter_add_props( DEVPROP_FILTER_EXPRESSION *filters, ULONG props_len, const DEVPROPERTY *props, BOOL equals ) +{ + ULONG i; + + for (i = 0; i < props_len; i++) + { + filters[i].Operator = equals ? DEVPROP_OPERATOR_EQUALS : DEVPROP_OPERATOR_NOT_EQUALS; + filters[i].Property = props[i]; + } +} + static void test_DevGetObjects( void ) { struct { @@ -810,6 +821,42 @@ static void test_DevGetObjects( void ) 3, }, }; + static const DEVPROP_OPERATOR invalid_ops[] = { + /* Logical operators, need to paired with their CLOSE counterpart. */ + DEVPROP_OPERATOR_AND_OPEN, + DEVPROP_OPERATOR_AND_CLOSE, + DEVPROP_OPERATOR_OR_OPEN, + DEVPROP_OPERATOR_OR_CLOSE, + DEVPROP_OPERATOR_NOT_OPEN, + DEVPROP_OPERATOR_NOT_CLOSE, + /* Mask value, cannot be used by itself in a filter. */ + DEVPROP_OPERATOR_MASK_LOGICAL, + /* Non-existent operators */ + 0xdeadbeef, + }; + static const DEVPROP_OPERATOR boolean_open_ops[] = { + DEVPROP_OPERATOR_AND_OPEN, + DEVPROP_OPERATOR_OR_OPEN, + DEVPROP_OPERATOR_NOT_OPEN, + }; + /* The difference between CLOSE and OPEN operators is always 0x100000, this is just here for clariy's sake. */ + static const DEVPROP_OPERATOR boolean_close_ops[] = { + DEVPROP_OPERATOR_AND_CLOSE, + DEVPROP_OPERATOR_OR_CLOSE, + DEVPROP_OPERATOR_NOT_CLOSE, + }; + CHAR bool_val_extra[] = { DEVPROP_TRUE, 0xde, 0xad, 0xbe, 0xef }; + DEVPROP_BOOLEAN bool_val = DEVPROP_TRUE; + const DEVPROP_FILTER_EXPRESSION valid_filter = { + DEVPROP_OPERATOR_EQUALS, + { + { DEVPKEY_DeviceInterface_Enabled, DEVPROP_STORE_SYSTEM, NULL }, + DEVPROP_TYPE_BOOLEAN, + sizeof( bool_val ), + &bool_val, + } + }; + DEVPROP_FILTER_EXPRESSION filters[4]; const DEV_OBJECT *objects = NULL; DEVPROPCOMPKEY prop_key = {0}; HRESULT hr; @@ -863,6 +910,210 @@ static void test_DevGetObjects( void ) ok( len == 0, "got len %lu\n", len ); ok( !objects, "got objects %p\n", objects );
+ /* Filter expressions */ + memset( filters, 0, sizeof( filters ) ); + filters[0] = valid_filter; + + /* Invalid buffer value */ + filters[0].Property.Buffer = NULL; + len = 0xdeadbeef; + objects = (DEV_OBJECT *)0xdeadbeef; + hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 0, NULL, 1, &filters[0], &len, &objects ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + /* Filters are validated before len and objects are modified. */ + todo_wine ok( len == 0xdeadbeef, "got len %lu\n", len ); + todo_wine ok( objects == (DEV_OBJECT *)0xdeadbeef, "got objects %p\n", objects ); + + /* Mismatching BufferSize */ + len = 0xdeadbeef; + objects = (DEV_OBJECT *)0xdeadbeef; + filters[0].Property.Buffer = &bool_val; + filters[0].Property.BufferSize = 0; + hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 0, NULL, 1, &filters[0], &len, &objects ); + /* BufferSize is not validated in Windows 10 and before, but no objects are returned. */ + todo_wine ok( hr == E_INVALIDARG || broken( hr == S_OK ), "got hr %#lx\n", hr ); + todo_wine ok( len == 0xdeadbeef || broken( !len ), "got len %lu\n", len ); + todo_wine ok( objects == (DEV_OBJECT *)0xdeadbeef || broken( !objects ), "got objects %p\n", objects ); + + len = 0xdeadbeef; + objects = (DEV_OBJECT *)0xdeadbeef; + filters[0].Property.Buffer = bool_val_extra; + filters[0].Property.BufferSize = sizeof( bool_val_extra ); + hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 0, NULL, 1, &filters[0], &len, &objects ); + /* The extra bytes are ignored in Windows 10 and before. */ + todo_wine ok( hr == E_INVALIDARG || broken( hr == S_OK ), "got hr %#lx\n", hr ); + todo_wine ok( len == 0xdeadbeef || broken( len ), "got len %lu\n", len ); + todo_wine ok( objects == (DEV_OBJECT *)0xdeadbeef || broken( !!objects ), "got objects %p\n", objects ); + if (SUCCEEDED( hr )) pDevFreeObjects( len, objects ); + + for (i = 0; i < ARRAY_SIZE( invalid_ops ); i++) + { + winetest_push_context( "invalid_ops[%lu]", i ); + filters[0].Operator = invalid_ops[i]; + + hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 0, NULL, 1, filters, &len, &objects ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 0, NULL, 2, filters, &len, &objects ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + winetest_pop_context(); + } + + memset( &filters[0], 0, sizeof( *filters ) ); + /* MSDN says this is not a valid operator. However, using this does not fail, but the returned object list is empty. */ + filters[0].Operator = DEVPROP_OPERATOR_NONE; + len = 0xdeadbeef; + 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 ); + + filters[1] = valid_filter; + /* DEVPROP_OPERATOR_NONE preceeding the next filter expression has the same result. */ + len = 0xdeadbeef; + 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 ); + + /* However, filter expressions are still validated. */ + filters[1].Property.Buffer = NULL; + len = 0xdeadbeef; + objects = (DEV_OBJECT *)0xdeadbeef; + hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 0, NULL, 2, filters, &len, &objects ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + todo_wine ok( len == 0xdeadbeef, "got len %lu\n", len ); + todo_wine ok( objects == (DEV_OBJECT *)0xdeadbeef, "got objects %p\n", objects ); + + filters[0] = valid_filter; + /* DEVPROP_OPERATOR_EXISTS ignores the property type. */ + len = 0; + objects = NULL; + filters[1].Operator = DEVPROP_OPERATOR_EXISTS; + filters[1].Property.Type = DEVPROP_TYPE_GUID; + hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 0, NULL, 1, &filters[0], &len, &objects ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( len, "got len %lu\n", len ); + pDevFreeObjects( len, objects ); + + /* Don't fetch any properties, but still use them in the filter. */ + len = 0; + objects = NULL; + hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagNone, 0, NULL, 1, &filters[0], &len, &objects ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( len, "got len %lu\n", len ); + pDevFreeObjects( len, objects ); + + /* AND/OR with a single expression */ + memset( filters, 0, sizeof( filters ) ); + filters[0].Operator = DEVPROP_OPERATOR_AND_OPEN; + filters[1] = valid_filter; + filters[2].Operator = DEVPROP_OPERATOR_AND_CLOSE; + len = 0; + objects = NULL; + hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 0, NULL, 3, filters, &len, &objects ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( len, "got len %lu\n", len ); + for (i = 0; i < len; i++) + { + const DEVPROPERTY *prop; + + prop = pDevFindProperty( &valid_filter.Property.CompKey.Key, valid_filter.Property.CompKey.Store, + valid_filter.Property.CompKey.LocaleName, objects[i].cPropertyCount, + objects[i].pProperties ); + ok( !!prop, "got prop %p\n", prop ); + } + pDevFreeObjects( len, objects ); + + filters[0].Operator = DEVPROP_OPERATOR_OR_OPEN; + filters[2].Operator = DEVPROP_OPERATOR_OR_CLOSE; + len = 0; + objects = NULL; + hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 0, NULL, 3, filters, &len, &objects ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( len, "got len %lu\n", len ); + for (i = 0; i < len; i++) + { + const DEVPROPERTY *prop; + + prop = pDevFindProperty( &valid_filter.Property.CompKey.Key, valid_filter.Property.CompKey.Store, + valid_filter.Property.CompKey.LocaleName, objects[i].cPropertyCount, + objects[i].pProperties ); + ok( !!prop, "got prop %p\n", prop ); + } + pDevFreeObjects( len, objects ); + + memset(filters, 0, sizeof( filters ) ); + filters[0] = valid_filter; + filters[0].Operator = DEVPROP_OPERATOR_NOT_EXISTS; + /* All device interfaces have this property, so this should not return any objects. */ + len = 0xdeadbeef; + objects = (DEV_OBJECT *)0xdeadbeef; + hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagNone, 0, NULL, 1, &filters[0], &len, &objects ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + todo_wine ok( len == 0, "got len %lu\n", len ); + todo_wine ok( !objects, "got objects %p\n", objects ); + + /* Empty expressions */ + memset( filters, 0, sizeof( filters ) ); + for (i = 0; i < ARRAY_SIZE( boolean_open_ops ); i++) + { + DEVPROP_OPERATOR open = boolean_open_ops[i], close = boolean_close_ops[i]; + + winetest_push_context( "open=%#x, close=%#x", open, close ); + + filters[0].Operator = open; + filters[1].Operator = close; + len = 0xdeadbeef; + objects = (DEV_OBJECT *)0xdeadbeef; + hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagNone, 0, NULL, 2, filters, &len, &objects ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + todo_wine ok( len == 0xdeadbeef, "got len %lu\n", len ); + todo_wine ok( objects == (DEV_OBJECT *)0xdeadbeef, "got objects %p\n", objects ); + + /* Empty nested expressions */ + filters[0].Operator = filters[1].Operator = open; + filters[2].Operator = filters[3].Operator = close; + len = 0xdeadbeef; + objects = (DEV_OBJECT *)0xdeadbeef; + hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagNone, 0, NULL, 4, filters, &len, &objects ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + todo_wine ok( len == 0xdeadbeef, "got len %lu\n", len ); + todo_wine ok( objects == (DEV_OBJECT *)0xdeadbeef, "got objects %p\n", objects ); + + winetest_pop_context(); + } + + memset( filters, 0, sizeof( filters ) ); + filters[1] = valid_filter; + /* Improperly paired expressions */ + for (i = 0; i < ARRAY_SIZE( boolean_open_ops ); i++) + { + DEVPROP_OPERATOR open = boolean_open_ops[i]; + ULONG j; + + for (j = 0; j < ARRAY_SIZE( boolean_close_ops ) && i != j; j++) + { + DEVPROP_OPERATOR close = boolean_close_ops[j]; + + winetest_push_context( "open=%#x, close=%#x", open, close ); + + filters[0].Operator = open; + filters[2].Operator = close; + len = 0xdeadbeef; + objects = (DEV_OBJECT *)0xdeadbeef; + hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagNone, 0, NULL, 3, filters, &len, &objects ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + todo_wine ok( len == 0xdeadbeef, "got len %lu\n", len ); + todo_wine ok( objects == (DEV_OBJECT *)0xdeadbeef, "got objects %p\n", objects ); + + winetest_pop_context(); + } + } + for (i = 0; i < ARRAY_SIZE( test_cases ); i++) { const DEV_OBJECT *objects = NULL; @@ -876,7 +1127,10 @@ static void test_DevGetObjects( void ) ok( hr == S_OK, "got hr %#lx\n", hr ); for (j = 0; j < len; j++) { - const DEV_OBJECT *obj = &objects[j]; + DEVPROP_FILTER_EXPRESSION *filters; + const DEV_OBJECT *obj = &objects[j], *objects2; + ULONG k, len2 = 0; + BOOL found = FALSE;
winetest_push_context( "device %s", debugstr_w( obj->pszObjectId ) ); ok( obj->ObjectType == test_cases[i].object_type, "got ObjectType %d\n", obj->ObjectType ); @@ -884,6 +1138,50 @@ static void test_DevGetObjects( void ) winetest_push_context( "%d", __LINE__ ); test_DevGetObjectProperties( obj->ObjectType, obj->pszObjectId, obj->pProperties, obj->cPropertyCount ); winetest_pop_context(); + + /* Create a filter for all properties of this object. */ + filters = calloc( obj->cPropertyCount, sizeof( *filters ) ); + /* If there are no logical operators present, then logical AND is used. */ + filter_add_props( filters, obj->cPropertyCount, obj->pProperties, TRUE ); + hr = pDevGetObjects( test_cases[i].object_type, DevQueryFlagAllProperties, 0, NULL, obj->cPropertyCount, + filters, &len2, &objects2 ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + /* For device interface objects, DEVPKEY_Device_Instance and DEVPKEY_DeviceInterface_ClassGuid are a + * 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 ); + else + ok( len2, "got len2 %lu\n", len2 ); + for (k = 0; k < len2; k++) + { + if (!wcsicmp(objects2[k].pszObjectId, obj->pszObjectId)) + { + found = TRUE; + break; + } + } + ok( found, "failed to get object using query filters\n" ); + pDevFreeObjects( len2, objects2 ); + + /* Create a filter to _not_ match this object. */ + len2 = 0; + found = FALSE; + filter_add_props( filters, obj->cPropertyCount, obj->pProperties, FALSE ); + hr = pDevGetObjects( test_cases[i].object_type, DevQueryFlagAllProperties, 0, NULL, obj->cPropertyCount, + filters, &len2, &objects2 ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + for (k = 0; k < len2; k++) + { + if (!wcsicmp(objects2[k].pszObjectId, obj->pszObjectId)) + { + found = TRUE; + break; + } + } + todo_wine ok( !found, "expected to not find object\n" ); + pDevFreeObjects( len2, objects2 ); + free( filters ); winetest_pop_context(); } pDevFreeObjects( len, objects );
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/main.c | 169 ++++++++++++++++++++++++++++++++- dlls/cfgmgr32/tests/cfgmgr32.c | 50 +++++----- 2 files changed, 192 insertions(+), 27 deletions(-)
diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index 053a657d381..a3591bca5e2 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -20,6 +20,7 @@ #include "wine/debug.h" #include "wine/rbtree.h" #include "winreg.h" +#include "winternl.h" #include "cfgmgr32.h" #include "winuser.h" #include "dbt.h" @@ -317,6 +318,168 @@ static BOOL dev_properties_append( DEVPROPERTY **properties, ULONG *props_len, c return TRUE; }
+static HRESULT stack_push( DEVPROP_OPERATOR **stack, ULONG *len, DEVPROP_OPERATOR op ) +{ + DEVPROP_OPERATOR *tmp; + + if (!(tmp = realloc( *stack, (*len + 1) * sizeof( op ) ))) + return E_OUTOFMEMORY; + *stack = tmp; + tmp[*len] = op; + *len += 1; + return S_OK; +} + +static DEVPROP_OPERATOR stack_pop( DEVPROP_OPERATOR **stack, ULONG *len ) +{ + DEVPROP_OPERATOR op = DEVPROP_OPERATOR_NONE; + + if (*len) + { + op = (*stack)[*len - 1]; + *len -= 1; + } + return op; +} + +static BOOL devprop_type_validate( DEVPROPTYPE type, ULONG buf_size ) +{ + static const DWORD type_size[] = { + 0, 0, + sizeof( BYTE ),sizeof( BYTE ), + sizeof( INT16 ), sizeof( INT16 ), + sizeof( INT32 ), sizeof( INT32 ), + sizeof( INT64 ), sizeof( INT64 ), + sizeof( FLOAT ), sizeof( DOUBLE ), sizeof( DECIMAL ), + sizeof( GUID ), + sizeof( CURRENCY ), + sizeof( DATE ), + sizeof( FILETIME ), + sizeof( DEVPROP_BOOLEAN ), + [DEVPROP_TYPE_DEVPROPKEY] = sizeof( DEVPROPKEY ), + [DEVPROP_TYPE_DEVPROPTYPE] = sizeof( DEVPROPTYPE ), + [DEVPROP_TYPE_ERROR] = sizeof( ULONG ), + [DEVPROP_TYPE_NTSTATUS] = sizeof( NTSTATUS ) + }; + DWORD mod = type & DEVPROP_MASK_TYPEMOD, size; + + if (mod && mod != DEVPROP_TYPEMOD_ARRAY && mod != DEVPROP_TYPEMOD_LIST) + return FALSE; + + switch (type & DEVPROP_MASK_TYPE) + { + case DEVPROP_TYPE_EMPTY: + case DEVPROP_TYPE_NULL: + return !mod; + case DEVPROP_TYPE_SECURITY_DESCRIPTOR: + case DEVPROP_TYPE_STRING_INDIRECT: + return !mod && !!buf_size; + + case DEVPROP_TYPE_STRING: + case DEVPROP_TYPE_SECURITY_DESCRIPTOR_STRING: + return mod != DEVPROP_TYPEMOD_ARRAY && !!buf_size; + default: + /* The only valid modifier for the remaining types is DEVPROP_TYPEMOD_ARRAY */ + if (mod && mod != DEVPROP_TYPEMOD_ARRAY) + return FALSE; + if ((type & DEVPROP_MASK_TYPE) > DEVPROP_TYPE_NTSTATUS) + return FALSE; + size = type_size[type & DEVPROP_MASK_TYPE]; + } + + return mod == DEVPROP_TYPEMOD_ARRAY ? buf_size >= size : buf_size == size; +} + +static HRESULT devprop_filters_validate( ULONG filters_len, const DEVPROP_FILTER_EXPRESSION *filters ) +{ + DEVPROP_OPERATOR *stack = NULL; + ULONG i, logical_open, stack_top = 0; + HRESULT hr = S_OK; + + for (i = 0; i < filters_len; i++) + { + const DEVPROP_FILTER_EXPRESSION *filter = &filters[i]; + const DEVPROPERTY *prop = &filter->Property; + DEVPROP_OPERATOR op = filter->Operator; + 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; + + if ((compare && compare > DEVPROP_OPERATOR_CONTAINS) + || (logical && (op & DEVPROP_OPERATOR_MASK_NOT_LOGICAL)) + || (array && (op != DEVPROP_OPERATOR_ARRAY_CONTAINS)) + || !!prop->Buffer != !!prop->BufferSize) + { + hr = E_INVALIDARG; + break; + } + if (!op) continue; + if (compare && compare != DEVPROP_OPERATOR_EXISTS + && !devprop_type_validate( prop->Type, prop->BufferSize )) + { + hr = E_INVALIDARG; + break; + } + + switch (modifier) + { + case DEVPROP_OPERATOR_NONE: + case DEVPROP_OPERATOR_MODIFIER_NOT: + case DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE: + break; + default: + hr = E_INVALIDARG; + break; + } + + switch (list) + { + case DEVPROP_OPERATOR_NONE: + case DEVPROP_OPERATOR_LIST_CONTAINS: + case DEVPROP_OPERATOR_LIST_ELEMENT_BEGINS_WITH: + case DEVPROP_OPERATOR_LIST_ELEMENT_ENDS_WITH: + case DEVPROP_OPERATOR_LIST_ELEMENT_CONTAINS: + break; + default: + hr = E_INVALIDARG; + break; + } + + switch (logical) + { + case DEVPROP_OPERATOR_NONE: + break; + case DEVPROP_OPERATOR_AND_OPEN: + case DEVPROP_OPERATOR_OR_OPEN: + case DEVPROP_OPERATOR_NOT_OPEN: + hr = stack_push( &stack, &stack_top, logical ); + logical_open = i; + break; + case DEVPROP_OPERATOR_AND_CLOSE: + case DEVPROP_OPERATOR_OR_CLOSE: + case DEVPROP_OPERATOR_NOT_CLOSE: + { + DEVPROP_OPERATOR top = stack_pop( &stack, &stack_top ); + if (logical - top != (DEVPROP_OPERATOR_AND_CLOSE - DEVPROP_OPERATOR_AND_OPEN) || logical_open == i - 1) + hr = E_INVALIDARG; + break; + } + default: + hr = E_INVALIDARG; + break; + } + + if (FAILED( hr )) break; + } + + if (stack_top) + hr = E_INVALIDARG; + free( stack ); + return hr; +} + 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 ) { @@ -533,7 +696,8 @@ HRESULT WINAPI DevGetObjectsEx( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_l params_len, params, objs_len, objs );
if (!!props_len != !!props || !!filters_len != !!filters || !!params_len != !!params || (flags & ~valid_flags) - || (props_len && (flags & DevQueryFlagAllProperties))) + || (props_len && (flags & DevQueryFlagAllProperties)) + || FAILED( devprop_filters_validate( filters_len, filters ) )) return E_INVALIDARG; if (filters) FIXME( "Query filters are not supported!\n" ); @@ -985,7 +1149,8 @@ HRESULT WINAPI DevCreateObjectQueryEx( DEV_OBJECT_TYPE type, ULONG flags, ULONG filters, params_len, params, callback, user_data, devquery );
if (!!props_len != !!props || !!filters_len != !!filters || !!params_len != !!params || (flags & ~valid_flags) || !callback - || (props_len && (flags & DevQueryFlagAllProperties))) + || (props_len && (flags & DevQueryFlagAllProperties)) + || FAILED( devprop_filters_validate( filters_len, filters ) )) return E_INVALIDARG; if (filters) FIXME( "Query filters are not supported!\n" ); diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index b537ed44dae..ba08df743e2 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -919,10 +919,10 @@ static void test_DevGetObjects( void ) len = 0xdeadbeef; objects = (DEV_OBJECT *)0xdeadbeef; hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 0, NULL, 1, &filters[0], &len, &objects ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); /* Filters are validated before len and objects are modified. */ - todo_wine ok( len == 0xdeadbeef, "got len %lu\n", len ); - todo_wine ok( objects == (DEV_OBJECT *)0xdeadbeef, "got objects %p\n", objects ); + ok( len == 0xdeadbeef, "got len %lu\n", len ); + ok( objects == (DEV_OBJECT *)0xdeadbeef, "got objects %p\n", objects );
/* Mismatching BufferSize */ len = 0xdeadbeef; @@ -931,9 +931,9 @@ static void test_DevGetObjects( void ) filters[0].Property.BufferSize = 0; hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 0, NULL, 1, &filters[0], &len, &objects ); /* BufferSize is not validated in Windows 10 and before, but no objects are returned. */ - todo_wine ok( hr == E_INVALIDARG || broken( hr == S_OK ), "got hr %#lx\n", hr ); - todo_wine ok( len == 0xdeadbeef || broken( !len ), "got len %lu\n", len ); - todo_wine ok( objects == (DEV_OBJECT *)0xdeadbeef || broken( !objects ), "got objects %p\n", objects ); + ok( hr == E_INVALIDARG || broken( hr == S_OK ), "got hr %#lx\n", hr ); + ok( len == 0xdeadbeef || broken( !len ), "got len %lu\n", len ); + ok( objects == (DEV_OBJECT *)0xdeadbeef || broken( !objects ), "got objects %p\n", objects );
len = 0xdeadbeef; objects = (DEV_OBJECT *)0xdeadbeef; @@ -941,9 +941,9 @@ static void test_DevGetObjects( void ) filters[0].Property.BufferSize = sizeof( bool_val_extra ); hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 0, NULL, 1, &filters[0], &len, &objects ); /* The extra bytes are ignored in Windows 10 and before. */ - todo_wine ok( hr == E_INVALIDARG || broken( hr == S_OK ), "got hr %#lx\n", hr ); - todo_wine ok( len == 0xdeadbeef || broken( len ), "got len %lu\n", len ); - todo_wine ok( objects == (DEV_OBJECT *)0xdeadbeef || broken( !!objects ), "got objects %p\n", objects ); + ok( hr == E_INVALIDARG || broken( hr == S_OK ), "got hr %#lx\n", hr ); + ok( len == 0xdeadbeef || broken( len ), "got len %lu\n", len ); + ok( objects == (DEV_OBJECT *)0xdeadbeef || broken( !!objects ), "got objects %p\n", objects ); if (SUCCEEDED( hr )) pDevFreeObjects( len, objects );
for (i = 0; i < ARRAY_SIZE( invalid_ops ); i++) @@ -952,10 +952,10 @@ static void test_DevGetObjects( void ) filters[0].Operator = invalid_ops[i];
hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 0, NULL, 1, filters, &len, &objects ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr );
hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 0, NULL, 2, filters, &len, &objects ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr );
winetest_pop_context(); } @@ -984,9 +984,9 @@ static void test_DevGetObjects( void ) len = 0xdeadbeef; objects = (DEV_OBJECT *)0xdeadbeef; hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 0, NULL, 2, filters, &len, &objects ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); - todo_wine ok( len == 0xdeadbeef, "got len %lu\n", len ); - todo_wine ok( objects == (DEV_OBJECT *)0xdeadbeef, "got objects %p\n", objects ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( len == 0xdeadbeef, "got len %lu\n", len ); + ok( objects == (DEV_OBJECT *)0xdeadbeef, "got objects %p\n", objects );
filters[0] = valid_filter; /* DEVPROP_OPERATOR_EXISTS ignores the property type. */ @@ -1054,8 +1054,8 @@ static void test_DevGetObjects( void ) objects = (DEV_OBJECT *)0xdeadbeef; hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagNone, 0, NULL, 1, &filters[0], &len, &objects ); ok( hr == S_OK, "got hr %#lx\n", hr ); - todo_wine ok( len == 0, "got len %lu\n", len ); - todo_wine ok( !objects, "got objects %p\n", objects ); + ok( len == 0, "got len %lu\n", len ); + ok( !objects, "got objects %p\n", objects );
/* Empty expressions */ memset( filters, 0, sizeof( filters ) ); @@ -1070,9 +1070,9 @@ static void test_DevGetObjects( void ) len = 0xdeadbeef; objects = (DEV_OBJECT *)0xdeadbeef; hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagNone, 0, NULL, 2, filters, &len, &objects ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); - todo_wine ok( len == 0xdeadbeef, "got len %lu\n", len ); - todo_wine ok( objects == (DEV_OBJECT *)0xdeadbeef, "got objects %p\n", objects ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( len == 0xdeadbeef, "got len %lu\n", len ); + ok( objects == (DEV_OBJECT *)0xdeadbeef, "got objects %p\n", objects );
/* Empty nested expressions */ filters[0].Operator = filters[1].Operator = open; @@ -1080,9 +1080,9 @@ static void test_DevGetObjects( void ) len = 0xdeadbeef; objects = (DEV_OBJECT *)0xdeadbeef; hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagNone, 0, NULL, 4, filters, &len, &objects ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); - todo_wine ok( len == 0xdeadbeef, "got len %lu\n", len ); - todo_wine ok( objects == (DEV_OBJECT *)0xdeadbeef, "got objects %p\n", objects ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( len == 0xdeadbeef, "got len %lu\n", len ); + ok( objects == (DEV_OBJECT *)0xdeadbeef, "got objects %p\n", objects );
winetest_pop_context(); } @@ -1106,9 +1106,9 @@ static void test_DevGetObjects( void ) len = 0xdeadbeef; objects = (DEV_OBJECT *)0xdeadbeef; hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagNone, 0, NULL, 3, filters, &len, &objects ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); - todo_wine ok( len == 0xdeadbeef, "got len %lu\n", len ); - todo_wine ok( objects == (DEV_OBJECT *)0xdeadbeef, "got objects %p\n", objects ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( len == 0xdeadbeef, "got len %lu\n", len ); + ok( objects == (DEV_OBJECT *)0xdeadbeef, "got objects %p\n", objects );
winetest_pop_context(); }
From: Vibhav Pant vibhavp@gmail.com
Support filter expressions consisting of basic comparison operators. --- dlls/cfgmgr32/main.c | 237 +++++++++++++++++++++++++++++++-- dlls/cfgmgr32/tests/cfgmgr32.c | 14 +- 2 files changed, 236 insertions(+), 15 deletions(-)
diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index a3591bca5e2..4622420c9ce 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -318,6 +318,220 @@ 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 const DEVPROPERTY *find_devprop( ULONG props_len, const DEVPROPERTY *props, const DEVPROPCOMPKEY *key ) +{ + ULONG i; + for (i = 0; i < props_len; i++) + { + if (IsEqualDevPropKey( key->Key, props[i].CompKey.Key ) && key->Store == props[i].CompKey.Store) + return &props[i]; + } + return NULL; +} + +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. */ + if ((*prop = find_devprop( ctx->obj->cPropertyCount, ctx->obj->pProperties, key )) || ctx->all_props) + return S_OK; + if (!(*prop = find_devprop( ctx->props_cache_len, ctx->props_cache, key ))) + { + 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 */ + buf = calloc( 1, props[0].BufferSize ); + if (!buf) + 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: + if (FAILED( hr = devprop_filter_context_find_prop( ctx, &filter->Property.CompKey, &prop ))) + 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; + + if (FAILED(( hr = devprop_filter_context_find_prop( ctx, &filter->Property.CompKey, &prop ) ))) + 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; @@ -568,8 +782,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; @@ -639,14 +854,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 @@ -699,15 +921,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; @@ -1061,7 +1282,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 ba08df743e2..cd211981f02 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -967,8 +967,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. */ @@ -976,8 +976,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; @@ -1150,7 +1150,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++) @@ -1179,8 +1179,8 @@ static void test_DevGetObjects( void ) break; } } - todo_wine ok( !found, "expected to not find object\n" ); - pDevFreeObjects( len2, objects2 ); + ok( !found, "expected to not find object\n" ); + DevFreeObjects( len2, objects2 ); free( filters ); winetest_pop_context(); }