-- v4: windows.devices.enumeration: Implement IDeviceInformationStatics::{FindAllAsyncAqsFilterAndAdditionalProperties, CreateWatcherAqsFilterAndAdditionalProperties}.
From: Vibhav Pant vibhavp@gmail.com
--- .../tests/Makefile.in | 2 +- .../tests/devices.c | 221 +++++++++++++++++- 2 files changed, 210 insertions(+), 13 deletions(-)
diff --git a/dlls/windows.devices.enumeration/tests/Makefile.in b/dlls/windows.devices.enumeration/tests/Makefile.in index 0b47593720c..200e6707f58 100644 --- a/dlls/windows.devices.enumeration/tests/Makefile.in +++ b/dlls/windows.devices.enumeration/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = windows.devices.enumeration.dll -IMPORTS = combase uuid +IMPORTS = cfgmgr32 combase propsys uuid
SOURCES = \ devices.c diff --git a/dlls/windows.devices.enumeration/tests/devices.c b/dlls/windows.devices.enumeration/tests/devices.c index 433f9c33f2b..895ac7a9755 100644 --- a/dlls/windows.devices.enumeration/tests/devices.c +++ b/dlls/windows.devices.enumeration/tests/devices.c @@ -24,6 +24,10 @@ #include "winbase.h" #include "winerror.h" #include "winstring.h" +#include "devpropdef.h" +#include "devfiltertypes.h" +#include "devquery.h" +#include "propsys.h"
#include "initguid.h" #include "roapi.h" @@ -131,12 +135,20 @@ static ITypedEventHandler_IInspectable_IInspectable *inspectable_event_handler_c return &handler->iface; }
+struct device_property +{ + const WCHAR *name; + PropertyType type; +}; + struct device_watcher_added_handler_data { LONG devices_added; + const struct device_property *exp_props; + SIZE_T exp_props_len; };
-static void test_DeviceInformation_obj( int line, IDeviceInformation *info ); +static void test_DeviceInformation_obj( int line, IDeviceInformation *info, const struct device_property *exp_props, SIZE_T exp_props_len ); static void device_watcher_added_callback( IInspectable *arg1, IInspectable *arg2, void *param ) { struct device_watcher_added_handler_data *data = param; @@ -148,7 +160,7 @@ static void device_watcher_added_callback( IInspectable *arg1, IInspectable *arg ok( hr == S_OK, "got hr %#lx\n", hr );
InterlockedIncrement( &data->devices_added ); - test_DeviceInformation_obj( __LINE__, device_info ); + test_DeviceInformation_obj( __LINE__, device_info, data->exp_props, data->exp_props_len ); IDeviceInformation_Release( device_info ); }
@@ -344,27 +356,210 @@ static void check_device_information_collection_async_( int line, IAsyncOperatio } }
-static void test_DeviceInformation_obj( int line, IDeviceInformation *info ) +/* Find the DEVPROPKEY associated with prop_name, ensure propval matches the value retrieved from DevGetObjectProperties. + * If propval is NULL, then check the retrieved DEVPROPERTY has Type DEVPROP_TYPE_EMPTY. + * This assumes that the DeviceInformationKind is DeviceInterface (DevObjectTypeDeviceInterfaceDisplay). */ +static void test_DeviceInformation_property( const WCHAR *device_id, const WCHAR *prop_name, IPropertyValue *propval ) { + PropertyType type = 0xdeadbeef; + DEVPROPCOMPKEY comp_key = {0}; + const DEVPROPERTY *prop; + DEVPROPKEY key = {0}; HRESULT hr; - HSTRING str; + ULONG len; + + hr = PSGetPropertyKeyFromName( prop_name, (PROPERTYKEY *)&key ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + + comp_key.Key = key; + prop = NULL; + len = 0; + hr = DevGetObjectProperties( DevObjectTypeDeviceInterfaceDisplay, device_id, 0, 1, &comp_key, &len, &prop ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( !!prop, "got prop %p\n", prop ); + ok( len == 1, "got len %lu != 1\n", len ); + + if (propval) + { + hr = IPropertyValue_get_Type( propval, &type ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + + switch (type) + { + case PropertyType_Boolean: + { + boolean bool_val, exp_val; + + hr = IPropertyValue_GetBoolean( propval, &bool_val ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( prop->Type == DEVPROP_TYPE_BOOLEAN, "got Type %#lx\n", prop->Type ); + exp_val = !!*(DEVPROP_BOOLEAN *)prop->Buffer; + ok( bool_val == exp_val, "got bool_val %d != %d\n", bool_val, exp_val ); + break; + } + case PropertyType_String: + { + HSTRING str; + + hr = IPropertyValue_GetString( propval, &str ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( prop->Type == DEVPROP_TYPE_STRING || prop->Type == DEVPROP_TYPE_STRING_INDIRECT, "got Type %#lx\n", prop->Type ); + /* TODO: + * For DEVPROP_TYPE_STRING_INDIRECT, WinRT extracts the locale-specific string from the referenced INF. + * System.ItemNameDisplay's value is formatted differently by WinRT. */ + if (prop->Type == DEVPROP_TYPE_STRING) + ok( !wcsicmp( WindowsGetStringRawBuffer( str, NULL ), prop->Buffer ) || broken( !wcsicmp( prop_name, L"System.ItemNameDisplay" ) ), + "got str %s != %s\n", debugstr_hstring( str ), debugstr_w( prop->Buffer ) ); + WindowsDeleteString( str ); + break; + } + case PropertyType_Guid: + { + GUID guid; + + hr = IPropertyValue_GetGuid( propval, &guid ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( prop->Type == DEVPROP_TYPE_GUID, "got Type %#lx\n", prop->Type ); + ok( IsEqualGUID( &guid, prop->Buffer ), "got guid %s != %s\n", debugstr_guid( &guid ), debugstr_guid( prop->Buffer ) ); + break; + } + /* Used by System.Devices.PhysicalDeviceLocation */ + case PropertyType_UInt8Array: + { + BYTE *arr = NULL; + UINT32 len = 0; + + hr = IPropertyValue_GetUInt8Array( propval, &len, &arr ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( prop->Type == (DEVPROP_TYPEMOD_ARRAY | DEVPROP_TYPE_BYTE), "got Type %#lx\n", prop->Type ); + ok( prop->BufferSize == sizeof( BYTE ) * len, "got BufferSize %lu\n", prop->BufferSize ); + if (prop->BufferSize == sizeof( BYTE ) * len) + ok( !memcmp( arr, prop->Buffer, len ), "got arr %s != %s \n", debugstr_an( (char *)arr, len ), debugstr_an( (char *)prop->Buffer, len ) ); + CoTaskMemFree( arr ); + break; + } + default: + skip( "Unhandled type %d, skipping.\n", type ); + break; + } + } + else + ok( prop->Type == DEVPROP_TYPE_EMPTY, "got Type %#lx\n", prop->Type ); + + DevFreeObjectProperties( len, prop ); + winetest_pop_context(); +} + + +static void test_DeviceInformation_obj( int line, IDeviceInformation *info, const struct device_property *exp_props, SIZE_T exp_props_len ) +{ + IIterable_IKeyValuePair_HSTRING_IInspectable *iterable; + IIterator_IKeyValuePair_HSTRING_IInspectable *iterator; + IMapView_HSTRING_IInspectable *properties; + HSTRING str = NULL, id = NULL; + IInspectable *inspectable; + IPropertyValue *propval; + const WCHAR *id_buf; boolean bool_val; + HRESULT hr; + SIZE_T i;
- hr = IDeviceInformation_get_Id( info, &str ); - ok_(__FILE__, line)( hr == S_OK, "got hr %#lx\n", hr ); - WindowsDeleteString( str ); - str = NULL; + hr = IDeviceInformation_get_Id( info, &id ); + ok_(__FILE__, line)( hr == S_OK, "get_Id failed, got hr %#lx\n", hr ); + id_buf = WindowsGetStringRawBuffer( id, NULL ); hr = IDeviceInformation_get_Name( info, &str ); - todo_wine ok_(__FILE__, line)( hr == S_OK, "got hr %#lx\n", hr ); + todo_wine ok_(__FILE__, line)( hr == S_OK, "get_Name failed, got hr %#lx\n", hr ); WindowsDeleteString( str ); hr = IDeviceInformation_get_IsEnabled( info, &bool_val ); - todo_wine ok_(__FILE__, line)( hr == S_OK, "got hr %#lx\n", hr ); + todo_wine ok_(__FILE__, line)( hr == S_OK, "get_IsEnabled failed, got hr %#lx\n", hr ); hr = IDeviceInformation_get_IsDefault( info, &bool_val ); - todo_wine ok_(__FILE__, line)( hr == S_OK, "got hr %#lx\n", hr ); + todo_wine ok_(__FILE__, line)( hr == S_OK, "get_IsDefault failed, got hr %#lx\n", hr ); + hr = IDeviceInformation_get_Properties( info, &properties ); + todo_wine ok_(__FILE__, line)( hr == S_OK, "get_Properties failed, got hr %#lx\n", hr ); + if (FAILED(hr)) + { + WindowsDeleteString( id ); + return; + } + + for (i = 0; i < exp_props_len; i++) + { + PropertyType type = 0xdeadbeef; + HSTRING_HEADER hdr; + + winetest_push_context( "exp_props[%Iu]", i ); + hr = WindowsCreateStringReference( exp_props[i].name, wcslen( exp_props[i].name ), &hdr, &str ); + ok_(__FILE__, line)( hr == S_OK, "WindowsCreateStringReference failed, got hr %#lx\n", hr ); + hr = IMapView_HSTRING_IInspectable_Lookup( properties, str, &inspectable ); + ok_(__FILE__, line)( hr == S_OK, "Lookup failed, got hr %#lx\n", hr ); + hr = IInspectable_QueryInterface( inspectable, &IID_IPropertyValue, (void **)&propval ); + ok_(__FILE__, line)( hr == S_OK, "QueryInterface failed, got hr %#lx\n", hr ); + IInspectable_Release( inspectable ); + hr = IPropertyValue_get_Type( propval, &type ); + ok_(__FILE__, line)( hr == S_OK, "get_Type failed, got hr %#lx\n", hr ); + ok_(__FILE__, line)(type == exp_props[i].type, "got type %d != %d\n", type, exp_props[i].type ); + IPropertyValue_Release( propval ); + winetest_pop_context(); + } + + hr = IMapView_HSTRING_IInspectable_QueryInterface( properties, &IID_IIterable_IKeyValuePair_HSTRING_IInspectable, (void **)&iterable ); + ok_(__FILE__, line)( hr == S_OK, "QueryInterface failed, got hr %#lx\n", hr ); + IMapView_HSTRING_IInspectable_Release( properties ); + hr = IIterable_IKeyValuePair_HSTRING_IInspectable_First( iterable, &iterator ); + ok_(__FILE__, line)( hr == S_OK, "First failed, got hr %#lx\n", hr ); + IIterable_IKeyValuePair_HSTRING_IInspectable_Release( iterable ); + + i = 0; + bool_val = FALSE; + hr = IIterator_IKeyValuePair_HSTRING_IInspectable_get_HasCurrent( iterator, &bool_val ); + ok_(__FILE__, line)( hr == S_OK, "get_HasCurrent failed, got hr %#lx\n", hr ); + while (bool_val && SUCCEEDED( hr )) + { + IKeyValuePair_HSTRING_IInspectable *pair; + const WCHAR *prop_buf; + + winetest_push_context( "i=%Iu", i++ ); + + hr = IIterator_IKeyValuePair_HSTRING_IInspectable_get_Current( iterator, &pair ); + ok_(__FILE__, line)( hr == S_OK, "get_Current failed, got hr %#lx\n", hr ); + + str = NULL; + hr = IKeyValuePair_HSTRING_IInspectable_get_Key( pair, &str ); + ok_(__FILE__, line)( hr == S_OK, "got hr %#lx\n", hr ); + prop_buf = WindowsGetStringRawBuffer( str, NULL ); + inspectable = NULL; + hr = IKeyValuePair_HSTRING_IInspectable_get_Value( pair, &inspectable ); + ok_(__FILE__, line)( hr == S_OK, "got hr %#lx\n", hr ); + IKeyValuePair_HSTRING_IInspectable_Release( pair ); + + propval = NULL; + if (inspectable) + { + hr = IInspectable_QueryInterface( inspectable, &IID_IPropertyValue, (void **)&propval ); + ok_(__FILE__, line)( hr == S_OK, "got hr %#lx\n", hr ); + IInspectable_Release( inspectable ); + } + + winetest_push_context("%s: %d", debugstr_w( prop_buf ), line ); + test_DeviceInformation_property( id_buf, prop_buf, propval ); + WindowsDeleteString( str ); + winetest_pop_context(); + + hr = IIterator_IKeyValuePair_HSTRING_IInspectable_MoveNext( iterator, &bool_val ); + ok_(__FILE__, line)( hr == S_OK, "MoveNext failed, got hr %#lx\n", hr ); + + winetest_pop_context(); + } + WindowsDeleteString( id ); + IIterator_IKeyValuePair_HSTRING_IInspectable_Release( iterator ); }
static void test_DeviceInformation( void ) { + static const struct device_property device_iface_exp_props[] = { + { L"System.Devices.InterfaceEnabled",PropertyType_Boolean }, + { L"System.Devices.DeviceInstanceId", PropertyType_String }, + }; static const WCHAR *device_info_name = L"Windows.Devices.Enumeration.DeviceInformation";
ITypedEventHandler_DeviceWatcher_IInspectable *stopped_handler, *enumerated_handler; @@ -394,6 +589,8 @@ static void test_DeviceInformation( void ) stopped_data.event = CreateEventW( NULL, FALSE, FALSE, NULL ); ok( !!stopped_data.event, "failed to create event, got error %lu\n", GetLastError() );
+ device_added_data.exp_props = device_iface_exp_props; + device_added_data.exp_props_len = ARRAY_SIZE( device_iface_exp_props ); device_added_handler = device_watcher_added_handler_create( &device_added_data ); stopped_handler = device_watcher_once_handler_create( &stopped_data ); enumerated_handler = device_watcher_once_handler_create( &enumerated_data ); @@ -556,7 +753,7 @@ static void test_DeviceInformation( void ) winetest_push_context( "info_collection %u", i ); hr = IVectorView_DeviceInformation_GetAt( info_collection, i, &info ); ok( hr == S_OK, "got %#lx\n", hr ); - test_DeviceInformation_obj( __LINE__, info ); + test_DeviceInformation_obj( __LINE__, info, device_iface_exp_props, ARRAY_SIZE( device_iface_exp_props ) ); IDeviceInformation_Release( info ); winetest_pop_context(); }
From: Vibhav Pant vibhavp@gmail.com
--- .../tests/devices.c | 373 +++++++++++++++++- 1 file changed, 361 insertions(+), 12 deletions(-)
diff --git a/dlls/windows.devices.enumeration/tests/devices.c b/dlls/windows.devices.enumeration/tests/devices.c index 895ac7a9755..9e4cc378c75 100644 --- a/dlls/windows.devices.enumeration/tests/devices.c +++ b/dlls/windows.devices.enumeration/tests/devices.c @@ -32,6 +32,7 @@ #include "initguid.h" #include "roapi.h" #include "weakreference.h" +#include "ntddvdeo.h"
#define WIDL_using_Windows_Foundation #define WIDL_using_Windows_Foundation_Collections @@ -356,6 +357,231 @@ static void check_device_information_collection_async_( int line, IAsyncOperatio } }
+struct iterable_hstring +{ + IIterable_HSTRING IIterable_HSTRING_iface; + LONG ref; + + ULONG count; + HSTRING values[]; +}; + +C_ASSERT( sizeof( struct iterable_hstring ) == offsetof( struct iterable_hstring, values[0] ) ); + +static inline struct iterable_hstring *impl_from_IIterable_HSTRING( IIterable_HSTRING *iface ) +{ + return CONTAINING_RECORD( iface, struct iterable_hstring, IIterable_HSTRING_iface ); +} + +struct iterator_hstring +{ + IIterator_HSTRING IIterator_HSTRING_iface; + LONG ref; + + UINT32 index; + struct iterable_hstring *view; +}; + +static inline struct iterator_hstring *impl_from_IIterator_HSTRING( IIterator_HSTRING *iface ) +{ + return CONTAINING_RECORD( iface, struct iterator_hstring, IIterator_HSTRING_iface ); +} + +static HRESULT WINAPI iterator_hstring_QueryInterface( IIterator_HSTRING *iface, REFIID iid, void **out ) +{ + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IIterator_HSTRING )) + { + IIterator_HSTRING_AddRef(( *out = iface )); + return S_OK; + } + + if (winetest_debug > 1) trace( "%s not implemented, returning E_NO_INTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI iterator_hstring_AddRef( IIterator_HSTRING *iface ) +{ + struct iterator_hstring *impl = impl_from_IIterator_HSTRING( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + return ref; +} + +static ULONG WINAPI iterator_hstring_Release(IIterator_HSTRING *iface) +{ + struct iterator_hstring *impl = impl_from_IIterator_HSTRING( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + + if (!ref) + { + IIterable_HSTRING_Release( &impl->view->IIterable_HSTRING_iface ); + free( impl ); + } + + return ref; +} + +static HRESULT WINAPI iterator_hstring_GetIids( IIterator_HSTRING *iface, ULONG *iid_count, IID **iids ) { return E_NOTIMPL; } + +static HRESULT WINAPI iterator_hstring_GetRuntimeClassName( IIterator_HSTRING *iface, HSTRING *class_name ) { return E_NOTIMPL; } + +static HRESULT WINAPI iterator_hstring_GetTrustLevel( IIterator_HSTRING *iface, TrustLevel *trust_level ) { return E_NOTIMPL; } + +static HRESULT WINAPI iterator_hstring_get_Current( IIterator_HSTRING *iface, HSTRING *value ) +{ + struct iterator_hstring *impl = impl_from_IIterator_HSTRING( iface ); + + *value = NULL; + if (impl->index >= impl->view->count) return E_BOUNDS; + return WindowsDuplicateString( impl->view->values[impl->index], value ); +} + +static HRESULT WINAPI iterator_hstring_get_HasCurrent( IIterator_HSTRING *iface, boolean *value ) +{ + struct iterator_hstring *impl = impl_from_IIterator_HSTRING( iface ); + + *value = impl->index < impl->view->count; + return S_OK; +} + +static HRESULT WINAPI iterator_hstring_MoveNext( IIterator_HSTRING *iface, boolean *value ) +{ + struct iterator_hstring *impl = impl_from_IIterator_HSTRING( iface ); + + if (impl->index < impl->view->count) impl->index++; + return IIterator_HSTRING_get_HasCurrent( iface, value ); +} + +static HRESULT WINAPI iterator_hstring_GetMany( IIterator_HSTRING *iface, UINT32 items_size, HSTRING *items, UINT *count ) +{ + struct iterator_hstring *impl = impl_from_IIterator_HSTRING( iface ); + ULONG i, start = impl->index; + HRESULT hr = S_OK; + + for (i = start; i < impl->view->count && i < start + items_size; i++) + if (FAILED(hr = WindowsDuplicateString( impl->view->values[i], items + i - start ))) break; + + if (FAILED( hr )) while (i-- > start) WindowsDeleteString( items[i - start] ); + *count = i - start; + return hr; +} + +static const IIterator_HSTRINGVtbl iterator_hstring_vtbl = +{ + /* IUnknown methods */ + iterator_hstring_QueryInterface, + iterator_hstring_AddRef, + iterator_hstring_Release, + /* IInspectable methods */ + iterator_hstring_GetIids, + iterator_hstring_GetRuntimeClassName, + iterator_hstring_GetTrustLevel, + /* IIterator<HSTRING> methods */ + iterator_hstring_get_Current, + iterator_hstring_get_HasCurrent, + iterator_hstring_MoveNext, + iterator_hstring_GetMany, +}; + +static HRESULT STDMETHODCALLTYPE iterable_hstring_QueryInterface( IIterable_HSTRING *iface, REFIID iid, void **out ) +{ + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IIterable_HSTRING )) + { + IIterable_HSTRING_AddRef(( *out = iface )); + return S_OK; + } + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG STDMETHODCALLTYPE iterable_hstring_AddRef( IIterable_HSTRING *iface ) +{ + struct iterable_hstring *impl = impl_from_IIterable_HSTRING( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + return ref; +} + +static ULONG STDMETHODCALLTYPE iterable_hstring_Release( IIterable_HSTRING *iface ) +{ + struct iterable_hstring *impl = impl_from_IIterable_HSTRING( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + + if (!ref) + { + while (impl->count--) WindowsDeleteString( impl->values[impl->count] ); + free( impl ); + } + return ref; +} + +static HRESULT WINAPI iterable_hstring_GetIids( IIterable_HSTRING *iface, ULONG *iid_count, IID **iids ) { return E_NOTIMPL; } + +static HRESULT WINAPI iterable_hstring_GetRuntimeClassName( IIterable_HSTRING *iface, HSTRING *class_name ) { return E_NOTIMPL; } + +static HRESULT WINAPI iterable_hstring_GetTrustLevel( IIterable_HSTRING *iface, TrustLevel *trust_level ) { return E_NOTIMPL; } + +static HRESULT WINAPI iterable_hstring_First( IIterable_HSTRING *iface, IIterator_HSTRING **value ) +{ + struct iterable_hstring *impl = impl_from_IIterable_HSTRING( iface ); + struct iterator_hstring *iter; + + if (!(iter = calloc( 1, sizeof( *iter ) ))) return E_OUTOFMEMORY; + iter->IIterator_HSTRING_iface.lpVtbl = &iterator_hstring_vtbl; + iter->ref = 1; + + IIterable_HSTRING_AddRef( iface ); + iter->view = impl; + + *value = &iter->IIterator_HSTRING_iface; + return S_OK; +} + +static const struct IIterable_HSTRINGVtbl iterable_hstring_vtbl = +{ + /* IUnknown methods */ + iterable_hstring_QueryInterface, + iterable_hstring_AddRef, + iterable_hstring_Release, + /* IInspectable methods */ + iterable_hstring_GetIids, + iterable_hstring_GetRuntimeClassName, + iterable_hstring_GetTrustLevel, + /* IIterable<HSTRING> methods */ + iterable_hstring_First, +}; + +static IIterable_HSTRING *iterable_hstring_create( const WCHAR **values, SIZE_T count ) +{ + struct iterable_hstring *impl; + HRESULT hr; + SIZE_T i; + + if (!(impl = malloc( offsetof( struct iterable_hstring, values[count] ) ))) return NULL; + impl->ref = 1; + + impl->IIterable_HSTRING_iface.lpVtbl = &iterable_hstring_vtbl; + impl->count = count; + for (i = 0; i < count; i++) + { + if (FAILED(hr = WindowsCreateString( values[i], wcslen(values[i]), &impl->values[i] ))) + { + while(i) WindowsDeleteString( impl->values[--i] ); + free( impl ); + return NULL; + } + } + + return &impl->IIterable_HSTRING_iface; +} + + /* Find the DEVPROPKEY associated with prop_name, ensure propval matches the value retrieved from DevGetObjectProperties. * If propval is NULL, then check the retrieved DEVPROPERTY has Type DEVPROP_TYPE_EMPTY. * This assumes that the DeviceInformationKind is DeviceInterface (DevObjectTypeDeviceInterfaceDisplay). */ @@ -554,11 +780,37 @@ static void test_DeviceInformation_obj( int line, IDeviceInformation *info, cons IIterator_IKeyValuePair_HSTRING_IInspectable_Release( iterator ); }
+static void test_DeviceInformationCollection( int line, IVectorView_DeviceInformation *info_collection, const struct device_property *exp_props, + SIZE_T exp_props_len ) +{ + UINT32 size, i; + HRESULT hr; + + hr = IVectorView_DeviceInformation_get_Size( info_collection, &size ); + ok_(__FILE__, line)( hr == S_OK, "got %#lx\n", hr ); + for (i = 0; i < size; i++) + { + IDeviceInformation *info; + + winetest_push_context( "info_collection %u", i ); + hr = IVectorView_DeviceInformation_GetAt( info_collection, i, &info ); + ok_(__FILE__, line)( hr == S_OK, "got %#lx\n", hr ); + test_DeviceInformation_obj( line, info, exp_props, exp_props_len ); + IDeviceInformation_Release( info ); + winetest_pop_context(); + } +} + static void test_DeviceInformation( void ) { + static const WCHAR *device_iface_additional_props[] = { L"System.Devices.InterfaceClassGuid", L"{026e516e-b814-414b-83cd-856d6fef4822} 3" }; + static const WCHAR *device_invalid_props[] = { L"{026e516e-b814-414b-83cd-856d6fef4822}", L"{0-b814-414b-83cd-856d6fef4822} 3", L"{}" }; + static const WCHAR *device_nonexistent_props[] = { L"foo", L"", L" " }; static const struct device_property device_iface_exp_props[] = { { L"System.Devices.InterfaceEnabled",PropertyType_Boolean }, { L"System.Devices.DeviceInstanceId", PropertyType_String }, + /* Additional properties */ + { L"System.Devices.InterfaceClassGuid", PropertyType_Guid } }; static const WCHAR *device_info_name = L"Windows.Devices.Enumeration.DeviceInformation";
@@ -571,18 +823,18 @@ static void test_DeviceInformation( void ) IActivationFactory *factory; IDeviceInformationStatics2 *device_info_statics2; IDeviceInformationStatics *device_info_statics; + IIterable_HSTRING *additional_props; IDeviceWatcher *device_watcher; DeviceWatcherStatus status = 0xdeadbeef; IAsyncOperation_DeviceInformationCollection *info_collection_async = NULL; IVectorView_DeviceInformation *info_collection = NULL; - IDeviceInformation *info; IWeakReferenceSource *weak_src; IWeakReference *weak_ref; IDeviceWatcher *watcher; - UINT32 i, size; HSTRING str; HRESULT hr; ULONG ref; + int i;
enumerated_data.event = CreateEventW( NULL, FALSE, FALSE, NULL ); ok( !!enumerated_data.event, "failed to create event, got error %lu\n", GetLastError() ); @@ -590,7 +842,7 @@ static void test_DeviceInformation( void ) ok( !!stopped_data.event, "failed to create event, got error %lu\n", GetLastError() );
device_added_data.exp_props = device_iface_exp_props; - device_added_data.exp_props_len = ARRAY_SIZE( device_iface_exp_props ); + device_added_data.exp_props_len = ARRAY_SIZE( device_iface_exp_props ) - 1; device_added_handler = device_watcher_added_handler_create( &device_added_data ); stopped_handler = device_watcher_once_handler_create( &stopped_data ); enumerated_handler = device_watcher_once_handler_create( &enumerated_data ); @@ -745,19 +997,53 @@ static void test_DeviceInformation( void ) await_device_information_collection( info_collection_async ); check_device_information_collection_async( info_collection_async, 1, Completed, S_OK, &info_collection ); IAsyncOperation_DeviceInformationCollection_Release( info_collection_async ); + test_DeviceInformationCollection( __LINE__, info_collection, device_iface_exp_props, ARRAY_SIZE( device_iface_exp_props ) - 1 ); + IVectorView_DeviceInformation_Release( info_collection );
- hr = IVectorView_DeviceInformation_get_Size( info_collection, &size ); - ok( hr == S_OK, "got %#lx\n", hr ); - for (i = 0; i < size; i++) + hr = IDeviceInformationStatics_FindAllAsyncAqsFilterAndAdditionalProperties( device_info_statics, NULL, NULL, &info_collection_async ); + todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + + if (SUCCEEDED( hr )) { - winetest_push_context( "info_collection %u", i ); - hr = IVectorView_DeviceInformation_GetAt( info_collection, i, &info ); - ok( hr == S_OK, "got %#lx\n", hr ); - test_DeviceInformation_obj( __LINE__, info, device_iface_exp_props, ARRAY_SIZE( device_iface_exp_props ) ); - IDeviceInformation_Release( info ); + await_device_information_collection( info_collection_async ); + check_device_information_collection_async( info_collection_async, 2, Completed, S_OK, &info_collection ); + IAsyncOperation_DeviceInformationCollection_Release( info_collection_async ); + test_DeviceInformationCollection( __LINE__, info_collection, device_iface_exp_props, ARRAY_SIZE( device_iface_exp_props ) - 1 ); + IVectorView_DeviceInformation_Release( info_collection ); + } + + additional_props = iterable_hstring_create( device_iface_additional_props, ARRAY_SIZE( device_iface_additional_props ) ); + hr = IDeviceInformationStatics_FindAllAsyncAqsFilterAndAdditionalProperties( device_info_statics, NULL, additional_props, &info_collection_async ); + todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + IIterable_HSTRING_Release( additional_props ); + if (SUCCEEDED( hr )) + { + await_device_information_collection( info_collection_async ); + check_device_information_collection_async( info_collection_async, 3, Completed, S_OK, &info_collection ); + IAsyncOperation_DeviceInformationCollection_Release( info_collection_async ); + test_DeviceInformationCollection( __LINE__, info_collection, device_iface_exp_props, ARRAY_SIZE( device_iface_exp_props ) ); + IVectorView_DeviceInformation_Release( info_collection ); + } + + for (i = 0; i < ARRAY_SIZE( device_nonexistent_props ); i++ ) + { + winetest_push_context( "device_nonexistent_props[%d]", i ); + additional_props = iterable_hstring_create( &device_nonexistent_props[i], 1 ); + hr = IDeviceInformationStatics_FindAllAsyncAqsFilterAndAdditionalProperties( device_info_statics, NULL, additional_props, &info_collection_async ); + todo_wine ok( hr == TYPE_E_ELEMENTNOTFOUND, "got hr %#lx\n", hr ); + IIterable_HSTRING_Release( additional_props ); + winetest_pop_context(); + } + + for (i = 0; i < ARRAY_SIZE( device_invalid_props ); i++ ) + { + winetest_push_context( "device_invalid_props[%d]", i ); + additional_props = iterable_hstring_create( &device_invalid_props[i], 1 ); + hr = IDeviceInformationStatics_FindAllAsyncAqsFilterAndAdditionalProperties( device_info_statics, NULL, additional_props, &info_collection_async ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + IIterable_HSTRING_Release( additional_props ); winetest_pop_context(); } - IVectorView_DeviceInformation_Release( info_collection );
IDeviceInformationStatics_Release( device_info_statics );
@@ -994,10 +1280,44 @@ static const struct test_case_filter filters_invalid_operand[] = { { L" System.StructuredQueryType.Boolean#True := System.StructuredQueryType.Boolean#True", E_INVALIDARG }, };
+static void test_DeviceInformation_prop_guid( IDeviceInformation *info, const WCHAR *prop, const GUID *guid_val ) +{ + IMapView_HSTRING_IInspectable *props; + IInspectable *inspectable; + IReference_GUID *val; + GUID guid = {0}; + HSTRING str; + HRESULT hr; + + hr = IDeviceInformation_get_Properties( info, &props ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + + hr = WindowsCreateString( prop, wcslen( prop ), &str ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + hr = IMapView_HSTRING_IInspectable_Lookup( props, str, &inspectable ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + IMapView_HSTRING_IInspectable_Release( props ); + + hr = IInspectable_QueryInterface( inspectable, &IID_IReference_GUID, (void **)&val ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + IInspectable_Release( inspectable ); + hr = IReference_GUID_get_Value( val, &guid ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + IReference_GUID_Release( val ); + + ok( IsEqualGUID( &guid, guid_val ), "got guid %s != %s\n", debugstr_guid( &guid ), debugstr_guid( guid_val ) ); +} + static void test_aqs_filters( void ) { + static const WCHAR *filter_iface_display = L"System.Devices.InterfaceClassGuid := {e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}"; static const WCHAR *class_name = RuntimeClass_Windows_Devices_Enumeration_DeviceInformation; + static const WCHAR *prop_name_iface_guid = L"System.Devices.InterfaceClassGuid"; + IAsyncOperation_DeviceInformationCollection *info_collection_async; + IVectorView_DeviceInformation *info_collection; IDeviceInformationStatics *statics; + IIterable_HSTRING *props_iterable; + UINT32 i, size; HSTRING str; HRESULT hr;
@@ -1042,6 +1362,35 @@ static void test_aqs_filters( void ) test_FindAllAsyncAqsFilter( statics, filters_invalid_operand, FALSE, FALSE ); test_CreateWatcherAqsFilter( statics, filters_invalid_operand, FALSE, FALSE, FALSE, FALSE );
+ props_iterable = iterable_hstring_create( &prop_name_iface_guid, 1 ); + hr = WindowsCreateString( filter_iface_display, wcslen( filter_iface_display ), &str ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + hr = IDeviceInformationStatics_FindAllAsyncAqsFilterAndAdditionalProperties( statics, str, props_iterable, &info_collection_async ); + todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + WindowsDeleteString( str ); + IIterable_HSTRING_Release( props_iterable ); + if (SUCCEEDED( hr )) + { + await_device_information_collection( info_collection_async ); + check_device_information_collection_async_no_id( info_collection_async, Completed, S_OK, &info_collection ); + IAsyncOperation_DeviceInformationCollection_Release( info_collection_async ); + + hr = IVectorView_DeviceInformation_get_Size( info_collection, &size ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + for (i = 0; i < size; i++) + { + IDeviceInformation *info; + + winetest_push_context( "i=%u", i ); + hr = IVectorView_DeviceInformation_GetAt( info_collection, i, &info ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + test_DeviceInformation_prop_guid( info, prop_name_iface_guid, &GUID_DEVINTERFACE_MONITOR ); + IDeviceInformation_Release( info ); + winetest_pop_context(); + } + IVectorView_DeviceInformation_Release( info_collection ); + } + IDeviceInformationStatics_Release( statics ); }
From: Vibhav Pant vibhavp@gmail.com
--- .../windows.devices.enumeration/information.c | 188 ++++++++++++++++-- dlls/windows.devices.enumeration/main.c | 22 +- dlls/windows.devices.enumeration/private.h | 5 +- .../tests/devices.c | 2 +- 4 files changed, 195 insertions(+), 22 deletions(-)
diff --git a/dlls/windows.devices.enumeration/information.c b/dlls/windows.devices.enumeration/information.c index c09a9ea37ef..6047412f19f 100644 --- a/dlls/windows.devices.enumeration/information.c +++ b/dlls/windows.devices.enumeration/information.c @@ -19,6 +19,7 @@
#include "private.h"
+#include "roapi.h" #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(enumeration); @@ -28,10 +29,11 @@ struct device_information IDeviceInformation IDeviceInformation_iface; LONG ref;
- HSTRING path; + IMap_HSTRING_IInspectable *properties; + HSTRING id; };
-static inline struct device_information *impl_DeviceInterface_from_IDeviceInformation( IDeviceInformation *iface ) +static inline struct device_information *impl_from_IDeviceInformation( IDeviceInformation *iface ) { return CONTAINING_RECORD( iface, struct device_information, IDeviceInformation_iface ); } @@ -57,7 +59,7 @@ static HRESULT WINAPI device_information_QueryInterface( IDeviceInformation *ifa
static ULONG WINAPI device_information_AddRef( IDeviceInformation *iface ) { - struct device_information *impl = impl_DeviceInterface_from_IDeviceInformation( iface ); + struct device_information *impl = impl_from_IDeviceInformation( iface ); ULONG ref = InterlockedIncrement( &impl->ref ); TRACE( "iface %p, ref %lu.\n", iface, ref ); return ref; @@ -65,14 +67,15 @@ static ULONG WINAPI device_information_AddRef( IDeviceInformation *iface )
static ULONG WINAPI device_information_Release( IDeviceInformation *iface ) { - struct device_information *impl = impl_DeviceInterface_from_IDeviceInformation( iface ); + struct device_information *impl = impl_from_IDeviceInformation( iface ); ULONG ref = InterlockedDecrement( &impl->ref );
TRACE( "iface %p, ref %lu.\n", iface, ref );
if (!ref) { - WindowsDeleteString( impl->path ); + if (impl->properties) IMap_HSTRING_IInspectable_Release( impl->properties ); + WindowsDeleteString( impl->id ); free( impl ); }
@@ -100,9 +103,9 @@ static HRESULT WINAPI device_information_GetTrustLevel( IDeviceInformation *ifac
static HRESULT WINAPI device_information_get_Id( IDeviceInformation *iface, HSTRING *id ) { - struct device_information *impl = impl_DeviceInterface_from_IDeviceInformation( iface ); + struct device_information *impl = impl_from_IDeviceInformation( iface ); TRACE( "iface %p, id %p\n", iface, id ); - return WindowsDuplicateString( impl->path, id ); + return WindowsDuplicateString( impl->id, id ); }
static HRESULT WINAPI device_information_get_Name( IDeviceInformation *iface, HSTRING *name ) @@ -131,8 +134,9 @@ static HRESULT WINAPI device_information_get_EnclosureLocation( IDeviceInformati
static HRESULT WINAPI device_information_get_Properties( IDeviceInformation *iface, IMapView_HSTRING_IInspectable **properties ) { - FIXME( "iface %p, properties %p stub!\n", iface, properties ); - return E_NOTIMPL; + struct device_information *impl = impl_from_IDeviceInformation( iface ); + TRACE( "iface %p, properties %p.\n", iface, properties ); + return IMap_HSTRING_IInspectable_GetView( impl->properties, properties ); }
static HRESULT WINAPI device_information_Update( IDeviceInformation *iface, IDeviceInformationUpdate *update ) @@ -176,19 +180,179 @@ static const struct IDeviceInformationVtbl device_information_vtbl = device_information_GetGlyphThumbnailAsync, };
-HRESULT device_information_create( const WCHAR *path, IDeviceInformation **info ) +static const char *debugstr_DEVPROPKEY( const DEVPROPKEY *key ) +{ + if (!key) return "(null)"; + return wine_dbg_sprintf( "{%s, %04lx}", debugstr_guid( &key->fmtid ), key->pid ); +} + +static HRESULT create_device_properties( const DEVPROPERTY *props, ULONG len, IMap_HSTRING_IInspectable **map ) +{ + static const WCHAR *propertyset_name = RuntimeClass_Windows_Foundation_Collections_PropertySet; + static const WCHAR *propertyvalue_name = RuntimeClass_Windows_Foundation_PropertyValue; + + IPropertyValueStatics *propval_statics = NULL; + IPropertySet *propset; + HSTRING_HEADER hdr; + HSTRING str; + HRESULT hr; + ULONG i; + + TRACE( "props %p, len %lu, map %p.\n", props, len, map ); + + if (FAILED(hr = WindowsCreateStringReference( propertyset_name, wcslen( propertyset_name ), &hdr, &str ))) return hr; + if (FAILED(hr = RoActivateInstance( str, (IInspectable **)&propset ))) return hr; + hr = IPropertySet_QueryInterface( propset, &IID_IMap_HSTRING_IInspectable, (void **)map ); + IPropertySet_Release( propset ); + if (FAILED(hr)) goto done; + + if (FAILED(hr = WindowsCreateStringReference( propertyvalue_name, wcslen( propertyvalue_name ), &hdr, &str ))) goto done; + if (FAILED(hr = RoGetActivationFactory( str, &IID_IPropertyValueStatics, (void **)&propval_statics ))) goto done; + + for (i = 0; i < len; i++) + { + const DEVPROPERTY *prop = &props[i]; + const DEVPROPKEY *propkey = &prop->CompKey.Key; + HSTRING canonical_name; + IInspectable *val; + boolean replaced; + WCHAR *name; + + if (SUCCEEDED(hr = PSGetNameFromPropertyKey( (PROPERTYKEY *)propkey, &name ))) + { + hr = WindowsCreateString( name, wcslen( name ), &canonical_name ); + CoTaskMemFree( name ); + } + else if (hr == TYPE_E_ELEMENTNOTFOUND) + { + const GUID *fmtid = &propkey->fmtid; + WCHAR buf[80]; + + WARN( "Unknown property key: %s\n", debugstr_DEVPROPKEY( propkey ) ); + swprintf( buf, ARRAY_SIZE( buf ), L"{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x} %lu", fmtid->Data1, fmtid->Data2, fmtid->Data3, + fmtid->Data4[0], fmtid->Data4[1], fmtid->Data4[2], fmtid->Data4[3], fmtid->Data4[4], fmtid->Data4[5], fmtid->Data4[6], fmtid->Data4[7], propkey->pid ); + hr = WindowsCreateString( buf, wcslen( buf ), &canonical_name ); + } + if (FAILED(hr)) break; + + switch (prop->Type) + { + case DEVPROP_TYPE_BOOLEAN: + hr = IPropertyValueStatics_CreateBoolean( propval_statics, !!*(DEVPROP_BOOLEAN *)prop->Buffer, &val ); + break; + case DEVPROP_TYPE_BYTE: + case DEVPROP_TYPE_SBYTE: + hr = IPropertyValueStatics_CreateUInt8( propval_statics, *(BYTE *)prop->Buffer, &val ); + break; + case DEVPROP_TYPE_UINT16: + hr = IPropertyValueStatics_CreateUInt16( propval_statics, *(UINT16 *)prop->Buffer, &val ); + break; + case DEVPROP_TYPE_INT16: + hr = IPropertyValueStatics_CreateInt16( propval_statics, *(INT16 *)prop->Buffer, &val ); + break; + case DEVPROP_TYPE_UINT32: + hr = IPropertyValueStatics_CreateUInt32( propval_statics, *(UINT32 *)prop->Buffer, &val ); + break; + case DEVPROP_TYPE_INT32: + hr = IPropertyValueStatics_CreateInt32( propval_statics, *(INT32 *)prop->Buffer, &val ); + break; + case DEVPROP_TYPE_UINT64: + hr = IPropertyValueStatics_CreateUInt64( propval_statics, *(UINT64 *)prop->Buffer, &val ); + break; + case DEVPROP_TYPE_INT64: + hr = IPropertyValueStatics_CreateInt64( propval_statics, *(INT64 *)prop->Buffer, &val ); + break; + case DEVPROP_TYPE_FLOAT: + hr = IPropertyValueStatics_CreateSingle( propval_statics, *(FLOAT *)prop->Buffer, &val ); + break; + case DEVPROP_TYPE_DOUBLE: + hr = IPropertyValueStatics_CreateDouble( propval_statics, *(DOUBLE *)prop->Buffer, &val ); + break; + case DEVPROP_TYPE_GUID: + hr = IPropertyValueStatics_CreateGuid( propval_statics, *(GUID *)prop->Buffer, &val ); + break; + case DEVPROP_TYPE_FILETIME: + hr = IPropertyValueStatics_CreateDateTime( propval_statics, *(DateTime *)prop->Buffer, &val ); + break; + case DEVPROP_TYPE_STRING: + { + if (SUCCEEDED(hr = WindowsCreateString( prop->Buffer, wcslen( prop->Buffer ), &str ))) + { + hr = IPropertyValueStatics_CreateString( propval_statics, str, &val ); + WindowsDeleteString( str ); + } + break; + } + default: + FIXME("Unsupported DEVPROPTYPE: %#lx\n", prop->Type ); + WindowsDeleteString( canonical_name ); + continue; + } + if (FAILED(hr)) + { + WindowsDeleteString( canonical_name ); + break; + } + + hr = IMap_HSTRING_IInspectable_Insert( *map, canonical_name, val, &replaced ); + IInspectable_Release( val ); + WindowsDeleteString( canonical_name ); + if (FAILED(hr)) break; + } + +done: + if (propval_statics) IPropertyValueStatics_Release( propval_statics ); + if (FAILED(hr) && *map) IMap_HSTRING_IInspectable_Release( *map ); + return hr; +} + +static const char *debugstr_DEV_OBJECT_TYPE( DEV_OBJECT_TYPE type ) +{ + static const char *str[] = { + "DevObjectTypeUnknown", + "DevObjectTypeDeviceInterface", + "DevObjectTypeDeviceContainer", + "DevObjectTypeDevice", + "DevObjectTypeDeviceInterfaceClass", + "DevObjectTypeAEP", + "DevObjectTypeAEPContainer", + "DevObjectTypeDeviceInstallerClass", + "DevObjectTypeDeviceInterfaceDisplay", + "DevObjectTypeDeviceContainerDisplay", + "DevObjectTypeAEPService", + "DevObjectTypeDevicePanel", + "DevObjectTypeAEPProtocol", + }; + if (type >= ARRAY_SIZE( str )) return wine_dbg_sprintf( "(unknown %d)", type ); + return wine_dbg_sprintf( "%s", str[type] ); +} + +static const char *debugstr_DEV_OBJECT( const DEV_OBJECT *obj ) +{ + if (!obj) return "(null)"; + return wine_dbg_sprintf( "{%s, %s, %lu, %p}", debugstr_DEV_OBJECT_TYPE( obj->ObjectType ), debugstr_w( obj->pszObjectId ), obj->cPropertyCount, + obj->pProperties ); +} + +HRESULT device_information_create( const DEV_OBJECT *obj, IDeviceInformation **info ) { struct device_information *impl; HRESULT hr;
- TRACE( "path %s, info %p\n", debugstr_w(path), info ); + TRACE( "obj %s, info %p\n", debugstr_DEV_OBJECT( obj ), info );
if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY; impl->IDeviceInformation_iface.lpVtbl = &device_information_vtbl; impl->ref = 1; + if (FAILED(hr = create_device_properties( obj->pProperties, obj->cPropertyCount, &impl->properties ))) + { + free( impl ); + return hr; + }
- if (FAILED(hr = WindowsCreateString( path, wcslen( path ), &impl->path ))) + if (FAILED(hr = WindowsCreateString( obj->pszObjectId, wcslen( obj->pszObjectId ), &impl->id ))) { + IMap_HSTRING_IInspectable_Release( impl->properties ); free( impl ); return hr; } diff --git a/dlls/windows.devices.enumeration/main.c b/dlls/windows.devices.enumeration/main.c index 2125689eeef..c965ca363e0 100644 --- a/dlls/windows.devices.enumeration/main.c +++ b/dlls/windows.devices.enumeration/main.c @@ -24,10 +24,9 @@
#include "initguid.h" #include "private.h" -#include "devpropdef.h" -#include "devfiltertypes.h" #include "devquery.h" #include "aqs.h" +#include "devpkey.h"
#include "wine/debug.h"
@@ -347,7 +346,7 @@ static void WINAPI device_object_query_callback( HDEVQUERY query, void *data, case DevQueryResultAdd: { IDeviceInformation *info; - if (FAILED(hr = device_information_create( action_data->Data.DeviceObject.pszObjectId, &info ))) + if (FAILED(hr = device_information_create( &action_data->Data.DeviceObject, &info ))) break; typed_event_handlers_notify( &watcher->added_handlers, (IInspectable *)iface, (IInspectable *)info ); IDeviceInformation_Release( info ); @@ -361,6 +360,12 @@ static void WINAPI device_object_query_callback( HDEVQUERY query, void *data, IDeviceWatcher_Release( iface ); }
+static const DEVPROPCOMPKEY device_iface_default_props[] = +{ + { DEVPKEY_DeviceInterface_Enabled, DEVPROP_STORE_SYSTEM, NULL }, + { DEVPKEY_Device_InstanceId, DEVPROP_STORE_SYSTEM, NULL }, +}; + static HRESULT WINAPI device_watcher_Start( IDeviceWatcher *iface ) { struct device_watcher *impl = impl_from_IDeviceWatcher( iface ); @@ -394,8 +399,8 @@ static HRESULT WINAPI device_watcher_Start( IDeviceWatcher *iface ) }
IWeakReferenceSource_GetWeakReference( &impl->weak_reference_source.IWeakReferenceSource_iface, &weak ); - hr = DevCreateObjectQuery( DevObjectTypeDeviceInterfaceDisplay, DevQueryFlagAsyncClose, 0, NULL, filters_len, filters, device_object_query_callback, - weak, &impl->query ); + hr = DevCreateObjectQuery( DevObjectTypeDeviceInterfaceDisplay, DevQueryFlagAsyncClose, ARRAY_SIZE( device_iface_default_props ), + device_iface_default_props, filters_len, filters, device_object_query_callback, weak, &impl->query ); if (FAILED(hr)) { ERR( "Failed to create device query: %#lx\n", hr ); @@ -645,16 +650,17 @@ static HRESULT find_all_async( IUnknown *invoker, IUnknown *param, PROPVARIANT * filters_len = params->expr->len; } if (FAILED(hr = vector_create( &iids, (void *)&vector ))) return hr; - if (FAILED(hr = DevGetObjects( DevObjectTypeDeviceInterfaceDisplay, DevQueryFlagNone, 0, NULL, filters_len, filters, &len, &objects ))) + hr = DevGetObjects( DevObjectTypeDeviceInterfaceDisplay, DevQueryFlagNone, ARRAY_SIZE( device_iface_default_props ), device_iface_default_props, filters_len, filters, + &len, &objects ); + if (FAILED(hr)) { IVector_IInspectable_Release( vector ); - ERR("DevGetObjects failed, hr %#lx\n", hr); return hr; } for (i = 0; i < len && SUCCEEDED(hr); i++) { IDeviceInformation *info; - if (SUCCEEDED(hr = device_information_create( objects[i].pszObjectId, &info ))) + if (SUCCEEDED(hr = device_information_create( &objects[i], &info ))) { hr = IVector_IInspectable_Append( vector, (IInspectable *)info ); IDeviceInformation_Release( info ); diff --git a/dlls/windows.devices.enumeration/private.h b/dlls/windows.devices.enumeration/private.h index 0ce5624b21c..033e866b506 100644 --- a/dlls/windows.devices.enumeration/private.h +++ b/dlls/windows.devices.enumeration/private.h @@ -28,6 +28,9 @@ #include "winbase.h" #include "winstring.h" #include "objbase.h" +#include "devpropdef.h" +#include "devfiltertypes.h" +#include "devquerydef.h" #include "propsys.h"
#include "activation.h" @@ -63,7 +66,7 @@ extern HRESULT async_operation_inspectable_create( const GUID *iid, IUnknown *in extern HRESULT async_action_create( IUnknown *invoker, async_operation_callback callback, IAsyncAction **out );
extern HRESULT vector_create( const struct vector_iids *iids, void **out ); -extern HRESULT device_information_create( const WCHAR *path, IDeviceInformation **info ); +extern HRESULT device_information_create( const DEV_OBJECT *obj, IDeviceInformation **info );
#define DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from, iface_mem, expr ) \ static inline impl_type *impl_from( iface_type *iface ) \ diff --git a/dlls/windows.devices.enumeration/tests/devices.c b/dlls/windows.devices.enumeration/tests/devices.c index 9e4cc378c75..a646a41073b 100644 --- a/dlls/windows.devices.enumeration/tests/devices.c +++ b/dlls/windows.devices.enumeration/tests/devices.c @@ -701,7 +701,7 @@ static void test_DeviceInformation_obj( int line, IDeviceInformation *info, cons hr = IDeviceInformation_get_IsDefault( info, &bool_val ); todo_wine ok_(__FILE__, line)( hr == S_OK, "get_IsDefault failed, got hr %#lx\n", hr ); hr = IDeviceInformation_get_Properties( info, &properties ); - todo_wine ok_(__FILE__, line)( hr == S_OK, "get_Properties failed, got hr %#lx\n", hr ); + ok_(__FILE__, line)( hr == S_OK, "get_Properties failed, got hr %#lx\n", hr ); if (FAILED(hr)) { WindowsDeleteString( id );
From: Vibhav Pant vibhavp@gmail.com
--- dlls/windows.devices.enumeration/main.c | 204 ++++++++++++++---- .../tests/devices.c | 25 +-- 2 files changed, 174 insertions(+), 55 deletions(-)
diff --git a/dlls/windows.devices.enumeration/main.c b/dlls/windows.devices.enumeration/main.c index c965ca363e0..b16d26940fa 100644 --- a/dlls/windows.devices.enumeration/main.c +++ b/dlls/windows.devices.enumeration/main.c @@ -35,7 +35,10 @@ WINE_DEFAULT_DEBUG_CHANNEL(enumeration); struct devquery_params { IUnknown IUnknown_iface; + DEV_OBJECT_TYPE type; struct aqs_expr *expr; + DEVPROPCOMPKEY *prop_keys; + ULONG prop_keys_len; LONG ref; };
@@ -78,6 +81,7 @@ static ULONG WINAPI devquery_params_Release( IUnknown *iface ) if (!ref) { free_aqs_expr( impl->expr ); + free( impl->prop_keys ); free( impl ); } return ref; @@ -91,7 +95,7 @@ static const IUnknownVtbl devquery_params_vtbl = devquery_params_Release, };
-static HRESULT devquery_params_create( struct aqs_expr *expr, IUnknown **out ) +static HRESULT devquery_params_create( DEV_OBJECT_TYPE type, struct aqs_expr *expr, DEVPROPCOMPKEY *prop_keys, ULONG prop_keys_len, IUnknown **out ) { struct devquery_params *impl;
@@ -100,7 +104,10 @@ static HRESULT devquery_params_create( struct aqs_expr *expr, IUnknown **out )
impl->IUnknown_iface.lpVtbl = &devquery_params_vtbl; impl->ref = 1; + impl->type = type; impl->expr = expr; + impl->prop_keys = prop_keys; + impl->prop_keys_len = prop_keys_len; *out = &impl->IUnknown_iface; return S_OK; } @@ -399,8 +406,8 @@ static HRESULT WINAPI device_watcher_Start( IDeviceWatcher *iface ) }
IWeakReferenceSource_GetWeakReference( &impl->weak_reference_source.IWeakReferenceSource_iface, &weak ); - hr = DevCreateObjectQuery( DevObjectTypeDeviceInterfaceDisplay, DevQueryFlagAsyncClose, ARRAY_SIZE( device_iface_default_props ), - device_iface_default_props, filters_len, filters, device_object_query_callback, weak, &impl->query ); + hr = DevCreateObjectQuery( query_params->type, DevQueryFlagAsyncClose, query_params->prop_keys_len, query_params->prop_keys, filters_len, filters, + device_object_query_callback, weak, &impl->query ); if (FAILED(hr)) { ERR( "Failed to create device query: %#lx\n", hr ); @@ -468,10 +475,85 @@ static const struct IDeviceWatcherVtbl device_watcher_vtbl = device_watcher_Stop, };
-static HRESULT device_watcher_create( HSTRING filter, IDeviceWatcher **out ) +static HRESULT WINAPI append_devpropcompkeys_from_names( IIterable_HSTRING *names_iterable, DEVPROPCOMPKEY **keys, ULONG *keys_len ) { + IIterator_HSTRING *names; + boolean valid; + HRESULT hr; + + if (FAILED(hr = IIterable_HSTRING_First( names_iterable, &names ))) return hr; + if (FAILED(hr = IIterator_HSTRING_get_HasCurrent( names, &valid ))) + { + IIterator_HSTRING_Release( names ); + return hr; + } + while (valid && SUCCEEDED(hr)) + { + DEVPROPCOMPKEY *tmp; + const WCHAR *buf; + BOOL dup = FALSE; + DEVPROPKEY key; + HSTRING name; + ULONG i; + + if (FAILED(hr = IIterator_HSTRING_get_Current( names, &name ))) break; + buf = WindowsGetStringRawBuffer( name, NULL ); + if (buf[0] == '{') + hr = PSPropertyKeyFromString( buf, (PROPERTYKEY *)&key ); + else + hr = PSGetPropertyKeyFromName( buf, (PROPERTYKEY *)&key ); + WindowsDeleteString( name ); + if (FAILED(hr)) break; + for (i = 0; i < *keys_len; i++) + { + if (IsEqualDevPropKey( (*keys)[i].Key, key )) + { + dup = TRUE; + break; + } + } + if (dup) + { + hr = IIterator_HSTRING_MoveNext( names, &valid ); + continue; + } + if (!(tmp = realloc( *keys, sizeof( **keys ) * (*keys_len + 1) ))) + { + hr = E_OUTOFMEMORY; + break; + } + *keys = tmp; + tmp = &(*keys)[*keys_len]; + *keys_len += 1; + tmp->Key = key; + tmp->Store = DEVPROP_STORE_SYSTEM; + tmp->LocaleName = NULL; + + hr = IIterator_HSTRING_MoveNext( names, &valid ); + } + + IIterator_HSTRING_Release( names ); + return hr; +} + +static HRESULT device_watcher_create( HSTRING filter, IIterable_HSTRING *additional_props, DeviceInformationKind kind, IDeviceWatcher **out ) +{ + static const DEV_OBJECT_TYPE kind_type[] = { + DevObjectTypeUnknown, + DevObjectTypeDeviceInterfaceDisplay, + DevObjectTypeDeviceContainerDisplay, + DevObjectTypeDevice, + DevObjectTypeDeviceInterfaceClass, + DevObjectTypeAEP, + DevObjectTypeAEPContainer, + DevObjectTypeAEPService, + DevObjectTypeDevicePanel, + }; + DEV_OBJECT_TYPE type = DevObjectTypeUnknown; + DEVPROPCOMPKEY *prop_keys = NULL; + struct aqs_expr *expr = NULL; struct device_watcher *impl; - struct aqs_expr *expr; + ULONG prop_keys_len = 0; HRESULT hr;
if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY; @@ -483,19 +565,26 @@ static HRESULT device_watcher_create( HSTRING filter, IDeviceWatcher **out ) return hr; } /* If the filter string is all whitespaces, we return E_INVALIDARG in IDeviceWatcher_Start, not here. */ - if (FAILED(hr = aqs_parse_query( WindowsGetStringRawBuffer( filter, NULL ), &expr, &impl->aqs_all_whitespace )) && !impl->aqs_all_whitespace) - { - weak_reference_strong_release( &impl->weak_reference_source ); - free( impl ); - return hr; - } - if (FAILED(hr = devquery_params_create( expr, &impl->query_params ))) + if (FAILED(hr = aqs_parse_query( WindowsGetStringRawBuffer( filter, NULL ), &expr, &impl->aqs_all_whitespace )) && !impl->aqs_all_whitespace) goto failed; + + if (kind < ARRAY_SIZE( kind_type )) { - free_aqs_expr( expr ); - weak_reference_strong_release( &impl->weak_reference_source ); - free( impl ); - return hr; + type = kind_type[kind]; + if (kind == DeviceInformationKind_DeviceInterface) + { + prop_keys_len = ARRAY_SIZE( device_iface_default_props ); + if (!(prop_keys = calloc( prop_keys_len, sizeof( *prop_keys ) ))) + { + hr = E_OUTOFMEMORY; + goto failed; + } + memcpy( prop_keys, device_iface_default_props, prop_keys_len * sizeof( *prop_keys ) ); + } } + else FIXME( "Unknown DeviceInformationKind value: %u\n", kind ); + + if (additional_props && FAILED(hr = append_devpropcompkeys_from_names( additional_props, &prop_keys, &prop_keys_len ))) goto failed; + if (FAILED(hr = devquery_params_create( type, expr, prop_keys, prop_keys_len, &impl->query_params ))) goto failed;
list_init( &impl->added_handlers ); list_init( &impl->enumerated_handlers ); @@ -508,6 +597,13 @@ static HRESULT device_watcher_create( HSTRING filter, IDeviceWatcher **out ) *out = &impl->IDeviceWatcher_iface; TRACE( "created DeviceWatcher %p\n", *out ); return S_OK; + +failed: + free( prop_keys ); + free_aqs_expr( expr ); + weak_reference_strong_release( &impl->weak_reference_source ); + free( impl ); + return hr; }
struct device_information_statics @@ -650,8 +746,7 @@ static HRESULT find_all_async( IUnknown *invoker, IUnknown *param, PROPVARIANT * filters_len = params->expr->len; } if (FAILED(hr = vector_create( &iids, (void *)&vector ))) return hr; - hr = DevGetObjects( DevObjectTypeDeviceInterfaceDisplay, DevQueryFlagNone, ARRAY_SIZE( device_iface_default_props ), device_iface_default_props, filters_len, filters, - &len, &objects ); + hr = DevGetObjects( params->type, DevQueryFlagNone, params->prop_keys_len, params->prop_keys, filters_len, filters, &len, &objects ); if (FAILED(hr)) { IVector_IInspectable_Release( vector ); @@ -680,7 +775,7 @@ static HRESULT WINAPI device_statics_FindAllAsync( IDeviceInformationStatics *if IAsyncOperation_DeviceInformationCollection **op ) { TRACE( "iface %p, op %p\n", iface, op ); - return IDeviceInformationStatics_FindAllAsyncAqsFilter( iface, NULL, op ); + return IDeviceInformationStatics_FindAllAsyncAqsFilterAndAdditionalProperties( iface, NULL, NULL, op ); }
static HRESULT WINAPI device_statics_FindAllAsyncDeviceClass( IDeviceInformationStatics *iface, DeviceClass class, @@ -693,34 +788,44 @@ static HRESULT WINAPI device_statics_FindAllAsyncDeviceClass( IDeviceInformation static HRESULT WINAPI device_statics_FindAllAsyncAqsFilter( IDeviceInformationStatics *iface, HSTRING filter, IAsyncOperation_DeviceInformationCollection **op ) { - struct aqs_expr *expr; - IUnknown *params; - HRESULT hr; - TRACE( "iface %p, aqs %p, op %p\n", iface, debugstr_hstring(filter), op ); - - if (FAILED(hr = aqs_parse_query(WindowsGetStringRawBuffer( filter, NULL ), &expr, NULL ))) return hr; - if (FAILED(hr = devquery_params_create( expr, ¶ms ))) - { - free_aqs_expr( expr ); - return hr; - } - return async_operation_inspectable_create( &IID_IAsyncOperation_DeviceInformationCollection, (IUnknown *)iface, (IUnknown *)params, - find_all_async, (IAsyncOperation_IInspectable **)op ); + return IDeviceInformationStatics_FindAllAsyncAqsFilterAndAdditionalProperties( iface, filter, NULL, op ); }
static HRESULT WINAPI device_statics_FindAllAsyncAqsFilterAndAdditionalProperties( IDeviceInformationStatics *iface, HSTRING filter, IIterable_HSTRING *additional_properties, IAsyncOperation_DeviceInformationCollection **op ) { - FIXME( "iface %p, aqs %p, additional_properties %p, op %p stub!\n", iface, debugstr_hstring(filter), additional_properties, op ); - return E_NOTIMPL; + ULONG prop_keys_len = ARRAY_SIZE( device_iface_default_props ); + DEVPROPCOMPKEY *prop_keys = NULL; + struct aqs_expr *expr = NULL; + IUnknown *params; + HRESULT hr; + + TRACE( "iface %p, aqs %s, additional_properties %p, op %p\n", iface, debugstr_hstring(filter), additional_properties, op ); + + if (!(prop_keys = calloc( prop_keys_len, sizeof( *prop_keys ) ))) return E_OUTOFMEMORY; + memcpy( prop_keys, device_iface_default_props, prop_keys_len * sizeof( *prop_keys ) ); + + if (additional_properties && FAILED(hr = append_devpropcompkeys_from_names( additional_properties, &prop_keys, &prop_keys_len ))) goto failed; + if (FAILED(hr = aqs_parse_query(WindowsGetStringRawBuffer( filter, NULL ), &expr, NULL ))) goto failed; + if (FAILED(hr = devquery_params_create( DevObjectTypeDeviceInterfaceDisplay, expr, prop_keys, prop_keys_len, ¶ms ))) goto failed; + + hr = async_operation_inspectable_create( &IID_IAsyncOperation_DeviceInformationCollection, (IUnknown *)iface, params, find_all_async, + (IAsyncOperation_IInspectable **)op ); + IUnknown_Release( params ); + if (SUCCEEDED(hr)) return hr; + +failed: + free( prop_keys ); + free_aqs_expr( expr ); + return hr; }
static HRESULT WINAPI device_statics_CreateWatcher( IDeviceInformationStatics *iface, IDeviceWatcher **watcher ) { TRACE( "iface %p, watcher %p\n", iface, watcher ); - return device_watcher_create( NULL, watcher ); + return IDeviceInformationStatics_CreateWatcherAqsFilterAndAdditionalProperties( iface, NULL, NULL, watcher ); }
static HRESULT WINAPI device_statics_CreateWatcherDeviceClass( IDeviceInformationStatics *iface, DeviceClass class, IDeviceWatcher **watcher ) @@ -732,14 +837,14 @@ static HRESULT WINAPI device_statics_CreateWatcherDeviceClass( IDeviceInformatio static HRESULT WINAPI device_statics_CreateWatcherAqsFilter( IDeviceInformationStatics *iface, HSTRING filter, IDeviceWatcher **watcher ) { TRACE( "iface %p, filter %s, watcher %p\n", iface, debugstr_hstring(filter), watcher ); - return device_watcher_create( filter, watcher ); + return IDeviceInformationStatics_CreateWatcherAqsFilterAndAdditionalProperties( iface, filter, NULL, watcher ); }
static HRESULT WINAPI device_statics_CreateWatcherAqsFilterAndAdditionalProperties( IDeviceInformationStatics *iface, HSTRING filter, IIterable_HSTRING *additional_properties, IDeviceWatcher **watcher ) { - FIXME( "iface %p, aqs %p, additional_properties %p, watcher %p stub!\n", iface, debugstr_hstring(filter), additional_properties, watcher ); - return E_NOTIMPL; + TRACE( "iface %p, aqs %s, additional_properties %p, watcher %p\n", iface, debugstr_hstring(filter), additional_properties, watcher ); + return device_watcher_create( filter, additional_properties, DeviceInformationKind_DeviceInterface, watcher ); }
static const struct IDeviceInformationStaticsVtbl device_statics_vtbl = @@ -791,13 +896,30 @@ static HRESULT WINAPI device_statics2_FindAllAsync( IDeviceInformationStatics2 * return E_NOTIMPL; }
+static const char *debugstr_DeviceInformationKind( DeviceInformationKind kind ) +{ + static const char *str[] = { + "Unknown", + "DeviceInterface", + "DeviceContainer", + "Device", + "DeviceInterfaceClass", + "AssociationEndpoint", + "AssociationEndpointContainer", + "AssociationEndpointService", + "DevicePanel", + }; + if (kind < ARRAY_SIZE( str )) return wine_dbg_sprintf( "DeviceInformationKind_%s", str[kind] ); + return wine_dbg_sprintf("(unknown %u)\n", kind ); +} + static HRESULT WINAPI device_statics2_CreateWatcher( IDeviceInformationStatics2 *iface, HSTRING filter, IIterable_HSTRING *additional_properties, DeviceInformationKind kind, IDeviceWatcher **watcher ) { - FIXME( "iface %p, filter %s, additional_properties %p, kind %u, watcher %p semi-stub!\n", - iface, debugstr_hstring( filter ), additional_properties, kind, watcher ); - return device_watcher_create( filter, watcher ); + TRACE( "iface %p, filter %s, additional_properties %p, kind %s, watcher %p\n", + iface, debugstr_hstring( filter ), additional_properties, debugstr_DeviceInformationKind( kind ), watcher ); + return device_watcher_create( filter, additional_properties, kind, watcher ); }
static const struct IDeviceInformationStatics2Vtbl device_statics2_vtbl = diff --git a/dlls/windows.devices.enumeration/tests/devices.c b/dlls/windows.devices.enumeration/tests/devices.c index a646a41073b..891722d39fc 100644 --- a/dlls/windows.devices.enumeration/tests/devices.c +++ b/dlls/windows.devices.enumeration/tests/devices.c @@ -1001,25 +1001,22 @@ static void test_DeviceInformation( void ) IVectorView_DeviceInformation_Release( info_collection );
hr = IDeviceInformationStatics_FindAllAsyncAqsFilterAndAdditionalProperties( device_info_statics, NULL, NULL, &info_collection_async ); - todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( hr == S_OK, "got hr %#lx\n", hr );
- if (SUCCEEDED( hr )) - { - await_device_information_collection( info_collection_async ); - check_device_information_collection_async( info_collection_async, 2, Completed, S_OK, &info_collection ); - IAsyncOperation_DeviceInformationCollection_Release( info_collection_async ); - test_DeviceInformationCollection( __LINE__, info_collection, device_iface_exp_props, ARRAY_SIZE( device_iface_exp_props ) - 1 ); - IVectorView_DeviceInformation_Release( info_collection ); - } + await_device_information_collection( info_collection_async ); + check_device_information_collection_async_no_id( info_collection_async, Completed, S_OK, &info_collection ); + IAsyncOperation_DeviceInformationCollection_Release( info_collection_async ); + test_DeviceInformationCollection( __LINE__, info_collection, device_iface_exp_props, ARRAY_SIZE( device_iface_exp_props ) - 1 ); + IVectorView_DeviceInformation_Release( info_collection );
additional_props = iterable_hstring_create( device_iface_additional_props, ARRAY_SIZE( device_iface_additional_props ) ); hr = IDeviceInformationStatics_FindAllAsyncAqsFilterAndAdditionalProperties( device_info_statics, NULL, additional_props, &info_collection_async ); - todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( hr == S_OK, "got hr %#lx\n", hr ); IIterable_HSTRING_Release( additional_props ); if (SUCCEEDED( hr )) { await_device_information_collection( info_collection_async ); - check_device_information_collection_async( info_collection_async, 3, Completed, S_OK, &info_collection ); + check_device_information_collection_async_no_id( info_collection_async, Completed, S_OK, &info_collection ); IAsyncOperation_DeviceInformationCollection_Release( info_collection_async ); test_DeviceInformationCollection( __LINE__, info_collection, device_iface_exp_props, ARRAY_SIZE( device_iface_exp_props ) ); IVectorView_DeviceInformation_Release( info_collection ); @@ -1030,7 +1027,7 @@ static void test_DeviceInformation( void ) winetest_push_context( "device_nonexistent_props[%d]", i ); additional_props = iterable_hstring_create( &device_nonexistent_props[i], 1 ); hr = IDeviceInformationStatics_FindAllAsyncAqsFilterAndAdditionalProperties( device_info_statics, NULL, additional_props, &info_collection_async ); - todo_wine ok( hr == TYPE_E_ELEMENTNOTFOUND, "got hr %#lx\n", hr ); + ok( hr == TYPE_E_ELEMENTNOTFOUND, "got hr %#lx\n", hr ); IIterable_HSTRING_Release( additional_props ); winetest_pop_context(); } @@ -1040,7 +1037,7 @@ static void test_DeviceInformation( void ) winetest_push_context( "device_invalid_props[%d]", i ); additional_props = iterable_hstring_create( &device_invalid_props[i], 1 ); hr = IDeviceInformationStatics_FindAllAsyncAqsFilterAndAdditionalProperties( device_info_statics, NULL, additional_props, &info_collection_async ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); IIterable_HSTRING_Release( additional_props ); winetest_pop_context(); } @@ -1366,7 +1363,7 @@ static void test_aqs_filters( void ) hr = WindowsCreateString( filter_iface_display, wcslen( filter_iface_display ), &str ); ok( hr == S_OK, "got hr %#lx\n", hr ); hr = IDeviceInformationStatics_FindAllAsyncAqsFilterAndAdditionalProperties( statics, str, props_iterable, &info_collection_async ); - todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( hr == S_OK, "got hr %#lx\n", hr ); WindowsDeleteString( str ); IIterable_HSTRING_Release( props_iterable ); if (SUCCEEDED( hr ))
Rémi Bernon (@rbernon) commented about dlls/windows.devices.enumeration/tests/devices.c:
case PropertyType_String:
{ HSTRING str;
boolean bool_val;
hr = IDeviceInformation_get_Id( info, &str );
ok_(__FILE__, line)( hr == S_OK, "got hr %#lx\n", hr );
hr = IPropertyValue_GetString( propval, &str );
ok( hr == S_OK, "got hr %#lx\n", hr );
ok( prop->Type == DEVPROP_TYPE_STRING || prop->Type == DEVPROP_TYPE_STRING_INDIRECT, "got Type %#lx\n", prop->Type );
/* TODO:
* For DEVPROP_TYPE_STRING_INDIRECT, WinRT extracts the locale-specific string from the referenced INF.
* System.ItemNameDisplay's value is formatted differently by WinRT. */
if (prop->Type == DEVPROP_TYPE_STRING)
ok( !wcsicmp( WindowsGetStringRawBuffer( str, NULL ), prop->Buffer ) || broken( !wcsicmp( prop_name, L"System.ItemNameDisplay" ) ),
"got str %s != %s\n", debugstr_hstring( str ), debugstr_w( prop->Buffer ) );
I'm not sure we should use broken here, is it different across Windows versions or just different for this property? In the latter case, the property should be special cased and we should implement the same thing as native.
Rémi Bernon (@rbernon) commented about dlls/windows.devices.enumeration/tests/devices.c:
hr = IIterator_IKeyValuePair_HSTRING_IInspectable_MoveNext( iterator, &bool_val );
ok_(__FILE__, line)( hr == S_OK, "MoveNext failed, got hr %#lx\n", hr );
winetest_pop_context();
- }
- WindowsDeleteString( id );
- IIterator_IKeyValuePair_HSTRING_IInspectable_Release( iterator );
}
static void test_DeviceInformation( void ) {
- static const struct device_property device_iface_exp_props[] = {
{ L"System.Devices.InterfaceEnabled",PropertyType_Boolean },
{ L"System.Devices.DeviceInstanceId", PropertyType_String },
- };
Nit: inconsistent spacing.
Rémi Bernon (@rbernon) commented about dlls/windows.devices.enumeration/tests/devices.c:
- hr = IDeviceInformation_get_Properties( info, &properties );
- todo_wine ok_(__FILE__, line)( hr == S_OK, "get_Properties failed, got hr %#lx\n", hr );
- if (FAILED(hr))
- {
WindowsDeleteString( id );
return;
- }
- for (i = 0; i < exp_props_len; i++)
- {
PropertyType type = 0xdeadbeef;
HSTRING_HEADER hdr;
winetest_push_context( "exp_props[%Iu]", i );
hr = WindowsCreateStringReference( exp_props[i].name, wcslen( exp_props[i].name ), &hdr, &str );
ok_(__FILE__, line)( hr == S_OK, "WindowsCreateStringReference failed, got hr %#lx\n", hr );
I think the test helper is getting overused. All these tests will end up with the same line as you use `ok_(__FILE__, line)` everywhere, and it'll be tricky to identify any failing test. Is it useful to run all these property tests on every object enumerated? You might want to use test context instead, and/or you might want to reduce the tests to explicit list of object / property combination.
Rémi Bernon (@rbernon) commented about dlls/windows.devices.enumeration/tests/devices.c:
};
+static void test_DeviceInformation_prop_guid( IDeviceInformation *info, const WCHAR *prop, const GUID *guid_val ) +{
- IMapView_HSTRING_IInspectable *props;
- IInspectable *inspectable;
- IReference_GUID *val;
- GUID guid = {0};
- HSTRING str;
- HRESULT hr;
- hr = IDeviceInformation_get_Properties( info, &props );
- ok( hr == S_OK, "got hr %#lx\n", hr );
- hr = WindowsCreateString( prop, wcslen( prop ), &str );
- ok( hr == S_OK, "got hr %#lx\n", hr );
You're leaking str here.
Rémi Bernon (@rbernon) commented about dlls/windows.devices.enumeration/tests/devices.c:
+struct iterable_hstring +{
- IIterable_HSTRING IIterable_HSTRING_iface;
- LONG ref;
- ULONG count;
- HSTRING values[];
+};
+C_ASSERT( sizeof( struct iterable_hstring ) == offsetof( struct iterable_hstring, values[0] ) );
+static inline struct iterable_hstring *impl_from_IIterable_HSTRING( IIterable_HSTRING *iface ) +{
- return CONTAINING_RECORD( iface, struct iterable_hstring, IIterable_HSTRING_iface );
+}
Should we maybe copy and use winrt `vector.c` in the tests instead?
Rémi Bernon (@rbernon) commented about dlls/windows.devices.enumeration/information.c:
case DEVPROP_TYPE_DOUBLE:
hr = IPropertyValueStatics_CreateDouble( propval_statics, *(DOUBLE *)prop->Buffer, &val );
break;
case DEVPROP_TYPE_GUID:
hr = IPropertyValueStatics_CreateGuid( propval_statics, *(GUID *)prop->Buffer, &val );
break;
case DEVPROP_TYPE_FILETIME:
hr = IPropertyValueStatics_CreateDateTime( propval_statics, *(DateTime *)prop->Buffer, &val );
break;
case DEVPROP_TYPE_STRING:
{
if (SUCCEEDED(hr = WindowsCreateString( prop->Buffer, wcslen( prop->Buffer ), &str )))
{
hr = IPropertyValueStatics_CreateString( propval_statics, str, &val );
WindowsDeleteString( str );
}
What about using WindowsCreateStringReference here too?
Rémi Bernon (@rbernon) commented about dlls/windows.devices.enumeration/information.c:
if (SUCCEEDED(hr = PSGetNameFromPropertyKey( (PROPERTYKEY *)propkey, &name )))
{
hr = WindowsCreateString( name, wcslen( name ), &canonical_name );
CoTaskMemFree( name );
}
else if (hr == TYPE_E_ELEMENTNOTFOUND)
{
const GUID *fmtid = &propkey->fmtid;
WCHAR buf[80];
WARN( "Unknown property key: %s\n", debugstr_DEVPROPKEY( propkey ) );
swprintf( buf, ARRAY_SIZE( buf ), L"{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x} %lu", fmtid->Data1, fmtid->Data2, fmtid->Data3,
fmtid->Data4[0], fmtid->Data4[1], fmtid->Data4[2], fmtid->Data4[3], fmtid->Data4[4], fmtid->Data4[5], fmtid->Data4[6], fmtid->Data4[7], propkey->pid );
hr = WindowsCreateString( buf, wcslen( buf ), &canonical_name );
}
if (FAILED(hr)) break;
Is it supposed to be allowed to fail? Shouldn't the error case be handled in PSGetNameFromPropertyKey with a FIXME instead?
Rémi Bernon (@rbernon) commented about dlls/windows.devices.enumeration/information.c:
- if (FAILED(hr = RoGetActivationFactory( str, &IID_IPropertyValueStatics, (void **)&propval_statics ))) goto done;
- for (i = 0; i < len; i++)
- {
const DEVPROPERTY *prop = &props[i];
const DEVPROPKEY *propkey = &prop->CompKey.Key;
HSTRING canonical_name;
IInspectable *val;
boolean replaced;
WCHAR *name;
if (SUCCEEDED(hr = PSGetNameFromPropertyKey( (PROPERTYKEY *)propkey, &name )))
{
hr = WindowsCreateString( name, wcslen( name ), &canonical_name );
CoTaskMemFree( name );
}
What about moving the name query after the property creation and use WindowsCreateStringReference too?
Rémi Bernon (@rbernon) commented about dlls/windows.devices.enumeration/tests/devices.c:
IVectorView_DeviceInformation_Release( info_collection ); hr = IDeviceInformationStatics_FindAllAsyncAqsFilterAndAdditionalProperties( device_info_statics, NULL, NULL, &info_collection_async );
- todo_wine ok( hr == S_OK, "got hr %#lx\n", hr );
- ok( hr == S_OK, "got hr %#lx\n", hr );
- if (SUCCEEDED( hr ))
- { await_device_information_collection( info_collection_async );
check_device_information_collection_async( info_collection_async, 2, Completed, S_OK, &info_collection );
- check_device_information_collection_async_no_id( info_collection_async, Completed, S_OK, &info_collection ); IAsyncOperation_DeviceInformationCollection_Release( info_collection_async );
This change should probably be moved to when the test is being added, same below.
Rémi Bernon (@rbernon) commented about dlls/windows.devices.enumeration/tests/devices.c:
- if (SUCCEEDED( hr ))
- { await_device_information_collection( info_collection_async );
check_device_information_collection_async( info_collection_async, 2, Completed, S_OK, &info_collection );
- check_device_information_collection_async_no_id( info_collection_async, Completed, S_OK, &info_collection ); IAsyncOperation_DeviceInformationCollection_Release( info_collection_async ); test_DeviceInformationCollection( __LINE__, info_collection, device_iface_exp_props, ARRAY_SIZE( device_iface_exp_props ) - 1 ); IVectorView_DeviceInformation_Release( info_collection );
}
additional_props = iterable_hstring_create( device_iface_additional_props, ARRAY_SIZE( device_iface_additional_props ) ); hr = IDeviceInformationStatics_FindAllAsyncAqsFilterAndAdditionalProperties( device_info_statics, NULL, additional_props, &info_collection_async );
todo_wine ok( hr == S_OK, "got hr %#lx\n", hr );
- ok( hr == S_OK, "got hr %#lx\n", hr ); IIterable_HSTRING_Release( additional_props ); if (SUCCEEDED( hr ))
This if is now unnecessary.
Rémi Bernon (@rbernon) commented about dlls/windows.devices.enumeration/main.c:
-static HRESULT device_watcher_create( HSTRING filter, IDeviceWatcher **out ) +static HRESULT WINAPI append_devpropcompkeys_from_names( IIterable_HSTRING *names_iterable, DEVPROPCOMPKEY **keys, ULONG *keys_len ) {
- IIterator_HSTRING *names;
- boolean valid;
- HRESULT hr;
- if (FAILED(hr = IIterable_HSTRING_First( names_iterable, &names ))) return hr;
- if (FAILED(hr = IIterator_HSTRING_get_HasCurrent( names, &valid )))
- {
IIterator_HSTRING_Release( names );
return hr;
- }
- while (valid && SUCCEEDED(hr))
- {
```suggestion:-6+0 hr = IIterator_HSTRING_get_HasCurrent( names, &valid ); while (SUCCEEDED(hr) && valid) { ```
Rémi Bernon (@rbernon) commented about dlls/windows.devices.enumeration/main.c:
+static const char *debugstr_DeviceInformationKind( DeviceInformationKind kind ) +{
- static const char *str[] = {
"Unknown",
"DeviceInterface",
"DeviceContainer",
"Device",
"DeviceInterfaceClass",
"AssociationEndpoint",
"AssociationEndpointContainer",
"AssociationEndpointService",
"DevicePanel",
- };
- if (kind < ARRAY_SIZE( str )) return wine_dbg_sprintf( "DeviceInformationKind_%s", str[kind] );
- return wine_dbg_sprintf("(unknown %u)\n", kind );
```suggestion:-0+0 return wine_dbg_sprintf( "(unknown %u)\n", kind ); ```
Rémi Bernon (@rbernon) commented about dlls/windows.devices.enumeration/main.c:
hr = PSGetPropertyKeyFromName( buf, (PROPERTYKEY *)&key );
WindowsDeleteString( name );
if (FAILED(hr)) break;
for (i = 0; i < *keys_len; i++)
{
if (IsEqualDevPropKey( (*keys)[i].Key, key ))
{
dup = TRUE;
break;
}
}
if (dup)
{
hr = IIterator_HSTRING_MoveNext( names, &valid );
continue;
}
I think it'd be nicer with a dedicated lookup helper.
Rémi Bernon (@rbernon) commented about dlls/windows.devices.enumeration/main.c:
if (dup)
{
hr = IIterator_HSTRING_MoveNext( names, &valid );
continue;
}
if (!(tmp = realloc( *keys, sizeof( **keys ) * (*keys_len + 1) )))
{
hr = E_OUTOFMEMORY;
break;
}
*keys = tmp;
tmp = &(*keys)[*keys_len];
*keys_len += 1;
tmp->Key = key;
tmp->Store = DEVPROP_STORE_SYSTEM;
tmp->LocaleName = NULL;
It's usually not a good idea to increment realloc size by 1, you should rather count the max number of additional properties and allocate a large enough array upfront. It's fine to overallocate if there's duplicate entries.
On Mon Sep 29 11:01:08 2025 +0000, Rémi Bernon wrote:
It's usually not a good idea to increment realloc size by 1, you should rather count the max number of additional properties and allocate a large enough array upfront. It's fine to overallocate if there's duplicate entries.
Unfortunately `IIterator<HSTRING>` only allows moving forward, and it doesn't have a length property as well :/. I'll use a growth factor instead, thanks.
On Mon Sep 29 11:01:07 2025 +0000, Rémi Bernon wrote:
Is it supposed to be allowed to fail? Shouldn't the error case be handled in PSGetNameFromPropertyKey with a FIXME instead?
Yes, `PSGetNameFromPropertyKey` returns `TYPE_E_ELEMENTNOTFOUND` for unknown keys, so using the custom property syntax is something the caller needs to do:
```c PROPERTYKEY key = { 0 }; WCHAR* name; HRESULT hr;
key.pid = 0xdeadbeef; hr = PSGetNameFromPropertyKey( key, &name ); printf( "hr: %#lx\n", hr ); ```
That being said, we probably should log a FIXME in `PSGetNameFromPropertyKey` before returning TYPE_E_ELEMENTNOTFOUND.
On Mon Sep 29 11:01:06 2025 +0000, Rémi Bernon wrote:
I think the test helper is getting overused. All these tests will end up with the same line as you use `ok_(__FILE__, line)` everywhere, and it'll be tricky to identify any failing test. Is it useful to run all these property tests on every object enumerated? You might want to use test context instead, and/or you might want to reduce the tests to explicit list of object / property combination.
Yes, we can at least omit for the DeviceWatcher added handler. I have split this into `test_DeviceInformation_properties`, thanks.
On Mon Sep 29 13:47:19 2025 +0000, Vibhav Pant wrote:
Yes, `PSGetNameFromPropertyKey` returns `TYPE_E_ELEMENTNOTFOUND` for unknown keys, so using the custom property syntax is something the caller needs to do:
PROPERTYKEY key = { 0 }; WCHAR* name; HRESULT hr; key.pid = 0xdeadbeef; hr = PSGetNameFromPropertyKey( key, &name ); printf( "hr: %#lx\n", hr );
That being said, we probably should log a FIXME in `PSGetNameFromPropertyKey` before returning TYPE_E_ELEMENTNOTFOUND.
Sure but the question is still whether this will ever get called with a property for which it will return an error like this, or whether it will only be with properties we don't yet implement (and for which support in `PSGetNameFromPropertyKey` needs to be added)
On Mon Sep 29 11:01:07 2025 +0000, Rémi Bernon wrote:
What about using WindowsCreateStringReference here too?
We could, but I don't know if it would be helpful (for `canonical_name` as well).[`IPropertyValueStatics::CreateString` calls `WindowsDuplicateString`](), so the fast-pass string would anyway get copied to the heap.
OTOH, `WindowsCreateStringReference` is infallible, and the `WindowsDeleteString` call wouldn't be required (though we'll need to add a `HSTRING_HEADER` declaration):
```c case DEVPROP_TYPE_STRING: { HSTRING_HEADER hdr;
WindowsCreateStringReference( prop->Buffer, wcslen( prop->Buffer ), &str, &hdr ); hr = IPropertyValueStatics_CreateString( propval_statics, str, &val ); } ```
On Mon Sep 29 13:57:10 2025 +0000, Rémi Bernon wrote:
Sure but the question is still whether this will ever get called with a property for which it will return an error like this, or whether it will only be with properties we don't yet implement (and for which support in `PSGetNameFromPropertyKey` needs to be added)
The default set of properties that we always query (in `device_iface_default_props`) should always be known by propsys. While this bit is meant for when additional properties using the custom format (`{<guid>} <pid>`) were also requested, the `WARN` is for the edge case where an additional property uses the custom format, is known by propsys in native, but not in Wine.
On Mon Sep 29 11:01:06 2025 +0000, Rémi Bernon wrote:
Should we maybe copy and use winrt `vector.c` in the tests instead?
I wanted to, but I don't know how to add source files inside `tests/` that don't contain `START_TEST`, as the build expects it to have a `func_vector`. I could add a `START_TEST(vector) {}`, but that feels hacky.
On Mon Sep 29 11:01:06 2025 +0000, Rémi Bernon wrote:
I'm not sure we should use broken here, is it different across Windows versions or just different for this property? In the latter case, the property should be special cased and we should implement the same thing as native.
Ah, right. broken is not appropriate here. I don't know how to create the value for `Windows.ItemNameDisplay`, so I'll a skip at the very least, thanks.