Приложение на основе SAPI вызывает нарушение доступа к памяти при перечислении токенов

Недавно я загрузил Visual Studio 2015 (Community), чтобы разработать приложение с использованием Microsoft SAPI TTS. Я установил SDK 5.1, а затем SDK 5.4 для распознавания речи и установил языки выполнения для SAPI.

До сих пор мне удавалось заставить приложение работать, используя фразы, которые я хочу, чтобы SAPI произносил как с прямыми аргументами, так и с чтением SSML-файла .XML. Все функции SAPI и SSML работают, за исключением всего, что связано с изменением токена голоса. (например, <voice xml:lang="pl-PL"> ... <voice required="Gender:Female"> ... и т. д.)

Я прочитал несколько форумов о том, как установить язык/голосовой токен, и попробовал следующий код:

if(FAILED(::CoInitialized(NULL))
  return false;

HRESULT                         hr = S_OK;
CComPtr<ISpVoice>               cpVoice;
CComPtr<ISpObjectTokenCategory> cpObjectCat;
CComPtr<ISpObjectToken>         cpObjectToken;
CComPtr<IEnumSpObjectTokens>    cpEnum;

hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&cpVoice);

if(SUCCEEDED(hr))
{
    hr = SpGetDefaultTokenFromCategoryId(SPCAT_VOICES, &cpObjectToken);
} 
if(SUCCEEDED(hr))
{
    hr = SpGetCategoryFromId(SPCAT_VOICES, &cpObjectCat);
}
if(SUCCEEDED(hr))
{
    hr = cpObjectCat->EnumTokens(NULL, NULL, &cpEnum);
}
if(SUCCEEDED(hr))
{
    hr = cpEnum->Next(1, &cpObjectToken, NULL);
    //Currently only concerned with making 1 token assign without
    //throwing exception
}
if(SUCCEEDED(hr))
{
    hr = cpVoice->SetVoice(cpObjectToken);
}
if(SUCCEEDED(hr))
{
    hr = cpVoice->Speak(L"Hello There!", NULL, NULL);
}
//... lots of commented-out code here ...
cpVoice.Release();
::CoUninitialize();
return true; // <-- Throws Exception Here

Программа строится без ошибок, но выдает ошибку 0xC0000005 (Memory Access Violation) в финальном операторе возврата.

Мне пришлось изменить заголовочный файл <sphelper.h> из-за устаревшего метода ::GetVersionExW()... Мне удалось заставить его работать, используя методы из этой ссылки: http://www.codeproject.com/Articles/678606/Part-Overcoming-Windows-s-deprecation-of-GetVe . Чудесным образом (и с небольшим изменением исходного кода системы, вероятно, плохой идеей) это сработало.

Не знаю, почему программу выкидывает в самом конце, так как проблема должна быть в программе, обращающейся к токенам реестра. Я знаю, что обычно проблема заключается в указателях, так что же мне нужно сделать, чтобы это заработало?

Обратите внимание, что я пытаюсь заставить объект cpVoice использовать польский токен «Паулина». Есть ли способ вручную присвоить значение токена реестра объекту?


person WhytFng    schedule 10.03.2016    source источник


Ответы (1)


Вот как вы проходите через каждый из ваших установленных голосов. pszCurTokenId будет описанием голоса, который вы получили. Вероятно, вы могли бы вывести его на консоль или что-то в этом роде или просто посмотреть значение в отладчике.

Вам не нужно редактировать файл shelper.h, чтобы правильно выбрать голос. Иногда SAPI может занять несколько секунд, чтобы изменить голос, поэтому я был бы терпелив, если бы он чувствовал, что он зависает. Я только что запустил следующий код в Windows 7 и убедился, что он работает для этой платформы.

HRESULT hr = S_OK;
CComPtr<ISpObjectToken> cpVoiceToken;
CComPtr<ISpVoice> cpVoice;

::CoInitialize(NULL);
if(SUCCEEDED(hr))
    hr = cpVoice.CoCreateInstance(CLSID_SpVoice);

ULONG ulCount = 0;
CComPtr<IEnumSpObjectTokens> cpEnum;

if(SUCCEEDED(hr))
    hr = SpEnumTokens(SPCAT_VOICES, NULL, NULL, &cpEnum);

//Get the number of voices
if(SUCCEEDED(hr))
    hr = cpEnum->GetCount(&ulCount);

for(ULONG i = 0; i < ulCount; ++i) {
    CSpDynamicString* szDescription;
    CComPtr<ISpObjectToken> cpTempVoiceToken;
    cpEnum->Item(i, &cpTempVoiceToken);
    WCHAR* pszCurTokenId = NULL;
    SpGetDescription(cpTempVoiceToken, &pszCurTokenId);
    cpVoice->SetVoice(cpTempVoiceToken);
    cpVoice->Speak(L"This is a test phrase.", SPF_DEFAULT, NULL);
    cpTempVoiceToken.Release();
}

cpVoice.Release();
cpEnum.Release();
::CoUninitialize();
person Lesley Gushurst    schedule 11.03.2016
comment
Понятно... Я работаю в Windows 10, поэтому у меня могут возникнуть проблемы. Спасибо вам за помощь! - person WhytFng; 15.03.2016