sapi: Partially reimplement ISpObjectTokenEnumBuilder storing a token array.

This is needed in order to implement filtering and sorting in
ISpObjectTokenCategory::EnumTokens.
This commit is contained in:
Shaun Ren 2023-05-26 00:09:45 -04:00 committed by Alexandre Julliard
parent e071a70ba8
commit f286ee9bfa
2 changed files with 260 additions and 122 deletions

View file

@ -109,6 +109,63 @@ static void test_data_key(void)
ISpRegDataKey_Release( data_key );
}
static void setup_test_voice_tokens(void)
{
HKEY key;
ISpRegDataKey *data_key;
ISpDataKey *attrs_key;
LSTATUS ret;
HRESULT hr;
ret = RegCreateKeyExA( HKEY_CURRENT_USER, "Software\\Winetest\\sapi\\TestVoices\\Tokens", 0, NULL, 0,
KEY_ALL_ACCESS, NULL, &key, NULL );
ok( ret == ERROR_SUCCESS, "got %ld\n", ret );
hr = CoCreateInstance( &CLSID_SpDataKey, NULL, CLSCTX_INPROC_SERVER,
&IID_ISpRegDataKey, (void **)&data_key );
ok( hr == S_OK, "got %08lx\n", hr );
hr = ISpRegDataKey_SetKey( data_key, key, FALSE );
ok( hr == S_OK, "got %08lx\n", hr );
ISpRegDataKey_CreateKey( data_key, L"Voice1\\Attributes", &attrs_key );
ISpDataKey_SetStringValue( attrs_key, L"Language", L"409" );
ISpDataKey_SetStringValue( attrs_key, L"Gender", L"Female" );
ISpDataKey_SetStringValue( attrs_key, L"Age", L"Child" );
ISpDataKey_SetStringValue( attrs_key, L"Vendor", L"Vendor2" );
ISpDataKey_Release( attrs_key );
ISpRegDataKey_CreateKey( data_key, L"Voice2\\Attributes", &attrs_key );
ISpDataKey_SetStringValue( attrs_key, L"Language", L"406;407;408;409;40a" );
ISpDataKey_SetStringValue( attrs_key, L"Gender", L"Female" );
ISpDataKey_SetStringValue( attrs_key, L"Age", L"Adult" );
ISpDataKey_SetStringValue( attrs_key, L"Vendor", L"Vendor1" );
ISpDataKey_Release( attrs_key );
ISpRegDataKey_CreateKey( data_key, L"Voice3\\Attributes", &attrs_key );
ISpDataKey_SetStringValue( attrs_key, L"Language", L"409;411" );
ISpDataKey_SetStringValue( attrs_key, L"Gender", L"Female" );
ISpDataKey_SetStringValue( attrs_key, L"Age", L"Child" );
ISpDataKey_SetStringValue( attrs_key, L"Vendor", L"Vendor1" );
ISpDataKey_Release( attrs_key );
ISpRegDataKey_CreateKey( data_key, L"Voice4\\Attributes", &attrs_key );
ISpDataKey_SetStringValue( attrs_key, L"Language", L"411" );
ISpDataKey_SetStringValue( attrs_key, L"Gender", L"Male" );
ISpDataKey_SetStringValue( attrs_key, L"Age", L"Adult" );
ISpDataKey_Release( attrs_key );
ISpRegDataKey_CreateKey( data_key, L"Voice5\\Attributes", &attrs_key );
ISpDataKey_SetStringValue( attrs_key, L"Language", L"411" );
ISpDataKey_SetStringValue( attrs_key, L"Gender", L"Female" );
ISpDataKey_SetStringValue( attrs_key, L"Age", L"Adult" );
ISpDataKey_SetStringValue( attrs_key, L"Vendor", L"Vendor2" );
ISpDataKey_Release( attrs_key );
ISpRegDataKey_Release( data_key );
}
static const WCHAR test_cat[] = L"HKEY_CURRENT_USER\\Software\\Winetest\\sapi\\TestVoices";
static void test_token_category(void)
{
ISpObjectTokenCategory *cat;
@ -126,17 +183,19 @@ static void test_token_category(void)
hr = ISpObjectTokenCategory_SetId( cat, L"bogus", FALSE );
ok( hr == SPERR_INVALID_REGISTRY_KEY, "got %08lx\n", hr );
hr = ISpObjectTokenCategory_SetId( cat, SPCAT_VOICES, FALSE );
hr = ISpObjectTokenCategory_SetId( cat, test_cat, FALSE );
ok( hr == S_OK, "got %08lx\n", hr );
hr = ISpObjectTokenCategory_SetId( cat, SPCAT_VOICES, FALSE );
hr = ISpObjectTokenCategory_SetId( cat, test_cat, FALSE );
ok( hr == SPERR_ALREADY_INITIALIZED, "got %08lx\n", hr );
hr = ISpObjectTokenCategory_EnumTokens( cat, NULL, NULL, &enum_tokens );
ok( hr == S_OK, "got %08lx\n", hr );
count = 0xdeadbeef;
hr = IEnumSpObjectTokens_GetCount( enum_tokens, &count );
ok( hr == S_OK, "got %08lx\n", hr );
ok( count == 5, "got %lu\n", count );
IEnumSpObjectTokens_Release( enum_tokens );
ISpObjectTokenCategory_Release( cat );
@ -146,8 +205,11 @@ static void test_token_enum(void)
{
ISpObjectTokenEnumBuilder *token_enum;
HRESULT hr;
ISpObjectToken *token;
ISpObjectToken *tokens[3];
ISpObjectToken *out_tokens[3];
WCHAR token_id[MAX_PATH];
ULONG count;
int i;
hr = CoCreateInstance( &CLSID_SpObjectTokenEnum, NULL, CLSCTX_INPROC_SERVER,
&IID_ISpObjectTokenEnumBuilder, (void **)&token_enum );
@ -156,7 +218,7 @@ static void test_token_enum(void)
hr = ISpObjectTokenEnumBuilder_GetCount( token_enum, &count );
ok( hr == SPERR_UNINITIALIZED, "got %08lx\n", hr );
hr = ISpObjectTokenEnumBuilder_Next( token_enum, 1, &token, &count );
hr = ISpObjectTokenEnumBuilder_Next( token_enum, 1, tokens, &count );
ok( hr == SPERR_UNINITIALIZED, "got %08lx\n", hr );
hr = ISpObjectTokenEnumBuilder_SetAttribs( token_enum, NULL, NULL );
@ -168,11 +230,50 @@ static void test_token_enum(void)
ok( count == 0, "got %lu\n", count );
count = 0xdeadbeef;
hr = ISpObjectTokenEnumBuilder_Next( token_enum, 1, &token, &count );
hr = ISpObjectTokenEnumBuilder_Next( token_enum, 1, &out_tokens[0], &count );
ok( hr == S_FALSE, "got %08lx\n", hr );
ok( count == 0, "got %lu\n", count );
for ( i = 0; i < 3; i++ ) {
hr = CoCreateInstance( &CLSID_SpObjectToken, NULL, CLSCTX_INPROC_SERVER,
&IID_ISpObjectToken, (void **)&tokens[i] );
ok( hr == S_OK, "got %08lx\n", hr );
swprintf( token_id, MAX_PATH, L"%ls\\Tokens\\Voice%d", test_cat, i + 1 );
hr = ISpObjectToken_SetId( tokens[i], NULL, token_id, FALSE );
ok( hr == S_OK, "got %08lx\n", hr );
}
hr = ISpObjectTokenEnumBuilder_AddTokens( token_enum, 3, tokens );
ok( hr == S_OK, "got %08lx\n", hr );
out_tokens[0] = (ISpObjectToken *)0xdeadbeef;
hr = ISpObjectTokenEnumBuilder_Next( token_enum, 1, &out_tokens[0], NULL );
ok( hr == S_OK, "got %08lx\n", hr );
ok( out_tokens[0] == tokens[0], "got %p\n", out_tokens[0] );
out_tokens[0] = (ISpObjectToken *)0xdeadbeef;
hr = ISpObjectTokenEnumBuilder_Item( token_enum, 0, &out_tokens[0] );
ok( hr == S_OK, "got %08lx\n", hr );
ok( out_tokens[0] == tokens[0], "got %p\n", out_tokens[0] );
hr = ISpObjectTokenEnumBuilder_Item( token_enum, 3, &out_tokens[0] );
ok( hr == SPERR_NO_MORE_ITEMS, "got %08lx\n", hr );
hr = ISpObjectTokenEnumBuilder_Next( token_enum, 3, &out_tokens[1], NULL );
ok( hr == E_POINTER, "got %08lx\n", hr );
count = 0xdeadbeef;
out_tokens[1] = out_tokens[2] = (ISpObjectToken *)0xdeadbeef;
hr = ISpObjectTokenEnumBuilder_Next( token_enum, 3, &out_tokens[1], &count );
ok( hr == S_FALSE, "got %08lx\n", hr );
ok( count == 2, "got %lu\n", count );
ok( out_tokens[1] == tokens[1], "got %p\n", out_tokens[1] );
ok( out_tokens[2] == tokens[2], "got %p\n", out_tokens[2] );
ISpObjectTokenEnumBuilder_Release( token_enum );
for ( i = 0; i < 3; i++ ) ISpObjectToken_Release( tokens[i] );
}
static void test_default_token_id(void)
@ -558,6 +659,7 @@ START_TEST(token)
CoInitialize( NULL );
RegDeleteTreeA( HKEY_CURRENT_USER, "Software\\Winetest\\sapi" );
test_data_key();
setup_test_voice_tokens();
test_token_category();
test_token_enum();
test_default_token_id();

View file

@ -19,6 +19,7 @@
*/
#include <stdarg.h>
#include <stdint.h>
#define COBJMACROS
@ -328,6 +329,7 @@ struct token_category
LONG ref;
ISpRegDataKey *data_key;
WCHAR *id;
};
static struct token_category *impl_from_ISpObjectTokenCategory( ISpObjectTokenCategory *iface )
@ -375,6 +377,7 @@ static ULONG WINAPI token_category_Release( ISpObjectTokenCategory *iface )
if (!ref)
{
if (This->data_key) ISpRegDataKey_Release( This->data_key );
free( This->id );
free( This );
}
return ref;
@ -541,6 +544,8 @@ static HRESULT WINAPI token_category_SetId( ISpObjectTokenCategory *iface,
return hr;
}
This->id = wcsdup( id );
return hr;
}
@ -559,6 +564,12 @@ static HRESULT WINAPI token_category_GetDataKey( ISpObjectTokenCategory *iface,
return E_NOTIMPL;
}
struct token_with_score
{
ISpObjectToken *token;
uint64_t score;
};
struct token_enum
{
ISpObjectTokenEnumBuilder ISpObjectTokenEnumBuilder_iface;
@ -566,8 +577,8 @@ struct token_enum
BOOL init;
WCHAR *req, *opt;
ULONG count;
HKEY key;
struct token_with_score *tokens;
ULONG capacity, count;
DWORD index;
};
@ -582,8 +593,12 @@ static HRESULT WINAPI token_category_EnumTokens( ISpObjectTokenCategory *iface,
{
struct token_category *This = impl_from_ISpObjectTokenCategory( iface );
ISpObjectTokenEnumBuilder *builder;
struct token_enum *tokenenum;
struct data_key *this_data_key;
HKEY tokens_key;
DWORD count, max_subkey_size, root_len, token_id_size;
DWORD size, i;
WCHAR *token_id = NULL;
ISpObjectToken *token = NULL;
HRESULT hr;
TRACE( "(%p)->(%s %s %p)\n", This, debugstr_w( req ), debugstr_w( opt ), enum_tokens );
@ -599,12 +614,43 @@ static HRESULT WINAPI token_category_EnumTokens( ISpObjectTokenCategory *iface,
this_data_key = impl_from_ISpRegDataKey( This->data_key );
tokenenum = impl_from_ISpObjectTokenEnumBuilder( builder );
if (!RegOpenKeyExW( this_data_key->key, L"Tokens", 0, KEY_ALL_ACCESS, &tokenenum->key ))
if (!RegOpenKeyExW( this_data_key->key, L"Tokens", 0, KEY_ALL_ACCESS, &tokens_key ))
{
RegQueryInfoKeyW(tokenenum->key, NULL, NULL, NULL, &tokenenum->count, NULL, NULL,
NULL, NULL, NULL, NULL, NULL);
RegQueryInfoKeyW( tokens_key, NULL, NULL, NULL, &count, &max_subkey_size, NULL,
NULL, NULL, NULL, NULL, NULL );
max_subkey_size++;
root_len = wcslen( This->id );
token_id_size = root_len + sizeof("\\Tokens\\") + max_subkey_size;
token_id = malloc( token_id_size * sizeof(WCHAR) );
if (!token_id)
{
hr = E_OUTOFMEMORY;
goto fail;
}
root_len = swprintf( token_id, token_id_size, L"%ls%lsTokens\\",
This->id, This->id[root_len - 1] == L'\\' ? L"" : L"\\" );
for ( i = 0; i < count; i++ )
{
size = max_subkey_size;
hr = HRESULT_FROM_WIN32(RegEnumKeyExW( tokens_key, i, token_id + root_len, &size, NULL, NULL, NULL, NULL ));
if (FAILED(hr)) goto fail;
hr = token_create( NULL, &IID_ISpObjectToken, (void **)&token );
if (FAILED(hr)) goto fail;
hr = ISpObjectToken_SetId( token, NULL, token_id, FALSE );
if (FAILED(hr)) goto fail;
hr = ISpObjectTokenEnumBuilder_AddTokens( builder, 1, &token );
if (FAILED(hr)) goto fail;
ISpObjectToken_Release( token );
token = NULL;
}
hr = ISpObjectTokenEnumBuilder_Sort( builder, NULL );
if (FAILED(hr)) goto fail;
}
hr = ISpObjectTokenEnumBuilder_QueryInterface( builder, &IID_IEnumSpObjectTokens,
@ -612,6 +658,8 @@ static HRESULT WINAPI token_category_EnumTokens( ISpObjectTokenCategory *iface,
fail:
ISpObjectTokenEnumBuilder_Release( builder );
if ( token ) ISpObjectToken_Release( token );
free( token_id );
return hr;
}
@ -693,6 +741,7 @@ HRESULT token_category_create( IUnknown *outer, REFIID iid, void **obj )
This->ISpObjectTokenCategory_iface.lpVtbl = &token_category_vtbl;
This->ref = 1;
This->data_key = NULL;
This->id = NULL;
hr = ISpObjectTokenCategory_QueryInterface( &This->ISpObjectTokenCategory_iface, iid, obj );
@ -739,10 +788,16 @@ static ULONG WINAPI token_enum_Release( ISpObjectTokenEnumBuilder *iface )
if (!ref)
{
if (This->key)
RegCloseKey(This->key);
free( This->req );
free( This->opt );
if (This->tokens)
{
ULONG i;
for ( i = 0; i < This->count; i++ )
if ( This->tokens[i].token )
ISpObjectToken_Release( This->tokens[i].token );
free( This->tokens );
}
free( This );
}
@ -754,64 +809,23 @@ static HRESULT WINAPI token_enum_Next( ISpObjectTokenEnumBuilder *iface,
ULONG *fetched )
{
struct token_enum *This = impl_from_ISpObjectTokenEnumBuilder( iface );
struct object_token *object;
HRESULT hr;
DWORD retCode;
WCHAR *subkey_name;
HKEY sub_key;
DWORD size;
ISpRegDataKey *data_key;
ULONG i;
TRACE( "(%p)->(%lu %p %p)\n", This, num, tokens, fetched );
if (!This->init) return SPERR_UNINITIALIZED;
if (fetched) *fetched = 0;
if (!fetched && num != 1) return E_POINTER;
if (!tokens) return E_POINTER;
*tokens = NULL;
RegQueryInfoKeyW( This->key, NULL, NULL, NULL, NULL, &size, NULL, NULL, NULL, NULL, NULL, NULL );
size = (size+1) * sizeof(WCHAR);
subkey_name = malloc( size );
if (!subkey_name)
return E_OUTOFMEMORY;
retCode = RegEnumKeyExW( This->key, This->index, subkey_name, &size, NULL, NULL, NULL, NULL );
if (retCode != ERROR_SUCCESS)
for ( i = 0; i < num && This->index < This->count; i++, This->index++ )
{
free( subkey_name );
return S_FALSE;
ISpObjectToken_AddRef( This->tokens[This->index].token );
tokens[i] = This->tokens[This->index].token;
}
This->index++;
if (fetched) *fetched = i;
if (RegOpenKeyExW( This->key, subkey_name, 0, KEY_READ, &sub_key ) != ERROR_SUCCESS)
{
free( subkey_name );
return E_FAIL;
}
hr = create_data_key_with_hkey( sub_key, &data_key );
if (FAILED(hr))
{
free( subkey_name );
RegCloseKey( sub_key );
return hr;
}
hr = token_create( NULL, &IID_ISpObjectToken, (void**)tokens );
if (FAILED(hr))
{
free( subkey_name );
ISpRegDataKey_Release( data_key );
return hr;
}
object = impl_from_ISpObjectToken( *tokens );
object->data_key = data_key;
object->token_id = subkey_name;
if (fetched) *fetched = 1;
return hr;
return i == num ? S_OK : S_FALSE;
}
static HRESULT WINAPI token_enum_Skip( ISpObjectTokenEnumBuilder *iface,
@ -838,63 +852,18 @@ static HRESULT WINAPI token_enum_Item( ISpObjectTokenEnumBuilder *iface,
ULONG index, ISpObjectToken **token )
{
struct token_enum *This = impl_from_ISpObjectTokenEnumBuilder( iface );
struct object_token *object;
ISpObjectToken *subtoken;
HRESULT hr;
WCHAR *subkey;
DWORD size;
LONG ret;
HKEY key;
ISpRegDataKey *data_key;
TRACE( "%p, %lu, %p\n", This, index, token );
TRACE( "(%p)->(%lu %p)\n", This, index, token );
if (!This->init)
return SPERR_UNINITIALIZED;
if (!This->init) return SPERR_UNINITIALIZED;
RegQueryInfoKeyW(This->key, NULL, NULL, NULL, NULL, &size, NULL, NULL, NULL, NULL, NULL, NULL);
size = (size+1) * sizeof(WCHAR);
subkey = malloc( size );
if (!subkey)
return E_OUTOFMEMORY;
if (!token) return E_POINTER;
if (index >= This->count) return SPERR_NO_MORE_ITEMS;
ret = RegEnumKeyExW(This->key, index, subkey, &size, NULL, NULL, NULL, NULL);
if (ret != ERROR_SUCCESS)
{
free( subkey );
return HRESULT_FROM_WIN32(ret);
}
ISpObjectToken_AddRef( This->tokens[index].token );
*token = This->tokens[index].token;
ret = RegOpenKeyExW (This->key, subkey, 0, KEY_READ, &key);
if (ret != ERROR_SUCCESS)
{
free( subkey );
return HRESULT_FROM_WIN32(ret);
}
hr = create_data_key_with_hkey( key, &data_key );
if (FAILED(hr))
{
free( subkey );
RegCloseKey( key );
return hr;
}
hr = token_create( NULL, &IID_ISpObjectToken, (void**)&subtoken );
if (FAILED(hr))
{
free( subkey );
ISpRegDataKey_Release( data_key );
return hr;
}
object = impl_from_ISpObjectToken( subtoken );
object->data_key = data_key;
object->token_id = subkey;
*token = subtoken;
return hr;
return S_OK;
}
static HRESULT WINAPI token_enum_GetCount( ISpObjectTokenEnumBuilder *iface,
@ -939,11 +908,60 @@ out_of_mem:
return E_OUTOFMEMORY;
}
static BOOL grow_tokens_array( struct token_enum *This )
{
struct token_with_score *new_tokens;
ULONG new_cap;
if (This->count < This->capacity) return TRUE;
if (This->capacity > 0)
{
new_cap = This->capacity * 2;
new_tokens = realloc( This->tokens, new_cap * sizeof(*new_tokens) );
}
else
{
new_cap = 1;
new_tokens = malloc( sizeof(*new_tokens) );
}
if (!new_tokens) return FALSE;
This->tokens = new_tokens;
This->capacity = new_cap;
return TRUE;
}
static HRESULT WINAPI token_enum_AddTokens( ISpObjectTokenEnumBuilder *iface,
ULONG num, ISpObjectToken **tokens )
{
FIXME( "stub\n" );
return E_NOTIMPL;
struct token_enum *This = impl_from_ISpObjectTokenEnumBuilder( iface );
ULONG i;
TRACE( "(%p)->(%lu %p)\n", iface, num, tokens );
if (!This->init) return SPERR_UNINITIALIZED;
if (!tokens) return E_POINTER;
if (This->req || This->opt)
{
FIXME( "filtering and sorting of tokens is not implemented\n" );
return E_NOTIMPL;
}
for ( i = 0; i < num; i++ )
{
if (!tokens[i]) return E_POINTER;
if (!grow_tokens_array( This )) return E_OUTOFMEMORY;
ISpObjectToken_AddRef( tokens[i] );
This->tokens[This->count].token = tokens[i];
This->tokens[This->count].score = 0;
This->count++;
}
return S_OK;
}
static HRESULT WINAPI token_enum_AddTokensFromDataKey( ISpObjectTokenEnumBuilder *iface,
@ -964,8 +982,26 @@ static HRESULT WINAPI token_enum_AddTokensFromTokenEnum( ISpObjectTokenEnumBuild
static HRESULT WINAPI token_enum_Sort( ISpObjectTokenEnumBuilder *iface,
LPCWSTR first )
{
FIXME( "stub\n" );
return E_NOTIMPL;
struct token_enum *This = impl_from_ISpObjectTokenEnumBuilder( iface );
FIXME( "(%p)->(%s): semi-stub\n", iface, debugstr_w(first) );
if (!This->init) return SPERR_UNINITIALIZED;
if (!This->tokens) return S_OK;
if (first)
{
FIXME( "first != NULL is not implemented.\n" );
return E_NOTIMPL;
}
if (This->opt)
{
FIXME( "sorting with optional attributes is not implemented.\n" );
return E_NOTIMPL;
}
return S_OK;
}
const struct ISpObjectTokenEnumBuilderVtbl token_enum_vtbl =
@ -997,8 +1033,8 @@ HRESULT token_enum_create( IUnknown *outer, REFIID iid, void **obj )
This->req = NULL;
This->opt = NULL;
This->init = FALSE;
This->count = 0;
This->key = NULL;
This->tokens = NULL;
This->capacity = This->count = 0;
This->index = 0;
hr = ISpObjectTokenEnumBuilder_QueryInterface( &This->ISpObjectTokenEnumBuilder_iface, iid, obj );