diff --git a/dlls/sapi/main.c b/dlls/sapi/main.c index e8d4ae01583..9c644001faa 100644 --- a/dlls/sapi/main.c +++ b/dlls/sapi/main.c @@ -110,6 +110,7 @@ static struct class_factory file_stream_cf = { { &class_factory_vtbl }, file_ static struct class_factory speech_voice_cf = { { &class_factory_vtbl }, speech_voice_create }; static struct class_factory token_category_cf = { { &class_factory_vtbl }, token_category_create }; static struct class_factory token_enum_cf = { { &class_factory_vtbl }, token_enum_create }; +static struct class_factory token_cf = { { &class_factory_vtbl }, token_create }; /****************************************************************** * DllGetClassObject @@ -128,6 +129,8 @@ HRESULT WINAPI DllGetClassObject( REFCLSID clsid, REFIID iid, void **obj ) cf = &token_category_cf.IClassFactory_iface; else if (IsEqualCLSID( clsid, &CLSID_SpObjectTokenEnum )) cf = &token_enum_cf.IClassFactory_iface; + else if (IsEqualCLSID( clsid, &CLSID_SpObjectToken )) + cf = &token_cf.IClassFactory_iface; else if (IsEqualCLSID( clsid, &CLSID_SpVoice )) cf = &speech_voice_cf.IClassFactory_iface; diff --git a/dlls/sapi/sapi_classes.idl b/dlls/sapi/sapi_classes.idl index ed86d166fcd..082244e0812 100644 --- a/dlls/sapi/sapi_classes.idl +++ b/dlls/sapi/sapi_classes.idl @@ -51,6 +51,19 @@ coclass SpObjectTokenEnum [default] interface IEnumSpObjectTokens; } +[ + uuid(ef411752-3736-4cb4-9c8c-8ef4ccb58efe), + helpstring("Object Token"), + progid("SAPI.SpObjectToken.1"), + vi_progid("SAPI.SpObjectToken"), + threading(both) +] +coclass SpObjectToken +{ + interface ISpObjectToken; + [default] interface ISpDataKey; +} + [ uuid(96749377-3391-11d2-9ee3-00c04f797396), helpstring("Speech Voice"), diff --git a/dlls/sapi/sapi_private.h b/dlls/sapi/sapi_private.h index e9fed0ea443..414dea2755a 100644 --- a/dlls/sapi/sapi_private.h +++ b/dlls/sapi/sapi_private.h @@ -25,6 +25,7 @@ HRESULT file_stream_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_H HRESULT speech_voice_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_HIDDEN; HRESULT token_category_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_HIDDEN; HRESULT token_enum_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_HIDDEN; +HRESULT token_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_HIDDEN; static inline LPWSTR heap_strdupW(LPCWSTR str) { diff --git a/dlls/sapi/tests/token.c b/dlls/sapi/tests/token.c index 58d69920fd5..887539778a9 100644 --- a/dlls/sapi/tests/token.c +++ b/dlls/sapi/tests/token.c @@ -174,6 +174,130 @@ static void test_default_token_id(void) ISpObjectTokenCategory_Release( cat ); } +static void test_object_token(void) +{ + ISpObjectToken *token; + HRESULT hr; + LPWSTR tempW, token_id; + ISpObjectTokenCategory *cat; + + hr = CoCreateInstance( &CLSID_SpObjectToken, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectToken, (void **)&token ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = ISpObjectToken_GetId( token, NULL ); + todo_wine ok( hr == SPERR_UNINITIALIZED, "got %08x\n", hr ); + + tempW = (LPWSTR)0xdeadbeef; + hr = ISpObjectToken_GetId( token, &tempW ); + todo_wine ok( hr == SPERR_UNINITIALIZED, "got %08x\n", hr ); + ok( tempW == (LPWSTR)0xdeadbeef, "got %s\n", wine_dbgstr_w(tempW) ); + + hr = ISpObjectToken_GetCategory( token, NULL ); + todo_wine ok( hr == SPERR_UNINITIALIZED, "got %08x\n", hr ); + + cat = (LPVOID)0xdeadbeef; + hr = ISpObjectToken_GetCategory( token, &cat ); + todo_wine ok( hr == SPERR_UNINITIALIZED, "got %08x\n", hr ); + ok( cat == (LPVOID)0xdeadbeef, "got %p\n", cat ); + + hr = ISpObjectToken_SetId( token, NULL, NULL, FALSE ); + todo_wine ok( hr == E_POINTER, "got %08x\n", hr ); + hr = ISpObjectToken_SetId( token, L"bogus", NULL, FALSE ); + todo_wine ok( hr == E_POINTER, "got %08x\n", hr ); + + hr = ISpObjectToken_SetId( token, NULL, L"bogus", FALSE ); + todo_wine ok( hr == SPERR_NOT_FOUND, "got %08x\n", hr ); + hr = ISpObjectToken_SetId( token, NULL, L"HKEY_LOCAL_MACHINE\\SOFTWARE\\winetest bogus", FALSE ); + todo_wine ok( hr == SPERR_NOT_FOUND, "got %08x\n", hr ); + + /* SetId succeeds even if the key is invalid, but exists */ + hr = ISpObjectToken_SetId( token, NULL, L"HKEY_LOCAL_MACHINE\\SOFTWARE", FALSE ); + todo_wine ok( hr == S_OK, "got %08x\n", hr ); + + hr = ISpObjectToken_SetId( token, NULL, NULL, FALSE ); + todo_wine ok( hr == SPERR_ALREADY_INITIALIZED, "got %08x\n", hr ); + hr = ISpObjectToken_SetId( token, NULL, L"bogus", FALSE ); + todo_wine ok( hr == SPERR_ALREADY_INITIALIZED, "got %08x\n", hr ); + + hr = ISpObjectToken_GetId( token, NULL ); + todo_wine ok( hr == E_POINTER, "got %08x\n", hr ); + + hr = ISpObjectToken_GetCategory( token, NULL ); + todo_wine ok( hr == E_POINTER, "got %08x\n", hr ); + + tempW = NULL; + hr = ISpObjectToken_GetId( token, &tempW ); + todo_wine ok( hr == S_OK, "got %08x\n", hr ); + todo_wine ok( tempW != NULL, "got %p\n", tempW ); + if (tempW) { + ok( !wcscmp(tempW, L"HKEY_LOCAL_MACHINE\\SOFTWARE"), "got %s\n", + wine_dbgstr_w(tempW) ); + CoTaskMemFree( tempW ); + } + + cat = (LPVOID)0xdeadbeef; + hr = ISpObjectToken_GetCategory( token, &cat ); + todo_wine ok( hr == SPERR_INVALID_REGISTRY_KEY, "got %08x\n", hr ); + ok( cat == (LPVOID)0xdeadbeef, "got %p\n", cat ); + + /* get the default token id for SPCAT_AUDIOOUT */ + hr = CoCreateInstance( &CLSID_SpObjectTokenCategory, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectTokenCategory, (void **)&cat ); + ok( hr == S_OK, "got %08x\n", hr ); + hr = ISpObjectTokenCategory_SetId( cat, SPCAT_AUDIOOUT, FALSE ); + ok( hr == S_OK, "got %08x\n", hr ); + token_id = (LPWSTR)0xdeadbeef; + hr = ISpObjectTokenCategory_GetDefaultTokenId( cat, &token_id ); + if (hr == SPERR_NOT_FOUND) { + skip( "AudioOutput category not found for GetDefaultTokenId\n" ); + return; + } + ok( hr == S_OK, "got %08x\n", hr ); + ok( token_id != (LPWSTR)0xdeadbeef && token_id != NULL, "got %p\n", token_id ); + ISpObjectTokenCategory_Release( cat ); + + /* recreate token in order to SetId again */ + ISpObjectToken_Release( token ); + hr = CoCreateInstance( &CLSID_SpObjectToken, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectToken, (void **)&token ); + ok( hr == S_OK, "got %08x\n", hr ); + + /* NULL appears to auto-detect the category */ + hr = ISpObjectToken_SetId( token, NULL, token_id, FALSE ); + todo_wine ok( hr == S_OK, "got %08x\n", hr ); + + tempW = NULL; + hr = ISpObjectToken_GetId( token, &tempW ); + todo_wine ok( hr == S_OK, "got %08x\n", hr ); + todo_wine ok( tempW != NULL, "got %p\n", tempW ); + if (tempW) { + ok( !wcsncmp(tempW, token_id, wcslen(token_id)), + "got %s (expected %s)\n", wine_dbgstr_w(tempW), wine_dbgstr_w(token_id) ); + CoTaskMemFree( tempW ); + } + + cat = (LPVOID)0xdeadbeef; + hr = ISpObjectToken_GetCategory( token, &cat ); + todo_wine ok( hr == S_OK, "got %08x\n", hr ); + todo_wine ok( cat != (LPVOID)0xdeadbeef, "got %p\n", cat ); + if (cat != (LPVOID)0xdeadbeef) { + tempW = NULL; + hr = ISpObjectTokenCategory_GetId( cat, &tempW ); + todo_wine ok( hr == S_OK, "got %08x\n", hr ); + todo_wine ok( tempW != NULL, "got %p\n", tempW ); + if (tempW) { + ok( !wcscmp(tempW, SPCAT_AUDIOOUT), "got %s\n", wine_dbgstr_w(tempW) ); + CoTaskMemFree( tempW ); + } + + /* not freed by ISpObjectToken_Release */ + ISpObjectTokenCategory_Release( cat ); + } + + ISpObjectToken_Release( token ); +} + START_TEST(token) { CoInitialize( NULL ); @@ -181,5 +305,6 @@ START_TEST(token) test_token_category(); test_token_enum(); test_default_token_id(); + test_object_token(); CoUninitialize(); } diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index 21cd958282f..f7a00d73f02 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -776,3 +776,279 @@ HRESULT token_enum_create( IUnknown *outer, REFIID iid, void **obj ) ISpObjectTokenEnumBuilder_Release( &This->ISpObjectTokenEnumBuilder_iface ); return hr; } + +struct object_token +{ + ISpObjectToken ISpObjectToken_iface; + LONG ref; +}; + +static struct object_token *impl_from_ISpObjectToken( ISpObjectToken *iface ) +{ + return CONTAINING_RECORD( iface, struct object_token, ISpObjectToken_iface ); +} + +static HRESULT WINAPI token_QueryInterface( ISpObjectToken *iface, + REFIID iid, void **obj ) +{ + struct object_token *This = impl_from_ISpObjectToken( iface ); + + TRACE( "(%p)->(%s %p)\n", This, debugstr_guid( iid ), obj ); + + if (IsEqualIID( iid, &IID_IUnknown ) || + IsEqualIID( iid, &IID_ISpDataKey ) || + IsEqualIID( iid, &IID_ISpObjectToken )) + { + ISpObjectToken_AddRef( iface ); + *obj = iface; + return S_OK; + } + + FIXME( "interface %s not implemented\n", debugstr_guid( iid ) ); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI token_AddRef( ISpObjectToken *iface ) +{ + struct object_token *This = impl_from_ISpObjectToken( iface ); + ULONG ref = InterlockedIncrement( &This->ref ); + + TRACE( "(%p) ref = %u\n", This, ref ); + return ref; +} + +static ULONG WINAPI token_Release( ISpObjectToken *iface ) +{ + struct object_token *This = impl_from_ISpObjectToken( iface ); + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE( "(%p) ref = %u\n", This, ref ); + + if (!ref) + { + heap_free( This ); + } + + return ref; +} + +static HRESULT WINAPI token_SetData( ISpObjectToken *iface, + LPCWSTR name, ULONG size, + const BYTE *data ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_GetData( ISpObjectToken *iface, + LPCWSTR name, ULONG *size, + BYTE *data ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_SetStringValue( ISpObjectToken *iface, + LPCWSTR name, LPCWSTR value ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_GetStringValue( ISpObjectToken *iface, + LPCWSTR name, LPWSTR *value ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_SetDWORD( ISpObjectToken *iface, + LPCWSTR name, DWORD value ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_GetDWORD( ISpObjectToken *iface, + LPCWSTR name, DWORD *pdwValue ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_OpenKey( ISpObjectToken *iface, + LPCWSTR name, ISpDataKey **sub_key ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_CreateKey( ISpObjectToken *iface, + LPCWSTR name, ISpDataKey **sub_key ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_DeleteKey( ISpObjectToken *iface, + LPCWSTR name ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_DeleteValue( ISpObjectToken *iface, + LPCWSTR name ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_EnumKeys( ISpObjectToken *iface, + ULONG index, LPWSTR *sub_key ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_EnumValues( ISpObjectToken *iface, + ULONG index, LPWSTR *value ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_SetId( ISpObjectToken *iface, + LPCWSTR category_id, LPCWSTR token_id, + BOOL create ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_GetId( ISpObjectToken *iface, + LPWSTR *token_id ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_GetCategory( ISpObjectToken *iface, + ISpObjectTokenCategory **category ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_CreateInstance( ISpObjectToken *iface, + IUnknown *outer, + DWORD class_context, + REFIID riid, + void **object ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_GetStorageFileName( ISpObjectToken *iface, + REFCLSID caller, + LPCWSTR key_name, + LPCWSTR filename, + ULONG folder, + LPWSTR *filepath ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_RemoveStorageFileName( ISpObjectToken *iface, + REFCLSID caller, + LPCWSTR key_name, + BOOL delete_file ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_Remove( ISpObjectToken *iface, + REFCLSID caller ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_IsUISupported( ISpObjectToken *iface, + LPCWSTR ui_type, + void *extra_data, + ULONG extra_data_size, + IUnknown *object, + BOOL *supported ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_DisplayUI( ISpObjectToken *iface, + HWND parent, + LPCWSTR title, + LPCWSTR ui_type, + void *extra_data, + ULONG extra_data_size, + IUnknown *object ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +static HRESULT WINAPI token_MatchesAttributes( ISpObjectToken *iface, + LPCWSTR attributes, + BOOL *matches ) +{ + FIXME( "stub\n" ); + return E_NOTIMPL; +} + +const struct ISpObjectTokenVtbl token_vtbl = +{ + token_QueryInterface, + token_AddRef, + token_Release, + token_SetData, + token_GetData, + token_SetStringValue, + token_GetStringValue, + token_SetDWORD, + token_GetDWORD, + token_OpenKey, + token_CreateKey, + token_DeleteKey, + token_DeleteValue, + token_EnumKeys, + token_EnumValues, + token_SetId, + token_GetId, + token_GetCategory, + token_CreateInstance, + token_GetStorageFileName, + token_RemoveStorageFileName, + token_Remove, + token_IsUISupported, + token_DisplayUI, + token_MatchesAttributes +}; + +HRESULT token_create( IUnknown *outer, REFIID iid, void **obj ) +{ + struct object_token *This = heap_alloc( sizeof(*This) ); + HRESULT hr; + + if (!This) return E_OUTOFMEMORY; + This->ISpObjectToken_iface.lpVtbl = &token_vtbl; + This->ref = 1; + + hr = ISpObjectToken_QueryInterface( &This->ISpObjectToken_iface, iid, obj ); + + ISpObjectToken_Release( &This->ISpObjectToken_iface ); + return hr; +} diff --git a/include/sapi.idl b/include/sapi.idl index 9e6ba5be5ea..0a7a034cace 100644 --- a/include/sapi.idl +++ b/include/sapi.idl @@ -1064,6 +1064,19 @@ library SpeechLib interface ISpObjectTokenCategory; } + [ + uuid(ef411752-3736-4cb4-9c8c-8ef4ccb58efe), + helpstring("Object Token"), + progid("SAPI.SpObjectToken.1"), + vi_progid("SAPI.SpObjectToken"), + threading(both) + ] + coclass SpObjectToken + { + interface ISpObjectToken; + [default] interface ISpDataKey; + } + [ uuid(3bee4890-4fe9-4a37-8c1e-5e7e12791c1f) ]