diff --git a/NaturalVoiceSAPIAdapter/VoiceTokenEnumerator.cpp b/NaturalVoiceSAPIAdapter/VoiceTokenEnumerator.cpp index b43b507..5f8691d 100644 --- a/NaturalVoiceSAPIAdapter/VoiceTokenEnumerator.cpp +++ b/NaturalVoiceSAPIAdapter/VoiceTokenEnumerator.cpp @@ -21,7 +21,9 @@ inline static void CheckHr(HRESULT hr) throw std::system_error(hr, std::system_category()); } -static CComPtr s_pCachedEnum = nullptr; +// use non-smart pointer so that it won't be released automatically on DLL unload +static IEnumSpObjectTokens* s_pCachedEnum = nullptr; + static std::mutex s_cacheMutex; extern HANDLE g_hTimerQueue; static HANDLE s_hCacheTimer = nullptr; @@ -34,6 +36,9 @@ HRESULT CVoiceTokenEnumerator::FinalConstruct() std::lock_guard lock(s_cacheMutex); + if (s_pCachedEnum) + return s_pCachedEnum->Clone(&m_pEnum); + // Timer queues CANNOT be created in DllMain, otherwise deadlocks would happen on Windows XP // So we create the timer queue here on first use if (!g_hTimerQueue) @@ -87,7 +92,7 @@ HRESULT CVoiceTokenEnumerator::FinalConstruct() const auto clearCache = [](PVOID, BOOLEAN) { std::lock_guard lock(s_cacheMutex); - s_pCachedEnum.Release(); + s_pCachedEnum = nullptr; (void)DeleteTimerQueueTimer(g_hTimerQueue, s_hCacheTimer, nullptr); s_hCacheTimer = nullptr; }; @@ -95,11 +100,8 @@ HRESULT CVoiceTokenEnumerator::FinalConstruct() WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION); } - CComPtr temp; - RETONFAIL(pEnumBuilder->QueryInterface(&temp)); - s_pCachedEnum = std::move(temp); - - return pEnumBuilder->QueryInterface(&m_pEnum); + RETONFAIL(pEnumBuilder->QueryInterface(&s_pCachedEnum)); + return s_pCachedEnum->Clone(&m_pEnum); } static CComPtr MakeVoiceKey(StringPairCollection&& values, SubkeyCollection&& subkeys) diff --git a/NaturalVoiceSAPIAdapter/dllmain.cpp b/NaturalVoiceSAPIAdapter/dllmain.cpp index c892c9e..d0569c1 100644 --- a/NaturalVoiceSAPIAdapter/dllmain.cpp +++ b/NaturalVoiceSAPIAdapter/dllmain.cpp @@ -20,8 +20,16 @@ extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpRes if (dwReason == DLL_PROCESS_DETACH) { - // Use INVALID_HANDLE_VALUE to wait for callback functions to complete - (void)DeleteTimerQueueEx(g_hTimerQueue, INVALID_HANDLE_VALUE); + if (lpReserved == nullptr) // being unloaded dynamically + { + // Use INVALID_HANDLE_VALUE to wait for callback functions to complete + (void)DeleteTimerQueueEx(g_hTimerQueue, INVALID_HANDLE_VALUE); + } + else + { + // Use nullptr to delete without waiting for callbacks + (void)DeleteTimerQueueEx(g_hTimerQueue, nullptr); + } } return _AtlModule.DllMain(dwReason, lpReserved);