From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/cfgmgr32/cfgmgr32.c | 255 +++++++++++++++++++++- dlls/cfgmgr32/cfgmgr32.spec | 16 +- dlls/cfgmgr32/cfgmgr32_private.h | 2 +- dlls/cfgmgr32/devobject.c | 2 +- dlls/cfgmgr32/tests/cfgmgr32.c | 350 ++++++++++++++++++++++++++++++- 5 files changed, 609 insertions(+), 16 deletions(-) diff --git a/dlls/cfgmgr32/cfgmgr32.c b/dlls/cfgmgr32/cfgmgr32.c index 24a9ccb5643..8bd8a3bbe3b 100644 --- a/dlls/cfgmgr32/cfgmgr32.c +++ b/dlls/cfgmgr32/cfgmgr32.c @@ -132,12 +132,12 @@ static LSTATUS open_device_classes_key( HKEY root, const WCHAR *key, REGSAM acce return open_key( root, path, access, open, hkey ); } -LSTATUS init_property( struct property *prop, const DEVPROPKEY *key, DEVPROPTYPE *type, void *buffer, DWORD *size ) +LSTATUS init_property( struct property *prop, const DEVPROPKEY *key, DEVPROPTYPE *type, void *buffer, DWORD *size, BOOL binary ) { if (!key) return ERROR_INVALID_PARAMETER; if (!(prop->type = type) || !(prop->size = size)) return ERROR_INVALID_USER_BUFFER; if (!(prop->buffer = buffer) && (*prop->size)) return ERROR_INVALID_USER_BUFFER; - prop->flags = PROP_FLAG_BINARY; + prop->flags = binary ? PROP_FLAG_BINARY : 0; prop->key = *key; prop->reg_type = NULL; return ERROR_SUCCESS; @@ -786,6 +786,109 @@ static LSTATUS get_device_property_keys( HKEY root, const struct device *dev, DE return err; } +static LSTATUS get_device_strings( const WCHAR *instance_id, const DEVPROPKEY *key, ULONG *size, WCHAR *buffer ) +{ + const WCHAR *instance = instance_id && *instance_id ? instance_id : L"HTREE\\ROOT\\0"; + struct property prop; + struct device dev; + DEVPROPTYPE type; + LSTATUS err; + + if ((err = init_device( &dev, instance ))) return err; + if ((err = init_property( &prop, key, &type, buffer, size, TRUE ))) return err; + + if (!(err = get_device_property( HKEY_LOCAL_MACHINE, &dev, &prop ))) *size *= 3; /* maximum ANSI conversion size */ + return err; +} + +static LSTATUS matches_device_property( HKEY hkey, struct device *dev, const DEVPROPKEY *key, const WCHAR *value ) +{ + WCHAR buffer[MAX_PATH]; + ULONG size = sizeof(buffer); + struct property prop; + DEVPROPTYPE type; + LSTATUS err; + + if (!key) return ERROR_SUCCESS; + if ((err = init_property( &prop, key, &type, (BYTE *)buffer, &size, FALSE ))) return err; + if ((err = query_device_property( hkey, dev, &prop ))) return err == ERROR_FILE_NOT_FOUND ? ERROR_NO_MATCH : err; + return wcsicmp( buffer, value ) ? ERROR_NO_MATCH : ERROR_SUCCESS; +} + +static LSTATUS enum_device_instances( HKEY root, struct device *dev, const DEVPROPKEY *key, const WCHAR *value, + BOOL all, enum_objects_cb callback, void *context ) +{ + LSTATUS err = ERROR_SUCCESS; + HKEY hkey; + UINT len; + + for (UINT i = 0; !err && !(err = RegEnumKeyW( root, i, dev->instance, ARRAY_SIZE(dev->instance) )); i++) + { + if ((err = open_key( root, dev->instance, KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, TRUE, &hkey ))) break; + if ((err = matches_device_property( hkey, dev, key, value )) == ERROR_NO_MATCH) err = ERROR_SUCCESS; + else if (!err) + { + WCHAR path[MAX_PATH]; + len = swprintf( path, ARRAY_SIZE(path), L"%s\\%s\\%s", dev->enumerator, dev->device, dev->instance ); + err = callback( hkey, dev, path, len + 1, context ); + } + RegCloseKey( hkey ); + } + if (err == ERROR_NO_MORE_ITEMS) err = ERROR_SUCCESS; + + return err; +} + +static LSTATUS enum_enumerator_devices( HKEY root, struct device *dev, const WCHAR *device, const DEVPROPKEY *key, + const WCHAR *prop, BOOL all, enum_objects_cb callback, void *context ) +{ + LSTATUS err = ERROR_SUCCESS; + HKEY hkey; + + for (UINT i = 0; !err && !(err = RegEnumKeyW( root, i, dev->device, ARRAY_SIZE(dev->device) )); i++) + { + if (device && wcsicmp( dev->device, device )) continue; + if ((err = open_key( root, dev->device, KEY_ENUMERATE_SUB_KEYS, TRUE, &hkey ))) break; + err = enum_device_instances( hkey, dev, key, prop, all, callback, context ); + RegCloseKey( hkey ); + } + if (err == ERROR_NO_MORE_ITEMS) err = ERROR_SUCCESS; + + return err; +} + +static LSTATUS enum_devices( const WCHAR *filter, const DEVPROPKEY *key, const WCHAR *value, BOOL all, enum_objects_cb callback, void *context ) +{ + WCHAR enumerator[MAX_PATH], *device = NULL; + LSTATUS err = ERROR_SUCCESS; + struct device dev; + HKEY root, hkey; + + if (key && !value) return ERROR_INVALID_USER_BUFFER; + if (filter) + { + if (!*filter) return ERROR_NO_MORE_ITEMS; + lstrcpynW( enumerator, filter, ARRAY_SIZE(enumerator) ); + if ((device = wcschr( enumerator, '\\' ))) *device++ = 0; + if (device && wcschr( device, '\\' )) return ERROR_NO_MORE_ITEMS; + } + + root = cache_root_key( HKEY_LOCAL_MACHINE, enum_rootW, NULL ); + if (root == (HKEY)-1) return ERROR_FILE_NOT_FOUND; + + for (UINT i = 0; !err && !(err = RegEnumKeyW( root, i, dev.enumerator, ARRAY_SIZE(dev.enumerator) )); i++) + { + if (filter && wcsicmp( dev.enumerator, enumerator )) continue; + if ((err = open_key( root, dev.enumerator, KEY_ENUMERATE_SUB_KEYS, TRUE, &hkey ))) break; + err = enum_enumerator_devices( hkey, &dev, device, key, value, all, callback, context ); + RegCloseKey( hkey ); + } + if (err == ERROR_NO_MORE_ITEMS) err = ERROR_SUCCESS; + + if (!err) callback( NULL, NULL, L"", 1, context ); + return err; +} + static CRITICAL_SECTION devnode_cs; static CRITICAL_SECTION_DEBUG devnode_cs_debug = { 0, 0, &devnode_cs, @@ -1147,7 +1250,7 @@ CONFIGRET WINAPI CM_Get_Class_Property_ExW( const GUID *class, const DEVPROPKEY if (flags) FIXME( "flags %#lx not implemented!\n", flags ); if (!class) return CR_INVALID_POINTER; - if ((err = init_property( &prop, key, type, buffer, size ))) return map_error( err ); + if ((err = init_property( &prop, key, type, buffer, size, TRUE ))) return map_error( err ); return map_error( get_class_property( class, &prop ) ); } @@ -1360,7 +1463,7 @@ CONFIGRET WINAPI CM_Get_Device_Interface_Property_ExW( const WCHAR *name, const 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 ((err = init_property( &prop, key, type, buffer, size, TRUE ))) return map_error( err ); if (flags) return CR_INVALID_FLAG; return map_error( get_device_interface_property( &iface, &prop ) ); @@ -1408,6 +1511,148 @@ CONFIGRET WINAPI CM_Get_Device_Interface_Property_KeysW( const WCHAR *iface, DEV return CM_Get_Device_Interface_Property_Keys_ExW( iface, keys, count, flags, NULL ); } +/*********************************************************************** + * CM_Get_Device_ID_List_Size_ExW (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Device_ID_List_Size_ExW( ULONG *len, const WCHAR *filter, ULONG flags, HMACHINE machine ) +{ + BOOL all = !(flags & CM_GETIDLIST_FILTER_PRESENT); + LSTATUS err; + GUID guid; + + TRACE( "len %p, filter %s, flags %#lx, machine %p\n", len, debugstr_w(filter), flags, machine ); + if (machine) FIXME( "machine %p not implemented!\n", machine ); + + if (!len) return CR_INVALID_POINTER; + if (flags & ~CM_GETIDLIST_FILTER_BITS) return CR_INVALID_FLAG; + if (flags & CM_GETIDLIST_DONOTGENERATE) FIXME( "CM_GETIDLIST_DONOTGENERATE not implemented!\n" ); + + *len = 0; + if (flags & CM_GETIDLIST_FILTER_CLASS && filter && guid_from_string( filter, &guid )) return CR_INVALID_DATA; + if ((flags & CM_GETIDLIST_FILTER_ENUMERATOR) && !filter) return CR_INVALID_POINTER; + if (!(flags &= ~(CM_GETIDLIST_DONOTGENERATE | CM_GETIDLIST_FILTER_PRESENT))) filter = NULL; + + switch (flags) + { + case 0: err = enum_devices( NULL, NULL, NULL, all, enum_objects_size, len ); break; + case CM_GETIDLIST_FILTER_ENUMERATOR: err = enum_devices( filter, NULL, NULL, all, enum_objects_size, len ); break; + case CM_GETIDLIST_FILTER_CLASS: err = enum_devices( NULL, &DEVPKEY_Device_ClassGuid, filter, all, enum_objects_size, len ); break; + case CM_GETIDLIST_FILTER_SERVICE: err = enum_devices( NULL, &DEVPKEY_Device_Service, filter, all, enum_objects_size, len ); break; + case CM_GETIDLIST_FILTER_BUSRELATIONS: err = get_device_strings( filter, &DEVPKEY_Device_BusRelations, len, NULL ); break; + case CM_GETIDLIST_FILTER_TRANSPORTRELATIONS: err = get_device_strings( filter, &DEVPKEY_Device_TransportRelations, len, NULL ); break; + case CM_GETIDLIST_FILTER_EJECTRELATIONS: err = get_device_strings( filter, &DEVPKEY_Device_EjectionRelations, len, NULL ); break; + case CM_GETIDLIST_FILTER_POWERRELATIONS: err = get_device_strings( filter, &DEVPKEY_Device_PowerRelations, len, NULL ); break; + case CM_GETIDLIST_FILTER_REMOVALRELATIONS: err = get_device_strings( filter, &DEVPKEY_Device_RemovalRelations, len, NULL ); break; + default: FIXME( "Unsupported flags %#lx\n", flags ); return CR_INVALID_FLAG; + } + + return map_error( err ); +} + +/*********************************************************************** + * CM_Get_Device_ID_List_Size_ExA (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Device_ID_List_Size_ExA( ULONG *len, const char *filterA, ULONG flags, HMACHINE machine ) +{ + WCHAR filterW[MAX_PATH]; + + if (filterA) MultiByteToWideChar( CP_ACP, 0, filterA, -1, filterW, ARRAY_SIZE(filterW) ); + return CM_Get_Device_ID_List_Size_ExW( len, filterA ? filterW : NULL, flags, machine ); +} + +/*********************************************************************** + * CM_Get_Device_ID_List_SizeW (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Device_ID_List_SizeW( ULONG *len, const WCHAR *filter, ULONG flags ) +{ + return CM_Get_Device_ID_List_Size_ExW( len, filter, flags, NULL ); +} + +/*********************************************************************** + * CM_Get_Device_ID_List_SizeA (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Device_ID_List_SizeA( ULONG *len, const char *filter, ULONG flags ) +{ + return CM_Get_Device_ID_List_Size_ExA( len, filter, flags, NULL ); +} + +/*********************************************************************** + * CM_Get_Device_ID_List_ExW (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Device_ID_List_ExW( const WCHAR *filter, WCHAR *buffer, ULONG len, ULONG flags, HMACHINE machine ) +{ + struct enum_objects_append_params params = {.buffer = buffer, .len = len}; + BOOL all = !(flags & CM_GETIDLIST_FILTER_PRESENT); + LSTATUS err; + GUID guid; + + TRACE( "filter %s, buffer %p, len %lu, flags %#lx, machine %p\n", debugstr_w(filter), buffer, len, flags, machine ); + if (machine) FIXME( "machine %p not implemented!\n", machine ); + + if (!buffer) return CR_INVALID_POINTER; + if (!len) return buffer ? CR_INVALID_POINTER : CR_BUFFER_SMALL; + if (flags & ~CM_GETIDLIST_FILTER_BITS) return CR_INVALID_FLAG; + if (flags & CM_GETIDLIST_DONOTGENERATE) FIXME( "CM_GETIDLIST_DONOTGENERATE not implemented!\n" ); + + *buffer = 0; + if (flags & CM_GETIDLIST_FILTER_CLASS && filter && guid_from_string( filter, &guid )) return CR_INVALID_DATA; + if ((flags & CM_GETIDLIST_FILTER_ENUMERATOR) && !filter) return CR_INVALID_POINTER; + if (!(flags &= ~(CM_GETIDLIST_DONOTGENERATE | CM_GETIDLIST_FILTER_PRESENT))) filter = NULL; + + switch (flags) + { + case 0: err = enum_devices( NULL, NULL, NULL, all, enum_objects_append, ¶ms ); break; + case CM_GETIDLIST_FILTER_ENUMERATOR: err = enum_devices( filter, NULL, NULL, all, enum_objects_append, ¶ms ); break; + case CM_GETIDLIST_FILTER_CLASS: err = enum_devices( NULL, &DEVPKEY_Device_ClassGuid, filter, all, enum_objects_append, ¶ms ); break; + case CM_GETIDLIST_FILTER_SERVICE: err = enum_devices( NULL, &DEVPKEY_Device_Service, filter, all, enum_objects_append, ¶ms ); break; + case CM_GETIDLIST_FILTER_BUSRELATIONS: err = get_device_strings( filter, &DEVPKEY_Device_BusRelations, &len, buffer ); break; + case CM_GETIDLIST_FILTER_TRANSPORTRELATIONS: err = get_device_strings( filter, &DEVPKEY_Device_TransportRelations, &len, buffer ); break; + case CM_GETIDLIST_FILTER_EJECTRELATIONS: err = get_device_strings( filter, &DEVPKEY_Device_EjectionRelations, &len, buffer ); break; + case CM_GETIDLIST_FILTER_POWERRELATIONS: err = get_device_strings( filter, &DEVPKEY_Device_PowerRelations, &len, buffer ); break; + case CM_GETIDLIST_FILTER_REMOVALRELATIONS: err = get_device_strings( filter, &DEVPKEY_Device_RemovalRelations, &len, buffer ); break; + default: WARN( "Unsupported flags %#lx\n", flags ); return CR_INVALID_FLAG; + } + + return map_error( err ); +} + +/*********************************************************************** + * CM_Get_Device_ID_List_ExA (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Device_ID_List_ExA( const char *filterA, char *bufferA, ULONG len, ULONG flags, HMACHINE machine ) +{ + WCHAR filterW[MAX_PATH], *bufferW; + CONFIGRET ret; + + bufferW = bufferA ? malloc( len * sizeof(WCHAR) ) : NULL; + if (filterA) MultiByteToWideChar( CP_ACP, 0, filterA, -1, filterW, ARRAY_SIZE(filterW) ); + ret = CM_Get_Device_ID_List_ExW( filterA ? filterW : NULL, bufferA ? bufferW : NULL, len, flags, machine ); + if (!ret && bufferA && len && !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_ID_ListW (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Device_ID_ListW( const WCHAR *filter, WCHAR *buffer, ULONG len, ULONG flags ) +{ + return CM_Get_Device_ID_List_ExW( filter, buffer, len, flags, NULL ); +} + +/*********************************************************************** + * CM_Get_Device_ID_ListA (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Device_ID_ListA( const char *filter, char *buffer, ULONG len, ULONG flags ) +{ + return CM_Get_Device_ID_List_ExA( filter, buffer, len, flags, NULL ); +} + /*********************************************************************** * CM_Locate_DevNode_ExW (cfgmgr32.@) */ @@ -1677,7 +1922,7 @@ CONFIGRET WINAPI CM_Get_DevNode_Property_ExW( DEVINST node, const DEVPROPKEY *ke if (flags) FIXME( "flags %#lx not implemented!\n", flags ); if (devnode_get_device( node, &dev )) return CR_INVALID_DEVNODE; - if ((err = init_property( &prop, key, type, buffer, size ))) return map_error( err ); + if ((err = init_property( &prop, key, type, buffer, size, TRUE ))) return map_error( err ); return map_error( get_device_property( HKEY_LOCAL_MACHINE, &dev, &prop ) ); } diff --git a/dlls/cfgmgr32/cfgmgr32.spec b/dlls/cfgmgr32/cfgmgr32.spec index ae538de0a15..33bcb1bb32c 100644 --- a/dlls/cfgmgr32/cfgmgr32.spec +++ b/dlls/cfgmgr32/cfgmgr32.spec @@ -98,14 +98,14 @@ @ stdcall CM_Get_Device_IDW(ptr ptr long long) @ stdcall CM_Get_Device_ID_ExA(ptr ptr long long ptr) @ stdcall CM_Get_Device_ID_ExW(ptr ptr long long ptr) -@ stdcall CM_Get_Device_ID_ListA(str ptr long long) setupapi.CM_Get_Device_ID_ListA -@ stdcall CM_Get_Device_ID_ListW(wstr ptr long long) setupapi.CM_Get_Device_ID_ListW -@ stdcall CM_Get_Device_ID_List_ExA(str ptr long long ptr) setupapi.CM_Get_Device_ID_List_ExA -@ stdcall CM_Get_Device_ID_List_ExW(wstr ptr long long ptr) setupapi.CM_Get_Device_ID_List_ExW -@ stdcall CM_Get_Device_ID_List_SizeA(ptr str long) setupapi.CM_Get_Device_ID_List_SizeA -@ stdcall CM_Get_Device_ID_List_SizeW(ptr wstr long) setupapi.CM_Get_Device_ID_List_SizeW -@ stdcall CM_Get_Device_ID_List_Size_ExA(ptr str long ptr) setupapi.CM_Get_Device_ID_List_Size_ExA -@ stdcall CM_Get_Device_ID_List_Size_ExW(ptr wstr long ptr) setupapi.CM_Get_Device_ID_List_Size_ExW +@ stdcall CM_Get_Device_ID_ListA(str ptr long long) +@ stdcall CM_Get_Device_ID_ListW(wstr ptr long long) +@ stdcall CM_Get_Device_ID_List_ExA(str ptr long long ptr) +@ stdcall CM_Get_Device_ID_List_ExW(wstr ptr long long ptr) +@ stdcall CM_Get_Device_ID_List_SizeA(ptr str long) +@ stdcall CM_Get_Device_ID_List_SizeW(ptr wstr long) +@ stdcall CM_Get_Device_ID_List_Size_ExA(ptr str long ptr) +@ stdcall CM_Get_Device_ID_List_Size_ExW(ptr wstr long ptr) @ stdcall CM_Get_Device_ID_Size(ptr ptr long) @ stdcall CM_Get_Device_ID_Size_Ex(ptr ptr long ptr) @ stdcall CM_Get_Device_Interface_AliasA(str ptr ptr ptr long) setupapi.CM_Get_Device_Interface_AliasA diff --git a/dlls/cfgmgr32/cfgmgr32_private.h b/dlls/cfgmgr32/cfgmgr32_private.h index f614e594c58..9051897daa5 100644 --- a/dlls/cfgmgr32/cfgmgr32_private.h +++ b/dlls/cfgmgr32/cfgmgr32_private.h @@ -63,7 +63,7 @@ struct property DWORD *size; }; -extern LSTATUS init_property( struct property *prop, const DEVPROPKEY *key, DEVPROPTYPE *type, void *buffer, DWORD *size ); +extern LSTATUS init_property( struct property *prop, const DEVPROPKEY *key, DEVPROPTYPE *type, void *buffer, DWORD *size, BOOL binary ); struct device_interface { diff --git a/dlls/cfgmgr32/devobject.c b/dlls/cfgmgr32/devobject.c index 2ee2c1f8387..14591eef4ab 100644 --- a/dlls/cfgmgr32/devobject.c +++ b/dlls/cfgmgr32/devobject.c @@ -317,7 +317,7 @@ static LSTATUS copy_device_interface_property( HKEY hkey, const struct device_in struct property prop; LSTATUS err; - init_property( &prop, &property->CompKey.Key, &property->Type, property->Buffer, &property->BufferSize ); + init_property( &prop, &property->CompKey.Key, &property->Type, property->Buffer, &property->BufferSize, TRUE ); if (!(err = query_device_interface_property( hkey, iface, &prop ))) return ERROR_SUCCESS; if (err && err != ERROR_MORE_DATA) return ERROR_SUCCESS; diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index 8939cf008b3..1718a161b8b 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -187,7 +187,7 @@ const DEVPROPERTY* (WINAPI *pDevFindProperty)(const DEVPROPKEY *, DEVPROPSTORE, DEFINE_DEVPROPKEY(DEVPROPKEY_GPU_LUID, 0x60b193cb, 0x5276, 0x4d0f, 0x96, 0xfc, 0xf1, 0x73, 0xab, 0xad, 0x3e, 0xc6, 2); -static void test_CM_Get_Device_ID_List(void) +static void test_CM_Get_Device_ID_List_setupapi(void) { struct { @@ -3725,6 +3725,352 @@ static void test_CM_Get_Class_Property_Keys(void) if (len == 9) todo_wine ok( !memcmp( buffer + 5, &DEVPKEY_NAME, sizeof(*buffer) ), "got %s\n", debugstr_DEVPROPKEY( buffer + 5 ) ); } +static void test_CM_Get_Device_ID_List_Size(void) +{ + WCHAR instance[MAX_PATH], *buffer, *tmp; + ULONG size_all, size_present, size; + GUID guid = GUID_DEVINTERFACE_HID; + CONFIGRET ret; + + ret = CM_Get_Device_ID_List_SizeW( NULL, NULL, CM_GETIDLIST_FILTER_NONE ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + for (UINT flag = 1; flag; flag <<= 1) + { + if (flag & CM_GETIDLIST_FILTER_BITS) continue; + winetest_push_context( "%#x", flag ); + ret = CM_Get_Device_ID_List_SizeW( &size, NULL, flag ); + ok_x4( ret, ==, CR_INVALID_FLAG ); + winetest_pop_context(); + } + + size_present = 0; + ret = CM_Get_Device_ID_List_SizeW( &size_present, NULL, CM_GETIDLIST_FILTER_PRESENT ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( size_present, >, 0 ); + size_all = 0; + ret = CM_Get_Device_ID_List_SizeW( &size_all, NULL, CM_GETIDLIST_FILTER_NONE ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( size_all, >, 0 ); + + size = 0; + ret = CM_Get_Device_ID_List_SizeA( &size, NULL, CM_GETIDLIST_FILTER_PRESENT ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( size, ==, size_present ); + size = 0; + ret = CM_Get_Device_ID_List_SizeW( &size, (WCHAR *)L"", CM_GETIDLIST_FILTER_PRESENT ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( size, ==, size_present ); + size = 0; + ret = CM_Get_Device_ID_List_SizeA( &size, NULL, CM_GETIDLIST_FILTER_NONE ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( size, ==, size_all ); + size = 0; + ret = CM_Get_Device_ID_List_SizeW( &size, (WCHAR *)L"", CM_GETIDLIST_FILTER_NONE ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( size, ==, size_all ); + + + ret = CM_Get_Device_ID_List_SizeW( &size, NULL, CM_GETIDLIST_FILTER_ENUMERATOR ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + ret = CM_Get_Device_ID_List_SizeW( &size, L"", CM_GETIDLIST_FILTER_ENUMERATOR ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + size = 0xdeadbeef; + ret = CM_Get_Device_ID_List_SizeW( &size, L"INVALID", CM_GETIDLIST_FILTER_ENUMERATOR ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( size, ==, 1 ); + size = 0; + ret = CM_Get_Device_ID_List_SizeW( &size, L"HID\\VID", CM_GETIDLIST_FILTER_ENUMERATOR ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( size, ==, 1 ); + size = 0; + ret = CM_Get_Device_ID_List_SizeW( &size, L"HID", CM_GETIDLIST_FILTER_ENUMERATOR ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( size, >, 1 ); + + + ret = CM_Get_Device_ID_List_SizeW( &size, NULL, CM_GETIDLIST_FILTER_CLASS ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + ret = CM_Get_Device_ID_List_SizeW( &size, L"", CM_GETIDLIST_FILTER_CLASS ); + ok_x4( ret, ==, CR_INVALID_DATA ); + ret = CM_Get_Device_ID_List_SizeW( &size, L"INVALID", CM_GETIDLIST_FILTER_CLASS ); + ok_x4( ret, ==, CR_INVALID_DATA ); + size = 0; + ret = CM_Get_Device_ID_List_SizeW( &size, L"{4d1e55b2-f16f-11cf-88cb-001111000030}" /* GUID_DEVINTERFACE_HID */, CM_GETIDLIST_FILTER_CLASS ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( size, ==, 1 ); + size = 0; + ret = CM_Get_Device_ID_List_SizeW( &size, L"{745a17a0-74d3-11d0-b6fe-00a0c90f57da}" /* GUID_DEVCLASS_HIDCLASS */, CM_GETIDLIST_FILTER_CLASS ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( size, >, 1 ); + + + ret = CM_Get_Device_ID_List_SizeW( &size, NULL, CM_GETIDLIST_FILTER_SERVICE ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + size = 0; + ret = CM_Get_Device_ID_List_SizeW( &size, L"INVALID", CM_GETIDLIST_FILTER_SERVICE ); + todo_wine ok_x4( ret, ==, CR_SUCCESS ); + todo_wine ok_u4( size, ==, 1 ); + size = 0; + ret = CM_Get_Device_ID_List_SizeW( &size, L"", CM_GETIDLIST_FILTER_SERVICE ); + todo_wine ok_x4( ret, ==, CR_SUCCESS ); + todo_wine ok_u4( size, >, 1 ); + + + ret = CM_Get_Device_ID_List_SizeW( &size, NULL, CM_GETIDLIST_FILTER_EJECTRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_List_SizeW( &size, L"", CM_GETIDLIST_FILTER_EJECTRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_List_SizeW( &size, L"INVALID", CM_GETIDLIST_FILTER_EJECTRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_List_SizeW( &size, NULL, CM_GETIDLIST_FILTER_REMOVALRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_List_SizeW( &size, L"", CM_GETIDLIST_FILTER_REMOVALRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_List_SizeW( &size, L"INVALID", CM_GETIDLIST_FILTER_REMOVALRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_List_SizeW( &size, NULL, CM_GETIDLIST_FILTER_POWERRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_List_SizeW( &size, L"", CM_GETIDLIST_FILTER_POWERRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_List_SizeW( &size, L"INVALID", CM_GETIDLIST_FILTER_POWERRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_List_SizeW( &size, NULL, CM_GETIDLIST_FILTER_BUSRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_List_SizeW( &size, L"", CM_GETIDLIST_FILTER_BUSRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_List_SizeW( &size, L"INVALID", CM_GETIDLIST_FILTER_BUSRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_List_SizeW( &size, NULL, CM_GETIDLIST_FILTER_TRANSPORTRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_List_SizeW( &size, L"", CM_GETIDLIST_FILTER_TRANSPORTRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_List_SizeW( &size, L"INVALID", CM_GETIDLIST_FILTER_TRANSPORTRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + + + 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 ); + + wcscpy( instance, buffer + 4 ); + *wcsrchr( instance, '#' ) = 0; + while ((tmp = wcschr( instance, '#' ))) *tmp = '\\'; + + ret = CM_Get_Device_ID_List_SizeW( &size, instance, CM_GETIDLIST_FILTER_EJECTRELATIONS ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_List_SizeW( &size, instance, CM_GETIDLIST_FILTER_REMOVALRELATIONS ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_List_SizeW( &size, instance, CM_GETIDLIST_FILTER_POWERRELATIONS ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_List_SizeW( &size, instance, CM_GETIDLIST_FILTER_BUSRELATIONS ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_List_SizeW( &size, instance, CM_GETIDLIST_FILTER_TRANSPORTRELATIONS ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + + size = 0xdeadbeef; + ret = CM_Get_Device_ID_List_SizeW( &size, instance, CM_GETIDLIST_FILTER_ENUMERATOR ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ok_u4( size, ==, 0 ); + *wcsrchr( instance, '\\' ) = 0; + size = 0; + ret = CM_Get_Device_ID_List_SizeW( &size, instance, CM_GETIDLIST_FILTER_ENUMERATOR ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( size, >, 1 ); + + free( buffer ); +} + +static void test_CM_Get_Device_ID_List(void) +{ + WCHAR *buffer, *bufferW, *instance, *tmp; + CONFIGRET ret; + char *bufferA; + ULONG size; + + size = 0; + ret = CM_Get_Device_ID_List_SizeW( &size, NULL, CM_GETIDLIST_FILTER_NONE ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( size, >, 0 ); + + buffer = malloc( size * sizeof(*buffer) ); + ok_ptr( buffer, !=, NULL ); + bufferW = malloc( size * sizeof(*bufferW) ); + ok_ptr( bufferW, !=, NULL ); + + + ret = CM_Get_Device_ID_ListW( NULL, NULL, size, CM_GETIDLIST_FILTER_NONE ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + for (UINT flag = 1; flag; flag <<= 1) + { + if (flag & CM_GETIDLIST_FILTER_BITS) continue; + winetest_push_context( "%#x", flag ); + ret = CM_Get_Device_ID_ListW( NULL, buffer, size, flag ); + ok_x4( ret, ==, CR_INVALID_FLAG ); + winetest_pop_context(); + } + + ret = CM_Get_Device_ID_ListW( NULL, buffer, size, CM_GETIDLIST_FILTER_PRESENT ); + ok_x4( ret, ==, CR_SUCCESS ); + ret = CM_Get_Device_ID_ListW( NULL, buffer, size, CM_GETIDLIST_FILTER_NONE ); + ok_x4( ret, ==, CR_SUCCESS ); + + + bufferA = malloc( size * sizeof(*bufferA) ); + ok_ptr( bufferA, !=, NULL ); + ret = CM_Get_Device_ID_ListA( NULL, bufferA, size, CM_GETIDLIST_FILTER_NONE ); + ok_x4( ret, ==, CR_SUCCESS ); + + memset( bufferW, 0xcc, size * sizeof(*bufferW) ); + MultiByteToWideChar( CP_ACP, 0, bufferA, size, bufferW, size ); + ok( !memcmp( bufferW, buffer, size * sizeof(*buffer) ), "got %s, %s.\n", debugstr_wn( bufferW, size ), debugstr_wn( buffer, size ) ); + free( bufferA ); + + ret = CM_Get_Device_ID_ListW( L"", buffer, size, CM_GETIDLIST_FILTER_NONE ); + ok_x4( ret, ==, CR_SUCCESS ); + ok( !memcmp( bufferW, buffer, size * sizeof(*buffer) ), "got %s, %s.\n", debugstr_wn( bufferW, size ), debugstr_wn( buffer, size ) ); + + + ret = CM_Get_Device_ID_ListW( NULL, buffer, size, CM_GETIDLIST_FILTER_ENUMERATOR ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + ret = CM_Get_Device_ID_ListW( L"", buffer, size, CM_GETIDLIST_FILTER_ENUMERATOR ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_ListW( L"INVALID", buffer, size, CM_GETIDLIST_FILTER_ENUMERATOR ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_wcs( L"", buffer ); + ret = CM_Get_Device_ID_ListW( L"HID\\VID", buffer, size, CM_GETIDLIST_FILTER_ENUMERATOR ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_wcs( L"", buffer ); + ret = CM_Get_Device_ID_ListW( L"HID", buffer, size, CM_GETIDLIST_FILTER_ENUMERATOR ); + ok_x4( ret, ==, CR_SUCCESS ); + for (tmp = buffer; *tmp; tmp = tmp + wcslen( tmp ) + 1) + { + WCHAR *sep, substr[MAX_PATH], upper[MAX_PATH]; + ok( !wcsncmp( tmp, L"HID\\", 4 ), "got %s\n", debugstr_w( tmp ) ); + /* HID\\VID_XXXX&PID_XXXX upper case prefix */ + wcscpy( substr, tmp ); + if (!(sep = wcschr( substr + 4, '\\' ))) sep = substr + 4; + *sep = 0; + wcscpy( upper, substr ); + wcsupr( upper ); + ok_wcs( upper, substr ); + *sep = '\\'; + wcslwr( wcsrchr( substr, '\\' ) ); /* lowercase instance + refstring */ + flaky_wine ok_wcs( substr, tmp ); + } + ok( tmp > buffer, "got %s\n", debugstr_wn( buffer, size ) ); + + + ret = CM_Get_Device_ID_ListW( NULL, buffer, size, CM_GETIDLIST_FILTER_CLASS ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + ret = CM_Get_Device_ID_ListW( L"", buffer, size, CM_GETIDLIST_FILTER_CLASS ); + ok_x4( ret, ==, CR_INVALID_DATA ); + ret = CM_Get_Device_ID_ListW( L"INVALID", buffer, size, CM_GETIDLIST_FILTER_CLASS ); + ok_x4( ret, ==, CR_INVALID_DATA ); + ret = CM_Get_Device_ID_ListW( L"{4d1e55b2-f16f-11cf-88cb-001111000030}", buffer, size, CM_GETIDLIST_FILTER_CLASS ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_wcs( L"", buffer ); + ret = CM_Get_Device_ID_ListW( L"{745a17a0-74d3-11d0-b6fe-00a0c90f57da}", buffer, size, CM_GETIDLIST_FILTER_CLASS ); + ok_x4( ret, ==, CR_SUCCESS ); + for (tmp = buffer; *tmp; tmp = tmp + wcslen( tmp ) + 1) + ok( !!wcschr( tmp, '\\' ), "got %s\n", debugstr_w( tmp ) ); + ok( tmp > buffer, "got %s\n", debugstr_wn( buffer, size ) ); + + + ret = CM_Get_Device_ID_ListW( NULL, buffer, size, CM_GETIDLIST_FILTER_SERVICE ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + ret = CM_Get_Device_ID_ListW( L"INVALID", buffer, size, CM_GETIDLIST_FILTER_SERVICE ); + todo_wine ok_x4( ret, ==, CR_SUCCESS ); + ok_wcs( L"", buffer ); + ret = CM_Get_Device_ID_ListW( L"", buffer, size, CM_GETIDLIST_FILTER_SERVICE ); + todo_wine ok_x4( ret, ==, CR_SUCCESS ); + for (tmp = buffer; *tmp; tmp = tmp + wcslen( tmp ) + 1) + ok( !!wcschr( tmp, '\\' ), "got %s\n", debugstr_w( tmp ) ); + todo_wine ok( tmp > buffer, "got %s\n", debugstr_wn( buffer, size ) ); + + + ret = CM_Get_Device_ID_ListW( NULL, buffer, size, CM_GETIDLIST_FILTER_EJECTRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_ListW( L"", buffer, size, CM_GETIDLIST_FILTER_EJECTRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_ListW( L"INVALID", buffer, size, CM_GETIDLIST_FILTER_EJECTRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_ListW( NULL, buffer, size, CM_GETIDLIST_FILTER_REMOVALRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_ListW( L"", buffer, size, CM_GETIDLIST_FILTER_REMOVALRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_ListW( L"INVALID", buffer, size, CM_GETIDLIST_FILTER_REMOVALRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_ListW( NULL, buffer, size, CM_GETIDLIST_FILTER_POWERRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_ListW( L"", buffer, size, CM_GETIDLIST_FILTER_POWERRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_ListW( L"INVALID", buffer, size, CM_GETIDLIST_FILTER_POWERRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_ListW( NULL, buffer, size, CM_GETIDLIST_FILTER_BUSRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_ListW( L"", buffer, size, CM_GETIDLIST_FILTER_BUSRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_ListW( L"INVALID", buffer, size, CM_GETIDLIST_FILTER_BUSRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_ListW( NULL, buffer, size, CM_GETIDLIST_FILTER_TRANSPORTRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_ListW( L"", buffer, size, CM_GETIDLIST_FILTER_TRANSPORTRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_ListW( L"INVALID", buffer, size, CM_GETIDLIST_FILTER_TRANSPORTRELATIONS ); + todo_wine ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + + + ret = CM_Get_Device_ID_ListW( L"HID", buffer, size, CM_GETIDLIST_FILTER_ENUMERATOR ); + ok_x4( ret, ==, CR_SUCCESS ); + for (tmp = buffer; *tmp; tmp = tmp + wcslen( tmp ) + 1) + { + WCHAR *sep, substr[MAX_PATH], upper[MAX_PATH]; + ok( !wcsncmp( tmp, L"HID\\", 4 ), "got %s\n", debugstr_w( tmp ) ); + /* HID\\VID_XXXX&PID_XXXX upper case prefix */ + wcscpy( substr, tmp ); + if (!(sep = wcschr( substr + 4, '\\' ))) sep = substr + 4; + *sep = 0; + wcscpy( upper, substr ); + wcsupr( upper ); + ok_wcs( upper, substr ); + *sep = '\\'; + wcslwr( wcsrchr( substr, '\\' ) ); /* lowercase instance + refstring */ + flaky_wine ok_wcs( substr, tmp ); + } + ok( tmp > buffer, "got %s\n", debugstr_wn( buffer, size ) ); + memcpy( bufferW, buffer, size * sizeof(WCHAR) ); + + for (instance = bufferW; *instance; instance += wcslen( instance ) + 1) + { + ret = CM_Get_Device_ID_ListW( instance, buffer, size, CM_GETIDLIST_FILTER_EJECTRELATIONS ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_ListW( instance, buffer, size, CM_GETIDLIST_FILTER_REMOVALRELATIONS ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_ListW( instance, buffer, size, CM_GETIDLIST_FILTER_POWERRELATIONS ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_ListW( instance, buffer, size, CM_GETIDLIST_FILTER_BUSRELATIONS ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ret = CM_Get_Device_ID_ListW( instance, buffer, size, CM_GETIDLIST_FILTER_TRANSPORTRELATIONS ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + + ret = CM_Get_Device_ID_ListW( instance, buffer, size, CM_GETIDLIST_FILTER_ENUMERATOR ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + if ((tmp = wcsrchr( instance, '\\' ))) *tmp = 0; + ret = CM_Get_Device_ID_ListW( instance, buffer, size, CM_GETIDLIST_FILTER_ENUMERATOR ); + ok_x4( ret, ==, CR_SUCCESS ); + ok( !wcsncmp( buffer, instance, tmp - instance ), "got %s\n", debugstr_w( buffer ) ); + if (tmp) *tmp = '\\'; + } + + free( bufferW ); + free( buffer ); +} + START_TEST(cfgmgr32) { HMODULE mod = GetModuleHandleA("cfgmgr32.dll"); @@ -3751,7 +4097,9 @@ START_TEST(cfgmgr32) 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_Size(); test_CM_Get_Device_ID_List(); + test_CM_Get_Device_ID_List_setupapi(); test_CM_Register_Notification(); test_CM_Open_DevNode_Key(); test_CM_Get_DevNode_Registry_Property(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10584