How to get word list using ISpLexicon::GetWords? - c++

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

Related

Getting output of Async SAPI call?

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;
}

CreateDC() method,while setting up a Printer fails for certain PRINTERs as well as in certain Windows Environment

I have installed a HP Printer and added it to my list of Printer devices.
I am trying to use the following code:
QString printerName = "HP Designjet 500 24+HPGL2 Card";
DWORD infoSize, numBytes;
HANDLE hPrinter;
bool ok = OpenPrinter( ( LPWSTR )printerName.utf16(), ( LPHANDLE )&hPrinter, 0 );
if ( !ok )
{
qErrnoWarning( "QWin32PrintEngine::initialize: OpenPrinter failed" );
return;
}
GetPrinter( hPrinter, 2, NULL, 0, &infoSize );
HGLOBAL hMem;
hMem = GlobalAlloc( GHND, infoSize );
PRINTER_INFO_2 *pInfo;
pInfo = ( PRINTER_INFO_2* )GlobalLock( hMem );
ok = GetPrinter( hPrinter, 2, ( LPBYTE )pInfo, infoSize, &numBytes );
if ( !ok )
{
qErrnoWarning( "QWin32PrintEngine::initialize: GetPrinter failed" );
}
DEVMODE *devMode;
devMode = pInfo->pDevMode;
HDC hdc = NULL;
hdc = CreateDC( NULL, ( LPCWSTR )printerName.utf16(), 0, devMode );
Now,the CreateDC() method fails.I even tried to return the Error using GetLastError() method, and it returned as "203",which corresponds to "ERROR_ENVVAR_NOT_FOUND".
I am completely clueless as of now.
I would be really glad,if someone can help me regarding this.
Thanks in Advance.

Retrieve serial number from USB memory (Windows environment c++)

I would need to retrieve the serial number from my USB memory, namely the hard disk's serial number that the manufacturer assigns. For this reason I cannot use GetVolumeInformation() as suggested in some other threads. I would need to have the "unique" number
I kindly ask you if you can share an example in C++ and Windows environment (Visual c++)
Thanks!
You can check out this article:- http://oroboro.com/usb-serial-number/
#include <WinIOCtl.h>
#include <api/usbioctl.h>
#include <Setupapi.h>
DEFINE_GUID( GUID_DEVINTERFACE_USB_DISK,
0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2,
0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b );
void getDeviceInfo( int vol )
{
UsbDeviceInfo info;
// get the device handle
char devicePath[7] = "\\\\.\\#:";
devicePath[4] = (char)( vol + 'A' );
HANDLE deviceHandle = CreateFile( devicePath, 0,
FILE_SHARE_READ |
FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, 0, NULL );
if ( deviceHandle == INVALID_HANDLE_VALUE )
return;
// to get the device number
DWORD volumeDeviceNumber = getDeviceNumber( deviceHandle );
CloseHandle( deviceHandle );
// Get device interface info set handle
// for all devices attached to the system
HDEVINFO hDevInfo = SetupDiGetClassDevs(
&GUID_DEVINTERFACE_USB_DISK, NULL, NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE );
if ( hDevInfo == INVALID_HANDLE_VALUE )
return;
// Get a context structure for the device interface
// of a device information set.
BYTE Buf[1024];
PSP_DEVICE_INTERFACE_DETAIL_DATA pspdidd =
(PSP_DEVICE_INTERFACE_DETAIL_DATA)Buf;
SP_DEVICE_INTERFACE_DATA spdid;
SP_DEVINFO_DATA spdd;
spdid.cbSize = sizeof( spdid );
DWORD dwIndex = 0;
while ( true )
{
if ( ! SetupDiEnumDeviceInterfaces( hDevInfo, NULL,
&GUID_DEVINTERFACE_USB_DISK,
dwIndex, &spdid ))
break;
DWORD dwSize = 0;
SetupDiGetDeviceInterfaceDetail( hDevInfo, &spdid, NULL,
0, &dwSize, NULL );
if (( dwSize != 0 ) && ( dwSize <= sizeof( Buf )))
{
pspdidd->cbSize = sizeof( *pspdidd ); // 5 Bytes!
ZeroMemory((PVOID)&spdd, sizeof(spdd));
spdd.cbSize = sizeof(spdd);
long res = SetupDiGetDeviceInterfaceDetail(
hDevInfo, &spdid, pspdidd,
dwSize, &dwSize, &spdd );
if ( res )
{
HANDLE hDrive = CreateFile( pspdidd->DevicePath,0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL );
if ( hDrive != INVALID_HANDLE_VALUE )
{
DWORD usbDeviceNumber = getDeviceNumber( hDrive );
if ( usbDeviceNumber == volumeDeviceNumber )
{
fprintf( "%s", pspdidd->DevicePath );
}
}
CloseHandle( hDrive );
}
}
dwIndex++;
}
SetupDiDestroyDeviceInfoList(hDevInfo);
return;
}
You get the device number by calling DeviceIOControl() with the handle to your device:
DWORD getDeviceNumber( HANDLE deviceHandle )
{
STORAGE_DEVICE_NUMBER sdn;
sdn.DeviceNumber = -1;
DWORD dwBytesReturned = 0;
if ( !DeviceIoControl( deviceHandle,
IOCTL_STORAGE_GET_DEVICE_NUMBER,
NULL, 0, &sdn, sizeof( sdn ),
&dwBytesReturned, NULL ) )
{
// handle error - like a bad handle.
return U32_MAX;
}
return sdn.DeviceNumber;
}
Next here is a method to recognize if a volume is removable media (e.g. like a usb or firewire disk):
bool isRemovableMedia( s32 vol )
{
char rootPath[5] = "#:\\";
rootPath[0] = (char)( vol + 'A' );
char szDosDeviceName[MAX_PATH];
char dosDevicePath[3] = "#:";
// get the drive type
UINT DriveType = GetDriveType( rootPath );
if ( DriveType != DRIVE_REMOVABLE )
return false;
dosDevicePath[0] = (char)( vol + 'A' );
QueryDosDevice( dosDevicePath, szDosDeviceName, MAX_PATH );
if ( strstr( szDosDeviceName,"\\Floppy") != NULL )
{
// its a floppy
return false;
}
return true;
}

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

How do I programmatically send email w/attachment to a known recipient using MAPI in C++? MAPISendMail()

This question is similar, but does not show how to add a recipient.
How do I do both?
We'd like the widest support possible for as many Windows platforms as possible (from XP and greater)
We're using visual studio 2008
Essentially we want to send an email with:
pre-filled destination address
file attachment
subject line
from our program and give the user the ability to add any information or cancel it.
EDIT
I am trying to use MAPISendMail()
I copied much of the code from the questions linked near the top, but I get no email dlg box and the error return I get from the call is: 0x000f - "The system cannot find the drive specified"
If I comment out the lines to set the recipient, it works fine (of course then I have no recipient pre-filled in)
Here is the code:
#include <tchar.h>
#include <windows.h>
#include <mapi.h>
#include <mapix.h>
int _tmain( int argc, wchar_t *argv[] )
{
HMODULE hMapiModule = LoadLibrary( _T( "mapi32.dll" ) );
if ( hMapiModule != NULL )
{
LPMAPIINITIALIZE lpfnMAPIInitialize = NULL;
LPMAPIUNINITIALIZE lpfnMAPIUninitialize = NULL;
LPMAPILOGONEX lpfnMAPILogonEx = NULL;
LPMAPISENDDOCUMENTS lpfnMAPISendDocuments = NULL;
LPMAPISESSION lplhSession = NULL;
LPMAPISENDMAIL lpfnMAPISendMail = NULL;
lpfnMAPIInitialize = (LPMAPIINITIALIZE)GetProcAddress( hMapiModule, "MAPIInitialize" );
lpfnMAPIUninitialize = (LPMAPIUNINITIALIZE)GetProcAddress( hMapiModule, "MAPIUninitialize" );
lpfnMAPILogonEx = (LPMAPILOGONEX)GetProcAddress( hMapiModule, "MAPILogonEx" );
lpfnMAPISendDocuments = (LPMAPISENDDOCUMENTS)GetProcAddress( hMapiModule, "MAPISendDocuments" );
lpfnMAPISendMail = (LPMAPISENDMAIL)GetProcAddress( hMapiModule, "MAPISendMail" );
if ( lpfnMAPIInitialize && lpfnMAPIUninitialize && lpfnMAPILogonEx && lpfnMAPISendDocuments )
{
HRESULT hr = (*lpfnMAPIInitialize)( NULL );
if ( SUCCEEDED( hr ) )
{
hr = (*lpfnMAPILogonEx)( 0, NULL, NULL, MAPI_EXTENDED | MAPI_USE_DEFAULT, &lplhSession );
if ( SUCCEEDED( hr ) )
{
// this opens the email client
// create the msg. We need to add recipients AND subject AND the dmp file
// file attachment
MapiFileDesc filedesc;
::ZeroMemory(&filedesc, sizeof(filedesc));
filedesc.nPosition = (ULONG)-1;
filedesc.lpszPathName = "E:\\Development\\Open\\testmail\\testmail.cpp";
// recipient(s)
MapiRecipDesc recip;
::ZeroMemory(&recip, sizeof(recip));
recip.lpszName = "QA email";
recip.lpszAddress = "qa#myaccount.com";
// the message
MapiMessage msg;
::ZeroMemory(&msg, sizeof(msg));
msg.lpszSubject = "Test";
msg.nRecipCount = 1; // if I comment out this line it works fine...
msg.lpRecips = &recip;
msg.nFileCount = 1;
msg.lpFiles = &filedesc;
hr = (*lpfnMAPISendMail)(0, NULL, &msg, MAPI_LOGON_UI|MAPI_DIALOG, 0);
if ( SUCCEEDED( hr ) )
{
hr = lplhSession->Logoff( 0, 0, 0 );
hr = lplhSession->Release();
lplhSession = NULL;
}
}
}
(*lpfnMAPIUninitialize)();
}
FreeLibrary( hMapiModule );
}
return 0;
}
Oops - I forgot to set
recip.ulRecipClass = MAPI_TO;
Works great now.