Support filter expressions consisting of basic comparison operators in DevGetObjects(Ex).
-- v8: 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 | 81 +++++++++++++++++++++++++++++----- 3 files changed, 84 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..2d06e8dcacc 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,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 +674,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 +696,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 +730,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 +772,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 +817,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 +910,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 +953,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 +1153,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 +1179,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 +1188,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 2d06e8dcacc..07551133283 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -641,7 +641,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--; @@ -679,7 +679,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; } @@ -702,7 +702,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 ); @@ -775,7 +775,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; @@ -921,7 +921,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. */ @@ -960,7 +960,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 | 286 ++++++++++++++++++++++++++++++++- 1 file changed, 282 insertions(+), 4 deletions(-)
diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index 07551133283..5426fa3f536 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -786,6 +786,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 { @@ -812,6 +823,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; @@ -865,6 +912,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; @@ -878,7 +1129,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 ); @@ -886,11 +1140,35 @@ static void test_DevGetObjects( void ) winetest_push_context( "%d", __LINE__ ); test_DevGetObjectProperties( obj->ObjectType, obj->pszObjectId, obj->pProperties, obj->cPropertyCount ); winetest_pop_context(); - 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 ); } 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++) { @@ -933,7 +1211,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. */ @@ -964,6 +1241,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 | 170 ++++++++++++++++++++++++++++++++- dlls/cfgmgr32/tests/cfgmgr32.c | 50 +++++----- 2 files changed, 193 insertions(+), 27 deletions(-)
diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index 053a657d381..9563419465d 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,169 @@ 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 = 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) + { + 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 +697,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 +1150,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 5426fa3f536..0af12233a38 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -921,10 +921,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; @@ -933,9 +933,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; @@ -943,9 +943,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++) @@ -954,10 +954,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(); } @@ -986,9 +986,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. */ @@ -1056,8 +1056,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 ) ); @@ -1072,9 +1072,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; @@ -1082,9 +1082,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(); } @@ -1108,9 +1108,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 | 229 +++++++++++++++++++++++++++++++-- dlls/cfgmgr32/tests/cfgmgr32.c | 10 +- 2 files changed, 226 insertions(+), 13 deletions(-)
diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index 9563419465d..b62e8bc284f 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -318,6 +318,212 @@ 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 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. */ + *prop = DevFindProperty( &key->Key, key->Store, key->LocaleName, ctx->obj->cPropertyCount, ctx->obj->pProperties ); + if (*prop || ctx->all_props) + return S_OK; + *prop = DevFindProperty( &key->Key, key->Store, key->LocaleName, ctx->props_cache_len, ctx->props_cache ); + if (!*prop) + { + 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. */ + if (!(buf = calloc( 1, props[0].BufferSize ))) + 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: + hr = devprop_filter_context_find_prop( ctx, &filter->Property.CompKey, &prop ); + if (FAILED( hr )) + 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; + + hr = devprop_filter_context_find_prop( ctx, &filter->Property.CompKey, &prop ); + if (FAILED( hr )) + 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; @@ -569,8 +775,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; @@ -640,14 +847,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 @@ -700,15 +914,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; @@ -1062,7 +1275,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 0af12233a38..a43e83b34f8 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -969,8 +969,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. */ @@ -978,8 +978,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; @@ -1152,7 +1152,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++)
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/main.c:
return hr;
}
+static const char *debugstr_DEVPROPERTY( const DEVPROPKEY *key ) +{
- if (!key) return wine_dbg_sprintf("(null)");
```suggestion:-0+0 if (!key) return "(null)"; ```
Also, lets make this consistent with the other `debugstr_DEVPROPERTY` elsewhere.
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/main.c:
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 );
```suggestion:-1+0 FIXME( "(%s, %d, %s, %lu, %p): stub!\n", debugstr_DEVPROPERTY( key ), store, debugstr_w( locale ), props_len, props ); ```
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/main.c:
static const char *debugstr_DEVPROPERTY( const DEVPROPKEY *key ) {
- if (!key) return wine_dbg_sprintf("(null)");
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.
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/tests/cfgmgr32.c:
winetest_push_context( "%d", __LINE__ ); test_DevGetObjectProperties( obj->ObjectType, obj->pszObjectId, obj->pProperties, obj->cPropertyCount ); winetest_pop_context();
winetest_pop_context();
Doesn't seem right, there's two push_context right before?
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/tests/cfgmgr32.c:
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 );
These are actually still failing at this point.
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/main.c:
+{
- 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 ),
```suggestion:-0+0 sizeof( BYTE ), sizeof( BYTE ), ```
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/main.c:
- {
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;
Should we add a FIXME in case they add new operator bits?
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/main.c:
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;
Same here, maybe a FIXME would be appropriate?
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/main.c:
- 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;
And here.
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/main.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)");
```suggestion:-0+0 if (!key) return "(null)"; ```
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/main.c:
+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)");
```suggestion:-0+0 if (!filter) return "(null)"; ```
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/main.c:
- 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:
hr = devprop_filter_context_find_prop( ctx, &filter->Property.CompKey, &prop );
if (FAILED( hr ))
```suggestion:-0+0 if (FAILED( hr )) ```
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/main.c:
- 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)");
```suggestion:-0+0 if (!obj) return "(null)"; ```
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/main.c:
obj.ObjectType = type; obj.pszObjectId = detail->DevicePath; if (SUCCEEDED( (hr = dev_object_iface_get_props( &obj, set, &iface, props_len, props, all_props )) ))
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.
Related question then, do we have tests that the filter applies to properties that weren't requested?
On Thu Aug 7 10:18:04 2025 +0000, Rémi Bernon wrote:
Doesn't seem right, there's two push_context right before?
Ah, right. There should be a pop_context and the end of the loop body. Thanks.