(Follow up from !6788)
The goal is to use the device object API as the backend for Windows.Devices.Enumeration, as most concepts from the latter have a one-to-one mapping to the former:
* The device object API is centred around the [`DEV_OBJECT`](https://learn.microsoft.com/en-us/windows/win32/api/devquerydef/ns-devqueryd...) type, which provides a unified representation for devnodes, device interfaces/containers, device interface classes, and association endpoints (networking protocols and service instances, like UPnP and Bluetooth), together with the properties associated with it. The [`DevObjectType`](https://learn.microsoft.com/en-us/windows/win32/api/devquerydef/ne-devqueryd...) enum maps to [`Windows.Devices.Enumeration.DeviceInformationKind`](https://learn.microsoft.com/en-us/uwp/api/windows.devices.enumeration.device...). This would be used to implement the [`DeviceInformation`](https://learn.microsoft.com/en-us/uwp/api/windows.devices.enumeration.device...) class. * [`DEVPROP_FILTER_EXPRESSION`](https://learn.microsoft.com/en-us/windows/win32/api/devfiltertypes/ns-devfil...) allows filtering device queries by their properties. AQS filter strings would be parsed into an array of filters, which are then passed to the query object methods. * `DevCreateObjectQuery` would be the backend for `DeviceWatcher`, as it provides asynchronous callbacks for initial device enumeration, device addition, removal and updates. * [`DevGetObjects`](https://learn.microsoft.com/en-us/windows/win32/api/devquery/nf-devquery-dev...) would be used to implement the `FindAllAsync*` methods.
This MR adds a basic implementation and tests for `DevGetObjects`.
-- v10: cfgmgr32: Add a basic implementation for DevGetObjects(Ex) for device interface objects. cfgmgr32/test: Add tests for DevGetObjects.
From: Vibhav Pant vibhavp@gmail.com
--- include/Makefile.in | 1 + include/devpropdef.h | 22 +++++++++++ include/devquerydef.h | 90 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 include/devquerydef.h
diff --git a/include/Makefile.in b/include/Makefile.in index 650d69815f8..cdc9bf29784 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -234,6 +234,7 @@ SOURCES = \ devicetopology.idl \ devpkey.h \ devpropdef.h \ + devquerydef.h \ dhcpcsdk.h \ dhtmldid.h \ dhtmled.idl \ diff --git a/include/devpropdef.h b/include/devpropdef.h index 94046f5bce0..de82648e661 100644 --- a/include/devpropdef.h +++ b/include/devpropdef.h @@ -100,4 +100,26 @@ typedef struct _DEVPROPKEY { #else #define IsEqualDevPropKey(a,b) (((a).pid == (b).pid) && IsEqualIID(&(a).fmtid,&(b).fmtid)) #endif + +typedef enum _DEVPROPSTORE +{ + DEVPROP_STORE_SYSTEM, + DEVPROP_STORE_USER, +} DEVPROPSTORE, *PDEVPROPSTORE; + +typedef struct _DEVPROPCOMPKEY +{ + DEVPROPKEY Key; + DEVPROPSTORE Store; + PCWSTR LocaleName; +} DEVPROPCOMPKEY, *PDEVPROPCOMPKEY; + +typedef struct _DEVPROPERTY +{ + DEVPROPCOMPKEY CompKey; + DEVPROPTYPE Type; + ULONG BufferSize; + void *Buffer; +} DEVPROPERTY, *PDEVPROPERTY; + #endif diff --git a/include/devquerydef.h b/include/devquerydef.h new file mode 100644 index 00000000000..5ad01205a16 --- /dev/null +++ b/include/devquerydef.h @@ -0,0 +1,90 @@ +/* + * Copyright 2025 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __DEVQUERYDEF_H__ +#define __DEVQUERYDEF_H__ + +typedef enum _DEV_OBJECT_TYPE +{ + DevObjectTypeUnknown, + DevObjectTypeDeviceInterface, + DevObjectTypeDeviceContainer, + DevObjectTypeDevice, + DevObjectTypeDeviceInterfaceClass, + DevObjectTypeAEP, + DevObjectTypeAEPContainer, + DevObjectTypeDeviceInstallerClass, + DevObjectTypeDeviceInterfaceDisplay, + DevObjectTypeDeviceContainerDisplay, + DevObjectTypeAEPService, + DevObjectTypeDevicePanel, + DevObjectTypeAEPProtocol, +} DEV_OBJECT_TYPE, *PDEV_OBJECT_TYPE; + +typedef enum _DEV_QUERY_FLAGS +{ + DevQueryFlagNone = 0x0, + DevQueryFlagUpdateResults = 0x1, + DevQueryFlagAllProperties = 0x2, + DevQueryFlagLocalize = 0x4, + DevQueryFlagAsyncClose = 0x8, +} DEV_QUERY_FLAGS, *PDEV_QUERY_FLAGS; + +typedef enum _DEV_QUERY_STATE +{ + DevQueryStateInitialized, + DevQueryStateEnumCompleted, + DevQueryStateAborted, + DevQueryStateClosed, +} DEV_QUERY_STATE, *PDEV_QUERY_STATE; + +typedef enum _DEV_QUERY_RESULT_ACTION +{ + DevQueryResultStateChange, + DevQueryResultAdd, + DevQueryResultUpdate, + DevQueryResultRemove, +} DEV_QUERY_RESULT_ACTION, *PDEV_QUERY_RESULT_ACTION; + +typedef struct _DEV_OBJECT +{ + DEV_OBJECT_TYPE ObjectType; + PCWSTR pszObjectId; + ULONG cPropertyCount; + const DEVPROPERTY *pProperties; +} DEV_OBJECT, *PDEV_OBJECT; + +typedef struct _DEV_QUERY_RESULT_ACTION_DATA +{ + DEV_QUERY_RESULT_ACTION Action; + union + { + DEV_QUERY_STATE State; + DEV_OBJECT DeviceObject; + } Data; +} DEV_QUERY_RESULT_ACTION_DATA, *PDEV_QUERY_RESULT_ACTION_DATA; + +typedef struct _DEV_QUERY_PARAMETER +{ + DEVPROPKEY Key; + DEVPROPTYPE Type; + ULONG BufferSize; + void *Buffer; +} DEV_QUERY_PARAMETER, *PDEV_QUERY_PARAMETER; + +#endif /* __DEVQUERYDEF_H__ */
From: Vibhav Pant vibhavp@gmail.com
--- include/Makefile.in | 1 + include/devfiltertypes.h | 74 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 include/devfiltertypes.h
diff --git a/include/Makefile.in b/include/Makefile.in index cdc9bf29784..8b85d6d680a 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -230,6 +230,7 @@ SOURCES = \ ddstream.idl \ delayloadhandler.h \ devenum.idl \ + devfiltertypes.h \ devguid.h \ devicetopology.idl \ devpkey.h \ diff --git a/include/devfiltertypes.h b/include/devfiltertypes.h new file mode 100644 index 00000000000..50772e8216b --- /dev/null +++ b/include/devfiltertypes.h @@ -0,0 +1,74 @@ +/* + * Copyright 2025 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __DEVFILTERTYPES_H__ +#define __DEVFILTERTYPES_H__ + +typedef enum _DEVPROP_OPERATOR +{ + DEVPROP_OPERATOR_MODIFIER_NOT = 0x00010000, + DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE = 0x00020000, + DEVPROP_OPERATOR_NONE = 0x00000000, + DEVPROP_OPERATOR_EXISTS = 0x00000001, + DEVPROP_OPERATOR_NOT_EXISTS = DEVPROP_OPERATOR_MODIFIER_NOT | DEVPROP_OPERATOR_EXISTS, + DEVPROP_OPERATOR_EQUALS = 0x00000002, + DEVPROP_OPERATOR_NOT_EQUALS = DEVPROP_OPERATOR_MODIFIER_NOT | DEVPROP_OPERATOR_EQUALS, + DEVPROP_OPERATOR_GREATER_THAN = 0x00000003, + DEVPROP_OPERATOR_LESS_THAN = 0x00000004, + DEVPROP_OPERATOR_GREATER_THAN_EQUALS = 0x00000005, + DEVPROP_OPERATOR_LESS_THAN_EQUALS = 0x00000006, + DEVPROP_OPERATOR_EQUALS_IGNORE_CASE = DEVPROP_OPERATOR_EQUALS | DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE, + DEVPROP_OPERATOR_NOT_EQUALS_IGNORE_CASE = DEVPROP_OPERATOR_MODIFIER_NOT | DEVPROP_OPERATOR_EQUALS | DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE, + DEVPROP_OPERATOR_BITWISE_AND = 0x00000007, + DEVPROP_OPERATOR_BITWISE_OR = 0x00000008, + DEVPROP_OPERATOR_BEGINS_WITH = 0x00000009, + DEVPROP_OPERATOR_ENDS_WITH = 0x0000000a, + DEVPROP_OPERATOR_CONTAINS = 0x0000000b, + DEVPROP_OPERATOR_BEGINS_WITH_IGNORE_CASE = DEVPROP_OPERATOR_BEGINS_WITH | DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE, + DEVPROP_OPERATOR_ENDS_WITH_IGNORE_CASE = DEVPROP_OPERATOR_ENDS_WITH | DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE, + DEVPROP_OPERATOR_CONTAINS_IGNORE_CASE = DEVPROP_OPERATOR_CONTAINS | DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE, + DEVPROP_OPERATOR_LIST_CONTAINS = 0x00001000, + DEVPROP_OPERATOR_LIST_ELEMENT_BEGINS_WITH = 0x00002000, + DEVPROP_OPERATOR_LIST_ELEMENT_ENDS_WITH = 0x00003000, + DEVPROP_OPERATOR_LIST_ELEMENT_CONTAINS = 0x00004000, + DEVPROP_OPERATOR_LIST_CONTAINS_IGNORE_CASE = DEVPROP_OPERATOR_LIST_CONTAINS | DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE, + DEVPROP_OPERATOR_LIST_ELEMENT_BEGINS_WITH_IGNORE_CASE = DEVPROP_OPERATOR_LIST_ELEMENT_BEGINS_WITH | DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE, + DEVPROP_OPERATOR_LIST_ELEMENT_ENDS_WITH_IGNORE_CASE = DEVPROP_OPERATOR_LIST_ELEMENT_ENDS_WITH | DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE, + DEVPROP_OPERATOR_LIST_ELEMENT_CONTAINS_IGNORE_CASE = DEVPROP_OPERATOR_LIST_ELEMENT_CONTAINS | DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE, + DEVPROP_OPERATOR_AND_OPEN = 0x00100000, + DEVPROP_OPERATOR_AND_CLOSE = 0x00200000, + DEVPROP_OPERATOR_OR_OPEN = 0x00300000, + DEVPROP_OPERATOR_OR_CLOSE = 0x00400000, + DEVPROP_OPERATOR_NOT_OPEN = 0x00500000, + DEVPROP_OPERATOR_NOT_CLOSE = 0x00600000, + DEVPROP_OPERATOR_ARRAY_CONTAINS = 0x10000000, + DEVPROP_OPERATOR_MASK_EVAL = 0x00000fff, + DEVPROP_OPERATOR_MASK_LIST = 0x0000f000, + DEVPROP_OPERATOR_MASK_MODIFIER = 0x000f0000, + DEVPROP_OPERATOR_MASK_NOT_LOGICAL = 0xf00fffff, + DEVPROP_OPERATOR_MASK_LOGICAL = 0x0ff00000, + DEVPROP_OPERATOR_MASK_ARRAY = 0xf0000000, +} DEVPROP_OPERATOR, *PDEVPROP_OPERATOR; + +typedef struct _DEVPROP_FILTER_EXPRESSION +{ + DEVPROP_OPERATOR Operator; + DEVPROPERTY Property; +} DEVPROP_FILTER_EXPRESSION, *PDEVPROP_FILTER_EXPRESSION; + +#endif /* __DEVFILTERTYPES_H__ */
From: Vibhav Pant vibhavp@gmail.com
--- include/Makefile.in | 1 + include/devquery.h | 69 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 include/devquery.h
diff --git a/include/Makefile.in b/include/Makefile.in index 8b85d6d680a..e8bf1aa7de2 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -235,6 +235,7 @@ SOURCES = \ devicetopology.idl \ devpkey.h \ devpropdef.h \ + devquery.h \ devquerydef.h \ dhcpcsdk.h \ dhtmldid.h \ diff --git a/include/devquery.h b/include/devquery.h new file mode 100644 index 00000000000..5acfc3e676b --- /dev/null +++ b/include/devquery.h @@ -0,0 +1,69 @@ +/* + * Copyright 2025 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __DEVQUERY_H__ +#define __DEVQUERY_H__ + +#include <devquerydef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +DECLARE_HANDLE(HDEVQUERY); +typedef HDEVQUERY *PHDEVQUERY; + +typedef void (WINAPI *PDEV_QUERY_RESULT_CALLBACK)( HDEVQUERY devquery, void *user_data, const DEV_QUERY_RESULT_ACTION_DATA *action_data ); + +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 ); +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 ); + +HRESULT WINAPI DevCreateObjectQueryFromId( DEV_OBJECT_TYPE type, const WCHAR *id, 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 ); +HRESULT WINAPI DevCreateObjectQueryFromIdEx( DEV_OBJECT_TYPE type, const WCHAR *id, 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 ); +HRESULT WINAPI DevCreateObjectQueryFromIds( DEV_OBJECT_TYPE type, const WCHAR *id_sz, 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 ); +HRESULT WINAPI DevCreateObjectQueryFromIdsEx( DEV_OBJECT_TYPE type, const WCHAR *id_sz, 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 ); +void WINAPI DevCloseObjectQuery( HDEVQUERY devquery ); + +HRESULT WINAPI DevGetObjects( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_len, const DEVPROPCOMPKEY *props, ULONG filters_len, + const DEVPROP_FILTER_EXPRESSION *filters, ULONG *objs_len, const DEV_OBJECT **objs ); +HRESULT WINAPI DevGetObjectsEx( 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, ULONG *objs_len, + const DEV_OBJECT **objs ); +void WINAPI DevFreeObjects( ULONG len, const DEV_OBJECT *objs ); + +HRESULT WINAPI DevGetObjectProperties( DEV_OBJECT_TYPE type, const WCHAR *id, ULONG flags, ULONG props_len, const DEVPROPCOMPKEY *props, ULONG *buf_len, + const DEVPROPERTY **buf ); +HRESULT WINAPI DevGetObjectPropertiesEx( DEV_OBJECT_TYPE type, const WCHAR *id, ULONG flags, ULONG props_len, const DEVPROPCOMPKEY *props, ULONG params_len, + const DEV_QUERY_PARAMETER *params, ULONG *buf_len, const DEVPROPERTY **buf ); +const DEVPROPERTY *WINAPI DevFindProperty( const DEVPROPKEY *key, DEVPROPSTORE store, const WCHAR *locale, ULONG props_len, const DEVPROPERTY *props ); +void WINAPI DevFreeObjectProperties( ULONG len, const DEVPROPERTY *props ); + +#ifdef __cplusplus +} +#endif + +#endif /* __DEVQUERY_H__ */
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/cfgmgr32.spec | 2 + dlls/cfgmgr32/main.c | 15 ++++ dlls/cfgmgr32/tests/cfgmgr32.c | 154 +++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+)
diff --git a/dlls/cfgmgr32/cfgmgr32.spec b/dlls/cfgmgr32/cfgmgr32.spec index 6d493144455..3fd0d8bd6f3 100644 --- a/dlls/cfgmgr32/cfgmgr32.spec +++ b/dlls/cfgmgr32/cfgmgr32.spec @@ -188,3 +188,5 @@ @ stub CM_Unregister_Device_Interface_ExA @ stub CM_Unregister_Device_Interface_ExW @ stdcall CM_Unregister_Notification(ptr) +@ stdcall DevFreeObjects(long ptr) +@ stdcall DevGetObjects(long long long ptr long ptr ptr ptr) diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index fbff2c2fbf9..3e7cfc1c767 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -24,6 +24,8 @@ #include "dbt.h" #include "wine/plugplay.h" #include "setupapi.h" +#include "devfiltertypes.h" +#include "devquery.h"
#include "initguid.h" #include "devpkey.h" @@ -296,3 +298,16 @@ CONFIGRET WINAPI CM_Get_Device_Interface_PropertyW( LPCWSTR device_interface, co if (!err) return CR_SUCCESS; return err == ERROR_INSUFFICIENT_BUFFER ? CR_BUFFER_SMALL : CR_FAILURE; } + +HRESULT WINAPI DevGetObjects( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_len, const DEVPROPCOMPKEY *props, ULONG filters_len, + const DEVPROP_FILTER_EXPRESSION *filters, ULONG *objs_len, const DEV_OBJECT **objs ) +{ + FIXME( "(%d, %#lx, %lu, %p, %lu, %p, %p, %p): stub!\n", type, flags, props_len, props, filters_len, filters, objs_len, objs ); + return E_NOTIMPL; +} + +void WINAPI DevFreeObjects( ULONG objs_len, const DEV_OBJECT *objs ) +{ + FIXME( "(%lu, %p): stub!\n", objs_len, objs ); + return; +} diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index 95f64698733..fe2dbf4de0a 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -28,6 +28,8 @@ #include "setupapi.h" #include "cfgmgr32.h" #include "ntddvdeo.h" +#include "devfiltertypes.h" +#include "devquery.h"
static void test_CM_MapCrToWin32Err(void) { @@ -483,10 +485,162 @@ 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); }
+static void test_DevGetObjects( void ) +{ + struct { + DEV_OBJECT_TYPE object_type; + struct { + DEVPROPKEY key; + DEVPROPTYPE type; + } exp_props[3]; + ULONG props_len; + } test_cases[] = { + { + DevObjectTypeDeviceInterface, + { + { DEVPKEY_DeviceInterface_ClassGuid, DEVPROP_TYPE_GUID }, + { DEVPKEY_DeviceInterface_Enabled, DEVPROP_TYPE_BOOLEAN }, + { DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING } + }, + 3, + }, + { + DevObjectTypeDeviceInterfaceDisplay, + { + { DEVPKEY_DeviceInterface_ClassGuid, DEVPROP_TYPE_GUID }, + { DEVPKEY_DeviceInterface_Enabled, DEVPROP_TYPE_BOOLEAN }, + { DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING } + }, + 3, + }, + }; + const DEV_OBJECT *objects = NULL; + HDEVINFO set; + HRESULT hr; + ULONG i, len = 0; + + hr = DevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagNone, 1, NULL, 0, NULL, &len, &objects ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = DevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagNone, 0, NULL, 1, NULL, &len, &objects ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = DevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagNone, 0, (void *)0xdeadbeef, 0, NULL, &len, &objects ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = DevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagNone, 0, NULL, 0, (void *)0xdeadbeef, &len, &objects ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = DevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagUpdateResults, 0, NULL, 0, (void *)0xdeadbeef, &len, &objects ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = DevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAsyncClose, 0, NULL, 0, (void *)0xdeadbeef, &len, &objects ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = DevGetObjects( DevObjectTypeDeviceInterface, 0xdeadbeef, 0, NULL, 0, (void *)0xdeadbeef, &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 ); + todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + todo_wine ok( len == 0, "got len %lu\n", len ); + todo_wine ok( !objects, "got objects %p\n", objects ); + + len = 0xdeadbeef; + objects = (DEV_OBJECT *)0xdeadbeef; + hr = DevGetObjects( 0xdeadbeef, DevQueryFlagNone, 0, NULL, 0, NULL, &len, &objects ); + todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + todo_wine ok( len == 0, "got len %lu\n", len ); + todo_wine ok( !objects, "got objects %p\n", objects ); + + 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; + ULONG j, len = 0; + + objects = NULL; + len = 0; + winetest_push_context( "test_cases[%lu]", i ); + hr = DevGetObjects( test_cases[i].object_type, DevQueryFlagAllProperties, 0, NULL, 0, NULL, &len, &objects ); + todo_wine 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 ); + winetest_pop_context(); + } + winetest_pop_context(); + DevFreeObjects( len, objects ); + } + + SetupDiDestroyDeviceInfoList( set ); +} + START_TEST(cfgmgr32) { test_CM_MapCrToWin32Err(); test_CM_Get_Device_ID_List(); test_CM_Register_Notification(); test_CM_Get_Device_Interface_List(); + test_DevGetObjects(); }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/Makefile.in | 2 +- dlls/cfgmgr32/cfgmgr32.spec | 1 + dlls/cfgmgr32/main.c | 142 +++++++++++++++++++++++++++++++-- dlls/cfgmgr32/tests/cfgmgr32.c | 28 +++---- 4 files changed, 153 insertions(+), 20 deletions(-)
diff --git a/dlls/cfgmgr32/Makefile.in b/dlls/cfgmgr32/Makefile.in index e8d9fecdcdd..fd819760e6f 100644 --- a/dlls/cfgmgr32/Makefile.in +++ b/dlls/cfgmgr32/Makefile.in @@ -1,6 +1,6 @@ MODULE = cfgmgr32.dll IMPORTLIB = cfgmgr32 -IMPORTS = setupapi sechost +IMPORTS = advapi32 rpcrt4 sechost setupapi
SOURCES = \ main.c diff --git a/dlls/cfgmgr32/cfgmgr32.spec b/dlls/cfgmgr32/cfgmgr32.spec index 3fd0d8bd6f3..e4afeb0a461 100644 --- a/dlls/cfgmgr32/cfgmgr32.spec +++ b/dlls/cfgmgr32/cfgmgr32.spec @@ -190,3 +190,4 @@ @ stdcall CM_Unregister_Notification(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 3e7cfc1c767..2de4f4e94e8 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -299,15 +299,147 @@ CONFIGRET WINAPI CM_Get_Device_Interface_PropertyW( LPCWSTR device_interface, co return err == ERROR_INSUFFICIENT_BUFFER ? CR_BUFFER_SMALL : CR_FAILURE; }
-HRESULT WINAPI DevGetObjects( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_len, const DEVPROPCOMPKEY *props, ULONG filters_len, - const DEVPROP_FILTER_EXPRESSION *filters, ULONG *objs_len, const DEV_OBJECT **objs ) +BOOL dev_objects_append_iface( DEV_OBJECT **objects, ULONG *len, const WCHAR *path, DEV_OBJECT_TYPE type ) { - FIXME( "(%d, %#lx, %lu, %p, %lu, %p, %p, %p): stub!\n", type, flags, props_len, props, filters_len, filters, objs_len, objs ); - return E_NOTIMPL; + 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; + return TRUE; +} + +HRESULT WINAPI DevGetObjects( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_len, const DEVPROPCOMPKEY *props, + ULONG filters_len, const DEVPROP_FILTER_EXPRESSION *filters, ULONG *objs_len, + const DEV_OBJECT **objs ) +{ + TRACE( "(%d, %#lx, %lu, %p, %lu, %p, %p, %p)\n", type, flags, props_len, props, filters_len, filters, objs_len, objs ); + return DevGetObjectsEx( type, flags, props_len, props, filters_len, filters, 0, NULL, objs_len, objs ); +} + +HRESULT WINAPI DevGetObjectsEx( 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, ULONG *objs_len, const DEV_OBJECT **objs ) +{ + ULONG objects_len = 0, valid_flags = DevQueryFlagAllProperties | DevQueryFlagLocalize; + DEV_OBJECT *objects = NULL; + 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 ); + + if (!!props_len != !!props || !!filters_len != !!filters || !!params_len != !!params || (flags & ~valid_flags)) + 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) + FIXME( "Query parameters are not supported!\n" ); + + *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; + } + + if (hr == S_OK) + { + *objs = objects; + *objs_len = objects_len; + } + else + DevFreeObjects( objects_len, objects ); + + return hr; }
void WINAPI DevFreeObjects( ULONG objs_len, const DEV_OBJECT *objs ) { - FIXME( "(%lu, %p): stub!\n", objs_len, objs ); + DEV_OBJECT *objects = (DEV_OBJECT *)objs; + ULONG i; + + TRACE( "(%lu, %p)\n", objs_len, objs ); + + for (i = 0; i < objs_len; i++) + free( (void *)objects[i].pszObjectId ); + free( objects ); return; } diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index fe2dbf4de0a..eeee008a003 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -520,39 +520,39 @@ static void test_DevGetObjects( void ) ULONG i, len = 0;
hr = DevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagNone, 1, NULL, 0, NULL, &len, &objects ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr );
hr = DevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagNone, 0, NULL, 1, NULL, &len, &objects ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr );
hr = DevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagNone, 0, (void *)0xdeadbeef, 0, NULL, &len, &objects ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr );
hr = DevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagNone, 0, NULL, 0, (void *)0xdeadbeef, &len, &objects ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr );
hr = DevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagUpdateResults, 0, NULL, 0, (void *)0xdeadbeef, &len, &objects ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr );
hr = DevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagAsyncClose, 0, NULL, 0, (void *)0xdeadbeef, &len, &objects ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr );
hr = DevGetObjects( DevObjectTypeDeviceInterface, 0xdeadbeef, 0, NULL, 0, (void *)0xdeadbeef, &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; hr = DevGetObjects( DevObjectTypeUnknown, DevQueryFlagNone, 0, NULL, 0, NULL, &len, &objects ); - todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); - todo_wine ok( len == 0, "got len %lu\n", len ); - todo_wine ok( !objects, "got objects %p\n", objects ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( len == 0, "got len %lu\n", len ); + ok( !objects, "got objects %p\n", objects );
len = 0xdeadbeef; objects = (DEV_OBJECT *)0xdeadbeef; hr = DevGetObjects( 0xdeadbeef, DevQueryFlagNone, 0, NULL, 0, NULL, &len, &objects ); - todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); - todo_wine ok( len == 0, "got len %lu\n", len ); - todo_wine ok( !objects, "got objects %p\n", objects ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + 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() ); @@ -566,7 +566,7 @@ static void test_DevGetObjects( void ) len = 0; winetest_push_context( "test_cases[%lu]", i ); hr = DevGetObjects( test_cases[i].object_type, DevQueryFlagAllProperties, 0, NULL, 0, NULL, &len, &objects ); - todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( hr == S_OK, "got hr %#lx\n", hr ); for (j = 0; j < len; j++) { ULONG rem_props = test_cases[i].props_len, k;
v8: Replace `HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)` with `E_INVALIDARG` >_<