diff --git a/src/windows/com/comentrypoints.c b/src/windows/com/comentrypoints.c index f4b29c6c..3700cd21 100644 --- a/src/windows/com/comentrypoints.c +++ b/src/windows/com/comentrypoints.c @@ -16,19 +16,68 @@ */ #include "config.h" +#define INITGUID #include -HRESULT __stdcall DllGetClassObject(REFCLSID classId, REFIID iface, void **pObject) -{ - if (!pObject) - return E_POINTER; +// {61D23633-CE59-4101-8158-569FC6B51B49} +DEFINE_GUID(CLSID_TtsEngine, 0x61d23633, 0xce59, 0x4101, 0x81, 0x58, 0x56, 0x9f, 0xc6, 0xb5, 0x1b, 0x49); - *pObject = NULL; +extern HRESULT __stdcall TtsEngine_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID iid, void **object); + +ULONG ObjectCount = 0; +static ULONG LockCount = 0; + +static ULONG __stdcall ClassFactory_AddRef(IClassFactory *iface) +{ + return 1; +} + +static ULONG __stdcall ClassFactory_Release(IClassFactory *iface) +{ + return 1; +} + +static HRESULT __stdcall ClassFactory_QueryInterface(IClassFactory *iface, REFIID iid, void **object) +{ + if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IClassFactory)) { + *object = iface; + iface->lpVtbl->AddRef(iface); + return S_OK; + } + + *object = NULL; + return E_NOINTERFACE; +} + +static HRESULT __stdcall ClassFactory_LockServer(IClassFactory *iface, BOOL lock) +{ + if (lock) + InterlockedIncrement(&LockCount); + else + InterlockedDecrement(&LockCount); + return S_OK; +} + +static const IClassFactoryVtbl TtsEngine_ClassFactoryVtbl = { + ClassFactory_QueryInterface, + ClassFactory_AddRef, + ClassFactory_Release, + TtsEngine_CreateInstance, + ClassFactory_LockServer +}; +static IClassFactory TtsEngine_ClassFactory = { &TtsEngine_ClassFactoryVtbl }; + +HRESULT __stdcall DllGetClassObject(REFCLSID classId, REFIID iid, void **object) +{ + if (IsEqualCLSID(classId, &CLSID_TtsEngine)) + return ClassFactory_QueryInterface(&TtsEngine_ClassFactory, iid, object); + + *object = NULL; return CLASS_E_CLASSNOTAVAILABLE; } HRESULT __stdcall DllCanUnloadNow(void) { - return S_OK; + return (ObjectCount == 0 && LockCount == 0) ? S_OK : S_FALSE; } diff --git a/src/windows/com/ttsengine.cpp b/src/windows/com/ttsengine.cpp new file mode 100644 index 00000000..13d5a080 --- /dev/null +++ b/src/windows/com/ttsengine.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2016 Reece H. Dunn + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see: . + */ + +#include "config.h" + +#include +#include + +#include + +extern "C" ULONG ObjectCount; + +struct TtsEngine + : public ISpObjectWithToken + , public ISpTTSEngine +{ + TtsEngine(); + ~TtsEngine(); + + // IUnknown + + ULONG __stdcall AddRef(); + ULONG __stdcall Release(); + + HRESULT __stdcall QueryInterface(REFIID iid, void **object); + + // ISpObjectWithToken + + HRESULT __stdcall GetObjectToken(ISpObjectToken **token); + HRESULT __stdcall SetObjectToken(ISpObjectToken *token); + + // ISpTTSEngine + + HRESULT __stdcall + Speak(DWORD flags, + REFGUID formatId, + const WAVEFORMATEX *format, + const SPVTEXTFRAG *textFragList, + ISpTTSEngineSite *site); + + HRESULT __stdcall + GetOutputFormat(const GUID *targetFormatId, + const WAVEFORMATEX *targetFormat, + GUID *formatId, + WAVEFORMATEX **format); +private: + ULONG refCount; +}; + +TtsEngine::TtsEngine() + : refCount(1) +{ + InterlockedIncrement(&ObjectCount); +} + +TtsEngine::~TtsEngine() +{ + InterlockedDecrement(&ObjectCount); +} + +ULONG __stdcall TtsEngine::AddRef() +{ + return InterlockedIncrement(&refCount); +} + +ULONG __stdcall TtsEngine::Release() +{ + ULONG ret = InterlockedDecrement(&refCount); + if (ret == 0) + delete this; + return ret; +} + +HRESULT __stdcall TtsEngine::QueryInterface(REFIID iid, void **object) +{ + *object = NULL; + if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_ISpTTSEngine)) + *object = (ISpTTSEngine *)this; + else if (IsEqualIID(iid, IID_ISpObjectWithToken)) + *object = (ISpObjectWithToken *)this; + else + return E_NOINTERFACE; + + this->AddRef(); + return S_OK; +} + +HRESULT __stdcall TtsEngine::GetObjectToken(ISpObjectToken **token) +{ + return E_NOTIMPL; +} + +HRESULT __stdcall TtsEngine::SetObjectToken(ISpObjectToken *token) +{ + return E_NOTIMPL; +} + +HRESULT __stdcall +TtsEngine::Speak(DWORD flags, + REFGUID formatId, + const WAVEFORMATEX *format, + const SPVTEXTFRAG *textFragList, + ISpTTSEngineSite *site) +{ + return E_NOTIMPL; +} + +HRESULT __stdcall +TtsEngine::GetOutputFormat(const GUID *targetFormatId, + const WAVEFORMATEX *targetFormat, + GUID *formatId, + WAVEFORMATEX **format) +{ + return E_NOTIMPL; +} + +extern "C" HRESULT __stdcall TtsEngine_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID iid, void **object) +{ + if (outer != NULL) + return CLASS_E_NOAGGREGATION; + + TtsEngine *engine = new (std::nothrow) TtsEngine(); + if (!engine) + return E_OUTOFMEMORY; + + HRESULT ret = engine->QueryInterface(iid, object); + engine->Release(); + return ret; +} diff --git a/src/windows/libespeak-ng.vcxproj b/src/windows/libespeak-ng.vcxproj index bbc610fc..21c50c03 100644 --- a/src/windows/libespeak-ng.vcxproj +++ b/src/windows/libespeak-ng.vcxproj @@ -178,6 +178,7 @@ + diff --git a/src/windows/libespeak-ng.vcxproj.filters b/src/windows/libespeak-ng.vcxproj.filters index ec7c4876..eaffecd5 100644 --- a/src/windows/libespeak-ng.vcxproj.filters +++ b/src/windows/libespeak-ng.vcxproj.filters @@ -93,6 +93,9 @@ Source Files\com + + Source Files\com +