Getting output of Async SAPI call? - c++

I have a working example using the Windows text-to-speech API, but the problem is it freezes when given a large amount of text.
To solve this I want to make the process asynchronous. I've found the SpeechVoiceSpeakFlags::SVSFlagsAsync flag, but how do I get the results of a previously-submitted request?
Here is my code:
char* TextToWavInner( const wchar_t* voiceRequiredAttributes, const wchar_t* voiceOptionalAttributes, long rate, const wchar_t* textToRender, ULONG* pBytesRead )
{
HRESULT hr;
CComPtr<ISpVoice> cpVoice; //Will send data to ISpStream
CComPtr<ISpStream> cpStream; //Will contain IStream
CComPtr<IStream> cpBaseStream; //raw data
ISpObjectToken* cpToken( NULL ); //Will set voice characteristics
GUID guidFormat;
WAVEFORMATEX* pWavFormatEx = nullptr;
hr = cpVoice.CoCreateInstance( CLSID_SpVoice );
if ( FAILED( hr ) )
return NULL;
hr = SpFindBestToken( SPCAT_VOICES, voiceRequiredAttributes, voiceOptionalAttributes, &cpToken );
if ( FAILED( hr ) )
return NULL;
hr = cpVoice->SetVoice( cpToken );
cpToken->Release();
if ( FAILED( hr ) )
return NULL;
cpVoice->SetRate( rate );
hr = cpStream.CoCreateInstance( CLSID_SpStream );
if ( FAILED( hr ) )
return NULL;
hr = CreateStreamOnHGlobal( NULL, true, &cpBaseStream );
if ( FAILED( hr ) )
return NULL;
hr = SpConvertStreamFormatEnum( SPSF_44kHz16BitMono, &guidFormat, &pWavFormatEx );
if ( FAILED( hr ) )
return NULL;
hr = cpStream->SetBaseStream( cpBaseStream, guidFormat, pWavFormatEx );
if ( FAILED( hr ) )
return NULL;
hr = cpVoice->SetOutput( cpStream, false );
if ( FAILED( hr ) )
return NULL;
SpeechVoiceSpeakFlags voiceFlags = ( SpeechVoiceSpeakFlags ) ( SpeechVoiceSpeakFlags::SVSFlagsAsync | SpeechVoiceSpeakFlags::SVSFPurgeBeforeSpeak );
hr = cpVoice->Speak( textToRender, voiceFlags, NULL );
if ( FAILED( hr ) )
return NULL;
LARGE_INTEGER a = { 0 };
hr = cpStream->Seek( a, STREAM_SEEK_SET, NULL );
if ( FAILED( hr ) )
return NULL;
STATSTG stats;
cpStream->Stat( &stats, STATFLAG_NONAME );
ULONG sSize = stats.cbSize.LowPart;
char* pBuffer = new char[ sSize ];
cpStream->Read( pBuffer, sSize, pBytesRead );
return pBuffer;
}

Related

How to get word list using ISpLexicon::GetWords?

I'm developping a Text-To-Speech application using Microsoft SAPI. I found out that it is possible to add customized prononciations of words in a dictionnary (correct me if I'm wrong). I implemented a function which allows to add words into this dictionnary. Here is my code:
int addPrononciation( const char* addPron, const char* phon )
{
hr = cpLexicon.CoCreateInstance( CLSID_SpLexicon );
hr = cpContainerLexicon.CoCreateInstance( CLSID_SpLexicon );
hr = SpEnumTokens( SPCAT_VOICES, NULL, NULL, &cpEnum );
cpEnum->Item( saveVoice, &cpVoiceToken ); //get saveVoice token defined at line 136
cpVoice->SetVoice( cpVoiceToken ); //Initialization of the voice
hr = cpContainerLexicon->AddLexicon( cpLexicon, eLEXTYPE_APP );
langId = MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US );
hr = SpCreatePhoneConverter( langId, NULL, NULL, &cpPhoneConv );
int wchars_num = MultiByteToWideChar( CP_ACP, 0, addPron, -1, NULL, 0 );
wchar_t* pronWstr = new wchar_t[ wchars_num ];
MultiByteToWideChar( CP_ACP, 0, addPron, -1, pronWstr, wchars_num );
int phonWchars_num = MultiByteToWideChar( CP_ACP, 0, phon, -1, NULL, 0 );
wchar_t* phonWstr = new wchar_t[ phonWchars_num ];
MultiByteToWideChar( CP_ACP, 0, phon, -1, phonWstr, phonWchars_num );
if(SUCCEEDED( hr ))
{
hr = cpPhoneConv->PhoneToId( phonWstr, wszId );
hr = cpVoice->Speak( phonWstr, SPF_DEFAULT, NULL );
hr = cpLexicon->AddPronunciation( pronWstr, langId, SPPS_Noun, wszId );
hr = cpVoice->Speak( pronWstr, SPF_DEFAULT, NULL );
if( SUCCEEDED( hr ) )
{
printf( "Success\n" );
}
else
{
printf( "Failed\n" );
}
}
cpEnum.Release();
cpVoiceToken.Release();
cpContainerLexicon.Release();
cpLexicon.Release();
cpPhoneConv.Release();
delete new wchar_t[ wchars_num ];
delete new wchar_t[ phonWchars_num ];
return true;
}
Now I would like to list these words using ISpLexicon::GetWords.
I already read the documentation on the Microsoft website and tried to implement the function, but I can't figure out how to initialize the variable spWordList.
Here is my code:
ZeroMemory( &spWordList, sizeof( spWordList ) );
if( SUCCEEDED( hr ) )
{
hr = cpLexicon->GetWords( eLEXTYPE_APP, &dwGeneration, &dwCookie, &spWordList );
printf( "Words: %ls\n", spWordList ); //print words but the output is null
}
CoTaskMemFree( spWordList.pvBuffer );
I'm triying to print the words, but the output is null. I think the spWordList variable is not initialized. Here is a screenshot of the variable values.
How can I initialize it?
I found out how to initialize spWordList. You have to replace eLEXTYPE_APP with eLEXTYPE_USER. However, you can keep both of them like I did. Below you will find an example on how it lists the words.
ZeroMemory( &spWordList, sizeof( spWordList ) );
hr = S_FALSE;
if( hr == S_FALSE )
{
hr = cpLexicon->GetWords( eLEXTYPE_USER | eLEXTYPE_APP, &dwGeneration, &dwCookie, &spWordList );
for( spWord = spWordList.pFirstWord; spWord != NULL; spWord = spWord->pNextWord )
{
for( spWordPron = spWord->pFirstWordPronunciation; spWordPron != NULL; spWordPron = spWordPron->pNextWordPronunciation )
{
printf( "Words in dictionnary: %i\n", dwGeneration );
printf( "Word: %ls\n", spWord->pszWord );
//you can also display the pronunciation of words if you wish
}
}
}
CoTaskMemFree( spWordList.pvBuffer );
In the code, I loop through the entire dictionnary. Notice that the listed words are displayed randomly. I'll update my answer if I find other important information about ISpLexicon::GetWords

enumerate forms in BeforeNavigate2 event

I'm writing an IE BHO, I'd like to know how to enumerate forms in event callback.
here's the code that enumerates forms in BeforeNavigate2 event, but the length is always 0.
STDMETHODIMP CEventSink::Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS *pDispParams,VARIANT *pVarResult,EXCEPINFO *pExcepInfo,UINT *puArgErr)
{
HRESULT hr;
char bf[1024];
if(!IsEqualIID(riid, IID_NULL))
return DISP_E_UNKNOWNINTERFACE;
if(dispIdMember == DISPID_BEFORENAVIGATE2) {
IWebBrowser2* pSite = (IWebBrowser2*)pDispParams->rgvarg[6].pdispVal;
IDispatch* pHtmlDocDispatch;
hr = pSite->get_Document(&pHtmlDocDispatch);
if (FAILED(hr) || !pHtmlDocDispatch)
return S_OK;
IHTMLDocument2* pHtmlDoc = 0;
hr = pHtmlDocDispatch->QueryInterface(IID_IHTMLDocument2, (void**)&pHtmlDoc);
if(SUCCEEDED(hr) && pHtmlDoc) {
CComPtr<IHTMLElementCollection> pColl=NULL;
hr = pHtmlDoc->get_forms(&pColl);
if (SUCCEEDED (hr) && (pColl != NULL))
{
long nLength = 0;
hr = pColl->get_length (&nLength);
if(SUCCEEDED(hr)) {
sprintf(bf, "len = %d", nLength);
OutputDebugString(bf); // always 0
}
}
}
}
return S_OK;
}
Why it always output 0?
Thanks.
I just copy/pasted your code in my BHO and got a non-zero length collection.
Try that URL: http://linuxfr.org/ Use random user/password values and try to connect. That will trigger a DISPID_BEFORENAVIGATE2 and you will get 3 forms.
So, it seems there is no form in the page your are navigating away.
Also, your code leaks memory and is not quite correct in using COM interfaces (ag: you should QueryInterface for obtaining an IWebBrowser2 from a IDispatch.
Rewritten:
CComPtr<IDispatch> spIDispatch( pDispParams->rgvarg[6].pdispVal );
CComPtr<IWebBrowser2> spIWebBrowser2;
HRESULT hr = spIDispatch.QueryInterface<IWebBrowser2>( &spIWebBrowser2 );
if ( SUCCEEDED( hr ) && spIWebBrowser2 ) {
CComPtr<IDispatch> spIDispatchDoc;
hr = spIWebBrowser2->get_Document( &spIDispatchDoc );
if ( SUCCEEDED( hr ) && spIDispatchDoc ) {
CComPtr<IHTMLDocument2> spIHTMLDocument2;
hr = spIDispatchDoc.QueryInterface<IHTMLDocument2>( &spIHTMLDocument2 );
if ( SUCCEEDED( hr ) && spIHTMLDocument2 ) {
CComPtr<IHTMLElementCollection> spIHTMLElementCollection;
hr = spIHTMLDocument2->get_forms( &spIHTMLElementCollection );
if ( SUCCEEDED( hr ) && spIHTMLElementCollection ) {
[...]
}
}
}
}

Firewall exception code just works for outgoing connections

I took this code from the web to add a firewall exception for my application:
STDAPI AddApplicationToExceptionListW( const WCHAR* strGameExeFullPath, const WCHAR* strFriendlyAppName )
{
HRESULT hr = E_FAIL;
bool bCleanupCOM = false;
BSTR bstrFriendlyAppName = NULL;
BSTR bstrGameExeFullPath = NULL;
INetFwAuthorizedApplication* pFwApp = NULL;
INetFwAuthorizedApplications* pFwApps = NULL;
INetFwProfile* pFwProfile = NULL;
#ifdef SHOW_DEBUG_MSGBOXES
WCHAR sz[1024];
StringCchPrintf( sz, 1024, L"strFriendlyAppName='%s' strGameExeFullPath='%s'", strFriendlyAppName, strGameExeFullPath );
MessageBox( NULL, sz, L"AddApplicationToExceptionListW", MB_OK );
#endif
if( strGameExeFullPath == NULL || strFriendlyAppName == NULL )
{
assert( false );
return E_INVALIDARG;
}
bstrGameExeFullPath = SysAllocString( strGameExeFullPath );
bstrFriendlyAppName = SysAllocString( strFriendlyAppName );
if( bstrGameExeFullPath == NULL || bstrFriendlyAppName == NULL )
{
hr = E_OUTOFMEMORY;
goto LCleanup;
}
hr = CoInitialize( 0 );
bCleanupCOM = SUCCEEDED( hr );
pFwProfile = GetFirewallProfile();
if( pFwProfile == NULL )
{
hr = E_FAIL;
goto LCleanup;
}
hr = pFwProfile->get_AuthorizedApplications( &pFwApps );
if( FAILED( hr ) )
goto LCleanup;
// Create an instance of an authorized application.
hr = CoCreateInstance( __uuidof( NetFwAuthorizedApplication ), NULL,
CLSCTX_INPROC_SERVER, __uuidof( INetFwAuthorizedApplication ), ( void** )&pFwApp );
if( FAILED( hr ) )
goto LCleanup;
// Set the process image file name.
hr = pFwApp->put_ProcessImageFileName( bstrGameExeFullPath );
if( FAILED( hr ) )
goto LCleanup;
// Set the application friendly name.
hr = pFwApp->put_Name( bstrFriendlyAppName );
if( FAILED( hr ) )
goto LCleanup;
// Add the application to the collection.
hr = pFwApps->Add( pFwApp );
LCleanup:
if( bstrFriendlyAppName ) SysFreeString( bstrFriendlyAppName );
if( bstrGameExeFullPath ) SysFreeString( bstrGameExeFullPath );
if( pFwApp ) pFwApp->Release();
if( pFwApps ) pFwApps->Release();
if( pFwProfile ) pFwProfile->Release();
if( bCleanupCOM ) CoUninitialize();
return hr;
}
Everything works great when I try to send data through the Windows firewall, but incoming connections are still blocked. So I have to disable my firewall to recieve data. I thought, that exception would allow all connections (outgoing and incoming)...
Does somebody know what I should add to this code so I can recieve incoming data?
It is not enough to just add the application by itself. The firewall has no way of discovering which port(s) the application is listening on for inbound connections. You have to tell the firewall which port(s) the application is using. You do that via the INetFwProfile::GloballyOpenPorts collection, eg:
INetFwOpenPorts *pFwPorts = NULL;
INetFwOpenPort *pFWPort = NULL;
...
hr = pFwProfile->get_GloballyOpenPorts( &pFwPorts );
if( FAILED( hr ) )
goto LCleanup;
// Create an instance of an open port.
hr = CoCreateInstance( __uuidof( NetFwOpenPort ), NULL, CLSCTX_INPROC_SERVER, __uuidof( INetFwOpenPort ), ( void** )&pFwPort );
if( FAILED( hr ) )
goto LCleanup;
// Set the port number.
hr = pFWPort->put_Port( ... );
if( FAILED( hr ) )
goto LCleanup;
// Add the port to the collection.
hr = pFwPorts->Add( pFwPort );
...
if( pFwPort ) pFwPort->Release();
if( pFwPorts ) pFwPorts->Release();
In my case the solution was deleting the firewall rules that blocked my application. I don't know where these rules came from, but now it finally works.

SetInputType returns error code c00d6d60

I am trying to use MediaFoundation dolby encoder in my project. I am able to initialize output type but while setting up input tyep it gives me c00d6d60 error code
IMFMediaType* pInputMediaType = NULL;
IMFMediaType *pOutputType = NULL;
if (!m_pMFT)
{
return MF_E_NOT_INITIALIZED;
}
HRESULT hr = S_OK;
DWORD pcInputStreams = 0;
DWORD pcOutputStreams = 0;
CHECK_HR(m_pMFT->GetStreamCount(&pcInputStreams,&pcOutputStreams));
assert(pcInputStreams==1 && pcInputStreams == pcOutputStreams);
hr = m_pMFT->GetStreamIDs(1, &m_dwInputID, 1, &m_dwOutputID);
if (hr == E_NOTIMPL)
{
// The stream identifiers are zero-based.
m_dwInputID = 0;
m_dwOutputID = 0;
hr = S_OK;
}
else if (FAILED(hr))
{
goto done;
}
SafeRelease(&m_pOutputType);
CHECK_HR( MFCreateMediaType( &pOutputType ) );
CHECK_HR( pOutputType->SetGUID( MF_MT_MAJOR_TYPE, MFMediaType_Audio ) );
CHECK_HR( pOutputType->SetGUID( MF_MT_SUBTYPE, MFAudioFormat_Dolby_AC3 ) );
CHECK_HR( pOutputType->SetUINT32( MF_MT_AUDIO_SAMPLES_PER_SECOND, inSamplePerSec ) );
CHECK_HR( pOutputType->SetUINT32( MF_MT_AUDIO_AVG_BYTES_PER_SECOND, inOutputAvgBytesPerSec ) );
CHECK_HR( pOutputType->SetUINT32( MF_MT_AUDIO_NUM_CHANNELS, inNumChannels ) );
LogMediaType(pOutputType);
hr = m_pMFT->SetOutputType(m_dwOutputID, pOutputType, 0);
if (SUCCEEDED(hr))
{
m_pOutputType = pOutputType;
m_pOutputType->AddRef();
}
CHECK_HR( MFCreateMediaType( &pInputMediaType ) );
CHECK_HR( pInputMediaType->SetGUID( MF_MT_MAJOR_TYPE, MFMediaType_Audio ) );
CHECK_HR( pInputMediaType->SetGUID( MF_MT_SUBTYPE, MFAudioFormat_PCM ) );
CHECK_HR( pInputMediaType->SetUINT32( MF_MT_AUDIO_BITS_PER_SAMPLE, 16 ) );
CHECK_HR( pInputMediaType->SetUINT32( MF_MT_AUDIO_SAMPLES_PER_SECOND, inSamplePerSec ) );
CHECK_HR( pInputMediaType->SetUINT32( MF_MT_AUDIO_NUM_CHANNELS, inNumChannels ) );
//CHECK_HR( spMFTypeIn->SetUINT32( MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1 ) );
CHECK_HR( pInputMediaType->SetUINT32( MF_MT_AUDIO_BLOCK_ALIGNMENT, inNumChannels*2 ) );
CHECK_HR( pInputMediaType->SetUINT32( MF_MT_AUDIO_AVG_BYTES_PER_SECOND, inNumChannels*2*inSamplePerSec) );
LogMediaType(pInputMediaType);
if (FAILED(hr))
{
goto done;
}
hr = m_pMFT->SetInputType(m_dwInputID, pInputMediaType, 0);
The guide I am using is http://msdn.microsoft.com/en-us/library/windows/desktop/hh447682%28v=vs.85%29.aspx. Can some please provide some insight
0xC00D6D60 is MF_E_TRANSFORM_TYPE_NOT_SET "A valid type has not been set for this stream or a stream that it depends on."
MSDN says that this error code is returned when it is too early to set input/output media type and you need to configure transform from the other end.
In case of Dolby Digital Audio Encoder real cause should be the unavailability of the encoder to you:
Important The Microsoft implementation of the Dolby Digital technology is restricted under terms of the Dolby Digital licensing program to use by Microsoft applications.
If you are using third party transform (you don't show instantiation code), then additional conditions or initialization sequence might apply.

How to pin Application icon to the metro start screen in windows 8 programmatically

How can I pin application icon to metro start screen in win8 programmatically(c++)? I know how to do it manually. I also know that it will be added automatically once I launch that application.
I found this solution here
BOOL PinToStart( LPCWSTR szFilePath )
{
BOOL bSuccess = FALSE;
// break into file name and path
WCHAR lpszDirectoryName[ MAX_PATH ] = { 0 };
LPCWSTR lpszFileName = ::PathFindFileName( szFilePath );
wcscpy_s( lpszDirectoryName, szFilePath );
::PathRemoveFileSpec( lpszDirectoryName );
// load shell32.dll
HMODULE hShell32 = LoadLibrary( L"SHELL32" );
if( hShell32 != NULL )
{
// get the localized translation of 'Pin to Start' verb
WCHAR szPinToStartLocalized[ MAX_PATH ] = { 0 };
int nPinToStartLocalizedLength = LoadString( (HINSTANCE)hShell32, 51201, szPinToStartLocalized, MAX_PATH );
if( nPinToStartLocalizedLength > 0 )
{
// create the shell object
IShellDispatch *pShellDispatch = NULL;
HRESULT hr = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void**)&pShellDispatch);
if( SUCCEEDED( hr ) )
{
Folder *pFolder = NULL;
variant_t vaDirectory( lpszDirectoryName );
// get the namespace
if( SUCCEEDED( pShellDispatch->NameSpace( vaDirectory, &pFolder ) ) )
{
FolderItem *pItem = NULL;
bstr_t vaFileName( lpszFileName );
// parse the name
if( SUCCEEDED( pFolder->ParseName( vaFileName, &pItem ) ) )
{
FolderItemVerbs* pVerbs = NULL;
// get the verbs
if( SUCCEEDED( pItem->Verbs(&pVerbs) ) )
{
long nCount = 0;
if( SUCCEEDED ( pVerbs->get_Count( &nCount ) ) )
{
variant_t vaIndex;
vaIndex.vt = VT_I4;
// iterate through verbs
for( vaIndex.lVal = 0; vaIndex.lVal<nCount; vaIndex.lVal++ )
{
FolderItemVerb* pVerb = NULL;
if( SUCCEEDED( pVerbs->Item( vaIndex, &pVerb ) ) )
{
BSTR bstrVerbName = NULL;
// check for 'Pin to Start' verb
if( SUCCEEDED( pVerb->get_Name( &bstrVerbName ) ) )
{
if( 0 == wcscmp( bstrVerbName, szPinToStartLocalized ) )
{
bSuccess = SUCCEEDED( pVerb->DoIt() );
vaIndex.lVal = nCount; // break for
}
::SysFreeString( bstrVerbName );
}
pVerb->Release();
} // if
} // for
}
pVerbs->Release();
}
pItem->Release();
}
pFolder->Release();
}
pShellDispatch->Release();
}
}
::FreeLibrary( hShell32 );
}
return bSuccess;
}
Hope it's help you