-- v8: cfgmgr32: Implement initial device enumeration for DevCreateObjectQuery.
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/tests/cfgmgr32.c | 66 ++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-)
diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index 02741d0d963..d84448fce89 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -500,6 +500,8 @@ static void test_CM_Get_Device_Interface_List(void) ok(ret == CR_NO_SUCH_DEVICE_INTERFACE || broken(ret == CR_INVALID_DATA) /* w7 */, "got %#lx.\n", ret); }
+DEFINE_DEVPROPKEY(DEVPKEY_dummy, 0xdeadbeef, 0xdead, 0xbeef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 1); + static void test_DevGetObjects( void ) { struct { @@ -530,6 +532,7 @@ static void test_DevGetObjects( void ) }, }; const DEV_OBJECT *objects = NULL; + DEVPROPCOMPKEY prop_key = {0}; HDEVINFO set; HRESULT hr; ULONG i, len = 0; @@ -555,6 +558,13 @@ static void test_DevGetObjects( void ) hr = DevGetObjects( DevObjectTypeDeviceInterface, 0xdeadbeef, 0, NULL, 0, (void *)0xdeadbeef, &len, &objects ); ok( hr == E_INVALIDARG, "got hr %#lx\n", hr );
+ prop_key.Key = test_cases[0].exp_props[0].key; + prop_key.Store = DEVPROP_STORE_SYSTEM; + prop_key.LocaleName = NULL; + /* DevQueryFlagAllProperties is mutually exlusive with requesting specific properties. */ + hr = DevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 1, &prop_key, 0, NULL, &len, &objects ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + len = 0xdeadbeef; objects = (DEV_OBJECT *)0xdeadbeef; hr = DevGetObjects( DevObjectTypeUnknown, DevQueryFlagNone, 0, NULL, 0, NULL, &len, &objects ); @@ -575,6 +585,7 @@ static void test_DevGetObjects( void ) for (i = 0; i < ARRAY_SIZE( test_cases ); i++) { const DEV_OBJECT *objects = NULL; + DEVPROPCOMPKEY prop_key = {0}; ULONG j, len = 0;
objects = NULL; @@ -644,11 +655,62 @@ static void test_DevGetObjects( void ) todo_wine ok( rem_props == 0, "got rem %lu != 0\n", rem_props ); winetest_pop_context(); } - winetest_pop_context(); DevFreeObjects( len, objects ); - }
+ len = 0; + objects = NULL; + prop_key.Key = test_cases[i].exp_props[0].key; + prop_key.LocaleName = NULL; + prop_key.Store = DEVPROP_STORE_SYSTEM; + hr = DevGetObjects( DevObjectTypeDeviceInterface, 0, 1, &prop_key, 0, NULL, &len, &objects ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( len, "got len %lu\n", len ); + ok( !!objects, "got objects %p\n", objects ); + for (i = 0; i < len; i++) + { + const DEV_OBJECT *obj = &objects[i]; + + winetest_push_context( "objects[%lu]", i ); + todo_wine ok( obj->cPropertyCount == 1, "got cPropertyCount %lu != 1\n", obj->cPropertyCount ); + todo_wine ok( !!obj->pProperties, "got pProperties %p\n", obj->pProperties ); + if (obj->pProperties) + ok( IsEqualDevPropKey( obj->pProperties[0].CompKey.Key, prop_key.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.Key.fmtid ), prop_key.Key.pid ); + winetest_pop_context(); + } + DevFreeObjects( len, objects ); + winetest_pop_context(); + } SetupDiDestroyDeviceInfoList( set ); + + /* Non-existent device properties will still have an entry in the returned DEV_OBJECT, albeit with Type set to + * DEVPROP_TYPE_EMPTY. */ + len = 0; + objects = NULL; + prop_key.Key = DEVPKEY_dummy; + hr = DevGetObjects( DevObjectTypeDeviceInterface, 0, 1, &prop_key, 0, NULL, &len, &objects ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( len, "got len %lu\n", len ); + ok( !!objects, "got objects %p\n", objects ); + for (i = 0; i < len; i++) + { + const DEV_OBJECT *obj = &objects[i]; + + winetest_push_context( "objects[%lu]", i ); + todo_wine ok( obj->cPropertyCount == 1, "got cPropertyCount %lu != 1\n", obj->cPropertyCount ); + todo_wine 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 ); + ok( obj->pProperties[0].Type == DEVPROP_TYPE_EMPTY, "got Type %#lx != %#x", obj->pProperties[0].Type, + DEVPROP_TYPE_EMPTY ); + } + winetest_pop_context(); + } + DevFreeObjects( len, objects ); }
START_TEST(cfgmgr32)
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/main.c | 98 ++++++++++++++++++++++++++++++++-- dlls/cfgmgr32/tests/cfgmgr32.c | 16 +++--- 2 files changed, 101 insertions(+), 13 deletions(-)
diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index 28b43a22b3c..a7cf40495cd 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -295,8 +295,33 @@ CONFIGRET WINAPI CM_Get_Device_Interface_PropertyW( LPCWSTR device_interface, co } }
-BOOL dev_objects_append_iface( DEV_OBJECT **objects, ULONG *len, const WCHAR *path, DEV_OBJECT_TYPE type ) +static BOOL dev_properties_append( DEVPROPERTY **properties, ULONG *props_len, const DEVPROPKEY *key, DEVPROPTYPE type, + ULONG buf_size, void *buf ) { + DEVPROPERTY *tmp; + + if (!(tmp = realloc( *properties, (*props_len + 1) * sizeof( **properties )))) + return FALSE; + *properties = tmp; + + tmp = &tmp[*props_len]; + tmp->CompKey.Key = *key; + tmp->CompKey.Store = DEVPROP_STORE_SYSTEM; + tmp->CompKey.LocaleName = NULL; + tmp->Type = type; + tmp->BufferSize = buf_size; + tmp->Buffer = buf; + + *props_len += 1; + return TRUE; +} + +BOOL dev_objects_append_iface( DEV_OBJECT **objects, ULONG *len, const WCHAR *path, DEV_OBJECT_TYPE type, HDEVINFO set, + SP_DEVICE_INTERFACE_DATA *iface_data, ULONG props_len, const DEVPROPCOMPKEY *props, + BOOL all_props ) +{ + DEVPROPKEY *all_keys = NULL; + DWORD keys_len = 0, i = 0; DEV_OBJECT *tmp; WCHAR *id;
@@ -316,6 +341,59 @@ BOOL dev_objects_append_iface( DEV_OBJECT **objects, ULONG *len, const WCHAR *pa tmp->pProperties = NULL;
*len += 1; + + if (!props && !all_props) + return TRUE; + + if (all_props) + { + DWORD req = 0; + if (SetupDiGetDeviceInterfacePropertyKeys( set, iface_data, NULL, 0, &req, 0 ) + || GetLastError() != ERROR_INSUFFICIENT_BUFFER) + return TRUE; + + keys_len = req; + if (!(all_keys = calloc( keys_len, sizeof( *all_keys ) ))) + return TRUE; + if (!SetupDiGetDeviceInterfacePropertyKeys( set, iface_data, all_keys, keys_len, &req, 0 )) + { + free( all_keys ); + return TRUE; + } + } + else + keys_len = props_len; + + for (i = 0; i < keys_len; i++) + { + const DEVPROPKEY *key = all_keys ? &all_keys[i] : &props[i].Key; + DWORD req = 0, size; + DEVPROPTYPE type; + BYTE *buf; + + if (props && props[i].Store != DEVPROP_STORE_SYSTEM) + { + FIXME( "Unsupported Store value: %d\n", props[i].Store ); + continue; + } + if (SetupDiGetDeviceInterfacePropertyW( set, iface_data, key, &type, NULL, 0, &req, 0 ) + || GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + if (props) + dev_properties_append( (DEVPROPERTY **)&tmp->pProperties, &tmp->cPropertyCount, key, DEVPROP_TYPE_EMPTY, + 0, NULL ); + continue; + } + + size = req; + if (!(buf = calloc( 1, size ))) continue; + if (SetupDiGetDeviceInterfacePropertyW( set, iface_data, key, &type, buf, size, &req, 0 ) + && dev_properties_append( (DEVPROPERTY **)&tmp->pProperties, &tmp->cPropertyCount, key, type, size, buf )) + continue; + free( buf ); + } + + free( all_keys ); return TRUE; }
@@ -339,10 +417,9 @@ HRESULT WINAPI DevGetObjectsEx( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_l TRACE( "(%d, %#lx, %lu, %p, %lu, %p, %lu, %p, %p, %p)\n", type, flags, props_len, props, filters_len, filters, params_len, params, objs_len, objs );
- if (!!props_len != !!props || !!filters_len != !!filters || !!params_len != !!params || (flags & ~valid_flags)) + if (!!props_len != !!props || !!filters_len != !!filters || !!params_len != !!params || (flags & ~valid_flags) + || (props_len && (flags & DevQueryFlagAllProperties))) return E_INVALIDARG; - if (props || flags & DevQueryFlagAllProperties) - FIXME( "Object properties are not supported!\n" ); if (filters) FIXME( "Query filters are not supported!\n" ); if (params) @@ -392,7 +469,9 @@ HRESULT WINAPI DevGetObjectsEx( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_l { detail->cbSize = sizeof( *detail ); if (!SetupDiGetDeviceInterfaceDetailW( set, &iface, detail, sizeof( buffer ), NULL, NULL )) continue; - if (!dev_objects_append_iface( &objects, &objects_len, detail->DevicePath, type )) hr = E_OUTOFMEMORY; + if (!dev_objects_append_iface( &objects, &objects_len, detail->DevicePath, type, set, &iface, props_len, + props, !!(flags & DevQueryFlagAllProperties) )) + hr = E_OUTOFMEMORY; }
if (set != INVALID_HANDLE_VALUE) @@ -435,7 +514,16 @@ void WINAPI DevFreeObjects( ULONG objs_len, const DEV_OBJECT *objs ) TRACE( "(%lu, %p)\n", objs_len, objs );
for (i = 0; i < objs_len; i++) + { + DEVPROPERTY *props = (DEVPROPERTY *)objects[i].pProperties; + ULONG j; + + for (j = 0; j < objects[i].cPropertyCount; j++) + free( props[j].Buffer ); + free( props ); + free( (void *)objects[i].pszObjectId ); + } free( objects ); return; } diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index d84448fce89..a0e9ae6db60 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -563,7 +563,7 @@ static void test_DevGetObjects( void ) prop_key.LocaleName = NULL; /* DevQueryFlagAllProperties is mutually exlusive with requesting specific properties. */ hr = DevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties, 1, &prop_key, 0, NULL, &len, &objects ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr );
len = 0xdeadbeef; objects = (DEV_OBJECT *)0xdeadbeef; @@ -600,8 +600,8 @@ static void test_DevGetObjects( void )
winetest_push_context( "device %s", debugstr_w( obj->pszObjectId ) ); ok( obj->ObjectType == test_cases[i].object_type, "got ObjectType %d\n", obj->ObjectType ); - todo_wine ok( obj->cPropertyCount >= test_cases[i].props_len, "got cPropertyCount %lu, should be >= %lu\n", - obj->cPropertyCount, test_cases[i].props_len ); + ok( obj->cPropertyCount >= test_cases[i].props_len, "got cPropertyCount %lu, should be >= %lu\n", + obj->cPropertyCount, test_cases[i].props_len ); for (k = 0; k < obj->cPropertyCount && rem_props; k++) { const DEVPROPERTY *property = &obj->pProperties[k]; @@ -652,7 +652,7 @@ static void test_DevGetObjects( void ) } } } - todo_wine ok( rem_props == 0, "got rem %lu != 0\n", rem_props ); + ok( rem_props == 0, "got rem %lu != 0\n", rem_props ); winetest_pop_context(); } DevFreeObjects( len, objects ); @@ -671,8 +671,8 @@ static void test_DevGetObjects( void ) const DEV_OBJECT *obj = &objects[i];
winetest_push_context( "objects[%lu]", i ); - todo_wine ok( obj->cPropertyCount == 1, "got cPropertyCount %lu != 1\n", obj->cPropertyCount ); - todo_wine ok( !!obj->pProperties, "got pProperties %p\n", obj->pProperties ); + 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.Key ), "got property {%s, %#lx} != {%s, %#lx}\n", debugstr_guid( &obj->pProperties[0].CompKey.Key.fmtid ), @@ -698,8 +698,8 @@ static void test_DevGetObjects( void ) const DEV_OBJECT *obj = &objects[i];
winetest_push_context( "objects[%lu]", i ); - todo_wine ok( obj->cPropertyCount == 1, "got cPropertyCount %lu != 1\n", obj->cPropertyCount ); - todo_wine ok( !!obj->pProperties, "got pProperties %p\n", obj->pProperties ); + 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, DEVPKEY_dummy ),
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/cfgmgr32.spec | 3 + dlls/cfgmgr32/main.c | 25 +++ dlls/cfgmgr32/tests/cfgmgr32.c | 330 ++++++++++++++++++++++++--------- 3 files changed, 270 insertions(+), 88 deletions(-)
diff --git a/dlls/cfgmgr32/cfgmgr32.spec b/dlls/cfgmgr32/cfgmgr32.spec index e4afeb0a461..a437eb72afc 100644 --- a/dlls/cfgmgr32/cfgmgr32.spec +++ b/dlls/cfgmgr32/cfgmgr32.spec @@ -188,6 +188,9 @@ @ stub CM_Unregister_Device_Interface_ExA @ stub CM_Unregister_Device_Interface_ExW @ stdcall CM_Unregister_Notification(ptr) +@ stdcall DevCloseObjectQuery(ptr) +@ stdcall DevCreateObjectQuery(long long long ptr long ptr ptr ptr ptr) +@ stdcall DevCreateObjectQueryEx(long long long ptr long ptr long ptr ptr ptr ptr) @ stdcall DevFreeObjects(long ptr) @ stdcall DevGetObjects(long long long ptr long ptr ptr ptr) @ stdcall DevGetObjectsEx(long long long ptr long ptr long ptr ptr ptr) diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index a7cf40495cd..818b5d8ec93 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -527,3 +527,28 @@ void WINAPI DevFreeObjects( ULONG objs_len, const DEV_OBJECT *objs ) free( objects ); return; } + +HRESULT WINAPI DevCreateObjectQuery( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_len, const DEVPROPCOMPKEY *props, + ULONG filters_len, const DEVPROP_FILTER_EXPRESSION *filters, + PDEV_QUERY_RESULT_CALLBACK callback, void *user_data, HDEVQUERY *devquery ) +{ + TRACE( "(%d, %#lx, %lu, %p, %lu, %p, %p, %p, %p)\n", type, flags, props_len, props, filters_len, filters, callback, + user_data, devquery ); + return DevCreateObjectQueryEx( type, flags, props_len, props, filters_len, filters, 0, NULL, callback, user_data, + devquery ); +} + +HRESULT WINAPI DevCreateObjectQueryEx( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_len, const DEVPROPCOMPKEY *props, + ULONG filters_len, const DEVPROP_FILTER_EXPRESSION *filters, ULONG params_len, + const DEV_QUERY_PARAMETER *params, PDEV_QUERY_RESULT_CALLBACK callback, + void *user_data, HDEVQUERY *devquery ) +{ + FIXME( "(%d, %#lx, %lu, %p, %lu, %p, %lu, %p, %p, %p, %p): stub!\n", type, flags, props_len, props, filters_len, + filters, params_len, params, callback, user_data, devquery ); + return E_NOTIMPL; +} + +void WINAPI DevCloseObjectQuery( HDEVQUERY query ) +{ + FIXME( "(%p): stub!\n", query ); +} diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index a0e9ae6db60..f8c9a0245d0 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -500,16 +500,114 @@ static void test_CM_Get_Device_Interface_List(void) ok(ret == CR_NO_SUCH_DEVICE_INTERFACE || broken(ret == CR_INVALID_DATA) /* w7 */, "got %#lx.\n", ret); }
+struct test_property +{ + DEVPROPKEY key; + DEVPROPTYPE type; +}; + DEFINE_DEVPROPKEY(DEVPKEY_dummy, 0xdeadbeef, 0xdead, 0xbeef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 1);
+static void test_dev_object_iface_props( int line, const DEV_OBJECT *obj, const struct test_property *exp_props, + DWORD props_len ) +{ + DWORD i, rem_props = props_len, buf_len = 0; + const DEV_OBJECT *objects = NULL; + DEVPROPCOMPKEY prop_key = {0}; + HDEVINFO set; + HRESULT hr; + + set = SetupDiCreateDeviceInfoListExW( NULL, NULL, NULL, NULL ); + ok_( __FILE__, line )( set != INVALID_HANDLE_VALUE, "SetupDiCreateDeviceInfoListExW failed: %lu\n", + GetLastError() ); + ok_( __FILE__, line )( obj->cPropertyCount >= props_len, "got cPropertyCount %lu, should be >= %lu\n", + obj->cPropertyCount, props_len ); + for (i = 0; i < obj->cPropertyCount && rem_props; i++) + { + const DEVPROPERTY *property = &obj->pProperties[i]; + ULONG j; + + for (j = 0; j < props_len; j++) + { + if (IsEqualDevPropKey( property->CompKey.Key, exp_props[j].key )) + { + SP_INTERFACE_DEVICE_DATA iface_data = {0}; + DEVPROPTYPE type = DEVPROP_TYPE_EMPTY; + ULONG size = 0; + CONFIGRET ret; + BYTE *buf; + + winetest_push_context( "exp_props[%lu]", j ); + rem_props--; + ok_( __FILE__, line )( property->Type == exp_props[j].type, "got type %#lx\n", property->Type ); + /* Ensure the value matches the value retrieved via SetupDiGetDeviceInterfacePropertyW */ + buf = calloc( property->BufferSize, 1 ); + iface_data.cbSize = sizeof( iface_data ); + ret = SetupDiOpenDeviceInterfaceW( set, obj->pszObjectId, 0, &iface_data ); + ok_( __FILE__, line )( ret, "SetupDiOpenDeviceInterfaceW failed: %lu\n", GetLastError() ); + ret = SetupDiGetDeviceInterfacePropertyW( set, &iface_data, &property->CompKey.Key, &type, buf, + property->BufferSize, &size, 0 ); + ok_( __FILE__, line )( ret, "SetupDiGetDeviceInterfacePropertyW failed: %lu\n", GetLastError() ); + SetupDiDeleteDeviceInterfaceData( set, &iface_data ); + + ok_( __FILE__, line )( size == property->BufferSize, "got size %lu\n", size ); + ok_( __FILE__, line )( type == property->Type, "got type %#lx\n", type ); + if (size == property->BufferSize) + { + switch (type) + { + case DEVPROP_TYPE_STRING: + ok_( __FILE__, line )( !wcsicmp( (WCHAR *)buf, (WCHAR *)property->Buffer ), + "got instance id %s != %s\n", debugstr_w( (WCHAR *)buf ), + debugstr_w( (WCHAR *)property->Buffer ) ); + break; + default: + ok_( __FILE__, line )( !memcmp( buf, property->Buffer, size ), + "got mistmatching property values\n" ); + break; + } + } + free( buf ); + winetest_pop_context(); + break; + } + } + } + ok_( __FILE__, line )( rem_props == 0, "got rem %lu != 0\n", rem_props ); + SetupDiDestroyDeviceInfoList( set ); + + prop_key.Key = exp_props[0].key; + prop_key.LocaleName = NULL; + prop_key.Store = DEVPROP_STORE_SYSTEM; + hr = DevGetObjects( DevObjectTypeDeviceInterface, 0, 1, &prop_key, 0, NULL, &buf_len, &objects ); + ok_( __FILE__, line )( hr == S_OK, "got hr %#lx\n", hr ); + ok_( __FILE__, line )( buf_len, "got buf_len %lu\n", buf_len ); + ok_( __FILE__, line )( !!objects, "got objects %p\n", objects ); + + for (i = 0; i < buf_len; i++) + { + const DEV_OBJECT *obj = &objects[i]; + + winetest_push_context( "objects[%lu]", i ); + ok_( __FILE__, line )( obj->cPropertyCount == 1, "got cPropertyCount %lu != 1\n", + obj->cPropertyCount ); + ok_( __FILE__, line )( !!obj->pProperties, "got pProperties %p\n", obj->pProperties ); + if (obj->pProperties) + ok_( __FILE__, line )( IsEqualDevPropKey( obj->pProperties[0].CompKey.Key, exp_props[0].key ), + "got property {%s, %#lx} != {%s, %#lx}\n", + debugstr_guid( &obj->pProperties[0].CompKey.Key.fmtid ), + obj->pProperties[0].CompKey.Key.pid, debugstr_guid( &exp_props[0].key.fmtid ), + exp_props[0].key.pid ); + winetest_pop_context(); + } + DevFreeObjects( buf_len, objects ); +} + static void test_DevGetObjects( void ) { struct { DEV_OBJECT_TYPE object_type; - struct { - DEVPROPKEY key; - DEVPROPTYPE type; - } exp_props[3]; + struct test_property exp_props[3]; ULONG props_len; } test_cases[] = { { @@ -533,7 +631,6 @@ static void test_DevGetObjects( void ) }; const DEV_OBJECT *objects = NULL; DEVPROPCOMPKEY prop_key = {0}; - HDEVINFO set; HRESULT hr; ULONG i, len = 0;
@@ -579,13 +676,9 @@ static void test_DevGetObjects( void ) ok( len == 0, "got len %lu\n", len ); ok( !objects, "got objects %p\n", objects );
- set = SetupDiCreateDeviceInfoListExW( NULL, NULL, NULL, NULL ); - ok( set != INVALID_HANDLE_VALUE, "SetupDiCreateDeviceInfoListExW failed: %lu\n", GetLastError() ); - for (i = 0; i < ARRAY_SIZE( test_cases ); i++) { const DEV_OBJECT *objects = NULL; - DEVPROPCOMPKEY prop_key = {0}; ULONG j, len = 0;
objects = NULL; @@ -595,94 +688,16 @@ static void test_DevGetObjects( void ) ok( hr == S_OK, "got hr %#lx\n", hr ); for (j = 0; j < len; j++) { - ULONG rem_props = test_cases[i].props_len, k; const DEV_OBJECT *obj = &objects[j];
winetest_push_context( "device %s", debugstr_w( obj->pszObjectId ) ); ok( obj->ObjectType == test_cases[i].object_type, "got ObjectType %d\n", obj->ObjectType ); - ok( obj->cPropertyCount >= test_cases[i].props_len, "got cPropertyCount %lu, should be >= %lu\n", - obj->cPropertyCount, test_cases[i].props_len ); - for (k = 0; k < obj->cPropertyCount && rem_props; k++) - { - const DEVPROPERTY *property = &obj->pProperties[k]; - ULONG l; - - for (l = 0; l < test_cases[i].props_len; l++) - { - if (IsEqualDevPropKey( property->CompKey.Key, test_cases[i].exp_props[l].key )) - { - SP_INTERFACE_DEVICE_DATA iface_data = {0}; - DEVPROPTYPE type = DEVPROP_TYPE_EMPTY; - ULONG size = 0; - CONFIGRET ret; - BYTE *buf; - - winetest_push_context( "exp_props[%lu]", l ); - rem_props--; - ok( property->Type == test_cases[i].exp_props[l].type, "got type %#lx\n", property->Type ); - - /* Ensure the value matches the value retrieved via SetupDiGetDeviceInterfacePropertyW */ - buf = calloc( property->BufferSize, 1 ); - iface_data.cbSize = sizeof( iface_data ); - ret = SetupDiOpenDeviceInterfaceW( set, obj->pszObjectId, 0, &iface_data ); - ok( ret, "SetupDiOpenDeviceInterfaceW failed: %lu\n", GetLastError() ); - ret = SetupDiGetDeviceInterfacePropertyW( set, &iface_data, &property->CompKey.Key, &type, buf, - property->BufferSize, &size, 0 ); - ok( ret, "SetupDiGetDeviceInterfacePropertyW failed: %lu\n", GetLastError() ); - SetupDiDeleteDeviceInterfaceData( set, &iface_data ); - - ok( size == property->BufferSize, "got size %lu\n", size ); - ok( type == property->Type, "got type %#lx\n", type ); - if (size == property->BufferSize) - { - switch (type) - { - case DEVPROP_TYPE_STRING: - ok( !wcsicmp( (WCHAR *)buf, (WCHAR *)property->Buffer ), "got instance id %s != %s\n", - debugstr_w( (WCHAR *)buf ), debugstr_w( (WCHAR *)property->Buffer ) ); - break; - default: - ok( !memcmp( buf, property->Buffer, size ), "got mistmatching property values\n" ); - break; - } - } - free( buf ); - winetest_pop_context(); - break; - } - } - } - ok( rem_props == 0, "got rem %lu != 0\n", rem_props ); - winetest_pop_context(); - } - DevFreeObjects( len, objects ); - - len = 0; - objects = NULL; - prop_key.Key = test_cases[i].exp_props[0].key; - prop_key.LocaleName = NULL; - prop_key.Store = DEVPROP_STORE_SYSTEM; - hr = DevGetObjects( DevObjectTypeDeviceInterface, 0, 1, &prop_key, 0, NULL, &len, &objects ); - ok( hr == S_OK, "got hr %#lx\n", hr ); - ok( len, "got len %lu\n", len ); - ok( !!objects, "got objects %p\n", objects ); - for (i = 0; i < len; i++) - { - const DEV_OBJECT *obj = &objects[i]; - - winetest_push_context( "objects[%lu]", i ); - 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.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.Key.fmtid ), prop_key.Key.pid ); + test_dev_object_iface_props( __LINE__, obj, test_cases[i].exp_props, test_cases[i].props_len ); winetest_pop_context(); } DevFreeObjects( len, objects ); winetest_pop_context(); } - SetupDiDestroyDeviceInfoList( set );
/* Non-existent device properties will still have an entry in the returned DEV_OBJECT, albeit with Type set to * DEVPROP_TYPE_EMPTY. */ @@ -713,6 +728,144 @@ static void test_DevGetObjects( void ) DevFreeObjects( len, objects ); }
+struct query_callback_data +{ + int line; + DEV_OBJECT_TYPE exp_type; + const struct test_property *exp_props; + DWORD props_len; + + HANDLE enum_completed; + HANDLE closed; +}; + +static void WINAPI query_result_callback( HDEVQUERY query, void *user_data, const DEV_QUERY_RESULT_ACTION_DATA *action_data ) +{ + struct query_callback_data *data = user_data; + + ok( !!data, "got null user_data\n" ); + if (!data) return; + + switch (action_data->Action) + { + case DevQueryResultStateChange: + { + DEV_QUERY_STATE state = action_data->Data.State; + ok( state == DevQueryStateEnumCompleted || state == DevQueryStateClosed, + "got unexpected Data.State value: %d\n", state ); + switch (state) + { + case DevQueryStateEnumCompleted: + SetEvent( data->enum_completed ); + break; + case DevQueryStateClosed: + SetEvent( data->closed ); + default: + break; + } + break; + } + case DevQueryResultAdd: + { + const DEV_OBJECT *obj = &action_data->Data.DeviceObject; + winetest_push_context( "device %s", debugstr_w( obj->pszObjectId ) ); + ok_( __FILE__, data->line )( obj->ObjectType == data->exp_type, "got DeviceObject.ObjectType %d != %d", + obj->ObjectType, data->exp_type ); + test_dev_object_iface_props( data->line, &action_data->Data.DeviceObject, data->exp_props, data->props_len ); + winetest_pop_context(); + break; + } + default: + ok( action_data->Action == DevQueryResultUpdate || action_data->Action == DevQueryResultRemove, + "got unexpected Action %d\n", action_data->Action ); + break; + } +} + +#define call_DevCreateObjectQuery( a, b, c, d, e, f, g, h, i ) \ + call_DevCreateObjectQuery_(__LINE__, (a), (b), (c), (d), (e), (f), (g), (h), (i)) + +static HRESULT call_DevCreateObjectQuery_( int line, DEV_OBJECT_TYPE type, ULONG flags, ULONG props_len, + const DEVPROPCOMPKEY *props, ULONG filters_len, + const DEVPROP_FILTER_EXPRESSION *filters, PDEV_QUERY_RESULT_CALLBACK callback, + struct query_callback_data *data, HDEVQUERY *devquery ) +{ + data->line = line; + return DevCreateObjectQuery( type, flags, props_len, props, filters_len, filters, callback, data, devquery ); +} + +static void test_DevCreateObjectQuery( void ) +{ + struct test_property iface_props[3] = { + { DEVPKEY_DeviceInterface_ClassGuid, DEVPROP_TYPE_GUID }, + { DEVPKEY_DeviceInterface_Enabled, DEVPROP_TYPE_BOOLEAN }, + { DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING } + }; + struct query_callback_data data = {0}; + HDEVQUERY query = NULL; + HRESULT hr; + DWORD ret; + + hr = DevCreateObjectQuery( DevObjectTypeDeviceInterface, 0, 0, NULL, 0, NULL, NULL, NULL, &query ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( !query, "got query %p\n", query ); + + hr = DevCreateObjectQuery( DevObjectTypeDeviceInterface, 0xdeadbeef, 0, NULL, 0, NULL, query_result_callback, + NULL, &query ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( !query, "got query %p\n", query ); + + data.enum_completed = CreateEventW( NULL, FALSE, FALSE, NULL ); + data.closed = CreateEventW( NULL, FALSE, FALSE, NULL ); + + hr = call_DevCreateObjectQuery( DevObjectTypeUnknown, 0, 0, NULL, 0, NULL, &query_result_callback, &data, &query ); + todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + ret = WaitForSingleObject( data.enum_completed, 1000 ); + todo_wine ok( !ret, "got ret %lu\n", ret ); + DevCloseObjectQuery( query ); + + hr = call_DevCreateObjectQuery( 0xdeadbeef, 0, 0, NULL, 0, NULL, &query_result_callback, &data, &query ); + todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + ret = WaitForSingleObject( data.enum_completed, 1000 ); + todo_wine ok( !ret, "got ret %lu\n", ret ); + DevCloseObjectQuery( query ); + + hr = call_DevCreateObjectQuery( DevObjectTypeUnknown, DevQueryFlagAsyncClose, 0, NULL, 0, NULL, &query_result_callback, + &data, &query ); + todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + ret = WaitForSingleObject( data.enum_completed, 1000 ); + todo_wine ok( !ret, "got ret %lu\n", ret ); + DevCloseObjectQuery( query ); + ret = WaitForSingleObject( data.closed, 1000 ); + todo_wine ok( !ret, "got ret %lu\n", ret ); + + data.exp_props = iface_props; + data.props_len = ARRAY_SIZE( iface_props ); + + data.exp_type = DevObjectTypeDeviceInterface; + hr = call_DevCreateObjectQuery( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties | DevQueryFlagAsyncClose, 0, + NULL, 0, NULL, &query_result_callback, &data, &query ); + todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + ret = WaitForSingleObject( data.enum_completed, 5000 ); + todo_wine ok( !ret, "got ret %lu\n", ret ); + DevCloseObjectQuery( query ); + ret = WaitForSingleObject( data.closed, 1000 ); + todo_wine ok( !ret, "got ret %lu\n", ret ); + + data.exp_type = DevObjectTypeDeviceInterfaceDisplay; + hr = call_DevCreateObjectQuery( DevObjectTypeDeviceInterfaceDisplay, DevQueryFlagAllProperties | DevQueryFlagAsyncClose, + 0, NULL, 0, NULL, &query_result_callback, &data, &query ); + todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + ret = WaitForSingleObject( data.enum_completed, 5000 ); + todo_wine ok( !ret, "got ret %lu\n", ret ); + DevCloseObjectQuery( query ); + ret = WaitForSingleObject( data.closed, 1000 ); + todo_wine ok( !ret, "got ret %lu\n", ret ); + + CloseHandle( data.enum_completed ); + CloseHandle( data.closed ); +} + START_TEST(cfgmgr32) { test_CM_MapCrToWin32Err(); @@ -720,4 +873,5 @@ START_TEST(cfgmgr32) test_CM_Register_Notification(); test_CM_Get_Device_Interface_List(); test_DevGetObjects(); + test_DevCreateObjectQuery(); }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/main.c | 496 +++++++++++++++++++++++++-------- dlls/cfgmgr32/tests/cfgmgr32.c | 30 +- 2 files changed, 400 insertions(+), 126 deletions(-)
diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index 818b5d8ec93..7cb8395aef0 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -316,49 +316,32 @@ static BOOL dev_properties_append( DEVPROPERTY **properties, ULONG *props_len, c return TRUE; }
-BOOL dev_objects_append_iface( DEV_OBJECT **objects, ULONG *len, const WCHAR *path, DEV_OBJECT_TYPE type, HDEVINFO set, - SP_DEVICE_INTERFACE_DATA *iface_data, ULONG props_len, const DEVPROPCOMPKEY *props, - BOOL all_props ) +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 ) { DEVPROPKEY *all_keys = NULL; DWORD keys_len = 0, i = 0; - DEV_OBJECT *tmp; - WCHAR *id; - - if (!(id = wcsdup( path ))) - return FALSE; - if (!(tmp = realloc( *objects, (*len + 1) * sizeof( **objects ) ))) - { - free( id ); - return FALSE; - } - *objects = tmp; - - tmp = &tmp[*len]; - tmp->ObjectType = type; - tmp->pszObjectId = id; - tmp->cPropertyCount = 0; - tmp->pProperties = NULL; - - *len += 1; + HRESULT hr = S_OK;
+ obj->cPropertyCount = 0; + obj->pProperties = NULL; if (!props && !all_props) - return TRUE; + return S_OK;
if (all_props) { DWORD req = 0; if (SetupDiGetDeviceInterfacePropertyKeys( set, iface_data, NULL, 0, &req, 0 ) || GetLastError() != ERROR_INSUFFICIENT_BUFFER) - return TRUE; + return HRESULT_FROM_WIN32( GetLastError() );
keys_len = req; if (!(all_keys = calloc( keys_len, sizeof( *all_keys ) ))) - return TRUE; + return E_OUTOFMEMORY; if (!SetupDiGetDeviceInterfacePropertyKeys( set, iface_data, all_keys, keys_len, &req, 0 )) { free( all_keys ); - return TRUE; + return HRESULT_FROM_WIN32( GetLastError() ); } } else @@ -379,22 +362,153 @@ BOOL dev_objects_append_iface( DEV_OBJECT **objects, ULONG *len, const WCHAR *pa if (SetupDiGetDeviceInterfacePropertyW( set, iface_data, key, &type, NULL, 0, &req, 0 ) || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { - if (props) - dev_properties_append( (DEVPROPERTY **)&tmp->pProperties, &tmp->cPropertyCount, key, DEVPROP_TYPE_EMPTY, - 0, NULL ); + if (props && !dev_properties_append( (DEVPROPERTY **)&obj->pProperties, &obj->cPropertyCount, key, + DEVPROP_TYPE_EMPTY, 0, NULL )) + { + hr = E_OUTOFMEMORY; + goto done; + } continue; }
size = req; - if (!(buf = calloc( 1, size ))) continue; - if (SetupDiGetDeviceInterfacePropertyW( set, iface_data, key, &type, buf, size, &req, 0 ) - && dev_properties_append( (DEVPROPERTY **)&tmp->pProperties, &tmp->cPropertyCount, key, type, size, buf )) - continue; - free( buf ); + if (!(buf = calloc( 1, size ))) + { + hr = E_OUTOFMEMORY; + goto done; + } + if (!SetupDiGetDeviceInterfacePropertyW( set, iface_data, key, &type, buf, size, &req, 0 )) + { + hr = HRESULT_FROM_WIN32( GetLastError() ); + free( buf ); + goto done; + } + if (!dev_properties_append( (DEVPROPERTY **)&obj->pProperties, &obj->cPropertyCount, key, type, size, buf )) + { + free( buf ); + hr = E_OUTOFMEMORY; + goto done; + } }
+done: free( all_keys ); - return TRUE; + if (hr != S_OK) + { + for (i = 0; i < obj->cPropertyCount; i++) + free( ( (DEVPROPERTY *)obj[i].pProperties )->Buffer ); + free( (DEVPROPERTY *)obj->pProperties ); + obj->cPropertyCount = 0; + obj->pProperties = NULL; + } + 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 ) +{ + HKEY iface_key; + HRESULT hr = S_OK; + + DWORD i; + + switch (type) + { + case DevObjectTypeDeviceInterface: + case DevObjectTypeDeviceInterfaceDisplay: + break; + case DevObjectTypeDeviceContainer: + case DevObjectTypeDevice: + case DevObjectTypeDeviceInterfaceClass: + case DevObjectTypeAEP: + case DevObjectTypeAEPContainer: + case DevObjectTypeDeviceInstallerClass: + case DevObjectTypeDeviceContainerDisplay: + case DevObjectTypeAEPService: + case DevObjectTypeDevicePanel: + case DevObjectTypeAEPProtocol: + FIXME("Unsupported DEV_OJBECT_TYPE: %d\n", type ); + default: + return S_OK; + } + + if (!(iface_key = SetupDiOpenClassRegKeyExW( NULL, KEY_ENUMERATE_SUB_KEYS, DIOCR_INTERFACE, NULL, NULL ))) + return HRESULT_FROM_WIN32( GetLastError() ); + + for (i = 0; SUCCEEDED( hr ); i++) + { + char buffer[sizeof( SP_DEVICE_INTERFACE_DETAIL_DATA_W ) + MAX_PATH * sizeof( WCHAR )]; + SP_DEVICE_INTERFACE_DATA iface = {.cbSize = sizeof( iface )}; + SP_DEVICE_INTERFACE_DETAIL_DATA_W *detail = (void *)buffer; + HDEVINFO set = INVALID_HANDLE_VALUE; + WCHAR iface_guid_str[40]; + DWORD ret, len, j; + GUID iface_class; + + len = ARRAY_SIZE( iface_guid_str ); + ret = RegEnumKeyExW( iface_key, i, iface_guid_str, &len, NULL, NULL, NULL, NULL ); + if (ret) + { + hr = (ret == ERROR_NO_MORE_ITEMS) ? S_OK : HRESULT_FROM_WIN32( ret ); + break; + } + + iface_guid_str[37] = '\0'; + if (!UuidFromStringW( &iface_guid_str[1], &iface_class )) + { + set = SetupDiGetClassDevsW( &iface_class, NULL, NULL, DIGCF_DEVICEINTERFACE ); + if (set == INVALID_HANDLE_VALUE) hr = HRESULT_FROM_WIN32( GetLastError() ); + } + else + ERR( "Could not parse device interface GUID %s\n", debugstr_w( iface_guid_str ) ); + + for (j = 0; SUCCEEDED( hr ) && SetupDiEnumDeviceInterfaces( set, NULL, &iface_class, j, &iface ); j++) + { + DEV_OBJECT obj = {0}; + + detail->cbSize = sizeof( *detail ); + if (!SetupDiGetDeviceInterfaceDetailW( set, &iface, detail, sizeof( buffer ), NULL, NULL )) continue; + + obj.ObjectType = type; + obj.pszObjectId = detail->DevicePath; + if (SUCCEEDED( dev_object_iface_get_props( &obj, set, &iface, props_len, props, all_props ) )) + hr = callback( obj, data ); + } + + if (set != INVALID_HANDLE_VALUE) + SetupDiDestroyDeviceInfoList( set ); + } + RegCloseKey( iface_key ); + return hr; +} + +struct objects_list +{ + DEV_OBJECT *objects; + ULONG len; +}; + +static HRESULT dev_objects_append( DEV_OBJECT obj, void *data ) +{ + struct objects_list *objects = data; + WCHAR *id; + DEV_OBJECT *tmp; + + if (!(id = wcsdup( obj.pszObjectId ))) + return E_OUTOFMEMORY; + if (!(tmp = realloc( objects->objects, (objects->len + 1) * sizeof( obj ) ))) + { + free( id ); + return E_OUTOFMEMORY; + } + + objects->objects = tmp; + tmp = &tmp[objects->len++]; + *tmp = obj; + tmp->pszObjectId = id; + return S_OK; }
HRESULT WINAPI DevGetObjects( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_len, const DEVPROPCOMPKEY *props, @@ -409,10 +523,9 @@ HRESULT WINAPI DevGetObjectsEx( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_l ULONG filters_len, const DEVPROP_FILTER_EXPRESSION *filters, ULONG params_len, const DEV_QUERY_PARAMETER *params, ULONG *objs_len, const DEV_OBJECT **objs ) { - ULONG objects_len = 0, valid_flags = DevQueryFlagAllProperties | DevQueryFlagLocalize; - DEV_OBJECT *objects = NULL; + ULONG valid_flags = DevQueryFlagAllProperties | DevQueryFlagLocalize; + struct objects_list objects = {0}; HRESULT hr = S_OK; - HKEY iface_key;
TRACE( "(%d, %#lx, %lu, %p, %lu, %p, %lu, %p, %p, %p)\n", type, flags, props_len, props, filters_len, filters, params_len, params, objs_len, objs ); @@ -428,80 +541,14 @@ HRESULT WINAPI DevGetObjectsEx( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_l *objs = NULL; *objs_len = 0;
- switch (type) - { - case DevObjectTypeDeviceInterface: - case DevObjectTypeDeviceInterfaceDisplay: - { - DWORD i; - - if (!(iface_key = SetupDiOpenClassRegKeyExW( NULL, KEY_ENUMERATE_SUB_KEYS, DIOCR_INTERFACE, NULL, NULL ))) - return HRESULT_FROM_WIN32( GetLastError() ); - - for (i = 0; SUCCEEDED( hr ); i++) - { - char buffer[sizeof( SP_DEVICE_INTERFACE_DETAIL_DATA_W ) + MAX_PATH * sizeof( WCHAR )]; - SP_DEVICE_INTERFACE_DATA iface = {.cbSize = sizeof( iface )}; - SP_DEVICE_INTERFACE_DETAIL_DATA_W *detail = (void *)buffer; - HDEVINFO set = INVALID_HANDLE_VALUE; - WCHAR iface_guid_str[40]; - DWORD ret, len, j; - GUID iface_class; - - len = ARRAY_SIZE( iface_guid_str ); - ret = RegEnumKeyExW( iface_key, i, iface_guid_str, &len, NULL, NULL, NULL, NULL ); - if (ret) - { - hr = (ret == ERROR_NO_MORE_ITEMS) ? S_OK : HRESULT_FROM_WIN32( ret ); - break; - } - - iface_guid_str[37] = '\0'; - if (!UuidFromStringW( &iface_guid_str[1], &iface_class )) - { - set = SetupDiGetClassDevsW( &iface_class, NULL, NULL, DIGCF_DEVICEINTERFACE ); - if (set == INVALID_HANDLE_VALUE) hr = HRESULT_FROM_WIN32( GetLastError() ); - } - else - ERR( "Could not parse device interface GUID %s\n", debugstr_w( iface_guid_str ) ); - - for (j = 0; SUCCEEDED( hr ) && SetupDiEnumDeviceInterfaces( set, NULL, &iface_class, j, &iface ); j++) - { - detail->cbSize = sizeof( *detail ); - if (!SetupDiGetDeviceInterfaceDetailW( set, &iface, detail, sizeof( buffer ), NULL, NULL )) continue; - if (!dev_objects_append_iface( &objects, &objects_len, detail->DevicePath, type, set, &iface, props_len, - props, !!(flags & DevQueryFlagAllProperties) )) - hr = E_OUTOFMEMORY; - } - - if (set != INVALID_HANDLE_VALUE) - SetupDiDestroyDeviceInfoList( set ); - } - RegCloseKey( iface_key ); - break; - } - case DevObjectTypeDeviceContainer: - case DevObjectTypeDevice: - case DevObjectTypeDeviceInterfaceClass: - case DevObjectTypeAEP: - case DevObjectTypeAEPContainer: - case DevObjectTypeDeviceInstallerClass: - case DevObjectTypeDeviceContainerDisplay: - case DevObjectTypeAEPService: - case DevObjectTypeDevicePanel: - case DevObjectTypeAEPProtocol: - FIXME( "Unsupported DEV_OBJECT_TYPE: %d\n", type ); - default: - break; - } - + hr = enum_dev_objects( type, props_len, props, !!(flags & DevQueryFlagAllProperties), dev_objects_append, &objects ); if (hr == S_OK) { - *objs = objects; - *objs_len = objects_len; + *objs = objects.objects; + *objs_len = objects.len; } else - DevFreeObjects( objects_len, objects ); + DevFreeObjects( objects.len, objects.objects );
return hr; } @@ -528,6 +575,177 @@ void WINAPI DevFreeObjects( ULONG objs_len, const DEV_OBJECT *objs ) return; }
+struct device_query_context +{ + LONG ref; + DEV_OBJECT_TYPE type; + ULONG flags; + ULONG prop_keys_len; + DEVPROPCOMPKEY *prop_keys; + + CRITICAL_SECTION cs; + PDEV_QUERY_RESULT_CALLBACK callback; + void *user_data; + DEV_QUERY_STATE state; + HANDLE closed; +}; + +static HRESULT device_query_context_add_object( DEV_OBJECT obj, void *data ) +{ + DEVPROPERTY *props = (DEVPROPERTY *)obj.pProperties; + DEV_QUERY_RESULT_ACTION_DATA action_data = {0}; + struct device_query_context *ctx = data; + HRESULT hr = S_OK; + ULONG i; + + action_data.Action = DevQueryResultAdd; + action_data.Data.DeviceObject = obj; + ctx->callback( (HDEVQUERY)ctx, ctx->user_data, &action_data ); + + EnterCriticalSection( &ctx->cs ); + if (ctx->state == DevQueryStateClosed) + hr = E_CHANGED_STATE; + LeaveCriticalSection( &ctx->cs ); + + for (i = 0; i < obj.cPropertyCount; i++) + free( props->Buffer ); + free( props ); + return hr; +} + +static HRESULT device_query_context_create( struct device_query_context **query, DEV_OBJECT_TYPE type, ULONG flags, + ULONG props_len, const DEVPROPCOMPKEY *props, + PDEV_QUERY_RESULT_CALLBACK callback, void *user_data ) +{ + struct device_query_context *ctx; + ULONG i; + + if (!(ctx = calloc( 1, sizeof( *ctx )))) + return E_OUTOFMEMORY; + ctx->ref = 1; + if (!(flags & DevQueryFlagAsyncClose)) + { + ctx->closed = CreateEventW( NULL, FALSE, FALSE, NULL ); + if (ctx->closed == INVALID_HANDLE_VALUE) + { + free( ctx ); + return HRESULT_FROM_WIN32( GetLastError() ); + } + } + ctx->prop_keys_len = props_len; + if (props_len && !(ctx->prop_keys = calloc( props_len, sizeof( *props ) ))) + { + if (ctx->closed) CloseHandle( ctx->closed ); + free( ctx ); + return E_OUTOFMEMORY; + } + for (i = 0; i < props_len; i++) + { + ctx->prop_keys[i].Key = props[i].Key; + ctx->prop_keys[i].Store = props[i].Store; + } + InitializeCriticalSectionEx( &ctx->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO ); + if (ctx->cs.DebugInfo) + ctx->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": device_query_context.cs"); + ctx->type = type; + ctx->flags = flags; + ctx->callback = callback; + ctx->user_data = user_data; + ctx->state = DevQueryStateInitialized; + + *query = ctx; + return S_OK; +} + +static void device_query_context_addref( struct device_query_context *ctx ) +{ + InterlockedIncrement( &ctx->ref ); +} + +static void device_query_context_release( struct device_query_context *ctx ) +{ + if (!InterlockedDecrement( &ctx->ref )) + { + free( ctx->prop_keys ); + if (ctx->cs.DebugInfo) + ctx->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection( &ctx->cs ); + if (ctx->closed) CloseHandle( ctx->closed ); + free( ctx ); + } +} + +static void device_query_context_notify_state_change( struct device_query_context *ctx, DEV_QUERY_STATE state ) +{ + DEV_QUERY_RESULT_ACTION_DATA action_data = {0}; + + action_data.Action = DevQueryResultStateChange; + action_data.Data.State = state; + ctx->callback( (HDEVQUERY)ctx, ctx->user_data, &action_data ); +} + +static void CALLBACK device_query_context_notify_enum_completed_async( TP_CALLBACK_INSTANCE *instance, void *data ) +{ + device_query_context_notify_state_change( data, DevQueryStateEnumCompleted ); + device_query_context_release( data ); +} + +static void CALLBACK device_query_context_notify_closed_async( TP_CALLBACK_INSTANCE *instance, void *data ) +{ + device_query_context_notify_state_change( data, DevQueryStateClosed ); + device_query_context_release( data ); +} + +static void CALLBACK device_query_context_notify_aborted_async( TP_CALLBACK_INSTANCE *instance, void *data ) +{ + device_query_context_notify_state_change( data, DevQueryStateAborted ); + device_query_context_release( data ); +} + +static void CALLBACK device_query_enum_objects_async( TP_CALLBACK_INSTANCE *instance, void *data ) +{ + struct device_query_context *ctx = data; + BOOL success = TRUE; + HRESULT hr; + + hr = enum_dev_objects( ctx->type, ctx->prop_keys_len, ctx->prop_keys, !!(ctx->flags & DevQueryFlagAllProperties), + device_query_context_add_object, ctx ); + + EnterCriticalSection( &ctx->cs ); + if (ctx->state == DevQueryStateClosed) + hr = E_CHANGED_STATE; + + switch (hr) + { + case S_OK: + ctx->state = DevQueryStateEnumCompleted; + success = TrySubmitThreadpoolCallback( device_query_context_notify_enum_completed_async, ctx, NULL ); + LeaveCriticalSection( &ctx->cs ); + break; + case E_CHANGED_STATE: + if (ctx->flags & DevQueryFlagAsyncClose) + { + success = TrySubmitThreadpoolCallback( device_query_context_notify_closed_async, ctx, NULL ); + LeaveCriticalSection( &ctx->cs ); + } + else + { + LeaveCriticalSection( &ctx->cs ); + SetEvent( ctx->closed ); + device_query_context_release( ctx ); + } + break; + default: + ctx->state = DevQueryStateAborted; + success = TrySubmitThreadpoolCallback( device_query_context_notify_aborted_async, ctx, NULL ); + LeaveCriticalSection( &ctx->cs ); + break; + } + + if (!success) + device_query_context_release( ctx ); +} + HRESULT WINAPI DevCreateObjectQuery( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_len, const DEVPROPCOMPKEY *props, ULONG filters_len, const DEVPROP_FILTER_EXPRESSION *filters, PDEV_QUERY_RESULT_CALLBACK callback, void *user_data, HDEVQUERY *devquery ) @@ -543,12 +761,68 @@ HRESULT WINAPI DevCreateObjectQueryEx( DEV_OBJECT_TYPE type, ULONG flags, ULONG const DEV_QUERY_PARAMETER *params, PDEV_QUERY_RESULT_CALLBACK callback, void *user_data, HDEVQUERY *devquery ) { - FIXME( "(%d, %#lx, %lu, %p, %lu, %p, %lu, %p, %p, %p, %p): stub!\n", type, flags, props_len, props, filters_len, + ULONG valid_flags = DevQueryFlagUpdateResults | DevQueryFlagAllProperties | DevQueryFlagLocalize | DevQueryFlagAsyncClose; + struct device_query_context *ctx = NULL; + HRESULT hr; + + TRACE( "(%d, %#lx, %lu, %p, %lu, %p, %lu, %p, %p, %p, %p)\n", type, flags, props_len, props, filters_len, filters, params_len, params, callback, user_data, devquery ); - return E_NOTIMPL; + + if (!!props_len != !!props || !!filters_len != !!filters || !!params_len != !!params || (flags & ~valid_flags) || !callback + || (props_len && (flags & DevQueryFlagAllProperties))) + return E_INVALIDARG; + if (filters) + FIXME( "Query filters are not supported!\n" ); + if (params) + FIXME( "Query parameters are not supported!\n" ); + + hr = device_query_context_create( &ctx, type, flags, props_len, props, callback, user_data ); + if (hr != S_OK) + return hr; + + device_query_context_addref( ctx ); + if (!TrySubmitThreadpoolCallback( device_query_enum_objects_async, ctx, NULL )) + hr = HRESULT_FROM_WIN32( GetLastError() ); + if (hr != S_OK) + { + device_query_context_release( ctx ); + ctx = NULL; + } + + *devquery = (HDEVQUERY)ctx; + return hr; }
void WINAPI DevCloseObjectQuery( HDEVQUERY query ) { - FIXME( "(%p): stub!\n", query ); + struct device_query_context *ctx = (struct device_query_context *)query; + BOOL async = ctx->flags & DevQueryFlagAsyncClose; + DEV_QUERY_STATE old; + + TRACE( "(%p)\n", query ); + + if (!query) + return; + + EnterCriticalSection( &ctx->cs ); + old = ctx->state; + ctx->state = DevQueryStateClosed; + + if (async && old == DevQueryStateEnumCompleted) + { + /* No asynchronous operation involving this query is active, so we need to notify DevQueryStateClosed. */ + BOOL success = TrySubmitThreadpoolCallback( device_query_context_notify_closed_async, ctx, NULL ); + LeaveCriticalSection( &ctx->cs ); + if (success) + return; + } + else if (!async && old == DevQueryStateInitialized) + { + LeaveCriticalSection( &ctx->cs ); + /* Wait for the active async operation to end. */ + WaitForSingleObject( ctx->closed, INFINITE ); + } + + device_query_context_release( ctx ); + return; } diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index f8c9a0245d0..c1a1f3a3bda 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -807,37 +807,37 @@ static void test_DevCreateObjectQuery( void ) DWORD ret;
hr = DevCreateObjectQuery( DevObjectTypeDeviceInterface, 0, 0, NULL, 0, NULL, NULL, NULL, &query ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); ok( !query, "got query %p\n", query );
hr = DevCreateObjectQuery( DevObjectTypeDeviceInterface, 0xdeadbeef, 0, NULL, 0, NULL, query_result_callback, NULL, &query ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); ok( !query, "got query %p\n", query );
data.enum_completed = CreateEventW( NULL, FALSE, FALSE, NULL ); data.closed = CreateEventW( NULL, FALSE, FALSE, NULL );
hr = call_DevCreateObjectQuery( DevObjectTypeUnknown, 0, 0, NULL, 0, NULL, &query_result_callback, &data, &query ); - todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( hr == S_OK, "got hr %#lx\n", hr ); ret = WaitForSingleObject( data.enum_completed, 1000 ); - todo_wine ok( !ret, "got ret %lu\n", ret ); + ok( !ret, "got ret %lu\n", ret ); DevCloseObjectQuery( query );
hr = call_DevCreateObjectQuery( 0xdeadbeef, 0, 0, NULL, 0, NULL, &query_result_callback, &data, &query ); - todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( hr == S_OK, "got hr %#lx\n", hr ); ret = WaitForSingleObject( data.enum_completed, 1000 ); - todo_wine ok( !ret, "got ret %lu\n", ret ); + ok( !ret, "got ret %lu\n", ret ); DevCloseObjectQuery( query );
hr = call_DevCreateObjectQuery( DevObjectTypeUnknown, DevQueryFlagAsyncClose, 0, NULL, 0, NULL, &query_result_callback, &data, &query ); - todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( hr == S_OK, "got hr %#lx\n", hr ); ret = WaitForSingleObject( data.enum_completed, 1000 ); - todo_wine ok( !ret, "got ret %lu\n", ret ); + ok( !ret, "got ret %lu\n", ret ); DevCloseObjectQuery( query ); ret = WaitForSingleObject( data.closed, 1000 ); - todo_wine ok( !ret, "got ret %lu\n", ret ); + ok( !ret, "got ret %lu\n", ret );
data.exp_props = iface_props; data.props_len = ARRAY_SIZE( iface_props ); @@ -845,22 +845,22 @@ static void test_DevCreateObjectQuery( void ) data.exp_type = DevObjectTypeDeviceInterface; hr = call_DevCreateObjectQuery( DevObjectTypeDeviceInterface, DevQueryFlagAllProperties | DevQueryFlagAsyncClose, 0, NULL, 0, NULL, &query_result_callback, &data, &query ); - todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( hr == S_OK, "got hr %#lx\n", hr ); ret = WaitForSingleObject( data.enum_completed, 5000 ); - todo_wine ok( !ret, "got ret %lu\n", ret ); + ok( !ret, "got ret %lu\n", ret ); DevCloseObjectQuery( query ); ret = WaitForSingleObject( data.closed, 1000 ); - todo_wine ok( !ret, "got ret %lu\n", ret ); + ok( !ret, "got ret %lu\n", ret );
data.exp_type = DevObjectTypeDeviceInterfaceDisplay; hr = call_DevCreateObjectQuery( DevObjectTypeDeviceInterfaceDisplay, DevQueryFlagAllProperties | DevQueryFlagAsyncClose, 0, NULL, 0, NULL, &query_result_callback, &data, &query ); - todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( hr == S_OK, "got hr %#lx\n", hr ); ret = WaitForSingleObject( data.enum_completed, 5000 ); - todo_wine ok( !ret, "got ret %lu\n", ret ); + ok( !ret, "got ret %lu\n", ret ); DevCloseObjectQuery( query ); ret = WaitForSingleObject( data.closed, 1000 ); - todo_wine ok( !ret, "got ret %lu\n", ret ); + ok( !ret, "got ret %lu\n", ret );
CloseHandle( data.enum_completed ); CloseHandle( data.closed );
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/tests/cfgmgr32.c:
winetest_pop_context();
} DevFreeObjects( len, objects );
}winetest_pop_context();
- SetupDiDestroyDeviceInfoList( set );
- /* Non-existent device properties will still have an entry in the returned DEV_OBJECT, albeit with Type set to
* DEVPROP_TYPE_EMPTY. */
- len = 0;
- objects = NULL;
- prop_key.Key = DEVPKEY_dummy;
- hr = DevGetObjects( DevObjectTypeDeviceInterface, 0, 1, &prop_key, 0, NULL, &len, &objects );
- ok( hr == S_OK, "got hr %#lx\n", hr );
- ok( len, "got len %lu\n", len );
Maybe check that len == 1 and get rid of the loop?
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/tests/cfgmgr32.c:
ok( !!objects, "got objects %p\n", objects );
for (i = 0; i < len; i++)
{
const DEV_OBJECT *obj = &objects[i];
winetest_push_context( "objects[%lu]", i );
todo_wine ok( obj->cPropertyCount == 1, "got cPropertyCount %lu != 1\n", obj->cPropertyCount );
todo_wine ok( !!obj->pProperties, "got pProperties %p\n", obj->pProperties );
if (obj->pProperties)
ok( IsEqualDevPropKey( obj->pProperties[0].CompKey.Key, prop_key.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.Key.fmtid ), prop_key.Key.pid ); winetest_pop_context();
} DevFreeObjects( len, objects );
winetest_pop_context();
This doesn't look right, `i` is reused here in the inner loop. Do you need to really need to run this test for every `test_cases`?
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/tests/cfgmgr32.c:
todo_wine ok( rem_props == 0, "got rem %lu != 0\n", rem_props ); winetest_pop_context(); }
DevFreeObjects( len, objects );
len = 0;
objects = NULL;
prop_key.Key = test_cases[i].exp_props[0].key;
prop_key.LocaleName = NULL;
prop_key.Store = DEVPROP_STORE_SYSTEM;
hr = DevGetObjects( DevObjectTypeDeviceInterface, 0, 1, &prop_key, 0, NULL, &len, &objects );
ok( hr == S_OK, "got hr %#lx\n", hr );
ok( len, "got len %lu\n", len );
Same here, and then you don't need the loop.
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/main.c:
return TRUE;
}
-BOOL dev_objects_append_iface( DEV_OBJECT **objects, ULONG *len, const WCHAR *path, DEV_OBJECT_TYPE type, HDEVINFO set,
SP_DEVICE_INTERFACE_DATA *iface_data, ULONG props_len, const DEVPROPCOMPKEY *props,
BOOL all_props )
+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 )
I think there's two approaches there, depending on the answer to the question "Do we really need to implement the async/callback version of this API?".
* No: then you don't need all this.
* Yes: then I'd suggest to start implementing it first, and change the bit of code that's already there before adding more code for properties, so that you don't have to undo all the synchronous code you just added to implement the asynchronous one. Same thing for the tests, you can probably write helpers to test the async API and use them for the sync API, rather than the other way around which requires undoing/moving all the code into helpers after you added it.
Also note that if this is only meant to be used internally, then for future-proofness and performance considerations, implementing only the callback version might be a smarter choice. This would make the implementation of the sync API later fairly easy but you don't need to bother with it now.