-- v10: cfgmgr32: Support fetching properties for device objects in Dev{GetObjects, CreateObjectQueryEx}. cfgmgr32: Implement initial device enumeration for DevCreateObjectQuery. cfgmgr32/test: Add tests for DevCreateObjectQuery.
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/tests/cfgmgr32.c | 206 +++++++++++++++++++++++---------- 1 file changed, 142 insertions(+), 64 deletions(-)
diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index 02741d0d963..8156a137173 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -500,14 +500,115 @@ 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() ); + todo_wine 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; + } + } + } + todo_wine ok_( __FILE__, line )( rem_props == 0, "got rem %lu != 0\n", rem_props ); + SetupDiDestroyDeviceInfoList( set ); + + /* Get all objects, but only with a single requested property. */ + 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 ); + todo_wine ok_( __FILE__, line )( obj->cPropertyCount == 1, "got cPropertyCount %lu != 1\n", + obj->cPropertyCount ); + todo_wine 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[] = { { @@ -530,7 +631,7 @@ static void test_DevGetObjects( void ) }, }; const DEV_OBJECT *objects = NULL; - HDEVINFO set; + DEVPROPCOMPKEY prop_key = {0}; HRESULT hr; ULONG i, len = 0;
@@ -555,6 +656,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 ); @@ -569,9 +677,6 @@ 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; @@ -584,71 +689,44 @@ 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 ); - todo_wine 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; - } - } - } - todo_wine ok( rem_props == 0, "got rem %lu != 0\n", rem_props ); + test_dev_object_iface_props( __LINE__, obj, test_cases[i].exp_props, test_cases[i].props_len ); winetest_pop_context(); } - 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/cfgmgr32.spec | 3 + dlls/cfgmgr32/main.c | 25 ++++++ dlls/cfgmgr32/tests/cfgmgr32.c | 141 ++++++++++++++++++++++++++++++++- 3 files changed, 168 insertions(+), 1 deletion(-)
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 28b43a22b3c..b82a29bfb98 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -439,3 +439,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 8156a137173..6f2af998ad0 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -700,7 +700,7 @@ static void test_DevGetObjects( void ) winetest_pop_context(); }
- /* Non-existent device properties will still have an entry in the returned DEV_OBJECT, albeit with Type set to + /* Non-existent device properties will still have an entry in the returned DEV_OBJECTs, albeit with Type set to * DEVPROP_TYPE_EMPTY. */ len = 0; objects = NULL; @@ -729,6 +729,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(); @@ -736,4 +874,5 @@ START_TEST(cfgmgr32) test_CM_Register_Notification(); test_CM_Get_Device_Interface_List(); test_DevGetObjects(); + test_DevCreateObjectQuery(); }
On Fri Jul 4 10:14:05 2025 +0000, Rémi Bernon wrote:
Same here, and then you don't need the loop.
Same as above.
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/main.c | 408 ++++++++++++++++++++++++++------- dlls/cfgmgr32/tests/cfgmgr32.c | 30 +-- 2 files changed, 335 insertions(+), 103 deletions(-)
diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index b82a29bfb98..894b6c85ad4 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -295,28 +295,112 @@ 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 ) +typedef HRESULT (*enum_device_object_cb)( DEV_OBJECT object, void *context ); + +static HRESULT enum_dev_objects( DEV_OBJECT_TYPE type, enum_device_object_cb callback, void *data ) { - DEV_OBJECT *tmp; + 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 ) ); + continue; + } + + 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; + 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( path ))) - return FALSE; - if (!(tmp = realloc( *objects, (*len + 1) * sizeof( **objects ) ))) + if (!(id = wcsdup( obj.pszObjectId ))) + return E_OUTOFMEMORY; + if (!(tmp = realloc( objects->objects, (objects->len + 1) * sizeof( obj ) ))) { free( id ); - return FALSE; + return E_OUTOFMEMORY; } - *objects = tmp;
- tmp = &tmp[*len]; - tmp->ObjectType = type; + objects->objects = tmp; + tmp = &tmp[objects->len++]; + *tmp = obj; tmp->pszObjectId = id; - tmp->cPropertyCount = 0; - tmp->pProperties = NULL; - - *len += 1; - return TRUE; + return S_OK; }
HRESULT WINAPI DevGetObjects( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_len, const DEVPROPCOMPKEY *props, @@ -331,10 +415,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 ); @@ -351,78 +434,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 )) 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, 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; } @@ -440,6 +459,159 @@ void WINAPI DevFreeObjects( ULONG objs_len, const DEV_OBJECT *objs ) return; }
+struct device_query_context +{ + LONG ref; + DEV_OBJECT_TYPE type; + ULONG flags; + + 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, + PDEV_QUERY_RESULT_CALLBACK callback, void *user_data ) +{ + struct device_query_context *ctx; + + 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() ); + } + } + 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 )) + { + 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, 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 ) @@ -455,12 +627,72 @@ 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 (props_len || (flags & DevQueryFlagAllProperties)) + FIXME( "Object properties are not supported!\n" ); + 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, 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 ); + } + else + LeaveCriticalSection( &ctx->cs ); + + device_query_context_release( ctx ); + return; } diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index 6f2af998ad0..a017ab5fa3f 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -808,37 +808,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 ); @@ -846,22 +846,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 );
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/main.c | 155 ++++++++++++++++++++++++++++++--- dlls/cfgmgr32/tests/cfgmgr32.c | 18 ++-- 2 files changed, 154 insertions(+), 19 deletions(-)
diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index 894b6c85ad4..8412db667f4 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -295,9 +295,119 @@ CONFIGRET WINAPI CM_Get_Device_Interface_PropertyW( LPCWSTR device_interface, co } }
+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; +} + +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; + HRESULT hr = S_OK; + + obj->cPropertyCount = 0; + obj->pProperties = NULL; + if (!props && !all_props) + return S_OK; + + if (all_props) + { + DWORD req = 0; + if (SetupDiGetDeviceInterfacePropertyKeys( set, iface_data, NULL, 0, &req, 0 ) + || GetLastError() != ERROR_INSUFFICIENT_BUFFER) + return HRESULT_FROM_WIN32( GetLastError() ); + + keys_len = req; + if (!(all_keys = calloc( keys_len, sizeof( *all_keys ) ))) + return E_OUTOFMEMORY; + if (!SetupDiGetDeviceInterfacePropertyKeys( set, iface_data, all_keys, keys_len, &req, 0 )) + { + free( all_keys ); + return HRESULT_FROM_WIN32( GetLastError() ); + } + } + 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 **)&obj->pProperties, &obj->cPropertyCount, key, + DEVPROP_TYPE_EMPTY, 0, NULL )) + { + hr = E_OUTOFMEMORY; + goto done; + } + continue; + } + + size = req; + 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 ); + 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, 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, enum_device_object_cb callback, void *data ) { HKEY iface_key; HRESULT hr = S_OK; @@ -366,7 +476,8 @@ static HRESULT enum_dev_objects( DEV_OBJECT_TYPE type, enum_device_object_cb cal
obj.ObjectType = type; obj.pszObjectId = detail->DevicePath; - hr = callback( obj, data ); + if (SUCCEEDED( dev_object_iface_get_props( &obj, set, &iface, props_len, props, all_props ) )) + hr = callback( obj, data ); }
if (set != INVALID_HANDLE_VALUE) @@ -422,10 +533,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) @@ -434,7 +544,7 @@ HRESULT WINAPI DevGetObjectsEx( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_l *objs = NULL; *objs_len = 0;
- hr = enum_dev_objects( type, dev_objects_append, &objects ); + hr = enum_dev_objects( type, props_len, props, !!(flags & DevQueryFlagAllProperties), dev_objects_append, &objects ); if (hr == S_OK) { *objs = objects.objects; @@ -454,7 +564,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; } @@ -464,6 +583,8 @@ 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; @@ -496,9 +617,11 @@ static HRESULT device_query_context_add_object( DEV_OBJECT obj, void *data ) }
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; @@ -512,6 +635,18 @@ static HRESULT device_query_context_create( struct device_query_context **query, 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"); @@ -534,6 +669,7 @@ 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 ); @@ -575,7 +711,8 @@ static void CALLBACK device_query_enum_objects_async( TP_CALLBACK_INSTANCE *inst BOOL success = TRUE; HRESULT hr;
- hr = enum_dev_objects( ctx->type, device_query_context_add_object, ctx ); + 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) @@ -637,14 +774,12 @@ HRESULT WINAPI DevCreateObjectQueryEx( DEV_OBJECT_TYPE type, ULONG flags, ULONG if (!!props_len != !!props || !!filters_len != !!filters || !!params_len != !!params || (flags & ~valid_flags) || !callback || (props_len && (flags & DevQueryFlagAllProperties))) return E_INVALIDARG; - if (props_len || (flags & DevQueryFlagAllProperties)) - FIXME( "Object properties are not supported!\n" ); 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, callback, user_data ); + hr = device_query_context_create( &ctx, type, flags, props_len, props, callback, user_data ); if (hr != S_OK) return hr;
diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index a017ab5fa3f..3ebad439439 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -520,8 +520,8 @@ static void test_dev_object_iface_props( int line, const DEV_OBJECT *obj, const set = SetupDiCreateDeviceInfoListExW( NULL, NULL, NULL, NULL ); ok_( __FILE__, line )( set != INVALID_HANDLE_VALUE, "SetupDiCreateDeviceInfoListExW failed: %lu\n", GetLastError() ); - todo_wine ok_( __FILE__, line )( obj->cPropertyCount >= props_len, "got cPropertyCount %lu, should be >= %lu\n", - obj->cPropertyCount, props_len ); + 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]; @@ -573,7 +573,7 @@ static void test_dev_object_iface_props( int line, const DEV_OBJECT *obj, const } } } - todo_wine ok_( __FILE__, line )( rem_props == 0, "got rem %lu != 0\n", rem_props ); + ok_( __FILE__, line )( rem_props == 0, "got rem %lu != 0\n", rem_props ); SetupDiDestroyDeviceInfoList( set );
/* Get all objects, but only with a single requested property. */ @@ -590,9 +590,9 @@ static void test_dev_object_iface_props( int line, const DEV_OBJECT *obj, const const DEV_OBJECT *obj = &objects[i];
winetest_push_context( "objects[%lu]", i ); - todo_wine ok_( __FILE__, line )( obj->cPropertyCount == 1, "got cPropertyCount %lu != 1\n", - obj->cPropertyCount ); - todo_wine ok_( __FILE__, line )( !!obj->pProperties, "got pProperties %p\n", obj->pProperties ); + 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", @@ -661,7 +661,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; @@ -714,8 +714,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 ),
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/tests/cfgmgr32.c:
- {
const DEV_OBJECT *obj = &objects[i];
winetest_push_context( "objects[%lu]", i );
todo_wine ok_( __FILE__, line )( obj->cPropertyCount == 1, "got cPropertyCount %lu != 1\n",
obj->cPropertyCount );
todo_wine 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 );
I still don't see how these additional tests are useful to run on every DEV_OBJECT enumerated by the caller. The inner `obj` variable even shadows the `obj` parameter, so it's obviously unused.
Rémi Bernon (@rbernon) commented about dlls/cfgmgr32/main.c:
+}
+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 );
+}
Let's change the commit subject to "cfgmgr32: Stub DevCreateObjectQuery(Ex) functions.", the change is not only about tests and in that case it shouldn't be "cfgmgr32/tests:".
On Mon Jul 7 08:06:42 2025 +0000, Rémi Bernon wrote:
Let's change the commit subject to "cfgmgr32: Stub DevCreateObjectQuery(Ex) functions.", the change is not only about tests and in that case it shouldn't be "cfgmgr32/tests:".
Sure. Thanks.