[PATCH v2 2/2] windows.media.speech: Add stub SpeechSynthesizer class.

Paul Gofman pgofman at codeweavers.com
Tue Nov 23 14:02:47 CST 2021


Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
v2:
    - use calloc / free instead of heap helpers;
    - validate supported class name in DllGetActivationFactory and add a test for that;
    - also add stub IClosable interface and test querying it.

 dlls/windows.media.speech/main.c         | 244 ++++++++++++++++++++++-
 dlls/windows.media.speech/tests/speech.c |  73 ++++++-
 2 files changed, 311 insertions(+), 6 deletions(-)

diff --git a/dlls/windows.media.speech/main.c b/dlls/windows.media.speech/main.c
index e34fa9d16b8..d23b8686f69 100644
--- a/dlls/windows.media.speech/main.c
+++ b/dlls/windows.media.speech/main.c
@@ -171,6 +171,226 @@ static struct voice_information_vector all_voices =
     0
 };
 
+struct speech_synthesizer
+{
+    ISpeechSynthesizer ISpeechSynthesizer_iface;
+    IClosable IClosable_iface;
+    LONG ref;
+};
+
+static inline struct speech_synthesizer *impl_from_ISpeechSynthesizer(ISpeechSynthesizer *iface)
+{
+    return CONTAINING_RECORD(iface, struct speech_synthesizer, ISpeechSynthesizer_iface);
+}
+
+static inline struct speech_synthesizer *impl_from_IClosable(IClosable *iface)
+{
+    return CONTAINING_RECORD(iface, struct speech_synthesizer, IClosable_iface);
+}
+
+static HRESULT STDMETHODCALLTYPE speech_synthesizer_QueryInterface(
+        ISpeechSynthesizer *iface, REFIID iid, void **out)
+{
+    struct speech_synthesizer *impl = impl_from_ISpeechSynthesizer(iface);
+
+    TRACE("iface %p, iid %s, out %p stub!\n", iface, debugstr_guid(iid), out);
+
+    if (IsEqualGUID(iid, &IID_IUnknown) ||
+        IsEqualGUID(iid, &IID_IInspectable) ||
+        IsEqualGUID(iid, &IID_ISpeechSynthesizer))
+    {
+        IUnknown_AddRef(iface);
+        *out = iface;
+        return S_OK;
+    }
+
+    if (IsEqualGUID(iid, &IID_IClosable))
+    {
+        IUnknown_AddRef(iface);
+        *out = &impl->IClosable_iface;
+        return S_OK;
+    }
+
+    FIXME("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid));
+    *out = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG STDMETHODCALLTYPE speech_synthesizer_AddRef(
+        ISpeechSynthesizer *iface)
+{
+    struct speech_synthesizer *impl = impl_from_ISpeechSynthesizer(iface);
+    ULONG ref = InterlockedIncrement(&impl->ref);
+
+    TRACE("iface %p, ref %u.\n", iface, ref);
+
+    return ref;
+}
+
+static ULONG STDMETHODCALLTYPE speech_synthesizer_Release(
+        ISpeechSynthesizer *iface)
+{
+    struct speech_synthesizer *impl = impl_from_ISpeechSynthesizer(iface);
+    ULONG ref = InterlockedDecrement(&impl->ref);
+
+    TRACE("iface %p, ref %u.\n", iface, ref);
+
+    if (!ref)
+        free(impl);
+
+    return ref;
+}
+
+static HRESULT STDMETHODCALLTYPE speech_synthesizer_GetIids(
+        ISpeechSynthesizer *iface, ULONG *iid_count, IID **iids)
+{
+    FIXME("iface %p, iid_count %p, iids %p stub.\n", iface, iid_count, iids);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE speech_synthesizer_GetRuntimeClassName(
+        ISpeechSynthesizer *iface, HSTRING *class_name)
+{
+    FIXME("iface %p, class_name %p stub.\n", iface, class_name);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE speech_synthesizer_GetTrustLevel(
+        ISpeechSynthesizer *iface, TrustLevel *trust_level)
+{
+    FIXME("iface %p, trust_level %p stub.\n", iface, trust_level);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE speech_synthesizer_SynthesizeTextToStreamAsync(ISpeechSynthesizer *iface,
+        HSTRING text, IAsyncOperation_SpeechSynthesisStream **operation)
+{
+    FIXME("iface %p, text %p, operation %p stub.\n", iface, text, operation);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE speech_synthesizer_SynthesizeSsmlToStreamAsync(ISpeechSynthesizer *iface,
+        HSTRING ssml, IAsyncOperation_SpeechSynthesisStream **operation)
+{
+    FIXME("iface %p, text %p, operation %p stub.\n", iface, ssml, operation);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE speech_synthesizer_put_Voice(ISpeechSynthesizer *iface, IVoiceInformation *value)
+{
+    FIXME("iface %p, value %p stub.\n", iface, value);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE speech_synthesizer_get_Voice(ISpeechSynthesizer *iface, IVoiceInformation **value)
+{
+    FIXME("iface %p, value %p stub.\n", iface, value);
+
+    return E_NOTIMPL;
+}
+
+static const struct ISpeechSynthesizerVtbl speech_synthesizer_vtbl =
+{
+    /* IUnknown methods */
+    speech_synthesizer_QueryInterface,
+    speech_synthesizer_AddRef,
+    speech_synthesizer_Release,
+    /* IInspectable methods */
+    speech_synthesizer_GetIids,
+    speech_synthesizer_GetRuntimeClassName,
+    speech_synthesizer_GetTrustLevel,
+    /* ISpeechSynthesizer methods */
+    speech_synthesizer_SynthesizeTextToStreamAsync,
+    speech_synthesizer_SynthesizeSsmlToStreamAsync,
+    speech_synthesizer_put_Voice,
+    speech_synthesizer_get_Voice,
+};
+
+static HRESULT STDMETHODCALLTYPE closable_QueryInterface(
+        IClosable *iface, REFIID iid, void **out)
+{
+    struct speech_synthesizer *impl = impl_from_IClosable(iface);
+
+    return speech_synthesizer_QueryInterface(&impl->ISpeechSynthesizer_iface, iid, out);
+}
+
+static ULONG STDMETHODCALLTYPE closable_AddRef(
+        IClosable *iface)
+{
+    struct speech_synthesizer *impl = impl_from_IClosable(iface);
+    ULONG ref = InterlockedIncrement(&impl->ref);
+
+    TRACE("iface %p, ref %u.\n", iface, ref);
+
+    return ref;
+}
+
+static ULONG STDMETHODCALLTYPE closable_Release(
+        IClosable *iface)
+{
+    struct speech_synthesizer *impl = impl_from_IClosable(iface);
+    ULONG ref = InterlockedDecrement(&impl->ref);
+
+    TRACE("iface %p, ref %u.\n", iface, ref);
+
+    if (!ref)
+        free(impl);
+
+    return ref;
+}
+
+static HRESULT STDMETHODCALLTYPE closable_GetIids(
+        IClosable *iface, ULONG *iid_count, IID **iids)
+{
+    FIXME("iface %p, iid_count %p, iids %p stub.\n", iface, iid_count, iids);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE closable_GetRuntimeClassName(
+        IClosable *iface, HSTRING *class_name)
+{
+    FIXME("iface %p, class_name %p stub.\n", iface, class_name);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE closable_GetTrustLevel(
+        IClosable *iface, TrustLevel *trust_level)
+{
+    FIXME("iface %p, trust_level %p stub.\n", iface, trust_level);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE closable_Close(
+        IClosable *iface)
+{
+    FIXME("iface %p stub.\n", iface);
+
+    return E_NOTIMPL;
+}
+
+static const struct IClosableVtbl closable_vtbl =
+{
+    /* IUnknown methods */
+    closable_QueryInterface,
+    closable_AddRef,
+    closable_Release,
+    /* IInspectable methods */
+    closable_GetIids,
+    closable_GetRuntimeClassName,
+    closable_GetTrustLevel,
+    /* IClosable methods */
+    closable_Close,
+};
+
 struct windows_media_speech
 {
     IActivationFactory IActivationFactory_iface;
@@ -259,8 +479,21 @@ static HRESULT STDMETHODCALLTYPE windows_media_speech_GetTrustLevel(
 static HRESULT STDMETHODCALLTYPE windows_media_speech_ActivateInstance(
         IActivationFactory *iface, IInspectable **instance)
 {
-    FIXME("iface %p, instance %p stub!\n", iface, instance);
-    return E_NOTIMPL;
+    struct speech_synthesizer *obj;
+
+    TRACE("iface %p, instance %p.\n", iface, instance);
+
+    if (!(obj = calloc(1, sizeof(*obj))))
+    {
+        *instance = NULL;
+        return E_OUTOFMEMORY;
+    }
+
+    obj->ISpeechSynthesizer_iface.lpVtbl = &speech_synthesizer_vtbl;
+    obj->IClosable_iface.lpVtbl = &closable_vtbl;
+    obj->ref = 1;
+    *instance = (IInspectable *)&obj->ISpeechSynthesizer_iface;
+    return S_OK;
 }
 
 static const struct IActivationFactoryVtbl activation_factory_vtbl =
@@ -364,6 +597,13 @@ HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **out)
 HRESULT WINAPI DllGetActivationFactory(HSTRING classid, IActivationFactory **factory)
 {
     TRACE("classid %s, factory %p.\n", debugstr_hstring(classid), factory);
+
+    if (lstrcmpW(WindowsGetStringRawBuffer(classid, NULL), L"Windows.Media.SpeechSynthesis.SpeechSynthesizer"))
+    {
+        ERR("Unknown classid %s.\n", debugstr_hstring(classid));
+        return CLASS_E_CLASSNOTAVAILABLE;
+    }
+
     *factory = &windows_media_speech.IActivationFactory_iface;
     IUnknown_AddRef(*factory);
     return S_OK;
diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c
index c949b35900b..38fd5c90dd0 100644
--- a/dlls/windows.media.speech/tests/speech.c
+++ b/dlls/windows.media.speech/tests/speech.c
@@ -34,19 +34,26 @@
 
 #include "wine/test.h"
 
+HRESULT WINAPI (*pDllGetActivationFactory)(HSTRING, IActivationFactory **);
+
 static void test_SpeechSynthesizer(void)
 {
     static const WCHAR *speech_synthesizer_name = L"Windows.Media.SpeechSynthesis.SpeechSynthesizer";
-
+    static const WCHAR *speech_synthesizer_name2 = L"windows.media.speechsynthesis.speechsynthesizer";
+    static const WCHAR *unknown_class_name = L"Unknown.Class";
+    IActivationFactory *factory = NULL, *factory2 = NULL;
     IVectorView_VoiceInformation *voices = NULL;
     IInstalledVoicesStatic *voices_static = NULL;
-    IActivationFactory *factory = NULL;
     IVoiceInformation *voice;
     IInspectable *inspectable = NULL, *tmp_inspectable = NULL;
     IAgileObject *agile_object = NULL, *tmp_agile_object = NULL;
-    HSTRING str;
+    ISpeechSynthesizer *synthesizer;
+    IClosable *closable;
+    HMODULE hdll;
+    HSTRING str, str2;
     HRESULT hr;
     UINT32 size;
+    ULONG ref;
 
     hr = RoInitialize(RO_INIT_MULTITHREADED);
     ok(hr == S_OK, "RoInitialize failed, hr %#x\n", hr);
@@ -54,9 +61,46 @@ static void test_SpeechSynthesizer(void)
     hr = WindowsCreateString(speech_synthesizer_name, wcslen(speech_synthesizer_name), &str);
     ok(hr == S_OK, "WindowsCreateString failed, hr %#x\n", hr);
 
+    hdll = LoadLibraryW(L"windows.media.speech.dll");
+    if (hdll)
+    {
+        pDllGetActivationFactory = (void *)GetProcAddress(hdll, "DllGetActivationFactory");
+        ok(!!pDllGetActivationFactory, "DllGetActivationFactory not found.\n");
+
+        hr = WindowsCreateString(unknown_class_name, wcslen(unknown_class_name), &str2);
+        ok(hr == S_OK, "WindowsCreateString failed, hr %#x\n", hr);
+
+        hr = pDllGetActivationFactory(str2, &factory);
+        ok(hr == CLASS_E_CLASSNOTAVAILABLE, "Got unexpected hr %#x.\n", hr);
+
+        WindowsDeleteString(str2);
+
+        hr = WindowsCreateString(speech_synthesizer_name2, wcslen(speech_synthesizer_name2), &str2);
+        ok(hr == S_OK, "WindowsCreateString failed, hr %#x\n", hr);
+
+        hr = pDllGetActivationFactory(str2, &factory2);
+        ok(hr == CLASS_E_CLASSNOTAVAILABLE, "Got unexpected hr %#x.\n", hr);
+
+        WindowsDeleteString(str2);
+
+        hr = pDllGetActivationFactory(str, &factory2);
+        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+    }
+    else
+    {
+        win_skip("Failed to load library, err %u.\n", GetLastError());
+    }
+
     hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory);
     ok(hr == S_OK, "RoGetActivationFactory failed, hr %#x\n", hr);
 
+    if (hdll)
+    {
+        ok(factory == factory2, "Got unexpected factory %p, factory2 %p.\n", factory, factory2);
+        IActivationFactory_Release(factory2);
+        FreeLibrary(hdll);
+    }
+
     hr = IActivationFactory_QueryInterface(factory, &IID_IInspectable, (void **)&inspectable);
     ok(hr == S_OK, "IActivationFactory_QueryInterface IID_IInspectable failed, hr %#x\n", hr);
 
@@ -107,8 +151,29 @@ static void test_SpeechSynthesizer(void)
 
     IAgileObject_Release(agile_object);
     IInspectable_Release(inspectable);
-    IActivationFactory_Release(factory);
 
+    hr = IActivationFactory_QueryInterface(factory, &IID_ISpeechSynthesizer, (void **)&synthesizer);
+    ok(hr == E_NOINTERFACE, "Got unexpected hr %#x.\n", hr);
+
+    hr = RoActivateInstance(str, &inspectable);
+    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+
+    hr = IInspectable_QueryInterface(inspectable, &IID_ISpeechSynthesizer, (void **)&synthesizer);
+    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+
+    hr = IInspectable_QueryInterface(inspectable, &IID_IClosable, (void **)&closable);
+    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+
+    ref = IClosable_Release(closable);
+    ok(ref == 2, "Got unexpected ref %u.\n", ref);
+
+    ref = ISpeechSynthesizer_Release(synthesizer);
+    ok(ref == 1, "Got unexpected ref %u.\n", ref);
+
+    ref = IInspectable_Release(inspectable);
+    ok(!ref, "Got unexpected ref %u.\n", ref);
+
+    IActivationFactory_Release(factory);
     WindowsDeleteString(str);
 
     RoUninitialize();
-- 
2.33.1




More information about the wine-devel mailing list