-- v6: msttsengine: Add ISpTTSEngine stub. msttsengine: Add stub dll.
From: Shaun Ren sren@codeweavers.com
--- configure.ac | 1 + dlls/msttsengine/Makefile.in | 9 ++ dlls/msttsengine/main.c | 123 +++++++++++++++++++++++ dlls/msttsengine/msttsengine.spec | 4 + dlls/msttsengine/msttsengine_classes.idl | 32 ++++++ dlls/msttsengine/msttsengine_private.h | 35 +++++++ dlls/msttsengine/tts.c | 31 ++++++ loader/wine.inf.in | 2 + 8 files changed, 237 insertions(+) create mode 100644 dlls/msttsengine/Makefile.in create mode 100644 dlls/msttsengine/main.c create mode 100644 dlls/msttsengine/msttsengine.spec create mode 100644 dlls/msttsengine/msttsengine_classes.idl create mode 100644 dlls/msttsengine/msttsengine_private.h create mode 100644 dlls/msttsengine/tts.c
diff --git a/configure.ac b/configure.ac index 7229e25dbed..266be3542ee 100644 --- a/configure.ac +++ b/configure.ac @@ -2804,6 +2804,7 @@ WINE_CONFIG_MAKEFILE(dlls/mssign32) WINE_CONFIG_MAKEFILE(dlls/mssip32) WINE_CONFIG_MAKEFILE(dlls/mstask) WINE_CONFIG_MAKEFILE(dlls/mstask/tests) +WINE_CONFIG_MAKEFILE(dlls/msttsengine) WINE_CONFIG_MAKEFILE(dlls/msv1_0) WINE_CONFIG_MAKEFILE(dlls/msvcirt) WINE_CONFIG_MAKEFILE(dlls/msvcirt/tests) diff --git a/dlls/msttsengine/Makefile.in b/dlls/msttsengine/Makefile.in new file mode 100644 index 00000000000..2949fced4d2 --- /dev/null +++ b/dlls/msttsengine/Makefile.in @@ -0,0 +1,9 @@ +MODULE = msttsengine.dll +IMPORTS = ole32 + +C_SRCS = \ + main.c \ + tts.c + +IDL_SRCS = \ + msttsengine_classes.idl diff --git a/dlls/msttsengine/main.c b/dlls/msttsengine/main.c new file mode 100644 index 00000000000..4dbf448cfa8 --- /dev/null +++ b/dlls/msttsengine/main.c @@ -0,0 +1,123 @@ +/* MSTTSEngine main file. + * + * Copyright 2023 Shaun Ren for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdarg.h> + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "initguid.h" +#include "objbase.h" +#include "sapiddk.h" + +#include "wine/debug.h" + +#include "msttsengine_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msttsengine); + +BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) +{ + if (reason == DLL_PROCESS_ATTACH) + { + DisableThreadLibraryCalls(instance); + __wine_init_unix_call(); + } + return TRUE; +} + +struct class_factory +{ + IClassFactory IClassFactory_iface; + HRESULT (*create_instance)(REFIID iid, void **out); +}; + +static inline struct class_factory *impl_from_IClassFactory(IClassFactory *iface) +{ + return CONTAINING_RECORD(iface, struct class_factory, IClassFactory_iface); +} + +static HRESULT WINAPI class_factory_QueryInterface(IClassFactory *iface, REFIID iid, void **out) +{ + TRACE("(%p, %s, %p).\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IClassFactory)) + { + *out = iface; + IClassFactory_AddRef(iface); + return S_OK; + } + + WARN("%s not implemented.\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI class_factory_AddRef(IClassFactory *iface) +{ + return 2; +} + +static ULONG WINAPI class_factory_Release(IClassFactory *iface) +{ + return 1; +} + +static HRESULT WINAPI class_factory_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID iid, void **obj) +{ + struct class_factory *This = impl_from_IClassFactory(iface); + + TRACE("(%p, %p, %s, %p).\n", iface, outer, debugstr_guid(iid), obj); + + *obj = NULL; + if (outer) return CLASS_E_NOAGGREGATION; + return This->create_instance(iid, obj); +} + +static HRESULT WINAPI class_factory_LockServer(IClassFactory *iface, BOOL lock) +{ + FIXME("(%d): stub.\n", lock); + return S_OK; +} + +static const IClassFactoryVtbl class_factory_vtbl = +{ + class_factory_QueryInterface, + class_factory_AddRef, + class_factory_Release, + class_factory_CreateInstance, + class_factory_LockServer, +}; + +static struct class_factory ttsengine_cf = {{&class_factory_vtbl}, ttsengine_create}; + +HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void **obj) +{ + IClassFactory *cf; + + TRACE("(%s, %s, %p).\n", debugstr_guid(clsid), debugstr_guid(iid), obj); + + if (IsEqualCLSID(clsid, &CLSID_MSTTSEngine)) + cf = &ttsengine_cf.IClassFactory_iface; + else + return CLASS_E_CLASSNOTAVAILABLE; + + return IClassFactory_QueryInterface(cf, iid, obj); +} diff --git a/dlls/msttsengine/msttsengine.spec b/dlls/msttsengine/msttsengine.spec new file mode 100644 index 00000000000..b16365d0c9f --- /dev/null +++ b/dlls/msttsengine/msttsengine.spec @@ -0,0 +1,4 @@ +@ stdcall -private DllCanUnloadNow() +@ stdcall -private DllGetClassObject(ptr ptr ptr) +@ stdcall -private DllRegisterServer() +@ stdcall -private DllUnregisterServer() diff --git a/dlls/msttsengine/msttsengine_classes.idl b/dlls/msttsengine/msttsengine_classes.idl new file mode 100644 index 00000000000..dec2dd7d72e --- /dev/null +++ b/dlls/msttsengine/msttsengine_classes.idl @@ -0,0 +1,32 @@ +/* + * MSTTSEngine classes. + * + * Copyright 2023 Shaun Ren for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#pragma makedep register + +[ + uuid(c64501f6-e6e6-451f-a150-25d0839bc510), + helpstring("Microsoft SAPI/SpeechFX TTS Engine Class"), + threading(both) +] +coclass TTSEngineCom +{ + interface ISpTTSEngine; + interface ISpObjectWithToken; +}; diff --git a/dlls/msttsengine/msttsengine_private.h b/dlls/msttsengine/msttsengine_private.h new file mode 100644 index 00000000000..e6d71f260a8 --- /dev/null +++ b/dlls/msttsengine/msttsengine_private.h @@ -0,0 +1,35 @@ +/* + * MSTTSEngine private header file. + * + * Copyright 2023 Shaun Ren for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_MSTTSENGINE_PRIVATE_H +#define __WINE_MSTTSENGINE_PRIVATE_H + +#include <stdbool.h> +#include <stdint.h> +#include "windef.h" +#include "winternl.h" + +#include "wine/unixlib.h" + +DEFINE_GUID(CLSID_MSTTSEngine, 0xC64501F6,0xE6E6,0x451F,0xA1,0x50,0x25,0xD0,0x83,0x9B,0xC5,0x10); + +HRESULT ttsengine_create(REFIID iid, void **obj); + +#endif /* __WINE_MSTTSENGINE_PRIVATE_H */ diff --git a/dlls/msttsengine/tts.c b/dlls/msttsengine/tts.c new file mode 100644 index 00000000000..c5fac6800eb --- /dev/null +++ b/dlls/msttsengine/tts.c @@ -0,0 +1,31 @@ +/* + * MSTTSEngine SAPI engine implementation. + * + * Copyright 2023 Shaun Ren for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdarg.h> + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" + +HRESULT ttsengine_create(REFIID iid, void **obj) +{ + return E_NOTIMPL; +} diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 382808c4876..2ea4d0b25b4 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -2145,6 +2145,7 @@ HKLM,%CurrentVersion%\Telephony\Country List\998,"SameAreaRule",,"G" 11,mui, 11,gecko\plugin,npmshtml.dll 11,Speech\Common,sapi.dll +11,Speech\Engines\TTS,msttsengine.dll 11,wbem,mofcomp.exe 11,wbem,wbemdisp.dll 11,wbem,wbemprox.dll @@ -2230,6 +2231,7 @@ HKLM,%CurrentVersion%\Telephony\Country List\998,"SameAreaRule",,"G" 11,,shdocvw.dll 11,gecko\plugin,npmshtml.dll 11,Speech\Common,sapi.dll +11,Speech\Engines\TTS,msttsengine.dll 11,wbem,mofcomp.exe 11,wbem,wbemdisp.dll 11,wbem,wbemprox.dll
From: Shaun Ren sren@codeweavers.com
--- dlls/msttsengine/tts.c | 191 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 190 insertions(+), 1 deletion(-)
diff --git a/dlls/msttsengine/tts.c b/dlls/msttsengine/tts.c index c5fac6800eb..5effec0e8cf 100644 --- a/dlls/msttsengine/tts.c +++ b/dlls/msttsengine/tts.c @@ -24,8 +24,197 @@
#include "windef.h" #include "winbase.h" +#include "objbase.h"
-HRESULT ttsengine_create(REFIID iid, void **obj) +#include "sapiddk.h" +#include "sperror.h" + +#include "wine/debug.h" +#include "wine/heap.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msttsengine); + +struct ttsengine +{ + ISpTTSEngine ISpTTSEngine_iface; + ISpObjectWithToken ISpObjectWithToken_iface; + LONG ref; + + ISpObjectToken *token; +}; + +static inline struct ttsengine *impl_from_ISpTTSEngine(ISpTTSEngine *iface) +{ + return CONTAINING_RECORD(iface, struct ttsengine, ISpTTSEngine_iface); +} + +static inline struct ttsengine *impl_from_ISpObjectWithToken(ISpObjectWithToken *iface) +{ + return CONTAINING_RECORD(iface, struct ttsengine, ISpObjectWithToken_iface); +} + +static HRESULT WINAPI ttsengine_QueryInterface(ISpTTSEngine *iface, REFIID iid, void **obj) +{ + struct ttsengine *This = impl_from_ISpTTSEngine(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(iid), obj); + + if (IsEqualIID(iid, &IID_IUnknown) || + IsEqualIID(iid, &IID_ISpTTSEngine)) + { + *obj = &This->ISpTTSEngine_iface; + } + else if (IsEqualIID(iid, &IID_ISpObjectWithToken)) + *obj = &This->ISpObjectWithToken_iface; + else + { + *obj = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*obj); + return S_OK; +} + +static ULONG WINAPI ttsengine_AddRef(ISpTTSEngine *iface) { + struct ttsengine *This = impl_from_ISpTTSEngine(iface); + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%lu\n", This, ref); + + return ref; +} + +static ULONG WINAPI ttsengine_Release(ISpTTSEngine *iface) +{ + struct ttsengine *This = impl_from_ISpTTSEngine(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%lu\n", This, ref); + + if (!ref) + { + if (This->token) ISpObjectToken_Release(This->token); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI ttsengine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUID fmtid, + const WAVEFORMATEX *wfx, const SPVTEXTFRAG *frag_list, + ISpTTSEngineSite *site) +{ + FIXME("(%p, %#lx, %s, %p, %p, %p): stub.\n", iface, flags, debugstr_guid(fmtid), wfx, frag_list, site); + return E_NOTIMPL; } + +static HRESULT WINAPI ttsengine_GetOutputFormat(ISpTTSEngine *iface, const GUID *fmtid, + const WAVEFORMATEX *wfx, GUID *out_fmtid, + WAVEFORMATEX **out_wfx) +{ + FIXME("(%p, %s, %p, %p, %p): stub.\n", iface, debugstr_guid(fmtid), wfx, out_fmtid, out_wfx); + + return E_NOTIMPL; +} + +static ISpTTSEngineVtbl ttsengine_vtbl = +{ + ttsengine_QueryInterface, + ttsengine_AddRef, + ttsengine_Release, + ttsengine_Speak, + ttsengine_GetOutputFormat, +}; + +static HRESULT WINAPI objwithtoken_QueryInterface(ISpObjectWithToken *iface, REFIID iid, void **obj) +{ + struct ttsengine *This = impl_from_ISpObjectWithToken(iface); + + TRACE("(%p, %s, %p).\n", iface, debugstr_guid(iid), obj); + + return ISpTTSEngine_QueryInterface(&This->ISpTTSEngine_iface, iid, obj); +} + +static ULONG WINAPI objwithtoken_AddRef(ISpObjectWithToken *iface) +{ + struct ttsengine *This = impl_from_ISpObjectWithToken(iface); + + TRACE("(%p).\n", iface); + + return ISpTTSEngine_AddRef(&This->ISpTTSEngine_iface); +} + +static ULONG WINAPI objwithtoken_Release(ISpObjectWithToken *iface) +{ + struct ttsengine *This = impl_from_ISpObjectWithToken(iface); + + TRACE("(%p).\n", iface); + + return ISpTTSEngine_Release(&This->ISpTTSEngine_iface); +} + +static HRESULT WINAPI objwithtoken_SetObjectToken(ISpObjectWithToken *iface, ISpObjectToken *token) +{ + struct ttsengine *This = impl_from_ISpObjectWithToken(iface); + + FIXME("(%p, %p): semi-stub.\n", iface, token); + + if (!token) + return E_INVALIDARG; + if (This->token) + return SPERR_ALREADY_INITIALIZED; + + ISpObjectToken_AddRef(token); + This->token = token; + return S_OK; +} + +static HRESULT WINAPI objwithtoken_GetObjectToken(ISpObjectWithToken *iface, ISpObjectToken **token) +{ + struct ttsengine *This = impl_from_ISpObjectWithToken(iface); + + TRACE("(%p, %p).\n", iface, token); + + if (!token) + return E_POINTER; + + *token = This->token; + if (*token) + { + ISpObjectToken_AddRef(*token); + return S_OK; + } + else + return S_FALSE; +} + +static const ISpObjectWithTokenVtbl objwithtoken_vtbl = +{ + objwithtoken_QueryInterface, + objwithtoken_AddRef, + objwithtoken_Release, + objwithtoken_SetObjectToken, + objwithtoken_GetObjectToken +}; + +HRESULT ttsengine_create(REFIID iid, void **obj) +{ + struct ttsengine *This; + HRESULT hr; + + if (!(This = heap_alloc(sizeof(*This)))) + return E_OUTOFMEMORY; + + This->ISpTTSEngine_iface.lpVtbl = &ttsengine_vtbl; + This->ISpObjectWithToken_iface.lpVtbl = &objwithtoken_vtbl; + This->ref = 1; + + This->token = NULL; + + hr = ISpTTSEngine_QueryInterface(&This->ISpTTSEngine_iface, iid, obj); + ISpTTSEngine_Release(&This->ISpTTSEngine_iface); + return hr; +}
On Wed Nov 1 02:56:34 2023 +0000, Huw Davies wrote:
Overall it looks like the commit could be split. e.g. the first commit could be the classfactory which calls a stub `ttsengine_create()`. That would be fleshed out in a second commit. Further, the changes to `configure.ac` don't seem to be required at this point.
Done.
Huw Davies (@huw) commented about dlls/msttsengine/Makefile.in:
+MODULE = msttsengine.dll +IMPORTS = ole32
+C_SRCS = \
- main.c \
- tts.c
+IDL_SRCS = \
- msttsengine_classes.idl
We've (very) recently switched to using `SOURCES` for all of these.
Huw Davies (@huw) commented about dlls/msttsengine/msttsengine_classes.idl:
+/*
- MSTTSEngine classes.
Could we reduce the filename length a bit. Perhaps `ttseng_classes.idl`?
Huw Davies (@huw) commented about dlls/msttsengine/msttsengine_private.h:
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
+#ifndef __WINE_MSTTSENGINE_PRIVATE_H +#define __WINE_MSTTSENGINE_PRIVATE_H
+#include <stdbool.h> +#include <stdint.h> +#include "windef.h" +#include "winternl.h"
+#include "wine/unixlib.h"
+DEFINE_GUID(CLSID_MSTTSEngine, 0xC64501F6,0xE6E6,0x451F,0xA1,0x50,0x25,0xD0,0x83,0x9B,0xC5,0x10);
We shouldn't need this, simply include `ttseng_classes.h` (and rerun `make depend`).
Could you also rename this file to something like `ttseng_private.h`?