SAPI 5.1 64-bit Issues
Overview
This document is intended to help application developers understand and use SAPI functionality on a 64-bit platform. All the discussions below are based on native Win64 programming.
64-bit Programming
Data types:
Win64 supports large memory requirements with 64-bit addressing. All pointers (including handles) are 64 bits long. LONG, INT, BOOL, etc. are still 32 bits long. WPARAM, LPARAM, and LRESULT are pointer based, thus are 64 bits long. Some new data types are defined. For example, fixed-precision data types such as INT32 and INT64; polymorphic data types such as LONG_PTR; specific pointer-precision data types like POINTER_32.
API changes:
There are some API changes in Win64. The constants GWL_xxx, GCL_xxx and DWL_xxx have been undefined. This allows the Win64 compiler to catch errors using the old Get/SetWindowLong APIs. Change your API calls to the new Get/SetWindowLongPtr, and use the newly defined GWLP_xxx, GCLP_xxx, and DWLP_xxx constants. For example, use SetWindowLong(hWnd, GWL_WNDPROC, (LONG)MyWndProc); in Win64 you will receive an error that GWL_WNDPROC is undefined. It should be changed to:SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MyWndProc); To write code that is compatible with both 32-bit and 64-bit versions of Windows, use SetWindowLongPtr.
The following functions have been added to basetsd.h: PtrToLong() and PtrToUlong(), IntToPtr() and UIntToPtr(), HandleToLong() and LongToHandle(), etc. These can help convert values of one type to another. However, IntToPtr sign-extends the INT value, UIntToPtr zero-extends the unsigned int value, LongToPtr sign-extends the long value, and ULongToPtr zero-extends the unsigned long value. Also note that PtrToLong and HandleToLong will truncate the pointer to a 32bit value. These values should not be used as pointers again.
Compiling the code:
Set /W3 compiler option, and clean up all Win64 related compiler warnings, particularly the following codes:
- C4305: Truncation warning. For example, "return": truncation from "unsigned int64" to "long."
- C4311: Truncation warning. For example, "type cast": pointer truncation from "int*_ptr64" to "int."
- C4312: Conversion to bigger-size warning. For example, "type cast": conversion from "int" to "int*_ptr64" of greater size.
- C4318: Passing zero length. For example, passing constant zero as the length to memset.
- C4319: Not operator. For example, "~": zero extending "unsigned long" to "unsigned _int64" of greater size.
- C4313: Calling the printf() family of routines with conflicting conversion type specifiers and arguments. For example, "printf": "%p" in format string conflicts with argument 2 of type "_int64." Another example is calling printf("%x", pointer_value); This causes a truncation of the upper 32 bits. The correct method is to call printf("%p", pointer_value).
- C4242 and C4244: return conversion. For example, "return": conversion from "_int64" to "unsigned int," possible loss of data.
Fix all Win64 related Compiler Errors, particularly --GWL_xxx and GCL_xxx not defined as discussed above.
For additional information on 64-bit Windows programming issues, check the Platform SDK documentation. Select "Windows Development," then choose "Whistler 64-bit Edition."
SAPI issues
SAPI speech recognition (SR) interfaces are disabled in a 64-bit SAPI 5.1. SR related objects like Recognizer (both Inproc and Shared) cannot use CoCreateInstance on a 64-bit platform. Following is a list of the disabled interfaces: IspRecognizer, IspRecoContext, IspPhraseBuilder, ISpCFGEngine, IspGrammarCompiler, IspGramCompBackend, and ISpITNProcessor.
However, SAPI still supports TTS functionalities on a 64-bit platform. Because there is not a 64-bit version of sapi.lib, you cannot get the CLSIDs directly. But you can use CLSIDFromProgID() to get them. Here are the available SAPI ProgIDs on a 64-bit Windows:
CLSIDs | ProgIDs |
---|---|
CLSID_SpVoice | SAPI.SpVoice |
CLSID_SpLexicon | SAPI.SpLexicon |
CLSID_SpUnCompressedLexicon | SAPI.SpUnCompressedLexicon |
CLSID_SpCompressedLexicon | SAPI.SpCompressedLexicon |
CLSID_SpPhoneConverter | SAPI.SpPhoneConverter |
CLSID_SpNullPhoneConverter | SAPI.SpNullPhoneConverter |
CLSID_SpObjectTokenCategory | SAPI.SpObjectTokenCategory |
CLSID_SpObjectTokenEnum | SAPI.SpObjectTokenEnum |
CLSID_SpObjectToken | SAPI.SpObjectToken< |
CLSID_SpDataKey | SAPI.SpDataKey |
CLSID_SpMMAudioEnum | SAPI.SpMMAudioEnum |
CLSID_SpMMAudioIn | SAPI.SpMMAudioIn |
CLSID_SpMMAudioOut | SAPI.SpMMAudioOut |
CLSID_SpStreamFormatConverter | SAPI.SpStreamFormatConverter |
CLSID_SpRecPlayAudio | SAPI.SpRecPlayAudio |
CLSID_SpStream | SAPI.SpStream |
CLSID_SpResourceManager | SAPI.SpResourceManager |
CLSID_SpNotifyTranslator | SAPI.SpNotifyTranslator< |
The following are used in Automation:
CLSIDs | ProgIDs |
---|---|
CLSID_SpTextSelectionInformation | SAPI.SpTextSelectionInformation |
CLSID_SpAudioFormat | SAPI.SpAudioFormat |
CLSID_SpWaveFormatEx | SAPI.SpWaveFormatEx |
CLSID_SpCustomStream | SAPI.SpCustomStream |
CLSID_SpFileStream | SAPI.SpFileStream |
CLSID_SpMemoryStream | SAPI.SpMemoryStream |
Here is a simple example of how to use the ProgIDs on 64-bit applications:
LPCOLESTR pProgID;
CLSID clsid = GUID_NULL;
CComPtr<ISpVoice> cpVoice;
pProgID = L"SAPI.SpVoice";
hr = CLSIDFromProgID(pProgID, &clsid;);
if(SUCCEEDED(hr))
{
hr = cpVoice.CoCreateInstance(clsid);
if(SUCCEEDED(hr))
hr = cpVoice->Speak(L"This is a 64 bit test.", SPF_DEFAULT, 0);
//do some more stuff here.
}