[PATCH v2 0/4] MR10148: Draft: cfgmgr32: Implement device interface access functions.
-- v2: cfgmgr32: Implement CM_Get_Device_Interface_Property_Keys(_Ex)W. cfgmgr32: Reimplement CM_Get_Device_Interface_Property(_Ex)W. cfgmgr32: Implement CM_Get_Device_Interface_List(_Size)(_Ex)(A|W). cfgmgr32: Implement CM_Get_Class_Property_Keys(_Ex). https://gitlab.winehq.org/wine/wine/-/merge_requests/10148
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/cfgmgr32/cfgmgr32.c | 84 ++++++++++++++++++++++++++++++++++ dlls/cfgmgr32/cfgmgr32.spec | 4 +- dlls/cfgmgr32/tests/cfgmgr32.c | 47 +++++++++++++++++++ 3 files changed, 133 insertions(+), 2 deletions(-) diff --git a/dlls/cfgmgr32/cfgmgr32.c b/dlls/cfgmgr32/cfgmgr32.c index 74f4c7317b2..8902d157ee7 100644 --- a/dlls/cfgmgr32/cfgmgr32.c +++ b/dlls/cfgmgr32/cfgmgr32.c @@ -39,6 +39,17 @@ static const WCHAR *guid_string( const GUID *guid, WCHAR *buffer, UINT length ) return buffer; } +static LSTATUS propkey_from_string( const WCHAR *str, DEVPROPKEY *key ) +{ + const WCHAR *pid; + LSTATUS err; + + if (!(pid = wcsrchr( str, '\\' ))) return ERROR_INVALID_DATA; + if ((err = guid_from_string( str, &key->fmtid ))) return err; + if (swscanf( pid + 1, L"%04X", &key->pid ) != 1) return ERROR_INVALID_DATA; + return ERROR_SUCCESS; +} + static const WCHAR *propkey_string( const DEVPROPKEY *key, const WCHAR *prefix, WCHAR *buffer, UINT length ) { swprintf( buffer, length, L"%s{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\\%04X", prefix, @@ -241,6 +252,51 @@ static LSTATUS get_class_property( const GUID *class, struct property *prop ) return err; } +static LSTATUS enum_class_property_keys( HKEY hkey, DEVPROPKEY *buffer, ULONG *size ) +{ + ULONG capacity = *size, count = 0; + LSTATUS err = ERROR_SUCCESS; + HKEY props_key; + + for (UINT i = 0; i < ARRAY_SIZE(class_properties); i++) + { + const struct property_desc *desc = class_properties + i; + if (desc->name && !RegQueryValueExW( hkey, desc->name, NULL, NULL, NULL, NULL )) + { + if (capacity < ++count || !buffer) err = ERROR_MORE_DATA; + else buffer[count - 1] = *desc->key; + } + } + + if (!open_key( hkey, L"Properties", KEY_ENUMERATE_SUB_KEYS, TRUE, &props_key )) + { + WCHAR name[MAX_PATH]; + for (ULONG i = 0, len = ARRAY_SIZE(name); !RegEnumValueW( props_key, i, name, &len, 0, NULL, NULL, NULL ); i++, len = ARRAY_SIZE(name)) + { + if (capacity < ++count || !buffer) err = ERROR_MORE_DATA; + else err = propkey_from_string( name, buffer + count - 1 ); + } + RegCloseKey( props_key ); + } + + *size = count; + return err; +} + +static LSTATUS get_class_property_keys( const GUID *class, DEVPROPKEY *buffer, ULONG *size ) +{ + WCHAR path[39]; + LSTATUS err; + HKEY hkey; + + guid_string( class, path, ARRAY_SIZE(path) ); + if ((err = open_class_key( HKEY_LOCAL_MACHINE, path, KEY_ALL_ACCESS, TRUE, &hkey ))) return err; + err = enum_class_property_keys( hkey, buffer, size ); + RegCloseKey( hkey ); + + return err; +} + struct device_interface { GUID class_guid; @@ -576,6 +632,34 @@ CONFIGRET WINAPI CM_Get_Class_PropertyW( const GUID *class, const DEVPROPKEY *ke return CM_Get_Class_Property_ExW( class, key, type, buffer, size, flags, NULL ); } +/*********************************************************************** + * CM_Get_Class_Property_Keys_Ex (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Class_Property_Keys_Ex( const GUID *class, DEVPROPKEY *keys, ULONG *count, ULONG flags, HMACHINE machine ) +{ + LSTATUS err; + + TRACE( "class %s, keys %p, size %p, flags %#lx, machine %p\n", debugstr_guid(class), keys, count, flags, machine ); + if (machine) FIXME( "machine %p not implemented!\n", machine ); + if (flags) FIXME( "flags %#lx not implemented!\n", flags ); + + if (!class) return CR_INVALID_POINTER; + if (!count) return CR_INVALID_POINTER; + if (*count && !keys) return CR_INVALID_POINTER; + + err = get_class_property_keys( class, keys, count ); + if (err && err != ERROR_MORE_DATA) *count = 0; + return map_error( err ); +} + +/*********************************************************************** + * CM_Get_Class_Property_Keys (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Class_Property_Keys( const GUID *class, DEVPROPKEY *keys, ULONG *count, ULONG flags ) +{ + return CM_Get_Class_Property_Keys_Ex( class, keys, count, flags, NULL ); +} + /*********************************************************************** * CM_Open_Device_Interface_Key_ExW (cfgmgr32.@) */ diff --git a/dlls/cfgmgr32/cfgmgr32.spec b/dlls/cfgmgr32/cfgmgr32.spec index 99aef888aa2..275b6d6f509 100644 --- a/dlls/cfgmgr32/cfgmgr32.spec +++ b/dlls/cfgmgr32/cfgmgr32.spec @@ -74,8 +74,8 @@ @ stub CM_Get_Class_Name_ExW @ stdcall CM_Get_Class_PropertyW(ptr ptr ptr ptr long long) @ stdcall CM_Get_Class_Property_ExW(ptr ptr ptr ptr long long ptr) -@ stdcall CM_Get_Class_Property_Keys(ptr ptr ptr long) setupapi.CM_Get_Class_Property_Keys -@ stdcall CM_Get_Class_Property_Keys_Ex(ptr ptr ptr long ptr) setupapi.CM_Get_Class_Property_Keys_Ex +@ stdcall CM_Get_Class_Property_Keys(ptr ptr ptr long) +@ stdcall CM_Get_Class_Property_Keys_Ex(ptr ptr ptr long ptr) @ stdcall CM_Get_Class_Registry_PropertyA(ptr long ptr ptr long long ptr) @ stdcall CM_Get_Class_Registry_PropertyW(ptr long ptr ptr long long ptr) @ stub CM_Get_Depth diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index beba61591e6..f98af2a9a99 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -2481,6 +2481,52 @@ static void test_CM_Open_Device_Interface_Key(void) } } +static void test_CM_Get_Class_Property_Keys(void) +{ + GUID guid = GUID_DEVCLASS_HIDCLASS; + DEVPROPKEY buffer[64]; + CONFIGRET ret; + ULONG len; + + ret = CM_Get_Class_Property_Keys( &guid, buffer, NULL, 0 ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + ret = CM_Get_Class_Property_Keys( NULL, buffer, &len, 0 ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + len = 1; + ret = CM_Get_Class_Property_Keys( &guid, NULL, &len, 0 ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + + len = 0; + ret = CM_Get_Class_Property_Keys( &guid, NULL, &len, 0 ); + ok_x4( ret, ==, CR_BUFFER_SMALL ); + todo_wine ok( len == 9 || broken(len == 10), "got len %lu\n", len ); + len = 0; + ret = CM_Get_Class_Property_Keys( &guid, buffer, &len, 0 ); + ok_x4( ret, ==, CR_BUFFER_SMALL ); + todo_wine ok( len == 9 || broken(len == 10), "got len %lu\n", len ); + + memset( &guid, 0xcd, sizeof(guid) ); + len = ARRAY_SIZE(buffer); + memset( buffer, 0xcd, sizeof(buffer) ); + ret = CM_Get_Class_Property_Keys( &guid, buffer, &len, 0 ); + ok_x4( ret, ==, CR_NO_SUCH_REGISTRY_KEY ); + ok_u4( len, ==, 0 ); + + guid = GUID_DEVCLASS_HIDCLASS; + len = ARRAY_SIZE(buffer); + memset( buffer, 0xcd, sizeof(buffer) ); + ret = CM_Get_Class_Property_Keys( &guid, buffer, &len, 0 ); + ok_x4( ret, ==, CR_SUCCESS ); + todo_wine ok( len == 9 || broken(len == 10), "got len %lu\n", len ); + + ok( !memcmp( buffer + 0, &DEVPKEY_DeviceClass_ClassName, sizeof(*buffer) ), "got %s\n", debugstr_DEVPROPKEY( buffer + 0 ) ); + ok( !memcmp( buffer + 1, &DEVPKEY_DeviceClass_Name, sizeof(*buffer) ), "got %s\n", debugstr_DEVPROPKEY( buffer + 1 ) ); + todo_wine ok( !memcmp( buffer + 2, &DEVPKEY_DeviceClass_Security, sizeof(*buffer) ), "got %s\n", debugstr_DEVPROPKEY( buffer + 2 ) ); + todo_wine ok( !memcmp( buffer + 3, &DEVPKEY_DeviceClass_NoInstallClass, sizeof(*buffer) ), "got %s\n", debugstr_DEVPROPKEY( buffer + 3 ) ); + todo_wine ok( !memcmp( buffer + 4, &DEVPKEY_DeviceClass_IconPath, sizeof(*buffer) ), "got %s\n", debugstr_DEVPROPKEY( buffer + 4 ) ); + if (len == 9) todo_wine ok( !memcmp( buffer + 5, &DEVPKEY_NAME, sizeof(*buffer) ), "got %s\n", debugstr_DEVPROPKEY( buffer + 5 ) ); +} + START_TEST(cfgmgr32) { HMODULE mod = GetModuleHandleA("cfgmgr32.dll"); @@ -2499,6 +2545,7 @@ START_TEST(cfgmgr32) test_CM_Open_Class_Key(); test_CM_Get_Class_Registry_Property(); test_CM_Get_Class_Property(); + test_CM_Get_Class_Property_Keys(); test_CM_Open_Device_Interface_Key(); test_CM_Get_Device_ID_List(); test_CM_Register_Notification(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10148
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/cfgmgr32/cfgmgr32.c | 192 +++++++++++++++++++++++++++++++++ dlls/cfgmgr32/cfgmgr32.spec | 16 +-- dlls/cfgmgr32/tests/cfgmgr32.c | 187 +++++++++++++++++++++++++++++++- 3 files changed, 385 insertions(+), 10 deletions(-) diff --git a/dlls/cfgmgr32/cfgmgr32.c b/dlls/cfgmgr32/cfgmgr32.c index 8902d157ee7..811f4304017 100644 --- a/dlls/cfgmgr32/cfgmgr32.c +++ b/dlls/cfgmgr32/cfgmgr32.c @@ -101,6 +101,11 @@ static LSTATUS open_key( HKEY root, const WCHAR *key, REGSAM access, BOOL open, return RegCreateKeyExW( root, key, 0, NULL, 0, access, NULL, hkey, NULL ); } +static LSTATUS query_value( HKEY hkey, const WCHAR *value, WCHAR *buffer, DWORD len ) +{ + return RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &len ); +} + static LSTATUS open_class_key( HKEY root, const WCHAR *key, REGSAM access, BOOL open, HKEY *hkey ) { WCHAR path[MAX_PATH]; @@ -182,6 +187,33 @@ static LSTATUS query_named_property( HKEY hkey, const WCHAR *nameW, DEVPROPTYPE return err; } +typedef LSTATUS (*enum_objects_cb)( HKEY hkey, const void *object, const WCHAR *path, UINT path_len, void *context ); + +static LSTATUS enum_objects_size( HKEY hkey, const void *object, const WCHAR *path, UINT path_len, void *context ) +{ + UINT *total = context; + *total += WideCharToMultiByte( CP_ACP, 0, path, path_len, NULL, 0, 0, 0 ); + return ERROR_SUCCESS; +} + +struct enum_objects_append_params +{ + WCHAR *buffer; + UINT len; +}; + +static LSTATUS enum_objects_append( HKEY hkey, const void *object, const WCHAR *path, UINT path_len, void *context ) +{ + struct enum_objects_append_params *params = context; + + if (path_len > params->len) return ERROR_MORE_DATA; + memcpy( params->buffer, path, path_len * sizeof(WCHAR) ); + params->buffer += path_len; + params->len -= path_len; + + return ERROR_SUCCESS; +} + struct property_desc { const DEVPROPKEY *key; @@ -328,6 +360,64 @@ static LSTATUS open_device_interface_key( const struct device_interface *iface, return open_device_classes_key( HKEY_LOCAL_MACHINE, path, access, open, hkey ); } +static DEVPROP_BOOLEAN device_interface_enabled( HKEY hkey, const struct device_interface *iface ) +{ + DWORD linked, linked_size = sizeof(linked); + WCHAR control[MAX_PATH]; + + swprintf( control, ARRAY_SIZE(control), L"%s\\Control", iface->refstr ); + if (RegGetValueW( hkey, control, L"Linked", RRF_RT_DWORD, NULL, &linked, &linked_size )) return DEVPROP_FALSE; + return linked ? DEVPROP_TRUE : DEVPROP_FALSE; +} + +static LSTATUS enum_class_device_interfaces( HKEY root, struct device_interface *iface, DEVINSTID_W instance_id, BOOL all, + enum_objects_cb callback, void *context ) +{ + LSTATUS err = ERROR_SUCCESS; + HKEY iface_key; + + for (UINT i = 0; !err && !(err = RegEnumKeyW( root, i, iface->name, ARRAY_SIZE(iface->name) )); i++) + { + WCHAR *tmp, instance[MAX_PATH], path[MAX_PATH]; + + if ((err = open_key( root, iface->name, KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, TRUE, &iface_key ))) continue; + + if (!(err = query_value( iface_key, L"DeviceInstance", instance, ARRAY_SIZE(instance) )) && + (!instance_id || !wcsicmp( instance_id, instance ))) + { + for (tmp = wcschr( instance, '\\' ); tmp; tmp = wcschr( tmp, '\\' )) *tmp = '#'; + for (UINT j = 0; !err && !(err = RegEnumKeyW( iface_key, j, iface->refstr, ARRAY_SIZE(iface->refstr) )); j++) + { + ULONG len = swprintf( path, ARRAY_SIZE(path), L"\\\\?\\%s%s%s", instance, iface->refstr, iface->class ); + if (all || device_interface_enabled( iface_key, iface )) err = callback( iface_key, iface, path, len + 1, context ); + } + if (err == ERROR_NO_MORE_ITEMS) err = ERROR_SUCCESS; + } + + RegCloseKey( iface_key ); + } + if (err == ERROR_NO_MORE_ITEMS) err = ERROR_SUCCESS; + + return err; +} + +static LSTATUS enum_device_interface_list( GUID *class, DEVINSTID_W instance_id, BOOL all, enum_objects_cb callback, void *context ) +{ + struct device_interface iface; + HKEY class_key; + LSTATUS err; + + if (instance_id && !*instance_id) instance_id = NULL; + + guid_string( class, iface.class, ARRAY_SIZE(iface.class) ); + if ((err = open_device_classes_key( HKEY_LOCAL_MACHINE, iface.class, KEY_ENUMERATE_SUB_KEYS, TRUE, &class_key ))) return err; + err = enum_class_device_interfaces( class_key, &iface, instance_id, all, callback, context ); + RegCloseKey( class_key ); + + if (!err) callback( NULL, NULL, L"", 1, context ); + return err; +} + static CONFIGRET map_error( LSTATUS err ) { switch (err) @@ -660,6 +750,108 @@ CONFIGRET WINAPI CM_Get_Class_Property_Keys( const GUID *class, DEVPROPKEY *keys return CM_Get_Class_Property_Keys_Ex( class, keys, count, flags, NULL ); } +/*********************************************************************** + * CM_Get_Device_Interface_List_Size_ExW (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Device_Interface_List_Size_ExW( ULONG *len, GUID *class, DEVINSTID_W instance_id, ULONG flags, HMACHINE machine ) +{ + BOOL all = flags == CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES; + + TRACE( "len %p, class %s, instance %s, flags %#lx, machine %p\n", len, debugstr_guid(class), debugstr_w(instance_id), flags, machine ); + if (machine) FIXME( "machine %p not implemented!\n", machine ); + + if (!class) return CR_FAILURE; + if (!len) return CR_INVALID_POINTER; + if (flags & ~CM_GET_DEVICE_INTERFACE_LIST_BITS) return CR_INVALID_FLAG; + + *len = 0; + return map_error( enum_device_interface_list( class, instance_id, all, enum_objects_size, len ) ); +} + +/*********************************************************************** + * CM_Get_Device_Interface_List_Size_ExA (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Device_Interface_List_Size_ExA( ULONG *len, GUID *class, DEVINSTID_A instance_idA, ULONG flags, HMACHINE machine ) +{ + WCHAR instance_idW[MAX_PATH]; + + if (instance_idA) MultiByteToWideChar( CP_ACP, 0, instance_idA, -1, instance_idW, ARRAY_SIZE(instance_idW) ); + return CM_Get_Device_Interface_List_Size_ExW( len, class, instance_idA ? instance_idW : NULL, flags, machine ); +} + +/*********************************************************************** + * CM_Get_Device_Interface_List_SizeW (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Device_Interface_List_SizeW( ULONG *len, GUID *class, DEVINSTID_W instance_id, ULONG flags ) +{ + return CM_Get_Device_Interface_List_Size_ExW( len, class, instance_id, flags, NULL ); +} + +/*********************************************************************** + * CM_Get_Device_Interface_List_SizeA (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Device_Interface_List_SizeA( ULONG *len, GUID *class, DEVINSTID_A instance_id, ULONG flags ) +{ + return CM_Get_Device_Interface_List_Size_ExA( len, class, instance_id, flags, NULL ); +} + +/*********************************************************************** + * CM_Get_Device_Interface_List_ExW (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Device_Interface_List_ExW( GUID *class, DEVINSTID_W instance_id, WCHAR *buffer, ULONG len, ULONG flags, HMACHINE machine ) +{ + struct enum_objects_append_params params = {.buffer = buffer, .len = len}; + BOOL all = flags == CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES; + + TRACE( "class %s, instance %s, buffer %p, len %lu, flags %#lx, machine %p\n", debugstr_guid(class), debugstr_w(instance_id), buffer, len, flags, machine ); + if (machine) FIXME( "machine %p not implemented!\n", machine ); + + if (!class) return CR_FAILURE; + if (!buffer) return CR_INVALID_POINTER; + if (!len) return CR_BUFFER_SMALL; + if (flags & ~CM_GET_DEVICE_INTERFACE_LIST_BITS) return CR_INVALID_FLAG; + + memset( buffer, 0, len * sizeof(WCHAR) ); + return map_error( enum_device_interface_list( class, instance_id, all, enum_objects_append, ¶ms ) ); +} + +/*********************************************************************** + * CM_Get_Device_Interface_List_ExA (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Device_Interface_List_ExA( GUID *class, DEVINSTID_A instance_idA, char *bufferA, ULONG len, ULONG flags, HMACHINE machine ) +{ + WCHAR instance_idW[MAX_PATH], *bufferW; + CONFIGRET ret; + + bufferW = bufferA ? malloc( len * sizeof(WCHAR) ) : NULL; + if (instance_idA) MultiByteToWideChar( CP_ACP, 0, instance_idA, -1, instance_idW, ARRAY_SIZE(instance_idW) ); + ret = CM_Get_Device_Interface_List_ExW( class, instance_idA ? instance_idW : NULL, bufferA ? bufferW : NULL, len, flags, machine ); + if (!ret && bufferA && !WideCharToMultiByte( CP_ACP, 0, bufferW, len, bufferA, len, 0, 0 )) + { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) ret = CR_BUFFER_SMALL; + else ret = CR_FAILURE; + } + free( bufferW ); + + return ret; +} + +/*********************************************************************** + * CM_Get_Device_Interface_ListW (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Device_Interface_ListW( GUID *class, DEVINSTID_W instance_id, WCHAR *buffer, ULONG len, ULONG flags ) +{ + return CM_Get_Device_Interface_List_ExW( class, instance_id, buffer, len, flags, NULL ); +} + +/*********************************************************************** + * CM_Get_Device_Interface_ListA (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Device_Interface_ListA( GUID *class, DEVINSTID_A instance_id, char *buffer, ULONG len, ULONG flags ) +{ + return CM_Get_Device_Interface_List_ExA( class, instance_id, buffer, len, flags, NULL ); +} + /*********************************************************************** * CM_Open_Device_Interface_Key_ExW (cfgmgr32.@) */ diff --git a/dlls/cfgmgr32/cfgmgr32.spec b/dlls/cfgmgr32/cfgmgr32.spec index 275b6d6f509..580f45e0ba4 100644 --- a/dlls/cfgmgr32/cfgmgr32.spec +++ b/dlls/cfgmgr32/cfgmgr32.spec @@ -112,14 +112,14 @@ @ stdcall CM_Get_Device_Interface_AliasW(wstr ptr ptr ptr long) setupapi.CM_Get_Device_Interface_AliasW @ stub CM_Get_Device_Interface_Alias_ExA @ stub CM_Get_Device_Interface_Alias_ExW -@ stdcall CM_Get_Device_Interface_ListA(ptr ptr ptr long long) setupapi.CM_Get_Device_Interface_ListA -@ stdcall CM_Get_Device_Interface_ListW(ptr ptr ptr long long) setupapi.CM_Get_Device_Interface_ListW -@ stdcall CM_Get_Device_Interface_List_ExA(ptr ptr ptr long long ptr) setupapi.CM_Get_Device_Interface_List_ExA -@ stdcall CM_Get_Device_Interface_List_ExW(ptr ptr ptr long long ptr) setupapi.CM_Get_Device_Interface_List_ExW -@ stdcall CM_Get_Device_Interface_List_SizeA(ptr ptr str long) setupapi.CM_Get_Device_Interface_List_SizeA -@ stdcall CM_Get_Device_Interface_List_SizeW(ptr ptr wstr long) setupapi.CM_Get_Device_Interface_List_SizeW -@ stdcall CM_Get_Device_Interface_List_Size_ExA(ptr ptr str long ptr) setupapi.CM_Get_Device_Interface_List_Size_ExA -@ stdcall CM_Get_Device_Interface_List_Size_ExW(ptr ptr wstr long ptr) setupapi.CM_Get_Device_Interface_List_Size_ExW +@ stdcall CM_Get_Device_Interface_ListA(ptr ptr ptr long long) +@ stdcall CM_Get_Device_Interface_ListW(ptr ptr ptr long long) +@ stdcall CM_Get_Device_Interface_List_ExA(ptr ptr ptr long long ptr) +@ stdcall CM_Get_Device_Interface_List_ExW(ptr ptr ptr long long ptr) +@ stdcall CM_Get_Device_Interface_List_SizeA(ptr ptr str long) +@ stdcall CM_Get_Device_Interface_List_SizeW(ptr ptr wstr long) +@ stdcall CM_Get_Device_Interface_List_Size_ExA(ptr ptr str long ptr) +@ stdcall CM_Get_Device_Interface_List_Size_ExW(ptr ptr wstr long ptr) @ stdcall CM_Get_Device_Interface_PropertyW(wstr ptr ptr ptr ptr long) @ stub CM_Get_Device_Interface_Property_ExW @ stub CM_Get_Device_Interface_Property_KeysW diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index f98af2a9a99..90d9132b1bb 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -76,6 +76,7 @@ static const char *debugstr_ok( const char *cond ) } while (0) #define ok_u4( r, op, e ) ok_ex( r, op, e, UINT, "%u" ) #define ok_x4( r, op, e ) ok_ex( r, op, e, UINT, "%#x" ) +#define ok_ptr( r, op, e ) ok_ex( r, op, e, void *, "%p" ) static const WCHAR *guid_string( const GUID *guid, WCHAR *buffer, UINT length ) { @@ -470,7 +471,7 @@ static void check_device_path_casing(const WCHAR *original_path) free(path); } -static void test_CM_Get_Device_Interface_List(void) +static void test_CM_Get_Device_Interface_PropertyW(void) { BYTE iface_detail_buffer[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + 256 * sizeof(WCHAR)]; SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)}; @@ -2441,6 +2442,186 @@ static void test_CM_Get_Class_Property(void) ok_x4( ret, ==, CR_NO_SUCH_VALUE ); } +static void test_CM_Get_Device_Interface_List_Size(void) +{ + GUID guid = GUID_DEVINTERFACE_HID; + ULONG size_all, size_present, size; + CONFIGRET ret; + + ret = CM_Get_Device_Interface_List_SizeW( NULL, &guid, NULL, CM_GET_DEVICE_INTERFACE_LIST_PRESENT ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + ret = CM_Get_Device_Interface_List_SizeW( &size, NULL, NULL, CM_GET_DEVICE_INTERFACE_LIST_PRESENT ); + ok_x4( ret, ==, CR_FAILURE ); + for (UINT flag = 2; flag; flag <<= 1) + { + winetest_push_context( "%#x", flag ); + ret = CM_Get_Device_Interface_List_SizeW( &size, &guid, NULL, flag ); + ok_x4( ret, ==, CR_INVALID_FLAG ); + winetest_pop_context(); + } + size = 0xdeadbeef; + ret = CM_Get_Device_Interface_List_SizeW( &size, &guid, (WCHAR *)L"INVALID", CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES ); + todo_wine ok_x4( ret, ==, CR_INVALID_DEVNODE ); + todo_wine ok_u4( size, ==, 0 ); + ret = CM_Get_Device_Interface_List_SizeW( &size, &guid, (WCHAR *)L"\\\\?\\", CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES ); + todo_wine ok_x4( ret, ==, CR_INVALID_DEVNODE ); + + size_present = 0; + ret = CM_Get_Device_Interface_List_SizeW( &size_present, &guid, NULL, CM_GET_DEVICE_INTERFACE_LIST_PRESENT ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( size_present, >, 0 ); + size_all = 0; + ret = CM_Get_Device_Interface_List_SizeW( &size_all, &guid, NULL, CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( size_all, >, 0 ); + + size = 0; + ret = CM_Get_Device_Interface_List_SizeW( &size, &guid, (WCHAR *)L"", CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES ); + ok_x4( ret, ==, CR_SUCCESS ); + ok( size == size_all || broken(size == 1), "got size %lu\n", size ); + + + size = 0; + ret = CM_Get_Device_Interface_List_SizeA( &size, &guid, NULL, CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( size, ==, size_all ); + size = 0; + ret = CM_Get_Device_Interface_List_SizeA( &size, &guid, NULL, CM_GET_DEVICE_INTERFACE_LIST_PRESENT ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( size, ==, size_present ); +} + +static void test_CM_Get_Device_Interface_List(void) +{ + GUID guid = GUID_DEVINTERFACE_HID; + WCHAR *tmp, *tmp2, *buffer, *bufferW, instance[MAX_PATH]; + CONFIGRET ret; + char *bufferA; + ULONG size; + + size = 0; + ret = CM_Get_Device_Interface_List_SizeW( &size, &guid, NULL, CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( size, >, 0 ); + + buffer = malloc( size * sizeof(*buffer) ); + ok_ptr( buffer, !=, NULL ); + + + ret = CM_Get_Device_Interface_ListW( &guid, NULL, NULL, 0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + ret = CM_Get_Device_Interface_ListW( NULL, NULL, buffer, size, CM_GET_DEVICE_INTERFACE_LIST_PRESENT ); + ok_x4( ret, ==, CR_FAILURE ); + for (UINT flag = 2; flag; flag <<= 1) + { + winetest_push_context( "%#x", flag ); + ret = CM_Get_Device_Interface_ListW( &guid, NULL, buffer, size, flag ); + ok_x4( ret, ==, CR_INVALID_FLAG ); + winetest_pop_context(); + } + ret = CM_Get_Device_Interface_ListW( &guid, (WCHAR *)L"INVALID", buffer, size, CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES ); + todo_wine ok_x4( ret, ==, CR_INVALID_DEVNODE ); + ret = CM_Get_Device_Interface_ListW( &guid, (WCHAR *)L"\\\\?\\", buffer, size, CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES ); + todo_wine ok_x4( ret, ==, CR_INVALID_DEVNODE ); + + + ret = CM_Get_Device_Interface_ListW( &guid, NULL, buffer, size, CM_GET_DEVICE_INTERFACE_LIST_PRESENT ); + ok_x4( ret, ==, CR_SUCCESS ); + ok( !wcsncmp( buffer, L"\\\\?\\HID#", 8 ), "got %s\n", debugstr_wn( buffer, size ) ); + for (tmp = buffer; *tmp; tmp = tmp + wcslen( tmp ) + 1) + { + WCHAR sep, substr[MAX_PATH], upper[MAX_PATH]; + UINT pos; + + ok( !wcsncmp( tmp, L"\\\\?\\HID#", 8 ), "got %s\n", debugstr_wn( buffer, size ) ); + + /* \\\\?\\HID#XXXX# uppercase prefix */ + wcscpy( substr, tmp ); + pos = wcschr( substr + 8, '#' ) - substr; + sep = substr[pos]; + substr[pos] = 0; + wcscpy( upper, substr ); + wcsupr( upper ); + ok_wcs( upper, substr ); + substr[pos] = sep; + + /* lower case instance, refstr and guid suffix */ + wcscpy( substr, wcschr( substr + 25, '#' ) ); + wcscpy( upper, substr ); + wcslwr( upper ); + flaky_wine ok_wcs( upper, substr ); + } + ok( tmp > buffer, "got %s\n", debugstr_wn( buffer, size ) ); + + + bufferA = malloc( size * sizeof(*bufferA) ); + ok_ptr( bufferA, !=, NULL ); + ret = CM_Get_Device_Interface_ListA( &guid, NULL, bufferA, size, CM_GET_DEVICE_INTERFACE_LIST_PRESENT ); + ok_x4( ret, ==, CR_SUCCESS ); + + bufferW = malloc( size * sizeof(*bufferW) ); + ok_ptr( bufferW, !=, NULL ); + memset( bufferW, 0xcc, size * sizeof(*bufferW) ); + MultiByteToWideChar( CP_ACP, 0, bufferA, size, bufferW, size ); + for (tmp = buffer, tmp2 = bufferW; *tmp && *tmp2; tmp = tmp + wcslen( tmp ) + 1, tmp2 = tmp2 + wcslen( tmp2 ) + 1) + ok( !wcscmp( tmp, tmp2 ), "got %s, %s.\n", debugstr_wn( bufferW, size ), debugstr_wn( buffer, size ) ); + ok( !*tmp, "got %s, %s.\n", debugstr_wn( bufferW, size ), debugstr_wn( buffer, size ) ); + ok( !*tmp2, "got %s, %s.\n", debugstr_wn( bufferW, size ), debugstr_wn( buffer, size ) ); + + + free( bufferA ); + free( bufferW ); + + + wcscpy( instance, buffer + 4 ); + *wcsrchr( instance, '#' ) = 0; + while ((tmp = wcschr( instance, '#' ))) *tmp = '\\'; + ret = CM_Get_Device_Interface_ListW( &guid, instance, buffer, size, CM_GET_DEVICE_INTERFACE_LIST_PRESENT ); + ok_x4( ret, ==, CR_SUCCESS ); + ok( !wcsncmp( buffer, L"\\\\?\\", 4 ), "got %s\n", debugstr_wn( buffer, size ) ); + ok( !wcscmp( buffer + wcslen( buffer ) + 1, L"" ), "got %s\n", debugstr_wn( buffer, size ) ); + + + free( buffer ); + + + guid = GUID_DEVINTERFACE_DISPLAY_ADAPTER; + + size = 0; + ret = CM_Get_Device_Interface_List_SizeW( &size, &guid, NULL, CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( size, >, 0 ); + + buffer = malloc( size * sizeof(*buffer) ); + ok_ptr( buffer, !=, NULL ); + + ret = CM_Get_Device_Interface_ListW( &guid, NULL, buffer, size, CM_GET_DEVICE_INTERFACE_LIST_PRESENT ); + ok_x4( ret, ==, CR_SUCCESS ); + ok( !wcsncmp( buffer, L"\\\\?\\", 4 ), "got %s\n", debugstr_wn( buffer, size ) ); + for (tmp = buffer; *tmp; tmp = tmp + wcslen( tmp ) + 1) + { + WCHAR *sep, substr[MAX_PATH], upper[MAX_PATH]; + ok( !wcsncmp( tmp, L"\\\\?\\", 4 ), "got %s\n", debugstr_wn( buffer, size ) ); + + /* upper case enumerator prefix */ + wcscpy( substr, tmp ); + if ((sep = wcschr( substr, '#' ))) *sep = 0; + wcscpy( upper, substr ); + wcsupr( upper ); + ok_wcs( upper, substr ); + *sep = '#'; + + /* lower case instance, refstr and guid suffix */ + wcscpy( substr, wcschr( sep + 1, '#' ) ); + wcscpy( upper, substr ); + wcslwr( upper ); + ok_wcs( upper, substr ); + } + ok( tmp > buffer, "got %s\n", debugstr_wn( buffer, size ) ); + + free( buffer ); +} + static void test_CM_Open_Device_Interface_Key(void) { WCHAR iface[4096], name[MAX_PATH], expect[MAX_PATH], buffer[39], *refstr; @@ -2546,10 +2727,12 @@ START_TEST(cfgmgr32) test_CM_Get_Class_Registry_Property(); test_CM_Get_Class_Property(); test_CM_Get_Class_Property_Keys(); + test_CM_Get_Device_Interface_List_Size(); + test_CM_Get_Device_Interface_List(); test_CM_Open_Device_Interface_Key(); test_CM_Get_Device_ID_List(); test_CM_Register_Notification(); - test_CM_Get_Device_Interface_List(); + test_CM_Get_Device_Interface_PropertyW(); test_DevGetObjects(); test_DevCreateObjectQuery(); test_DevGetObjectProperties_invalid(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10148
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/cfgmgr32/cfgmgr32.c | 113 ++++++++++++++++++++++----------- dlls/cfgmgr32/cfgmgr32.spec | 2 +- dlls/cfgmgr32/tests/cfgmgr32.c | 96 +++++++++++++++++++++++++++- 3 files changed, 172 insertions(+), 39 deletions(-) diff --git a/dlls/cfgmgr32/cfgmgr32.c b/dlls/cfgmgr32/cfgmgr32.c index 811f4304017..db9b65e9acb 100644 --- a/dlls/cfgmgr32/cfgmgr32.c +++ b/dlls/cfgmgr32/cfgmgr32.c @@ -187,6 +187,20 @@ static LSTATUS query_named_property( HKEY hkey, const WCHAR *nameW, DEVPROPTYPE return err; } +static LSTATUS return_property( struct property *prop, DEVPROPTYPE type, const void *buffer, UINT size ) +{ + LSTATUS err = *prop->size >= size ? ERROR_SUCCESS : ERROR_MORE_DATA; + if (!err && prop->buffer) memcpy( prop->buffer, buffer, size ); + *prop->size = size; + *prop->type = type; + return err; +} + +static LSTATUS return_property_bool( struct property *prop, DEVPROP_BOOLEAN value ) +{ + return return_property( prop, DEVPROP_TYPE_BOOLEAN, &value, sizeof(value) ); +} + typedef LSTATUS (*enum_objects_cb)( HKEY hkey, const void *object, const WCHAR *path, UINT path_len, void *context ); static LSTATUS enum_objects_size( HKEY hkey, const void *object, const WCHAR *path, UINT path_len, void *context ) @@ -418,6 +432,50 @@ static LSTATUS enum_device_interface_list( GUID *class, DEVINSTID_W instance_id, return err; } +static const struct property_desc device_interface_properties[] = +{ + { &DEVPKEY_Device_ContainerId, DEVPROP_TYPE_GUID }, + { &DEVPKEY_DeviceInterface_FriendlyName, DEVPROP_TYPE_STRING, L"FriendlyName" }, +}; + +static LSTATUS query_device_interface_property( HKEY hkey, const struct device_interface *iface, struct property *prop ) +{ + WCHAR prefix[MAX_PATH]; + + if (!memcmp( &DEVPKEY_DeviceInterface_Enabled, &prop->key, sizeof(prop->key) )) + return return_property_bool( prop, device_interface_enabled( hkey, iface ) ); + if (!memcmp( &DEVPKEY_Device_InstanceId, &prop->key, sizeof(prop->key) )) + return query_named_property( hkey, L"DeviceInstance", DEVPROP_TYPE_STRING, prop ); + if (!memcmp( &DEVPKEY_DeviceInterface_ClassGuid, &prop->key, sizeof(prop->key) )) + return return_property( prop, DEVPROP_TYPE_GUID, &iface->class_guid, sizeof(iface->class_guid) ); + + swprintf( prefix, ARRAY_SIZE(prefix), L"%s\\Properties\\", iface->refstr ); + for (UINT i = 0; i < ARRAY_SIZE(device_interface_properties); i++) + { + const struct property_desc *desc = device_interface_properties + i; + if (memcmp( desc->key, &prop->key, sizeof(prop->key) )) continue; + if (!desc->name) return query_property( hkey, prefix, desc->type, prop ); + return query_named_property( hkey, desc->name, desc->type, prop ); + } + + return query_property( hkey, prefix, DEVPROP_TYPE_EMPTY, prop ); +} + +static LSTATUS get_device_interface_property( const struct device_interface *iface, struct property *prop ) +{ + LSTATUS err; + HKEY hkey; + + if (!(err = open_device_interface_key( iface, KEY_QUERY_VALUE, TRUE, &hkey ))) + { + err = query_device_interface_property( hkey, iface, prop ); + RegCloseKey( hkey ); + } + + if (err && err != ERROR_MORE_DATA) *prop->size = 0; + return err; +} + static CONFIGRET map_error( LSTATUS err ) { switch (err) @@ -909,47 +967,30 @@ CONFIGRET WINAPI CM_Open_Device_Interface_KeyA( const char *iface, REGSAM access } /*********************************************************************** - * CM_Get_Device_Interface_PropertyW (cfgmgr32.@) + * CM_Get_Device_Interface_Property_ExW (cfgmgr32.@) */ -CONFIGRET WINAPI CM_Get_Device_Interface_PropertyW( LPCWSTR device_interface, const DEVPROPKEY *property_key, - DEVPROPTYPE *property_type, BYTE *property_buffer, - ULONG *property_buffer_size, ULONG flags ) +CONFIGRET WINAPI CM_Get_Device_Interface_Property_ExW( const WCHAR *name, const DEVPROPKEY *key, DEVPROPTYPE *type, + BYTE *buffer, ULONG *size, ULONG flags, HMACHINE machine ) { - SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)}; - HDEVINFO set; - DWORD err; - BOOL ret; + struct device_interface iface; + struct property prop; + LSTATUS err; - TRACE( "%s %p %p %p %p %ld.\n", debugstr_w(device_interface), property_key, property_type, property_buffer, - property_buffer_size, flags); + TRACE( "name %s, key %p, type %p, buffer %p, size %p, flags %#lx\n", debugstr_w(name), key, type, buffer, size, flags); + if (machine) FIXME( "machine %p not implemented!\n", machine ); - if (!property_key) return CR_FAILURE; - if (!device_interface || !property_type || !property_buffer_size) return CR_INVALID_POINTER; - if (*property_buffer_size && !property_buffer) return CR_INVALID_POINTER; + if (!name) return CR_INVALID_POINTER; + if (init_device_interface( &iface, name )) return CR_NO_SUCH_DEVICE_INTERFACE; + if ((err = init_property( &prop, key, type, buffer, size ))) return map_error( err ); if (flags) return CR_INVALID_FLAG; - set = SetupDiCreateDeviceInfoListExW( NULL, NULL, NULL, NULL ); - if (set == INVALID_HANDLE_VALUE) return CR_OUT_OF_MEMORY; - if (!SetupDiOpenDeviceInterfaceW( set, device_interface, 0, &iface )) - { - SetupDiDestroyDeviceInfoList( set ); - TRACE( "No interface %s, err %lu.\n", debugstr_w( device_interface ), GetLastError()); - return CR_NO_SUCH_DEVICE_INTERFACE; - } + return map_error( get_device_interface_property( &iface, &prop ) ); +} - ret = SetupDiGetDeviceInterfacePropertyW( set, &iface, property_key, property_type, property_buffer, - *property_buffer_size, property_buffer_size, 0 ); - err = ret ? 0 : GetLastError(); - SetupDiDestroyDeviceInfoList( set ); - switch (err) - { - case ERROR_SUCCESS: - return CR_SUCCESS; - case ERROR_INSUFFICIENT_BUFFER: - return CR_BUFFER_SMALL; - case ERROR_NOT_FOUND: - return CR_NO_SUCH_VALUE; - default: - return CR_FAILURE; - } +/*********************************************************************** + * CM_Get_Device_Interface_PropertyW (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Device_Interface_PropertyW( const WCHAR *iface, const DEVPROPKEY *key, DEVPROPTYPE *type, BYTE *buffer, ULONG *size, ULONG flags ) +{ + return CM_Get_Device_Interface_Property_ExW( iface, key, type, buffer, size, flags, NULL ); } diff --git a/dlls/cfgmgr32/cfgmgr32.spec b/dlls/cfgmgr32/cfgmgr32.spec index 580f45e0ba4..e2d4c029a01 100644 --- a/dlls/cfgmgr32/cfgmgr32.spec +++ b/dlls/cfgmgr32/cfgmgr32.spec @@ -121,7 +121,7 @@ @ stdcall CM_Get_Device_Interface_List_Size_ExA(ptr ptr str long ptr) @ stdcall CM_Get_Device_Interface_List_Size_ExW(ptr ptr wstr long ptr) @ stdcall CM_Get_Device_Interface_PropertyW(wstr ptr ptr ptr ptr long) -@ stub CM_Get_Device_Interface_Property_ExW +@ stdcall CM_Get_Device_Interface_Property_ExW(wstr ptr ptr ptr ptr long ptr) @ stub CM_Get_Device_Interface_Property_KeysW @ stub CM_Get_Device_Interface_Property_Keys_ExW @ stub CM_Get_First_Log_Conf diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index 90d9132b1bb..1ea1c715331 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -471,7 +471,7 @@ static void check_device_path_casing(const WCHAR *original_path) free(path); } -static void test_CM_Get_Device_Interface_PropertyW(void) +static void test_CM_Get_Device_Interface_Property_setupapi(void) { BYTE iface_detail_buffer[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + 256 * sizeof(WCHAR)]; SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)}; @@ -607,6 +607,97 @@ static void test_CM_Get_Device_Interface_PropertyW(void) ok(ret == CR_NO_SUCH_DEVICE_INTERFACE || broken(ret == CR_INVALID_DATA) /* w7 */, "got %#lx.\n", ret); } +static void test_CM_Get_Device_Interface_PropertyW(void) +{ + WCHAR expect[MAX_PATH]; + DEVINSTID_W iface; + BYTE buffer[4096]; + ULONG type, len; + CONFIGRET ret; + GUID guid; + + guid = GUID_DEVINTERFACE_HID; + ret = CM_Get_Device_Interface_List_SizeW( &len, &guid, NULL, CM_GET_DEVICE_INTERFACE_LIST_PRESENT ); + ok_x4( ret, ==, CR_SUCCESS ); + iface = malloc( len * sizeof(*iface) ); + ok_ptr( iface, !=, NULL ); + ret = CM_Get_Device_Interface_ListW( &guid, NULL, iface, len, CM_GET_DEVICE_INTERFACE_LIST_PRESENT ); + ok_x4( ret, ==, CR_SUCCESS ); + + len = sizeof(buffer); + ret = CM_Get_Device_Interface_PropertyW( NULL, &DEVPKEY_Device_InstanceId, &type, buffer, &len, 0 ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + ok_u4( len, ==, sizeof(buffer) ); + len = sizeof(buffer); + ret = CM_Get_Device_Interface_PropertyW( L"qqq", &DEVPKEY_Device_InstanceId, &type, buffer, &len, 0 ); + ok_x4( ret, ==, CR_NO_SUCH_DEVICE_INTERFACE ); + todo_wine ok_u4( len, ==, 0 ); + len = sizeof(buffer); + ret = CM_Get_Device_Interface_PropertyW( iface, &DEVPKEY_Device_InstanceId, &type, NULL, NULL, 0 ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + len = sizeof(buffer); + ret = CM_Get_Device_Interface_PropertyW( iface, &DEVPKEY_Device_InstanceId, &type, buffer, NULL, 0 ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + len = sizeof(buffer); + ret = CM_Get_Device_Interface_PropertyW( iface, NULL, &type, buffer, &len, 0 ); + ok_x4( ret, ==, CR_FAILURE ); + len = sizeof(buffer); + ret = CM_Get_Device_Interface_PropertyW( iface, &DEVPKEY_Device_InstanceId, NULL, buffer, &len, 0 ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + len = sizeof(buffer); + ret = CM_Get_Device_Interface_PropertyW( iface, &DEVPKEY_Device_InstanceId, &type, NULL, &len, 0 ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + ok_u4( len, ==, sizeof(buffer) ); + len = 0; + ret = CM_Get_Device_Interface_PropertyW( iface, &DEVPKEY_Device_InstanceId, &type, NULL, &len, 0 ); + ok_x4( ret, ==, CR_BUFFER_SMALL ); + ok( len > sizeof(WCHAR), "got %#lx\n", len ); + + type = 0xdeadbeef; + len = sizeof(buffer); + memset( buffer, 0xcd, sizeof(buffer) ); + ret = CM_Get_Device_Interface_PropertyW( iface, &DEVPKEY_DeviceInterface_Enabled, &type, buffer, &len, 0 ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_x4( type, ==, DEVPROP_TYPE_BOOLEAN ); + ok_x4( len, ==, 1 ); + ok_x4( *buffer, ==, 0xff ); + + type = 0xdeadbeef; + len = sizeof(buffer); + memset( buffer, 0xcd, sizeof(buffer) ); + ret = CM_Get_Device_Interface_PropertyW( iface, &DEVPKEY_Device_InstanceId, &type, buffer, &len, 0 ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_x4( type, ==, DEVPROP_TYPE_STRING ); + ok_x4( len, ==, (wcslen( (WCHAR *)buffer ) + 1) * sizeof(WCHAR) ); + ok( !wcsncmp( (WCHAR *)buffer, L"HID\\", 4 ), "got %s\n", debugstr_w( (WCHAR *)buffer ) ); + wcscpy( expect, (WCHAR *)buffer ); + wcsupr( expect ); /* uppercase HID\\XXXX\\ */ + len = wcschr( expect + 4, '\\' ) - expect; + wcscpy( expect + len, (WCHAR *)buffer + len ); + wcslwr( wcsrchr( expect, '\\' ) ); /* lowercase instance + refstring */ + ok_wcs( expect, (WCHAR *)buffer ); + + type = 0xdeadbeef; + len = sizeof(buffer); + memset( buffer, 0xcd, sizeof(buffer) ); + ret = CM_Get_Device_Interface_PropertyW( iface, &DEVPKEY_DeviceInterface_ClassGuid, &type, buffer, &len, 0 ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_x4( type, ==, DEVPROP_TYPE_GUID ); + ok_x4( len, ==, 16 ); + ok( IsEqualGUID( (GUID *)buffer, &GUID_DEVINTERFACE_HID ), "got %s\n", debugstr_guid( (GUID *)buffer ) ); + + type = 0xdeadbeef; + len = sizeof(buffer); + memset( buffer, 0xcd, sizeof(buffer) ); + ret = CM_Get_Device_Interface_PropertyW( iface, &DEVPKEY_Device_ContainerId, &type, buffer, &len, 0 ); + todo_wine ok_x4( ret, ==, CR_SUCCESS ); + todo_wine ok_x4( type, ==, DEVPROP_TYPE_GUID ); + todo_wine ok_x4( len, ==, 16 ); + + + free( iface ); +} + struct test_property { DEVPROPKEY key; @@ -2730,9 +2821,10 @@ START_TEST(cfgmgr32) test_CM_Get_Device_Interface_List_Size(); test_CM_Get_Device_Interface_List(); test_CM_Open_Device_Interface_Key(); + test_CM_Get_Device_Interface_PropertyW(); test_CM_Get_Device_ID_List(); test_CM_Register_Notification(); - test_CM_Get_Device_Interface_PropertyW(); + test_CM_Get_Device_Interface_Property_setupapi(); test_DevGetObjects(); test_DevCreateObjectQuery(); test_DevGetObjectProperties_invalid(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10148
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/cfgmgr32/cfgmgr32.c | 86 ++++++++++++++++++++++++++++++++++ dlls/cfgmgr32/cfgmgr32.spec | 4 +- dlls/cfgmgr32/tests/cfgmgr32.c | 80 ++++++++++++++++++++++++++++++- 3 files changed, 167 insertions(+), 3 deletions(-) diff --git a/dlls/cfgmgr32/cfgmgr32.c b/dlls/cfgmgr32/cfgmgr32.c index db9b65e9acb..af03d245559 100644 --- a/dlls/cfgmgr32/cfgmgr32.c +++ b/dlls/cfgmgr32/cfgmgr32.c @@ -476,6 +476,58 @@ static LSTATUS get_device_interface_property( const struct device_interface *ifa return err; } +static LSTATUS enum_device_interface_property_keys( HKEY hkey, const struct device_interface *iface, DEVPROPKEY *buffer, ULONG *size ) +{ + ULONG capacity = *size, count = 0; + LSTATUS err = ERROR_SUCCESS; + WCHAR path[MAX_PATH]; + HKEY props_key; + + if (capacity < ++count || !buffer) err = ERROR_MORE_DATA; + else buffer[count - 1] = DEVPKEY_DeviceInterface_Enabled; + if (capacity < ++count || !buffer) err = ERROR_MORE_DATA; + else buffer[count - 1] = DEVPKEY_Device_InstanceId; + if (capacity < ++count || !buffer) err = ERROR_MORE_DATA; + else buffer[count - 1] = DEVPKEY_DeviceInterface_ClassGuid; + + for (UINT i = 0; i < ARRAY_SIZE(device_interface_properties); i++) + { + const struct property_desc *desc = device_interface_properties + i; + if (desc->name && !RegQueryValueExW( hkey, desc->name, NULL, NULL, NULL, NULL )) + { + if (capacity < ++count || !buffer) err = ERROR_MORE_DATA; + else buffer[count - 1] = *desc->key; + } + } + + swprintf( path, ARRAY_SIZE(path), L"%s\\Properties", iface->refstr ); + if (!open_key( hkey, path, KEY_ENUMERATE_SUB_KEYS, TRUE, &props_key )) + { + WCHAR name[MAX_PATH]; + for (ULONG i = 0, len = ARRAY_SIZE(name); !RegEnumValueW( props_key, i, name, &len, 0, NULL, NULL, NULL ); i++, len = ARRAY_SIZE(name)) + { + if (capacity < ++count || !buffer) err = ERROR_MORE_DATA; + else err = propkey_from_string( name, buffer + count - 1 ); + } + RegCloseKey( props_key ); + } + + *size = count; + return err; +} + +static LSTATUS get_device_interface_property_keys( const struct device_interface *iface, DEVPROPKEY *buffer, ULONG *size ) +{ + LSTATUS err; + HKEY hkey; + + if ((err = open_device_interface_key( iface, KEY_ALL_ACCESS, TRUE, &hkey ))) return err; + err = enum_device_interface_property_keys( hkey, iface, buffer, size ); + RegCloseKey( hkey ); + + return err; +} + static CONFIGRET map_error( LSTATUS err ) { switch (err) @@ -994,3 +1046,37 @@ CONFIGRET WINAPI CM_Get_Device_Interface_PropertyW( const WCHAR *iface, const DE { return CM_Get_Device_Interface_Property_ExW( iface, key, type, buffer, size, flags, NULL ); } + +/*********************************************************************** + * CM_Get_Device_Interface_Property_Keys_ExW (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Device_Interface_Property_Keys_ExW( const WCHAR *name, DEVPROPKEY *buffer, ULONG *size, ULONG flags, HMACHINE machine ) +{ + struct device_interface iface; + LSTATUS err; + + TRACE( "name %s, buffer %p, size %p, flags %#lx\n", debugstr_w(name), buffer, size, flags); + if (machine) FIXME( "machine %p not implemented!\n", machine ); + if (flags) FIXME( "flags %#lx not implemented!\n", flags ); + + if (!name || !size) return CR_INVALID_POINTER; + if (*size && !buffer) return CR_INVALID_POINTER; + if (init_device_interface( &iface, name )) + { + *size = 0; + return CR_INVALID_DATA; + } + + err = get_device_interface_property_keys( &iface, buffer, size ); + if (err && err != ERROR_MORE_DATA) *size = 0; + if (err == ERROR_FILE_NOT_FOUND) return CR_NO_SUCH_DEVICE_INTERFACE; + return map_error( err ); +} + +/*********************************************************************** + * CM_Get_Device_Interface_Property_KeysW (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Device_Interface_Property_KeysW( const WCHAR *iface, DEVPROPKEY *keys, ULONG *count, ULONG flags ) +{ + return CM_Get_Device_Interface_Property_Keys_ExW( iface, keys, count, flags, NULL ); +} diff --git a/dlls/cfgmgr32/cfgmgr32.spec b/dlls/cfgmgr32/cfgmgr32.spec index e2d4c029a01..2aee10c2672 100644 --- a/dlls/cfgmgr32/cfgmgr32.spec +++ b/dlls/cfgmgr32/cfgmgr32.spec @@ -122,8 +122,8 @@ @ stdcall CM_Get_Device_Interface_List_Size_ExW(ptr ptr wstr long ptr) @ stdcall CM_Get_Device_Interface_PropertyW(wstr ptr ptr ptr ptr long) @ stdcall CM_Get_Device_Interface_Property_ExW(wstr ptr ptr ptr ptr long ptr) -@ stub CM_Get_Device_Interface_Property_KeysW -@ stub CM_Get_Device_Interface_Property_Keys_ExW +@ stdcall CM_Get_Device_Interface_Property_KeysW(wstr ptr ptr long) +@ stdcall CM_Get_Device_Interface_Property_Keys_ExW(wstr ptr ptr long ptr) @ stub CM_Get_First_Log_Conf @ stub CM_Get_First_Log_Conf_Ex @ stub CM_Get_Global_State diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index 1ea1c715331..68a5a7a9dc3 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -607,6 +607,83 @@ static void test_CM_Get_Device_Interface_Property_setupapi(void) ok(ret == CR_NO_SUCH_DEVICE_INTERFACE || broken(ret == CR_INVALID_DATA) /* w7 */, "got %#lx.\n", ret); } +static void test_CM_Get_Device_Interface_Property_Keys(void) +{ + DEVPROPKEY buffer[128]; + DEVINSTID_W iface; + CONFIGRET ret; + WCHAR *tmp; + ULONG size; + GUID guid; + + guid = GUID_DEVINTERFACE_HID; + ret = CM_Get_Device_Interface_List_SizeW( &size, &guid, NULL, CM_GET_DEVICE_INTERFACE_LIST_PRESENT ); + ok_x4( ret, ==, CR_SUCCESS ); + iface = malloc( size * sizeof(*iface) ); + ok_ptr( iface, !=, NULL ); + ret = CM_Get_Device_Interface_ListW( &guid, NULL, iface, size, CM_GET_DEVICE_INTERFACE_LIST_PRESENT ); + ok_x4( ret, ==, CR_SUCCESS ); + + size = sizeof(buffer); + ret = CM_Get_Device_Interface_Property_KeysW( NULL, buffer, &size, 0 ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + ok_u4( size, ==, sizeof(buffer) ); + size = sizeof(buffer); + ret = CM_Get_Device_Interface_Property_KeysW( L"qqq", buffer, &size, 0 ); + ok_x4( ret, ==, CR_INVALID_DATA ); + ok_u4( size, ==, 0 ); + size = sizeof(buffer); + ret = CM_Get_Device_Interface_Property_KeysW( iface, NULL, NULL, 0 ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + size = sizeof(buffer); + ret = CM_Get_Device_Interface_Property_KeysW( iface, buffer, NULL, 0 ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + size = sizeof(buffer); + ret = CM_Get_Device_Interface_Property_KeysW( iface, NULL, &size, 0 ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + ok_u4( size, ==, sizeof(buffer) ); + size = 0; + ret = CM_Get_Device_Interface_Property_KeysW( iface, NULL, &size, 0 ); + ok_x4( ret, ==, CR_BUFFER_SMALL ); + todo_wine ok( size == 10 || broken(size == 11), "got %#lx\n", size ); + size = sizeof(buffer); + memset( buffer, 0xcd, sizeof(buffer) ); + ret = CM_Get_Device_Interface_Property_KeysW( iface, buffer, &size, 0 ); + ok_x4( ret, ==, CR_SUCCESS ); + todo_wine ok( size == 10 || broken(size == 11), "got %#lx\n", size ); + ok( !memcmp( buffer + 0, &DEVPKEY_DeviceInterface_Enabled, sizeof(*buffer) ), "got {%s,%#lx}\n", debugstr_guid( &buffer[0].fmtid ), buffer[0].pid ); + ok( !memcmp( buffer + 1, &DEVPKEY_Device_InstanceId, sizeof(*buffer) ), "got {%s,%#lx}\n", debugstr_guid( &buffer[1].fmtid ), buffer[1].pid ); + ok( !memcmp( buffer + 2, &DEVPKEY_DeviceInterface_ClassGuid, sizeof(*buffer) ), "got {%s,%#lx}\n", debugstr_guid( &buffer[2].fmtid ), buffer[2].pid ); + todo_wine ok( !memcmp( buffer + 3, &DEVPKEY_Device_ContainerId, sizeof(*buffer) ), "got {%s,%#lx}\n", debugstr_guid( &buffer[3].fmtid ), buffer[3].pid ); + + tmp = wcsrchr( iface, '{' ); + tmp[1] = '6'; + size = sizeof(buffer); + ret = CM_Get_Device_Interface_Property_KeysW( iface, buffer, &size, 0 ); + ok_x4( ret, ==, CR_NO_SUCH_DEVICE_INTERFACE ); + tmp[1] = '5'; + + tmp[0] = '.'; + size = sizeof(buffer); + ret = CM_Get_Device_Interface_Property_KeysW( iface, buffer, &size, 0 ); + ok_x4( ret, ==, CR_INVALID_DATA ); + tmp[0] = '{'; + + tmp[-1] = 0; + size = sizeof(buffer); + ret = CM_Get_Device_Interface_Property_KeysW( iface, buffer, &size, 0 ); + ok_x4( ret, ==, CR_INVALID_DATA ); + tmp[-1] = '#'; + + tmp[-2]++; + size = sizeof(buffer); + ret = CM_Get_Device_Interface_Property_KeysW( iface, buffer, &size, 0 ); + ok_x4( ret, ==, CR_NO_SUCH_DEVICE_INTERFACE ); + tmp[-2]--; + + free( iface ); +} + static void test_CM_Get_Device_Interface_PropertyW(void) { WCHAR expect[MAX_PATH]; @@ -2821,10 +2898,11 @@ START_TEST(cfgmgr32) test_CM_Get_Device_Interface_List_Size(); test_CM_Get_Device_Interface_List(); test_CM_Open_Device_Interface_Key(); + test_CM_Get_Device_Interface_Property_Keys(); test_CM_Get_Device_Interface_PropertyW(); + test_CM_Get_Device_Interface_Property_setupapi(); test_CM_Get_Device_ID_List(); test_CM_Register_Notification(); - test_CM_Get_Device_Interface_Property_setupapi(); test_DevGetObjects(); test_DevCreateObjectQuery(); test_DevGetObjectProperties_invalid(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10148
v2: Don't expect HID device ids to start with VID_, the first HID device get on the Windows CI apparently starts with REV_XXXX without any VID/PID. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10148#note_130101
participants (2)
-
Rémi Bernon -
Rémi Bernon (@rbernon)