From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/cfgmgr32/cfgmgr32.c | 128 ++++++++++++++++++++++++++++ dlls/cfgmgr32/cfgmgr32.spec | 4 +- dlls/cfgmgr32/tests/cfgmgr32.c | 148 +++++++++++++++++++++++++++++++++ 3 files changed, 278 insertions(+), 2 deletions(-) diff --git a/dlls/cfgmgr32/cfgmgr32.c b/dlls/cfgmgr32/cfgmgr32.c index c47e50f37e5..cfa1d430409 100644 --- a/dlls/cfgmgr32/cfgmgr32.c +++ b/dlls/cfgmgr32/cfgmgr32.c @@ -96,6 +96,94 @@ static LSTATUS open_device_classes_key( HKEY root, const WCHAR *key, REGSAM acce return open_key( root, path, access, open, hkey ); } +struct property +{ + BOOL ansi; + DEVPROPKEY key; + DEVPROPTYPE *type; + DWORD *reg_type; + void *buffer; + DWORD *size; +}; + +static LSTATUS init_registry_property( struct property *prop, const DEVPROPKEY *base, UINT property, DWORD *type, void *buffer, DWORD *size, BOOL ansi ) +{ + if (!(prop->size = size)) return ERROR_INVALID_USER_BUFFER; + if (!(prop->buffer = buffer) && (*prop->size)) return ERROR_INVALID_USER_BUFFER; + prop->type = NULL; + prop->ansi = ansi; + memcpy( &prop->key, base, sizeof(prop->key) ); + prop->key.pid = property + 1; + prop->reg_type = type; + return ERROR_SUCCESS; +} + +static LSTATUS query_named_property( HKEY hkey, const WCHAR *nameW, DEVPROPTYPE type, struct property *prop ) +{ + LSTATUS err; + + if (!prop->ansi) err = RegQueryValueExW( hkey, nameW, NULL, prop->reg_type, prop->buffer, prop->size ); + else + { + char nameA[MAX_PATH]; + if (nameW) WideCharToMultiByte( CP_ACP, 0, nameW, -1, nameA, sizeof(nameA), NULL, NULL ); + err = RegQueryValueExA( hkey, nameW ? nameA : NULL, NULL, prop->reg_type, prop->buffer, prop->size ); + } + + if (!err && !prop->buffer) err = ERROR_MORE_DATA; + if ((!err || err == ERROR_MORE_DATA) && prop->type) *prop->type = type; + if (err == ERROR_FILE_NOT_FOUND) return ERROR_NOT_FOUND; + return err; +} + +struct property_desc +{ + const DEVPROPKEY *key; + DEVPROPTYPE type; + const WCHAR *name; +}; + +static const struct property_desc class_properties[] = +{ + /* ansi-compatible CM_CRP properties */ + { &DEVPKEY_DeviceClass_UpperFilters, DEVPROP_TYPE_STRING, L"UpperFilters" }, + { &DEVPKEY_DeviceClass_LowerFilters, DEVPROP_TYPE_STRING, L"LowerFilters" }, + { &DEVPKEY_DeviceClass_Security, DEVPROP_TYPE_SECURITY_DESCRIPTOR, L"Security" }, + { &DEVPKEY_DeviceClass_SecuritySDS, DEVPROP_TYPE_SECURITY_DESCRIPTOR_STRING, L"SecuritySDS" }, + { &DEVPKEY_DeviceClass_DevType, DEVPROP_TYPE_UINT32, L"DevType" }, + { &DEVPKEY_DeviceClass_Exclusive, DEVPROP_TYPE_BOOLEAN, L"Exclusive" }, + { &DEVPKEY_DeviceClass_Characteristics, DEVPROP_TYPE_INT32, L"Characteristics" }, +}; + +static LSTATUS query_class_property( HKEY hkey, struct property *prop ) +{ + for (UINT i = 0; i < ARRAY_SIZE(class_properties); i++) + { + const struct property_desc *desc = class_properties + i; + if (memcmp( desc->key, &prop->key, sizeof(prop->key) )) continue; + return query_named_property( hkey, desc->name, desc->type, prop ); + } + + return ERROR_UNKNOWN_PROPERTY; +} + +static LSTATUS get_class_property( const GUID *class, struct property *prop ) +{ + WCHAR path[39]; + LSTATUS err; + HKEY hkey; + + guid_string( class, path, ARRAY_SIZE(path) ); + if (!(err = open_class_key( HKEY_LOCAL_MACHINE, path, KEY_QUERY_VALUE, TRUE, &hkey ))) + { + err = query_class_property( hkey, prop ); + RegCloseKey( hkey ); + } + + if (err && err != ERROR_MORE_DATA) *prop->size = 0; + return err; +} + struct device_interface { GUID class_guid; @@ -131,9 +219,13 @@ static CONFIGRET map_error( LSTATUS err ) { switch (err) { + case ERROR_INVALID_USER_BUFFER: return CR_INVALID_POINTER; case ERROR_FILE_NOT_FOUND: return CR_NO_SUCH_REGISTRY_KEY; + case ERROR_MORE_DATA: return CR_BUFFER_SMALL; case ERROR_NO_MORE_ITEMS: return CR_NO_SUCH_VALUE; + case ERROR_NOT_FOUND: return CR_NO_SUCH_VALUE; case ERROR_SUCCESS: return CR_SUCCESS; + case ERROR_UNKNOWN_PROPERTY: return CR_INVALID_PROPERTY; default: WARN( "unmapped error %lu\n", err ); return CR_FAILURE; } } @@ -365,6 +457,42 @@ CONFIGRET WINAPI CM_Open_Class_KeyA( GUID *class, const char *name, REGSAM acces return CM_Open_Class_Key_ExA( class, name, access, disposition, hkey, flags, NULL ); } +/*********************************************************************** + * CM_Get_Class_Registry_PropertyW (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Class_Registry_PropertyW( GUID *class, ULONG property, ULONG *type, void *buffer, ULONG *len, ULONG flags, HMACHINE machine ) +{ + struct property prop; + LSTATUS err; + + TRACE( "class %s, property %#lx, type %p, buffer %p, len %p, flags %#lx, machine %p\n", debugstr_guid(class), property, type, buffer, len, 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 ((err = init_registry_property( &prop, &DEVPKEY_DeviceClass_UpperFilters, property, type, buffer, len, FALSE ))) return map_error( err ); + + return map_error( get_class_property( class, &prop ) ); +} + +/*********************************************************************** + * CM_Get_Class_Registry_PropertyA (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Class_Registry_PropertyA( GUID *class, ULONG property, ULONG *type, void *buffer, ULONG *len, ULONG flags, HMACHINE machine ) +{ + struct property prop; + LSTATUS err; + + TRACE( "class %s, property %#lx, type %p, buffer %p, len %p, flags %#lx, machine %p\n", debugstr_guid(class), property, type, buffer, len, 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 ((err = init_registry_property( &prop, &DEVPKEY_DeviceClass_UpperFilters, property, type, buffer, len, TRUE ))) return map_error( err ); + + return map_error( get_class_property( class, &prop ) ); +} + /*********************************************************************** * CM_Open_Device_Interface_Key_ExW (cfgmgr32.@) */ diff --git a/dlls/cfgmgr32/cfgmgr32.spec b/dlls/cfgmgr32/cfgmgr32.spec index 0695e73a685..ae361201e3f 100644 --- a/dlls/cfgmgr32/cfgmgr32.spec +++ b/dlls/cfgmgr32/cfgmgr32.spec @@ -76,8 +76,8 @@ @ stub CM_Get_Class_Property_ExW @ 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_Registry_PropertyA(ptr long ptr ptr long long ptr) setupapi.CM_Get_Class_Registry_PropertyA -@ stdcall CM_Get_Class_Registry_PropertyW(ptr long ptr ptr long long ptr) setupapi.CM_Get_Class_Registry_PropertyW +@ 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 @ stub CM_Get_Depth_Ex @ stub CM_Get_DevNode_Custom_PropertyA diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index 4bc26755ad3..41292ffa822 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -62,6 +62,12 @@ static const char *debugstr_ok( const char *cond ) const WCHAR *v = (r); \ ok( !wcscmp( v, (e) ), "%s %s\n", debugstr_ok(#r), debugstr_w(v) ); \ } while (0) +#define ok_str( e, r ) \ + do \ + { \ + const char *v = (r); \ + ok( !strcmp( v, (e) ), "%s %s\n", debugstr_ok(#r), debugstr_a(v) ); \ + } while (0) #define ok_ex( r, op, e, t, f, ... ) \ do \ { \ @@ -2121,6 +2127,147 @@ static void test_CM_Open_Class_Key(void) ok_x4( ret, ==, ERROR_SUCCESS ); } +static void test_CM_Get_Class_Registry_Property(void) +{ + GUID guid = GUID_DEVCLASS_DISPLAY; + WCHAR buffer[MAX_PATH]; + char bufferA[MAX_PATH]; + DWORD type, len; + CONFIGRET ret; + + ret = CM_Get_Class_Registry_PropertyW( NULL, CM_CRP_DEVTYPE, &type, buffer, &len, 0, NULL ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_DEVTYPE, NULL, NULL, NULL, 0, NULL ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + ret = CM_Get_Class_Registry_PropertyW( NULL, CM_CRP_DEVTYPE, &type, buffer, &len, 0, NULL ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_DEVTYPE, &type, buffer, NULL, 0, NULL ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + len = 1; + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_DEVTYPE, &type, NULL, &len, 0, NULL ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + + len = 0; + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_DEVTYPE, &type, NULL, &len, 0, NULL ); + todo_wine ok_x4( ret, ==, CR_BUFFER_SMALL ); + todo_wine ok_x4( len, ==, 4 ); + len = 1; + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_DEVTYPE, &type, buffer, &len, 0, NULL ); + todo_wine ok_x4( ret, ==, CR_BUFFER_SMALL ); + todo_wine ok_x4( len, ==, 4 ); + + len = sizeof(buffer); + memset( &guid, 0xcd, sizeof(guid) ); + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_DEVTYPE, &type, buffer, &len, 0, NULL ); + ok_x4( ret, ==, CR_NO_SUCH_REGISTRY_KEY ); + ok_x4( len, ==, 0 ); + guid = GUID_DEVCLASS_DISPLAY; + + + len = sizeof(buffer); + ret = CM_Get_Class_Registry_PropertyW( &guid, 0, &type, buffer, &len, 0, NULL ); + ok_x4( ret, ==, CR_INVALID_PROPERTY ); + + len = sizeof(buffer); + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_UPPERFILTERS, &type, buffer, &len, 0, NULL ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ok_x4( len, ==, 0 ); + + len = sizeof(buffer); + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_LOWERFILTERS, &type, buffer, &len, 0, NULL ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ok_x4( len, ==, 0 ); + + type = 0xdeadbeef; + len = sizeof(buffer); + memset( buffer, 0xcd, sizeof(buffer) ); + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_SECURITY, &type, buffer, &len, 0, NULL ); + todo_wine ok_x4( ret, ==, CR_SUCCESS ); + todo_wine ok_x4( type, ==, REG_BINARY ); + todo_wine ok_x4( len, ==, 0x30 ); + + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_SECURITY, NULL, buffer, &len, 0, NULL ); + todo_wine ok_x4( ret, ==, CR_SUCCESS ); + + type = 0xdeadbeef; + len = sizeof(buffer); + memset( buffer, 0xcd, sizeof(buffer) ); + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_SECURITY_SDS, &type, buffer, &len, 0, NULL ); + todo_wine ok_x4( ret, ==, CR_SUCCESS ); + todo_wine ok_x4( type, ==, REG_SZ ); + todo_wine ok_x4( len, ==, 0x20 ); + todo_wine ok_wcs( L"D:P(A;;GA;;;SY)", buffer ); + + + type = 0xdeadbeef; + len = sizeof(bufferA); + memset( bufferA, 0xcd, sizeof(bufferA) ); + ret = CM_Get_Class_Registry_PropertyA( &guid, CM_CRP_SECURITY, &type, bufferA, &len, 0, NULL ); + todo_wine ok_x4( ret, ==, CR_SUCCESS ); + todo_wine ok_x4( type, ==, REG_BINARY ); + todo_wine ok_x4( len, ==, 0x30 ); + + type = 0xdeadbeef; + len = sizeof(bufferA); + memset( bufferA, 0xcd, sizeof(bufferA) ); + ret = CM_Get_Class_Registry_PropertyA( &guid, CM_CRP_SECURITY_SDS, &type, bufferA, &len, 0, NULL ); + todo_wine ok_x4( ret, ==, CR_SUCCESS ); + todo_wine ok_x4( type, ==, REG_SZ ); + todo_wine ok_x4( len, ==, 0x10 ); + todo_wine ok_str( "D:P(A;;GA;;;SY)", bufferA ); + + + type = 0xdeadbeef; + len = sizeof(buffer); + memset( buffer, 0xcd, sizeof(buffer) ); + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_DEVTYPE, &type, buffer, &len, 0, NULL ); + todo_wine ok_x4( ret, ==, CR_SUCCESS ); + todo_wine ok_x4( type, ==, REG_DWORD ); + todo_wine ok_x4( len, ==, 4 ); + todo_wine ok_x4( *(DWORD *)buffer, ==, 0x23 /* FILE_DEVICE_VIDEO */ ); + + len = sizeof(buffer); + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_EXCLUSIVE, &type, buffer, &len, 0, NULL ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + ok_x4( len, ==, 0 ); + + type = 0xdeadbeef; + len = sizeof(buffer); + memset( buffer, 0xcd, sizeof(buffer) ); + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_CHARACTERISTICS, &type, buffer, &len, 0, NULL ); + todo_wine ok_x4( ret, ==, CR_SUCCESS ); + todo_wine ok_x4( type, ==, REG_DWORD ); + todo_wine ok_x4( len, ==, 4 ); + todo_wine ok_x4( *(DWORD *)buffer, ==, 0x100 ); + + + guid = GUID_DEVCLASS_HIDCLASS; + len = sizeof(buffer); + ret = CM_Get_Class_Registry_PropertyW( &guid, 0, &type, buffer, &len, 0, NULL ); + ok_x4( ret, ==, CR_INVALID_PROPERTY ); + len = sizeof(buffer); + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_UPPERFILTERS, &type, buffer, &len, 0, NULL ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + len = sizeof(buffer); + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_LOWERFILTERS, &type, buffer, &len, 0, NULL ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + len = sizeof(buffer); + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_SECURITY, &type, buffer, &len, 0, NULL ); + todo_wine ok_x4( ret, ==, CR_SUCCESS ); + len = sizeof(buffer); + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_SECURITY_SDS, &type, buffer, &len, 0, NULL ); + todo_wine ok_x4( ret, ==, CR_SUCCESS ); + len = sizeof(buffer); + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_DEVTYPE, &type, buffer, &len, 0, NULL ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + len = sizeof(buffer); + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_EXCLUSIVE, &type, buffer, &len, 0, NULL ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + len = sizeof(buffer); + ret = CM_Get_Class_Registry_PropertyW( &guid, CM_CRP_CHARACTERISTICS, &type, buffer, &len, 0, NULL ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); +} + static void test_CM_Open_Device_Interface_Key(void) { WCHAR iface[4096], name[MAX_PATH], expect[MAX_PATH], buffer[39], *refstr; @@ -2177,6 +2324,7 @@ START_TEST(cfgmgr32) test_CM_Enumerate_Enumerators(); test_CM_Get_Class_Key_Name(); test_CM_Open_Class_Key(); + test_CM_Get_Class_Registry_Property(); 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/10110