Support filter expressions consisting of basic comparison operators in DevGetObjects(Ex).
-- v9: 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. cfgmgr32: Implement DevFindProperty. cfgmgr32: Add stub for DevFindProperty.
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/cfgmgr32.spec | 1 + dlls/cfgmgr32/main.c | 14 ++++++ dlls/cfgmgr32/tests/cfgmgr32.c | 82 +++++++++++++++++++++++++++++----- 3 files changed, 85 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..2b0e4c029ba 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_DEVPROPKEY( 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_DEVPROPKEY( 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..6564a4eb5aa 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,23 @@ static const char *debugstr_DEVPROP_val( const DEVPROPERTY *prop ) } }
+static const char *debugstr_DEVPROPKEY( const DEVPROPKEY *key ) +{ + if (!key) return "(null)"; + return wine_dbg_sprintf( "{%s, %04lx}", debugstr_guid( &key->fmtid ), key->pid ); +} + static 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 +632,18 @@ 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 ); + 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)) + { + const DEVPROPERTY *found_prop; + 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 +675,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 +697,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 +731,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 +773,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 +818,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 +911,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 +954,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 +1154,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 +1180,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 +1189,5 @@ START_TEST(cfgmgr32) test_DevGetObjects(); test_DevCreateObjectQuery(); test_DevGetObjectProperties_invalid(); + test_DevFindProperty_invalid(); }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/main.c | 18 +++++++++++++++--- dlls/cfgmgr32/tests/cfgmgr32.c | 12 ++++++------ include/devpropdef.h | 9 +++++++++ 3 files changed, 30 insertions(+), 9 deletions(-)
diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index 2b0e4c029ba..f914fa0680b 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -1109,15 +1109,27 @@ HRESULT WINAPI DevGetObjectPropertiesEx( DEV_OBJECT_TYPE type, const WCHAR *id,
static const char *debugstr_DEVPROPKEY( const DEVPROPKEY *key ) { - if (!key) return wine_dbg_sprintf("(null)"); + if (!key) return "(null)"; return wine_dbg_sprintf( "{%s, %04lx}", debugstr_guid( &key->fmtid ), key->pid ); }
const DEVPROPERTY *WINAPI DevFindProperty( const DEVPROPKEY *key, DEVPROPSTORE store, const WCHAR *locale, ULONG props_len, const DEVPROPERTY *props ) { - FIXME( "(%s, %d, %s, %lu, %p): stub!\n", debugstr_DEVPROPKEY( key ), store, debugstr_w( locale ), props_len, - props ); + DEVPROPCOMPKEY comp_key; + ULONG i; + + TRACE( "(%s, %d, %s, %lu, %p)\n", debugstr_DEVPROPKEY( key ), store, debugstr_w( locale ), props_len, props ); + + /* Windows does not validate parameters here. */ + comp_key.Key = *key; + comp_key.Store = store; + comp_key.LocaleName = locale; + 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 6564a4eb5aa..6384262074b 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -642,7 +642,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--; @@ -680,7 +680,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; } @@ -703,7 +703,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 ); @@ -776,7 +776,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; @@ -922,7 +922,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. */ @@ -961,7 +961,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 | 313 ++++++++++++++++++++++++++++++++- 1 file changed, 310 insertions(+), 3 deletions(-)
diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index 6384262074b..2cf377e3a81 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -787,6 +787,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 { @@ -813,6 +824,43 @@ 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, + } + }; + static const DEVPROPCOMPKEY prop_iface_class = { DEVPKEY_DeviceInterface_ClassGuid, DEVPROP_STORE_SYSTEM, NULL }; + DEVPROP_FILTER_EXPRESSION filters[4]; const DEV_OBJECT *objects = NULL; DEVPROPCOMPKEY prop_key = {0}; HRESULT hr; @@ -866,6 +914,237 @@ 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[0].Operator = DEVPROP_OPERATOR_EXISTS; + filters[0].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. */ + filters[0] = valid_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 ); + for (i = 0; i < len; i++) + { + /* No properties should have been fetched. */ + winetest_push_context( "object %s", debugstr_w( objects[i].pszObjectId ) ); + ok( !objects[i].cPropertyCount, "got cPropertyCount %lu\n", objects[i].cPropertyCount ); + ok( !objects[i].pProperties, "got pProperties %p\n", objects[i].pProperties ); + winetest_pop_context(); + } + pDevFreeObjects( len, objects ); + + /* Request and filter different properties, make sure we *only* get the properties we requested. */ + len = 0; + objects = NULL; + hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagNone, 1, &prop_iface_class, 1, &filters[0], &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; + + winetest_push_context( "object %s", debugstr_w( objects[i].pszObjectId ) ); + ok( objects[i].cPropertyCount == 1, "got cPropertyCount %lu\n", objects[i].cPropertyCount ); + prop = pDevFindProperty( &prop_iface_class.Key, prop_iface_class.Store, prop_iface_class.LocaleName, + objects[i].cPropertyCount, objects[i].pProperties ); + ok (!!prop, "got prop %p\n", prop ); + winetest_pop_context(); + } + + /* 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; @@ -879,7 +1158,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 ); @@ -887,11 +1169,36 @@ 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 ); + free( filters ); winetest_pop_context(); } pDevFreeObjects( len, objects );
- /* Get all objects of this type, but only with a single requested property. */ for (j = 0; j < test_cases[i].props_len; j++) { @@ -934,7 +1241,6 @@ static void test_DevGetObjects( void ) pDevFreeObjects( len, objects ); winetest_pop_context(); } - winetest_pop_context();
/* Get all objects of this type, but with a non existent property. The returned objects will still have this * property, albeit with Type set to DEVPROP_TYPE_EMPTY. */ @@ -965,6 +1271,7 @@ static void test_DevGetObjects( void ) } winetest_pop_context(); } + winetest_pop_context(); pDevFreeObjects( len, objects ); } }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/main.c | 176 ++++++++++++++++++++++++++++++++- dlls/cfgmgr32/tests/cfgmgr32.c | 50 +++++----- 2 files changed, 199 insertions(+), 27 deletions(-)
diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index f914fa0680b..d11b1fd5f96 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,175 @@ 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) + { + FIXME( "Unknown DEVPROPTYPE value: %#lx\n", type ); + 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 ((type & DEVPROP_MASK_TYPE) > DEVPROP_TYPE_NTSTATUS || + (mod && mod != DEVPROP_TYPEMOD_ARRAY)) + { + FIXME( "Unknown DEVPROPTYPE value: %#lx\n", type ); + 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 = 0, 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) + { + FIXME( "Unknown operator: %#x\n", op ); + 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 ); + /* The operator should be correct paired, and shouldn't be empty. */ + 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 +703,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 +1156,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 2cf377e3a81..ff6e04e35b2 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -923,10 +923,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; @@ -935,9 +935,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; @@ -945,9 +945,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++) @@ -956,10 +956,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(); } @@ -988,9 +988,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. */ @@ -1085,8 +1085,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 ) ); @@ -1101,9 +1101,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; @@ -1111,9 +1111,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(); } @@ -1137,9 +1137,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 | 250 ++++++++++++++++++++++++++++++--- dlls/cfgmgr32/tests/cfgmgr32.c | 10 +- 2 files changed, 238 insertions(+), 22 deletions(-)
diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index d11b1fd5f96..656d1988503 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -318,6 +318,173 @@ static BOOL dev_properties_append( DEVPROPERTY **properties, ULONG *props_len, c return TRUE; }
+static const char *debugstr_DEVPROPKEY( const DEVPROPKEY *key ) +{ + if (!key) return "(null)"; + return wine_dbg_sprintf( "{%s, %04lx}", debugstr_guid( &key->fmtid ), key->pid ); +} + +static const char *debugstr_DEVPROPCOMPKEY( const DEVPROPCOMPKEY *key ) +{ + if (!key) return "(null)"; + return wine_dbg_sprintf( "{%s, %d, %s}", debugstr_DEVPROPKEY( &key->Key ), key->Store, + debugstr_w( key->LocaleName ) ); +} + +static const char *debugstr_DEV_OBJECT( const DEV_OBJECT *obj ) +{ + if (!obj) return "(null)"; + return wine_dbg_sprintf( "{%d, %s, %lu, %p}", obj->ObjectType, debugstr_w( obj->pszObjectId ), obj->cPropertyCount, + obj->pProperties ); +} + +static int devproperty_compare( const void *p1, const void *p2 ) +{ + const DEVPROPCOMPKEY *key1 = &((DEVPROPERTY *)p1)->CompKey; + const DEVPROPCOMPKEY *key2 = &((DEVPROPERTY *)p2)->CompKey; + int cmp = memcmp( key1, key2, offsetof( DEVPROPCOMPKEY, LocaleName )); + + if (cmp) + return cmp; + if (key1->LocaleName == key2->LocaleName) + return 0; + if (!key1->LocaleName) + return -1; + if (!key2->LocaleName) + return 1; + return wcsicmp( key1->LocaleName, key2->LocaleName ); +} + +static const char *debugstr_DEVPROP_OPERATOR( DEVPROP_OPERATOR op ) +{ + DWORD compare = op & DEVPROP_OPERATOR_MASK_EVAL; + DWORD list = op & DEVPROP_OPERATOR_MASK_LIST; + DWORD modifier = op & DEVPROP_OPERATOR_MASK_MODIFIER; + DWORD logical = op & DEVPROP_OPERATOR_MASK_LOGICAL; + DWORD array = op & DEVPROP_OPERATOR_MASK_ARRAY; + + return wine_dbg_sprintf( "(%#lx|%#lx|%#lx|%#lx|%#lx)", list, array, modifier, compare, logical ); +} + + +static const char *debugstr_DEVPROP_FILTER_EXPRESSION( const DEVPROP_FILTER_EXPRESSION *filter ) +{ + if (!filter) return "(null)"; + return wine_dbg_sprintf( "{%s, {%s, %#lx, %lu, %p}}", debugstr_DEVPROP_OPERATOR( filter->Operator ), + debugstr_DEVPROPCOMPKEY( &filter->Property.CompKey ), filter->Property.Type, + filter->Property.BufferSize, filter->Property.Buffer ); +} + +/* Evaluate a filter expression containing comparison operator. */ +static HRESULT devprop_filter_eval_compare( const DEV_OBJECT *obj, const DEVPROP_FILTER_EXPRESSION *filter ) +{ + const DEVPROPERTY *cmp_prop = &filter->Property; + DEVPROP_OPERATOR op = filter->Operator; + const DEVPROPERTY *prop = NULL; + BOOL ret = FALSE; + + TRACE( "(%s, %s)\n", debugstr_DEV_OBJECT( obj ), debugstr_DEVPROP_FILTER_EXPRESSION( filter ) ); + + if ((op & DEVPROP_OPERATOR_MASK_MODIFIER) & ~(DEVPROP_OPERATOR_MODIFIER_NOT | DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE)) + return E_INVALIDARG; + + switch (filter->Operator & DEVPROP_OPERATOR_MASK_EVAL) + { + case DEVPROP_OPERATOR_EXISTS: + prop = bsearch( &filter->Property.CompKey, obj->pProperties, obj->cPropertyCount, sizeof( *obj->pProperties ), + devproperty_compare ); + ret = prop && prop->Type != DEVPROP_TYPE_EMPTY; + break; + case DEVPROP_OPERATOR_EQUALS: + case DEVPROP_OPERATOR_LESS_THAN: + case DEVPROP_OPERATOR_GREATER_THAN: + case DEVPROP_OPERATOR_LESS_THAN_EQUALS: + case DEVPROP_OPERATOR_GREATER_THAN_EQUALS: + { + int cmp = 0; + + prop = bsearch( &filter->Property.CompKey, obj->pProperties, obj->cPropertyCount, sizeof( *obj->pProperties ), + devproperty_compare ); + if (prop && cmp_prop->Type == prop->Type && cmp_prop->BufferSize == prop->BufferSize) + { + switch (prop->Type) + { + case DEVPROP_TYPE_STRING: + cmp = op & DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE ? wcsicmp( prop->Buffer, cmp_prop->Buffer ) + : wcscmp( prop->Buffer, cmp_prop->Buffer ); + break; + default: + cmp = memcmp( prop->Buffer, cmp_prop->Buffer, prop->BufferSize ); + break; + } + if (op == DEVPROP_OPERATOR_EQUALS) + ret = !cmp; + else if (op & DEVPROP_OPERATOR_EQUALS && !cmp) + ret = TRUE; + else + ret = (op & DEVPROP_OPERATOR_LESS_THAN) ? cmp < 0 : cmp > 0; + } + break; + } + case DEVPROP_OPERATOR_BITWISE_AND: + case DEVPROP_OPERATOR_BITWISE_OR: + case DEVPROP_OPERATOR_BEGINS_WITH: + case DEVPROP_OPERATOR_ENDS_WITH: + case DEVPROP_OPERATOR_CONTAINS: + default: + FIXME( "Unsupported operator: %s", debugstr_DEVPROP_OPERATOR( filter->Operator & DEVPROP_OPERATOR_MASK_EVAL ) ); + return S_OK; + } + + if (op & DEVPROP_OPERATOR_MODIFIER_NOT) + ret = !ret; + return ret ? S_OK : S_FALSE; +} + +/* Return S_OK if the specified filter expressions match the object, S_FALSE if it doesn't. */ +static HRESULT devprop_filter_matches_object( const DEV_OBJECT *obj, ULONG filters_len, + const DEVPROP_FILTER_EXPRESSION *filters ) +{ + HRESULT hr = S_OK; + ULONG i; + + TRACE( "(%s, %lu, %p)\n", debugstr_DEV_OBJECT( obj ), filters_len, filters ); + + if (!filters_len) + return S_OK; + + /* By default, the evaluation is performed by AND-ing all individual filter expressions. */ + for (i = 0; i < filters_len; i++) + { + const DEVPROP_FILTER_EXPRESSION *filter = &filters[i]; + DEVPROP_OPERATOR op = filter->Operator; + + if (op == DEVPROP_OPERATOR_NONE) + { + hr = S_FALSE; + break; + } + if (op & (DEVPROP_OPERATOR_MASK_LIST | DEVPROP_OPERATOR_MASK_ARRAY)) + { + FIXME( "Unsupported list/array operator: %s\n", debugstr_DEVPROP_OPERATOR( op ) ); + continue; + } + if (op & DEVPROP_OPERATOR_MASK_LOGICAL) + { + FIXME( "Unsupported logical operator: %s\n", debugstr_DEVPROP_OPERATOR( op ) ); + continue; + } + if (op & DEVPROP_OPERATOR_MASK_EVAL) + { + hr = devprop_filter_eval_compare( obj, filter ); + if (FAILED( hr ) || hr == S_FALSE) + break; + } + } + + return hr; +} + static HRESULT stack_push( DEVPROP_OPERATOR **stack, ULONG *len, DEVPROP_OPERATOR op ) { DEVPROP_OPERATOR *tmp; @@ -488,7 +655,7 @@ static HRESULT devprop_filters_validate( ULONG filters_len, const DEVPROP_FILTER }
static HRESULT dev_object_iface_get_props( DEV_OBJECT *obj, HDEVINFO set, SP_DEVICE_INTERFACE_DATA *iface_data, - ULONG props_len, const DEVPROPCOMPKEY *props, BOOL all_props ) + ULONG props_len, const DEVPROPCOMPKEY *props, BOOL all_props, BOOL sort ) { DEVPROPKEY *all_keys = NULL; DWORD keys_len = 0, i = 0; @@ -570,13 +737,51 @@ done: obj->cPropertyCount = 0; obj->pProperties = NULL; } + else if (sort) /* Sort properties by DEVPROPCOMPKEY for faster filter evaluation. */ + qsort( (DEVPROPERTY *)obj->pProperties, obj->cPropertyCount, sizeof( *obj->pProperties ), devproperty_compare ); return hr; }
typedef HRESULT (*enum_device_object_cb)( DEV_OBJECT object, void *context );
-static HRESULT enum_dev_objects( DEV_OBJECT_TYPE type, ULONG props_len, const DEVPROPCOMPKEY *props, - BOOL all_props, enum_device_object_cb callback, void *data ) +static void dev_object_remove_unwanted_props( DEV_OBJECT *obj, ULONG keys_len, const DEVPROPCOMPKEY *keys_wanted ) +{ + DEVPROPERTY *props = (DEVPROPERTY *)obj->pProperties; + ULONG i = 0, j; + + if (!keys_len) + { + DevFreeObjectProperties( obj->cPropertyCount, obj->pProperties ); + obj->cPropertyCount = 0; + obj->pProperties = NULL; + } + + while (i < obj->cPropertyCount) + { + BOOL found = FALSE; + + for (j = 0; j < keys_len; j++) + { + if (IsEqualDevPropCompKey( props[i].CompKey, keys_wanted[j] )) + { + found = TRUE; + break; + } + } + if (!found) + { + free( obj->pProperties[i].Buffer ); + props[i] = props[obj->cPropertyCount - 1]; + obj->cPropertyCount--; + } + else + i++; + } +} + +static HRESULT enum_dev_objects( DEV_OBJECT_TYPE type, ULONG props_len, const DEVPROPCOMPKEY *props, BOOL all_props, + ULONG filters_len, const DEVPROP_FILTER_EXPRESSION *filters, + enum_device_object_cb callback, void *data ) { HKEY iface_key; HRESULT hr = S_OK; @@ -645,15 +850,33 @@ static HRESULT enum_dev_objects( DEV_OBJECT_TYPE type, ULONG props_len, const DE
obj.ObjectType = type; obj.pszObjectId = detail->DevicePath; - if (SUCCEEDED( (hr = dev_object_iface_get_props( &obj, set, &iface, props_len, props, all_props )) )) - hr = callback( obj, data ); + /* If we're also filtering objects, get all properties for this object. Once the filters have been + * evaluated, free properties that have not been requested, and set cPropertyCount to props_len. */ + if (filters_len) + hr = dev_object_iface_get_props( &obj, set, &iface, 0, NULL, TRUE, TRUE ); + else + hr = dev_object_iface_get_props( &obj, set, &iface, props_len, props, all_props, FALSE ); + if (SUCCEEDED( hr )) + { + if (filters_len) + { + hr = devprop_filter_matches_object( &obj, filters_len, filters ); + /* Shrink pProperties to only the desired ones, unless DevQueryFlagAllProperties is set. */ + if (!all_props) + dev_object_remove_unwanted_props( &obj, props_len, props ); + } + if (hr == S_OK) + hr = callback( obj, data ); + else + DevFreeObjectProperties( obj.cPropertyCount, obj.pProperties ); + } }
if (set != INVALID_HANDLE_VALUE) SetupDiDestroyDeviceInfoList( set ); } RegCloseKey( iface_key ); - return hr; + return SUCCEEDED( hr ) ? S_OK : hr; }
struct objects_list @@ -706,15 +929,14 @@ HRESULT WINAPI DevGetObjectsEx( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_l || (props_len && (flags & DevQueryFlagAllProperties)) || FAILED( devprop_filters_validate( filters_len, filters ) )) return E_INVALIDARG; - if (filters) - FIXME( "Query filters are not supported!\n" ); if (params) FIXME( "Query parameters are not supported!\n" );
*objs = NULL; *objs_len = 0;
- hr = enum_dev_objects( type, props_len, props, !!(flags & DevQueryFlagAllProperties), dev_objects_append, &objects ); + hr = enum_dev_objects( type, props_len, props, !!(flags & DevQueryFlagAllProperties), filters_len, filters, + dev_objects_append, &objects ); if (SUCCEEDED( hr )) { *objs = objects.objects; @@ -1068,7 +1290,7 @@ static void CALLBACK device_query_enum_objects_async( TP_CALLBACK_INSTANCE *inst
if (!ctx->filters) hr = enum_dev_objects( ctx->type, ctx->prop_keys_len, ctx->prop_keys, !!(ctx->flags & DevQueryFlagAllProperties), - device_query_context_add_object, ctx ); + 0, NULL, device_query_context_add_object, ctx );
EnterCriticalSection( &ctx->cs ); if (ctx->state == DevQueryStateClosed) @@ -1266,7 +1488,7 @@ HRESULT WINAPI DevGetObjectPropertiesEx( DEV_OBJECT_TYPE type, const WCHAR *id, return HRESULT_FROM_WIN32(err == ERROR_NO_SUCH_DEVICE_INTERFACE ? ERROR_FILE_NOT_FOUND : err); }
- hr = dev_object_iface_get_props( &obj, set, &iface, props_len, props, !!(flags & DevQueryFlagAllProperties) ); + hr = dev_object_iface_get_props( &obj, set, &iface, props_len, props, !!(flags & DevQueryFlagAllProperties), FALSE ); *buf = obj.pProperties; *buf_len = obj.cPropertyCount; break; @@ -1279,12 +1501,6 @@ HRESULT WINAPI DevGetObjectPropertiesEx( DEV_OBJECT_TYPE type, const WCHAR *id, return hr; }
-static const char *debugstr_DEVPROPKEY( const DEVPROPKEY *key ) -{ - if (!key) return "(null)"; - return wine_dbg_sprintf( "{%s, %04lx}", debugstr_guid( &key->fmtid ), key->pid ); -} - const DEVPROPERTY *WINAPI DevFindProperty( const DEVPROPKEY *key, DEVPROPSTORE store, const WCHAR *locale, ULONG props_len, const DEVPROPERTY *props ) { diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index ff6e04e35b2..1e69417d356 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -971,8 +971,8 @@ static void test_DevGetObjects( void ) objects = (DEV_OBJECT *)0xdeadbeef; hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 0, NULL, 1, &filters[0], &len, &objects ); ok( hr == S_OK, "got hr %#lx\n", hr ); - todo_wine ok( !len, "got len %lu\n", len ); - todo_wine ok( !objects, "got objects %p\n", objects ); + ok( !len, "got len %lu\n", len ); + ok( !objects, "got objects %p\n", objects );
filters[1] = valid_filter; /* DEVPROP_OPERATOR_NONE preceeding the next filter expression has the same result. */ @@ -980,8 +980,8 @@ static void test_DevGetObjects( void ) objects = (DEV_OBJECT *)0xdeadbeef; hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 0, NULL, 2, filters, &len, &objects ); ok( hr == S_OK, "got hr %#lx\n", hr ); - todo_wine ok( !len, "got len %lu\n", len ); - todo_wine ok( !objects, "got objects %p\n", objects ); + ok( !len, "got len %lu\n", len ); + ok( !objects, "got objects %p\n", objects );
/* However, filter expressions are still validated. */ filters[1].Property.Buffer = NULL; @@ -1181,7 +1181,7 @@ static void test_DevGetObjects( void ) * unique pair, so there should only be one object. */ if (test_cases[i].object_type == DevObjectTypeDeviceInterface || test_cases[i].object_type == DevObjectTypeDeviceInterfaceDisplay) - todo_wine ok( len2 == 1, "got len2 %lu\n", len2 ); + ok( len2 == 1, "got len2 %lu\n", len2 ); else ok( len2, "got len2 %lu\n", len2 ); for (k = 0; k < len2; k++)
Related question then, do we have tests that the filter applies to properties that weren't requested?
Yes, this one: https://gitlab.winehq.org/wine/wine/-/blob/c495af87400e5b3f978ebfb06ec53ded2...
I have added additional code to make sure that no properties have actually been fetched as well.
What about, instead of having to optionally cache all properties for filtering even when only some were requested, always request all properties here, then hide the undesired ones before the callback? This could be made efficient by keeping undesired properties at the end of the array and changing obj.cPropertyCount.
The latest revision does this, but also sorts the properties by their `DEVPROPCOMPKEY *` value to make filter evaluation a little more performant. I don't know of any applications that rely on the order of the property keys, so it shouldn't affect compatibility.
On Thu Aug 7 10:18:04 2025 +0000, Rémi Bernon wrote:
Should we add a FIXME in case they add new operator bits?
Sure.
On Thu Aug 7 10:18:03 2025 +0000, Rémi Bernon wrote:
Well, I think it was okay to have, would make sure we don't crash in the TRACE (and enable to see it) if the parameters are empty.
Sure. I have moved the comp_key initialization to after the `TRACE` call then.
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/tests/cfgmgr32.c:
{ 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 );
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))
{
const DEVPROPERTY *found_prop;
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] );
```suggestion:-0+0 todo_wine ok( found_prop == &buf[i], "got found_prop %p != %p\n", found_prop, &buf[i] ); ```
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/main.c:
return hr;
}
+static const char *debugstr_DEVPROPKEY( const DEVPROPKEY *key ) +{
- if (!key) return wine_dbg_sprintf("(null)");
```suggestion:-0+0 if (!key) return "(null)"; ```
This has been fixed up in 5d8d16d28075e59e04e0e36577fe4a783c9f7186 but it should be here. I'd suggest to just squash 2a42f91147fe0a2f97483e216709fbcf675ffc49 and 5d8d16d28075e59e04e0e36577fe4a783c9f7186.
This merge request was approved by Rémi Bernon.