/******************************************************************************* // Copyright Microsoft Corporation. All Rights Reserved. * SPHelper.h * *------------* * Description: * This is the header file for core helper functions implementation. *------------------------------------------------------------------------------- *******************************************************************************/ #ifndef SPHelper_h #define SPHelper_h #ifndef _INC_MALLOC #include #endif #ifndef __sapi_h__ #include #endif #ifndef __sapiddk_h__ #include #endif #ifndef SPError_h #include #endif #ifndef _INC_LIMITS #include #endif #ifndef _INC_MMSYSTEM #include #endif #ifndef __comcat_h__ #include #endif #ifndef _INC_MMREG #include #endif #ifndef __ATLBASE_H__ #include #endif #include #include #include #include #ifndef _PREFAST_ #pragma warning(disable:4068) #endif #ifndef REG_MUI_STRING_TRUNCATE #define REG_MUI_STRING_TRUNCATE 0x00000001 #endif //=== Constants ============================================================== #define sp_countof(x) ((sizeof(x) / sizeof(*(x)))) /*** CSpDynamicString helper class * */ class CSpDynamicString { public: WCHAR * m_psz; CSpDynamicString() { m_psz = NULL; } CSpDynamicString(ULONG cchReserve) { ULONGLONG ullAllocSize = (ULONGLONG) cchReserve * sizeof(WCHAR); if ((ullAllocSize > cchReserve) && ((ULONG) ullAllocSize == ullAllocSize)) { m_psz = (WCHAR *)::CoTaskMemAlloc((ULONG)ullAllocSize); } } WCHAR * operator=(const CSpDynamicString& src) { if(this != &src) { _ASSERT((m_psz == NULL) || (m_psz != src.m_psz)); ::CoTaskMemFree(m_psz); m_psz = src.Copy(); } return m_psz; } WCHAR * operator=(const WCHAR * pSrc) { if(pSrc != m_psz) { Clear(); if (pSrc) { size_t cbNeeded = (wcslen(pSrc) + 1) * sizeof(WCHAR); if (cbNeeded == (ULONG) cbNeeded) { m_psz = (WCHAR *)::CoTaskMemAlloc(cbNeeded); if (m_psz == NULL) { SetLastError(ERROR_OUTOFMEMORY); } else { memcpy(m_psz, pSrc, cbNeeded); } } } } return m_psz; } WCHAR * operator=(const char * pSrc) { Clear(); if (pSrc) { size_t cbNeeded = (::lstrlenA(pSrc) + 1) * sizeof(WCHAR); if ((ULONG) cbNeeded == cbNeeded) { m_psz = (WCHAR *)::CoTaskMemAlloc(cbNeeded); if (m_psz == NULL) { SetLastError(ERROR_OUTOFMEMORY); } else { ::MultiByteToWideChar(CP_ACP, 0, pSrc, -1, m_psz, (ULONG) cbNeeded/sizeof(WCHAR)); } } } return m_psz; } WCHAR * operator=(REFGUID rguid) { Clear(); ::StringFromCLSID(rguid, &m_psz); return m_psz; } /*explicit*/ CSpDynamicString(const WCHAR * pSrc) { m_psz = NULL; operator=(pSrc); } /*explicit*/ CSpDynamicString(const char * pSrc) { m_psz = NULL; operator=(pSrc); } /*explicit*/ CSpDynamicString(const CSpDynamicString& src) { m_psz = src.Copy(); } /*explicit*/ CSpDynamicString(REFGUID rguid) { ::StringFromCLSID(rguid, &m_psz); } ~CSpDynamicString() { ::CoTaskMemFree(m_psz); } unsigned int Length() const { if (m_psz == NULL) return 0; size_t cch = wcslen(m_psz); if ((unsigned int) cch != cch) { _ASSERT((unsigned int) cch == cch); // Too long SetLastError(ERROR_ARITHMETIC_OVERFLOW); return MAXDWORD; } return (unsigned int) cch; } operator WCHAR * () const { return m_psz; } //The assert on operator& usually indicates a bug. If this is really //what is needed, however, take the address of the m_psz member explicitly. WCHAR ** operator&() { _ASSERT(m_psz == NULL); return &m_psz; } // Versions of Append that return HRESULT HRESULT AppendHR(const WCHAR * pszSrc, const ULONG lenSrc) { HRESULT hr = S_OK; if (pszSrc && (lenSrc || !m_psz)) { ULONG lenMe = Length(); ULONGLONG ullcbNew = ((ULONGLONG) lenMe + lenSrc + 1) * sizeof(WCHAR); if (ullcbNew > ((ULONGLONG)lenMe + lenSrc + 1) && ((ULONG) ullcbNew == ullcbNew)) { WCHAR *pszNew = (WCHAR *)::CoTaskMemAlloc((ULONG)ullcbNew); if (pszNew) { if (m_psz) // Could append to an empty string so check... { if (lenMe) { memcpy(pszNew, m_psz, lenMe * sizeof(WCHAR)); } ::CoTaskMemFree(m_psz); } memcpy(pszNew + lenMe, pszSrc, lenSrc * sizeof(WCHAR)); *(pszNew + lenMe + lenSrc) = L'\0'; m_psz = pszNew; } else { hr = E_OUTOFMEMORY; } } else { hr = E_INVALIDARG; } } return hr; } HRESULT AppendHR(const WCHAR * pszSrc) { HRESULT hr = S_OK; if (pszSrc) { size_t lenSrc = wcslen(pszSrc); if ((ULONG) lenSrc != lenSrc) return E_OUTOFMEMORY; hr = AppendHR(pszSrc, (ULONG) lenSrc); } return hr; } // Original versions of Append that do not return HRESULTs WCHAR * Append(const WCHAR * pszSrc) { if(pszSrc && FAILED(AppendHR(pszSrc))) { _ASSERT(FALSE); } return m_psz; } WCHAR * Append(const WCHAR * pszSrc, const ULONG lenSrc) { if(FAILED(AppendHR(pszSrc, lenSrc))) { _ASSERT(FALSE); } return m_psz; } // Version of Append2 that returns HRESULT HRESULT Append2HR(const WCHAR * pszSrc1, const WCHAR * pszSrc2) { HRESULT hr = S_OK; size_t lenSrc1 = pszSrc1 ? wcslen(pszSrc1) : 0; size_t lenSrc2 = pszSrc2 ? wcslen(pszSrc2) : 0; if (lenSrc1 || lenSrc2 || !m_psz) { ULONG lenMe = Length(); size_t cbNew = (lenMe + lenSrc1 + lenSrc2 + 1) * sizeof(WCHAR); if ((ULONG) cbNew != cbNew) return E_OUTOFMEMORY; WCHAR *pszNew = (WCHAR *)::CoTaskMemAlloc(cbNew); if (pszNew) { if (m_psz) // Could append to an empty string so check... { if (lenMe) { memcpy(pszNew, m_psz, lenMe * sizeof(WCHAR)); } ::CoTaskMemFree(m_psz); } // In both of these cases, we copy the trailing NULL so that we're sure it gets // there (if lenSrc2 is 0 then we better copy it from pszSrc1). if (lenSrc1) { memcpy(pszNew + lenMe, pszSrc1, (lenSrc1 + 1) * sizeof(WCHAR)); } if (lenSrc2) { memcpy(pszNew + lenMe + lenSrc1, pszSrc2, (lenSrc2 + 1) * sizeof(WCHAR)); } m_psz = pszNew; } else { hr = E_OUTOFMEMORY; } } return hr; } // Original version of Append2 that does not return HRESULT WCHAR * Append2(const WCHAR * pszSrc1, const WCHAR * pszSrc2) { if(FAILED(Append2HR(pszSrc1, pszSrc2))) { _ASSERT(FALSE); } return m_psz; } WCHAR * Copy() const { if (m_psz) { CSpDynamicString szNew(m_psz); return szNew.Detach(); } return NULL; } CHAR * CopyToChar() const { if (m_psz) { CHAR* psz; ULONG cbNeeded = ::WideCharToMultiByte(CP_ACP, 0, m_psz, -1, NULL, NULL, NULL, NULL); psz = (CHAR *)::CoTaskMemAlloc(cbNeeded); if (psz == NULL) { SetLastError(ERROR_OUTOFMEMORY); } else { ::WideCharToMultiByte(CP_ACP, 0, m_psz, -1, psz, cbNeeded/sizeof(CHAR), NULL, NULL); } return psz; } return NULL; } void Attach(WCHAR __in * pszSrc) { _ASSERT(m_psz == NULL); m_psz = pszSrc; } WCHAR * Detach() { WCHAR * s = m_psz; m_psz = NULL; return s; } void Clear() { ::CoTaskMemFree(m_psz); m_psz = NULL; } bool operator!() const { return (m_psz == NULL); } HRESULT CopyToBSTR(BSTR * pbstr) { if (m_psz) { *pbstr = ::SysAllocString(m_psz); if (*pbstr == NULL) { return E_OUTOFMEMORY; } } else { *pbstr = NULL; } return S_OK; } void TrimToSize(ULONG ulNumChars) { if (m_psz && ulNumChars < Length()) { m_psz[ulNumChars] = 0; } } WCHAR * Compact() { if (m_psz) { ULONG cch = (ULONG) wcslen(m_psz); m_psz = (WCHAR *)::CoTaskMemRealloc(m_psz, (cch + 1) * sizeof(WCHAR)); } return m_psz; } WCHAR * ClearAndGrowTo(ULONG cch) { if (m_psz) { Clear(); } ULONGLONG ullcbNew = (ULONGLONG) cch * sizeof(WCHAR); if ((ullcbNew > cch) && ((ULONG) ullcbNew == ullcbNew)) { m_psz = (WCHAR *)::CoTaskMemAlloc((ULONG)ullcbNew); if (m_psz) { memset(m_psz, 0, (ULONG)ullcbNew); } } return m_psz; } WCHAR * LTrim() { if (m_psz) { WCHAR * pszRead = m_psz; while (iswspace(*pszRead)) { pszRead++; } if (pszRead != m_psz) { WCHAR * pszWrite = m_psz; size_t length = wcslen(pszWrite); StringCchCopyW(pszWrite, length +1, pszRead); } } return m_psz; } WCHAR * RTrim() { if (m_psz) { WCHAR * pszTail = m_psz + wcslen(m_psz); WCHAR * pszZeroTerm = pszTail; while (pszZeroTerm > m_psz && iswspace(pszZeroTerm[-1])) { pszZeroTerm--; } if (pszZeroTerm != pszTail) { *pszZeroTerm = '\0'; } } return m_psz; } WCHAR * TrimBoth() { RTrim(); return LTrim(); } }; // // Simple inline function converts a ulong to a hex string. // inline void SpHexFromUlong(__out_ecount (9) WCHAR * psz, ULONG ul) { // If for some reason we cannot convert a number, set it to 0 #ifndef UNDER_CE if (_ultow_s(ul, psz, 9, 16)) { psz[0] = L'0'; psz[1] = 0; } #else if (psz != NULL) { psz[0] = L'0'; psz[1] = 0; _ultow(ul, psz, 16); } #endif } inline HRESULT SpULongFromHex(const WCHAR *psz, unsigned *pResult) { HRESULT hr = S_OK; *pResult = 0; size_t cLen = wcslen (psz); if (cLen > 8) { hr = E_FAIL; } if (SUCCEEDED(hr)) { // Convert the token to its numeral form in a WCHAR bool fFirst = true; for (size_t i = 0; i < cLen; i++) { int k = tolower (*psz); if (k >= L'a') { k = 10 + k - L'a'; } else { if (k <= L'9') { k -= L'0'; } else { // character in the range ':' .. '@' k = -1; } } if (k < 0 || k > 15) { hr = E_FAIL; break; } if (fFirst) fFirst = false; else *pResult <<= 4; *pResult += (unsigned) k; psz++; } } return hr; } //=== Token helpers inline HRESULT SpGetTokenFromId( const WCHAR * pszTokenId, ISpObjectToken ** ppToken, BOOL fCreateIfNotExist = FALSE) { HRESULT hr; CComPtr cpToken; hr = cpToken.CoCreateInstance(CLSID_SpObjectToken); if (SUCCEEDED(hr)) { hr = cpToken->SetId(NULL, pszTokenId, fCreateIfNotExist); } if (SUCCEEDED(hr)) { *ppToken = cpToken.Detach(); } return hr; } inline HRESULT SpGetCategoryFromId( const WCHAR * pszCategoryId, ISpObjectTokenCategory ** ppCategory, BOOL fCreateIfNotExist = FALSE) { HRESULT hr; CComPtr cpTokenCategory; hr = cpTokenCategory.CoCreateInstance(CLSID_SpObjectTokenCategory); if (SUCCEEDED(hr)) { hr = cpTokenCategory->SetId(pszCategoryId, fCreateIfNotExist); } if (SUCCEEDED(hr)) { *ppCategory = cpTokenCategory.Detach(); } return hr; } inline HRESULT SpGetDefaultTokenIdFromCategoryId( const WCHAR * pszCategoryId, __deref_out WCHAR ** ppszTokenId) { HRESULT hr; CComPtr cpCategory; hr = SpGetCategoryFromId(pszCategoryId, &cpCategory); if (SUCCEEDED(hr)) { hr = cpCategory->GetDefaultTokenId(ppszTokenId); } return hr; } inline HRESULT SpSetDefaultTokenIdForCategoryId( const WCHAR * pszCategoryId, const WCHAR * pszTokenId) { HRESULT hr; CComPtr cpCategory; hr = SpGetCategoryFromId(pszCategoryId, &cpCategory); if (SUCCEEDED(hr)) { hr = cpCategory->SetDefaultTokenId(pszTokenId); } return hr; } inline HRESULT SpGetDefaultTokenFromCategoryId( const WCHAR * pszCategoryId, ISpObjectToken ** ppToken, BOOL fCreateCategoryIfNotExist = TRUE) { HRESULT hr; CComPtr cpCategory; hr = SpGetCategoryFromId(pszCategoryId, &cpCategory, fCreateCategoryIfNotExist); if (SUCCEEDED(hr)) { WCHAR * pszTokenId; hr = cpCategory->GetDefaultTokenId(&pszTokenId); if (SUCCEEDED(hr)) { hr = SpGetTokenFromId(pszTokenId, ppToken); ::CoTaskMemFree(pszTokenId); } } return hr; } inline HRESULT SpSetDefaultTokenForCategoryId( const WCHAR * pszCategoryId, ISpObjectToken * pToken) { HRESULT hr; WCHAR * pszTokenId; hr = pToken->GetId(&pszTokenId); if (SUCCEEDED(hr)) { hr = SpSetDefaultTokenIdForCategoryId(pszCategoryId, pszTokenId); ::CoTaskMemFree(pszTokenId); } return hr; } inline HRESULT SpSetCommonTokenData( ISpObjectToken * pToken, const CLSID * pclsid, const WCHAR * pszLangIndependentName, LANGID langid, const WCHAR * pszLangDependentName, ISpDataKey ** ppDataKeyAttribs) { HRESULT hr = S_OK; // Set the new token's CLSID (if specified) if (SUCCEEDED(hr) && pclsid) { CSpDynamicString dstrClsid; hr = StringFromCLSID(*pclsid, &dstrClsid); if (SUCCEEDED(hr)) { hr = pToken->SetStringValue(SPTOKENVALUE_CLSID, dstrClsid); } } // Set the token's lang independent name if (SUCCEEDED(hr) && pszLangIndependentName) { hr = pToken->SetStringValue(NULL, pszLangIndependentName); } // Set the token's lang dependent name if (SUCCEEDED(hr) && pszLangDependentName) { WCHAR szLangId[10]; StringCbPrintfW (szLangId, sizeof (szLangId), L"%x", langid); hr = pToken->SetStringValue(szLangId, pszLangDependentName); } // Open the attributes key if requested if (SUCCEEDED(hr) && ppDataKeyAttribs) { hr = pToken->CreateKey(L"Attributes", ppDataKeyAttribs); } return hr; } inline HRESULT SpCreateNewToken( const WCHAR * pszTokenId, ISpObjectToken ** ppToken) { HRESULT hr; // Forcefully create the token hr = SpGetTokenFromId(pszTokenId, ppToken, TRUE); return hr; } inline HRESULT SPCoCreateGuid(GUID *pGuid) { HRESULT hr = S_OK; hr = CoCreateGuid(pGuid); return hr; } inline HRESULT SpCreateNewToken( const WCHAR * pszCategoryId, const WCHAR * pszTokenKeyName, ISpObjectToken ** ppToken) { HRESULT hr; // Forcefully create the category CComPtr cpCategory; hr = SpGetCategoryFromId(pszCategoryId, &cpCategory, TRUE); // Come up with a token key name if one wasn't specified CSpDynamicString dstrTokenKeyName; if (SUCCEEDED(hr)) { if (pszTokenKeyName == NULL) { GUID guidTokenKeyName; hr = SPCoCreateGuid(&guidTokenKeyName); if (SUCCEEDED(hr)) { hr = StringFromCLSID(guidTokenKeyName, &dstrTokenKeyName); } if (SUCCEEDED(hr)) { pszTokenKeyName = dstrTokenKeyName; } } } // Build the token id CSpDynamicString dstrTokenId; if (SUCCEEDED(hr)) { dstrTokenId = pszCategoryId; dstrTokenId.Append2(L"\\Tokens\\", pszTokenKeyName); } // Forcefully create the token if (SUCCEEDED(hr)) { hr = SpGetTokenFromId(dstrTokenId, ppToken, TRUE); } return hr; } inline HRESULT SpCreateNewTokenEx( const WCHAR * pszCategoryId, const WCHAR * pszTokenKeyName, const CLSID * pclsid, const WCHAR * pszLangIndependentName, LANGID langid, const WCHAR * pszLangDependentName, ISpObjectToken ** ppToken, ISpDataKey ** ppDataKeyAttribs) { HRESULT hr; // Create the new token hr = SpCreateNewToken(pszCategoryId, pszTokenKeyName, ppToken); // Now set the extra data if (SUCCEEDED(hr)) { hr = SpSetCommonTokenData( *ppToken, pclsid, pszLangIndependentName, langid, pszLangDependentName, ppDataKeyAttribs); } return hr; } inline HRESULT SpCreateNewTokenEx( const WCHAR * pszTokenId, const CLSID * pclsid, const WCHAR * pszLangIndependentName, LANGID langid, const WCHAR * pszLangDependentName, ISpObjectToken ** ppToken, ISpDataKey ** ppDataKeyAttribs) { HRESULT hr; // Create the new token hr = SpCreateNewToken(pszTokenId, ppToken); // Now set the extra data if (SUCCEEDED(hr)) { hr = SpSetCommonTokenData( *ppToken, pclsid, pszLangIndependentName, langid, pszLangDependentName, ppDataKeyAttribs); } return hr; } inline HRESULT SpEnumTokens( const WCHAR * pszCategoryId, const WCHAR * pszReqAttribs, const WCHAR * pszOptAttribs, IEnumSpObjectTokens ** ppEnum) { HRESULT hr = S_OK; CComPtr cpCategory; hr = SpGetCategoryFromId(pszCategoryId, &cpCategory); if (SUCCEEDED(hr)) { hr = cpCategory->EnumTokens( pszReqAttribs, pszOptAttribs, ppEnum); } return hr; } inline HRESULT SpFindBestToken( const WCHAR * pszCategoryId, const WCHAR * pszReqAttribs, const WCHAR * pszOptAttribs, ISpObjectToken **ppObjectToken) { HRESULT hr = S_OK; const WCHAR *pszVendorPreferred = L"VendorPreferred"; const ULONG ulLenVendorPreferred = (ULONG) wcslen(pszVendorPreferred); // append VendorPreferred to the end of pszOptAttribs to force this preference ULONG ulLen; if (pszOptAttribs) { hr = ULongAdd((ULONG)wcslen(pszOptAttribs), ulLenVendorPreferred, &ulLen); if (SUCCEEDED(hr)) { hr = ULongAdd(ulLen, 1 + 1, &ulLen); // including 1 char here for null terminator } } else { hr = ULongAdd(ulLenVendorPreferred, 1, &ulLen); // including 1 char here for null terminator } if (FAILED(hr)) { hr = E_INVALIDARG; } else { WCHAR *pszOptAttribsVendorPref = new WCHAR[ulLen]; if (pszOptAttribsVendorPref) { if (pszOptAttribs) { StringCchCopyW (pszOptAttribsVendorPref, ulLen, pszOptAttribs); StringCchCatW (pszOptAttribsVendorPref, ulLen, L";"); StringCchCatW (pszOptAttribsVendorPref, ulLen, pszVendorPreferred); } else { StringCchCopyW (pszOptAttribsVendorPref, ulLen, pszVendorPreferred); } } else { hr = E_OUTOFMEMORY; } CComPtr cpEnum; if (SUCCEEDED(hr)) { hr = SpEnumTokens(pszCategoryId, pszReqAttribs, pszOptAttribsVendorPref, &cpEnum); } delete[] pszOptAttribsVendorPref; if (SUCCEEDED(hr)) { hr = cpEnum->Next(1, ppObjectToken, NULL); if (hr == S_FALSE) { *ppObjectToken = NULL; hr = SPERR_NOT_FOUND; } } } return hr; } template HRESULT SpCreateObjectFromToken(ISpObjectToken * pToken, T ** ppObject, IUnknown * pUnkOuter = NULL, DWORD dwClsCtxt = CLSCTX_ALL) { HRESULT hr; hr = pToken->CreateInstance(pUnkOuter, dwClsCtxt, __uuidof(T), (void **)ppObject); return hr; } template HRESULT SpCreateObjectFromTokenId(const WCHAR * pszTokenId, T ** ppObject, IUnknown * pUnkOuter = NULL, DWORD dwClsCtxt = CLSCTX_ALL) { ISpObjectToken * pToken; HRESULT hr = SpGetTokenFromId(pszTokenId, &pToken); if (SUCCEEDED(hr)) { hr = SpCreateObjectFromToken(pToken, ppObject, pUnkOuter, dwClsCtxt); pToken->Release(); } return hr; } template HRESULT SpCreateDefaultObjectFromCategoryId(const WCHAR * pszCategoryId, T ** ppObject, IUnknown * pUnkOuter = NULL, DWORD dwClsCtxt = CLSCTX_ALL) { ISpObjectToken * pToken; HRESULT hr = SpGetDefaultTokenFromCategoryId(pszCategoryId, &pToken); if (SUCCEEDED(hr)) { hr = SpCreateObjectFromToken(pToken, ppObject, pUnkOuter, dwClsCtxt); pToken->Release(); } return hr; } template HRESULT SpCreateBestObject( const WCHAR * pszCategoryId, const WCHAR * pszReqAttribs, const WCHAR * pszOptAttribs, T ** ppObject, IUnknown * pUnkOuter = NULL, DWORD dwClsCtxt = CLSCTX_ALL) { HRESULT hr; CComPtr cpToken; hr = SpFindBestToken(pszCategoryId, pszReqAttribs, pszOptAttribs, &cpToken); if (SUCCEEDED(hr)) { hr = SpCreateObjectFromToken(cpToken, ppObject, pUnkOuter, dwClsCtxt); } return hr; } inline HRESULT SpCreatePhoneConverter( LANGID LangID, const WCHAR * pszReqAttribs, const WCHAR * pszOptAttribs, ISpPhoneConverter ** ppPhoneConverter) { HRESULT hr = SPERR_NOT_FOUND; // If not IPA or UPS if (LangID != 0) { CSpDynamicString dstrReqAttribs; if (pszReqAttribs) { dstrReqAttribs = pszReqAttribs; dstrReqAttribs.Append(L";"); } WCHAR szLang[MAX_PATH]; SpHexFromUlong(szLang, LangID); WCHAR szLangCondition[MAX_PATH]; StringCchCopyW(szLangCondition, MAX_PATH, L"Language="); StringCchCatW(szLangCondition, MAX_PATH, szLang); dstrReqAttribs.Append(szLangCondition); hr = SpCreateBestObject(SPCAT_PHONECONVERTERS, dstrReqAttribs, pszOptAttribs, ppPhoneConverter); } #if _SAPI_VER >= 0x053 // If we cannot find a phone converter, use the Universal Phone Converter as default if (hr == SPERR_NOT_FOUND) { hr = CoCreateInstance(CLSID_SpPhoneConverter, NULL, CLSCTX_ALL, IID_ISpPhoneConverter, (LPVOID*)ppPhoneConverter); if (SUCCEEDED(hr)) { ISpPhoneticAlphabetSelection * pAlphabetSelection = NULL; hr = (*ppPhoneConverter)->QueryInterface(IID_ISpPhoneticAlphabetSelection, (LPVOID*) &pAlphabetSelection); if (SUCCEEDED(hr)) { hr = pAlphabetSelection->SetAlphabetToUPS(TRUE); pAlphabetSelection->Release(); } else { // We cannot query for the new interface, reset the error code hr = SPERR_NOT_FOUND; } if (FAILED(hr)) { (*ppPhoneConverter)->Release(); *ppPhoneConverter = NULL; } } } #endif return hr; } inline HRESULT SpGetLanguageFromToken(ISpObjectToken * pToken, LANGID * plangid) { HRESULT hr = S_OK; CComPtr cpDataKeyAttribs; hr = pToken->OpenKey(SPTOKENKEY_ATTRIBUTES, &cpDataKeyAttribs); CSpDynamicString dstrLanguage; if (SUCCEEDED(hr)) { hr = cpDataKeyAttribs->GetStringValue(L"Language", &dstrLanguage); } if (SUCCEEDED(hr)) { const WCHAR *pszLangId = dstrLanguage; WCHAR achHexNumber [5]; // LangIds cannot be more than 4 characters longs // check if the langid is in the format "409;9" - extract the "409" in this case const WCHAR *pSemiColon = wcschr (pszLangId, L';'); if (pSemiColon) { size_t cLen = pSemiColon - dstrLanguage; if (cLen <= 4) { StringCbCopyNW (achHexNumber, sizeof (achHexNumber), dstrLanguage, cLen * sizeof (achHexNumber [0])); pszLangId = achHexNumber; } } unsigned langid; if (FAILED (SpULongFromHex(pszLangId, &langid))) { hr = SPERR_INVALID_TOKEN_ID; } else { *plangid = (LANGID) langid; } } return hr; } inline HRESULT SpGetLanguageFromVoiceToken(ISpObjectToken * pToken, LANGID * plangid) { return SpGetLanguageFromToken(pToken, plangid); } /**************************************************************************** * SpHrFromWin32 * *---------------* * Description: * This inline function works around a basic problem with the macro * HRESULT_FROM_WIN32. The macro forces the expresion in ( ) to be evaluated * two times. By using this inline function, the expression will only be * evaluated once. * * Returns: * HRESULT of converted Win32 error code * *****************************************************************************/ inline HRESULT SpHrFromWin32(DWORD dwErr) { return HRESULT_FROM_WIN32(dwErr); } /**************************************************************************** * SpHrFromLastWin32Error * *------------------------* * Description: * This simple inline function is used to return a converted HRESULT * from the Win32 function ::GetLastError. Note that using HRESULT_FROM_WIN32 * will evaluate the error code twice so we don't want to use: * * HRESULT_FROM_WIN32(::GetLastError()) * * since that will call GetLastError twice. * * Returns: * HRESULT for ::GetLastError(). If the HRESULT is a success code, this * function will return E_FAIL to guarantee an error return code. * *****************************************************************************/ inline HRESULT SpHrFromLastWin32Error() { HRESULT hr = SpHrFromWin32(::GetLastError()); return FAILED(hr) ? hr : E_FAIL; } /**************************************************************************** * SpGetUserDefaultUILanguage * *----------------------------* * Description: * Now that we only support XP & Above, this is a straight call to * GetUserDefaultUILanguage * * Returns: * Default UI language * *****************************************************************************/ inline LANGID SpGetUserDefaultUILanguage(void) { return GetUserDefaultUILanguage() ; } inline HRESULT SpGetDescription(ISpObjectToken * pObjToken, __deref_out WCHAR ** ppszDescription, LANGID Language = SpGetUserDefaultUILanguage()) { WCHAR szLangId[10]; HRESULT hr = S_OK; #if _SAPI_VER >= 0x053 WCHAR* pRegKeyPath = 0; WCHAR* pszTemp = 0; HKEY Handle = NULL; // Windows Vista does not encourage localized strings in the registry // When running on Windows Vista query the localized engine name from a resource dll OSVERSIONINFO ver; ver.dwOSVersionInfoSize = sizeof( ver ); if (ppszDescription == NULL) { return E_POINTER; } *ppszDescription = NULL; if( ( ::GetVersionEx( &ver ) == TRUE ) && ( ver.dwMajorVersion >= 6 ) ) { // If we reach this code we are running under Windows Vista HMODULE hmodAdvapi32Dll = NULL; typedef HRESULT (WINAPI* LPFN_RegLoadMUIStringW)(HKEY, LPCWSTR, LPWSTR, DWORD, LPDWORD, DWORD, LPCWSTR); LPFN_RegLoadMUIStringW pfnRegLoadMUIStringW = NULL; // Delay bind with RegLoadMUIStringW since this function is not supported on previous versions of advapi32.dll // RegLoadMUIStringW is supported only on advapi32.dll that ships with Windows Vista and above // Calling RegLoadMUIStringW directly makes the loader try to resolve the function reference at load time which breaks, // hence we manually load advapi32.dll, query for the function pointer and invoke it. hmodAdvapi32Dll = ::LoadLibrary(TEXT("advapi32.dll")); if(hmodAdvapi32Dll) { #ifdef UNDER_CE pfnRegLoadMUIStringW = (LPFN_RegLoadMUIStringW) ::GetProcAddress(hmodAdvapi32Dll, L"RegLoadMUIStringW"); #else pfnRegLoadMUIStringW = (LPFN_RegLoadMUIStringW) ::GetProcAddress(hmodAdvapi32Dll, "RegLoadMUIStringW"); #endif if (!pfnRegLoadMUIStringW) { // This should not happen in Vista _ASSERT (pfnRegLoadMUIStringW); hr = TYPE_E_DLLFUNCTIONNOTFOUND; } } else { hr = HRESULT_FROM_WIN32(ERROR_DLL_NOT_FOUND); } if (SUCCEEDED(hr)) { hr = pObjToken->GetId(&pszTemp); } if (SUCCEEDED(hr)) { LONG lErrorCode = ERROR_SUCCESS; pRegKeyPath = wcschr(pszTemp, L'\\'); // Find the first occurance of '\\' in the absolute registry key path if(pRegKeyPath) { *pRegKeyPath = L'\0'; pRegKeyPath++; // pRegKeyPath now points to the path to the recognizer token under the HKLM or HKCR hive *ppszDescription = 0; // Open the registry key for read and get the handle if (wcsncmp(pszTemp, L"HKEY_LOCAL_MACHINE", MAX_PATH) == 0) { lErrorCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE, pRegKeyPath, 0, KEY_QUERY_VALUE, &Handle); } else if (wcsncmp(pszTemp, L"HKEY_CURRENT_USER", MAX_PATH) == 0) { lErrorCode = RegOpenKeyExW(HKEY_CURRENT_USER, pRegKeyPath, 0, KEY_QUERY_VALUE, &Handle); } else { lErrorCode = ERROR_BAD_ARGUMENTS; } // Use MUI RegLoadMUIStringW API to load the localized string if(ERROR_SUCCESS == lErrorCode) { *ppszDescription = (WCHAR*) CoTaskMemAlloc(MAX_PATH * sizeof(WCHAR)); // This should be enough memory to allocate the localized Engine Name lErrorCode = (*pfnRegLoadMUIStringW) (Handle, SR_LOCALIZED_DESCRIPTION, *ppszDescription, MAX_PATH * sizeof(WCHAR), NULL, REG_MUI_STRING_TRUNCATE, NULL); } } else { // pRegKeyPath should never be 0 if we are querying for relative hkey path lErrorCode = ERROR_BAD_ARGUMENTS; } hr = HRESULT_FROM_WIN32(lErrorCode); } // Close registry key handle if(Handle) { RegCloseKey(Handle); } // Free memory allocated to locals if(pszTemp) { CoTaskMemFree(pszTemp); } if (hmodAdvapi32Dll) { ::FreeLibrary(hmodAdvapi32Dll); } } else { hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); } _ASSERT(FAILED(hr) || *ppszDescription != NULL); // If running on OSes released before Windows Vista query the localized string from the registry // If RegLoadMUIStringW failed to retrieved the localized Engine name retrieve the localized string from the fallback (Default) attribute #else hr = E_FAIL; #endif // _SAPI_VER >= 0x053 if (FAILED(hr)) { // Free memory allocated above if necessary if (*ppszDescription != NULL) { CoTaskMemFree(*ppszDescription); *ppszDescription = NULL; } SpHexFromUlong(szLangId, Language); hr = pObjToken->GetStringValue(szLangId, ppszDescription); if (hr == SPERR_NOT_FOUND) { hr = pObjToken->GetStringValue(NULL, ppszDescription); } } return hr; } inline HRESULT SpSetDescription(ISpObjectToken * pObjToken, const WCHAR * pszDescription, LANGID Language = SpGetUserDefaultUILanguage(), BOOL fSetLangIndependentId = TRUE) { WCHAR szLangId[10]; SpHexFromUlong(szLangId, Language); HRESULT hr = pObjToken->SetStringValue(szLangId, pszDescription); if (SUCCEEDED(hr) && fSetLangIndependentId) { hr = pObjToken->SetStringValue(NULL, pszDescription); } return hr; } /**************************************************************************** * SpConvertStreamFormatEnum * *---------------------------* * Description: * This method converts the specified stream format into a wave format * structure. * *****************************************************************************/ inline HRESULT SpConvertStreamFormatEnum(SPSTREAMFORMAT eFormat, GUID * pFormatId, WAVEFORMATEX ** ppCoMemWaveFormatEx) { HRESULT hr = S_OK; if(pFormatId==NULL || ppCoMemWaveFormatEx==NULL) { return E_INVALIDARG; } const GUID * pFmtGuid = &GUID_NULL; // Assume failure case if( eFormat >= SPSF_8kHz8BitMono && eFormat <= SPSF_48kHz16BitStereo ) { WAVEFORMATEX * pwfex = (WAVEFORMATEX *)::CoTaskMemAlloc(sizeof(WAVEFORMATEX)); *ppCoMemWaveFormatEx = pwfex; if (pwfex) { DWORD dwIndex = eFormat - SPSF_8kHz8BitMono; BOOL bIsStereo = dwIndex & 0x1; BOOL bIs16 = dwIndex & 0x2; DWORD dwKHZ = (dwIndex & 0x3c) >> 2; static const DWORD adwKHZ[] = { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 }; pwfex->wFormatTag = WAVE_FORMAT_PCM; pwfex->nChannels = pwfex->nBlockAlign = (WORD)(bIsStereo ? 2 : 1); pwfex->nSamplesPerSec = (dwKHZ < sizeof(adwKHZ)/sizeof(adwKHZ[0])) ? adwKHZ[dwKHZ] : adwKHZ[0]; pwfex->wBitsPerSample = 8; if (bIs16) { pwfex->wBitsPerSample *= 2; pwfex->nBlockAlign *= 2; } pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nBlockAlign; pwfex->cbSize = 0; pFmtGuid = &SPDFID_WaveFormatEx; } else { hr = E_OUTOFMEMORY; } } else if( eFormat == SPSF_TrueSpeech_8kHz1BitMono ) { int NumBytes = sizeof( WAVEFORMATEX ) + 32; WAVEFORMATEX * pwfex = (WAVEFORMATEX *)::CoTaskMemAlloc( NumBytes ); *ppCoMemWaveFormatEx = pwfex; if( pwfex ) { memset( pwfex, 0, NumBytes ); pwfex->wFormatTag = WAVE_FORMAT_DSPGROUP_TRUESPEECH; pwfex->nChannels = 1; pwfex->nSamplesPerSec = 8000; pwfex->nAvgBytesPerSec = 1067; pwfex->nBlockAlign = 32; pwfex->wBitsPerSample = 1; pwfex->cbSize = 32; BYTE* pExtra = ((BYTE*)pwfex) + sizeof( WAVEFORMATEX ); pExtra[0] = 1; pExtra[2] = 0xF0; pFmtGuid = &SPDFID_WaveFormatEx; } else { hr = E_OUTOFMEMORY; } } else if( (eFormat >= SPSF_CCITT_ALaw_8kHzMono ) && (eFormat <= SPSF_CCITT_ALaw_44kHzStereo ) ) { WAVEFORMATEX * pwfex = (WAVEFORMATEX *)::CoTaskMemAlloc( sizeof(WAVEFORMATEX) ); *ppCoMemWaveFormatEx = pwfex; if( pwfex ) { memset( pwfex, 0, sizeof(WAVEFORMATEX) ); DWORD dwIndex = eFormat - SPSF_CCITT_ALaw_8kHzMono; DWORD dwKHZ = dwIndex / 2; static const DWORD adwKHZ[] = { 8000, 11025, 22050, 44100 }; BOOL bIsStereo = dwIndex & 0x1; pwfex->wFormatTag = WAVE_FORMAT_ALAW; pwfex->nChannels = pwfex->nBlockAlign = (WORD)(bIsStereo ? 2 : 1); pwfex->nSamplesPerSec = (dwKHZ < sizeof(adwKHZ)/sizeof(adwKHZ[0])) ? adwKHZ[dwKHZ] : adwKHZ[0]; pwfex->wBitsPerSample = 8; pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nBlockAlign; pwfex->cbSize = 0; pFmtGuid = &SPDFID_WaveFormatEx; } else { hr = E_OUTOFMEMORY; } } else if( (eFormat >= SPSF_CCITT_uLaw_8kHzMono ) && (eFormat <= SPSF_CCITT_uLaw_44kHzStereo ) ) { WAVEFORMATEX * pwfex = (WAVEFORMATEX *)::CoTaskMemAlloc( sizeof(WAVEFORMATEX) ); *ppCoMemWaveFormatEx = pwfex; if( pwfex ) { memset( pwfex, 0, sizeof(WAVEFORMATEX) ); DWORD dwIndex = eFormat - SPSF_CCITT_uLaw_8kHzMono; DWORD dwKHZ = dwIndex / 2; static const DWORD adwKHZ[] = { 8000, 11025, 22050, 44100 }; BOOL bIsStereo = dwIndex & 0x1; pwfex->wFormatTag = WAVE_FORMAT_MULAW; pwfex->nChannels = pwfex->nBlockAlign = (WORD)(bIsStereo ? 2 : 1); pwfex->nSamplesPerSec = (dwKHZ < sizeof(adwKHZ)/sizeof(adwKHZ[0])) ? adwKHZ[dwKHZ] : adwKHZ[0]; pwfex->wBitsPerSample = 8; pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nBlockAlign; pwfex->cbSize = 0; pFmtGuid = &SPDFID_WaveFormatEx; } else { hr = E_OUTOFMEMORY; } } else if( (eFormat >= SPSF_ADPCM_8kHzMono ) && (eFormat <= SPSF_ADPCM_44kHzStereo ) ) { int NumBytes = sizeof( WAVEFORMATEX ) + 32; WAVEFORMATEX * pwfex = (WAVEFORMATEX *)::CoTaskMemAlloc( NumBytes ); *ppCoMemWaveFormatEx = pwfex; if( pwfex ) { //--- Some of these values seem odd. We used what the codec told us. static const DWORD adwKHZ[] = { 8000, 11025, 22050, 44100 }; static const DWORD BytesPerSec[] = { 4096, 8192, 5644, 11289, 11155, 22311, 22179, 44359 }; static const DWORD BlockAlign[] = { 256, 256, 512, 1024 }; static const BYTE Extra811[32] = { 0xF4, 0x01, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x40, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xCC, 0x01, 0x30, 0xFF, 0x88, 0x01, 0x18, 0xFF }; static const BYTE Extra22[32] = { 0xF4, 0x03, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x40, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xCC, 0x01, 0x30, 0xFF, 0x88, 0x01, 0x18, 0xFF }; static const BYTE Extra44[32] = { 0xF4, 0x07, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x40, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xCC, 0x01, 0x30, 0xFF, 0x88, 0x01, 0x18, 0xFF }; static const BYTE* Extra[4] = { Extra811, Extra811, Extra22, Extra44 }; memset( pwfex, 0, NumBytes ); DWORD dwIndex = eFormat - SPSF_ADPCM_8kHzMono; DWORD dwKHZ = dwIndex / 2; BOOL bIsStereo = dwIndex & 0x1; pwfex->wFormatTag = WAVE_FORMAT_ADPCM; pwfex->nChannels = (WORD)(bIsStereo ? 2 : 1); pwfex->nSamplesPerSec = (dwKHZ < sizeof(adwKHZ)/sizeof(adwKHZ[0])) ? adwKHZ[dwKHZ] : adwKHZ[0]; pwfex->nAvgBytesPerSec = (dwIndex < sizeof(BytesPerSec)/sizeof(BytesPerSec[0])) ? BytesPerSec[dwIndex] : BytesPerSec[0]; pwfex->nBlockAlign = (WORD)(((dwKHZ < sizeof(BlockAlign)/sizeof(BlockAlign[0])) ? BlockAlign[dwKHZ] : BlockAlign[0]) * pwfex->nChannels); pwfex->wBitsPerSample = 4; pwfex->cbSize = 32; BYTE* pExtra = ((BYTE*)pwfex) + sizeof( WAVEFORMATEX ); memcpy( pExtra, (dwKHZ < sizeof(Extra)/sizeof(Extra[0])) ? Extra[dwKHZ] : Extra[0], 32 ); pFmtGuid = &SPDFID_WaveFormatEx; } else { hr = E_OUTOFMEMORY; } } else if( (eFormat >= SPSF_GSM610_8kHzMono ) && (eFormat <= SPSF_GSM610_44kHzMono ) ) { int NumBytes = sizeof( WAVEFORMATEX ) + 2; WAVEFORMATEX * pwfex = (WAVEFORMATEX *)::CoTaskMemAlloc( NumBytes ); *ppCoMemWaveFormatEx = pwfex; if( pwfex ) { //--- Some of these values seem odd. We used what the codec told us. static const DWORD adwKHZ[] = { 8000, 11025, 22050, 44100 }; static const DWORD BytesPerSec[] = { 1625, 2239, 4478, 8957 }; memset( pwfex, 0, NumBytes ); DWORD dwIndex = eFormat - SPSF_GSM610_8kHzMono; pwfex->wFormatTag = WAVE_FORMAT_GSM610; pwfex->nChannels = 1; pwfex->nSamplesPerSec = (dwIndex < sizeof(adwKHZ)/sizeof(adwKHZ[0])) ? adwKHZ[dwIndex] : adwKHZ[0]; pwfex->nAvgBytesPerSec = BytesPerSec[dwIndex]; pwfex->nBlockAlign = 65; pwfex->wBitsPerSample = 0; pwfex->cbSize = 2; BYTE* pExtra = ((BYTE*)pwfex) + sizeof( WAVEFORMATEX ); pExtra[0] = 0x40; pExtra[1] = 0x01; pFmtGuid = &SPDFID_WaveFormatEx; } else { hr = E_OUTOFMEMORY; } } else { *ppCoMemWaveFormatEx = NULL; switch (eFormat) { case SPSF_NoAssignedFormat: break; case SPSF_Text: pFmtGuid = &SPDFID_Text; break; default: hr = E_INVALIDARG; break; } } *pFormatId = *pFmtGuid; return hr; } class CSpStreamFormat { public: GUID m_guidFormatId; WAVEFORMATEX * m_pCoMemWaveFormatEx; static HRESULT CoMemCopyWFEX(const WAVEFORMATEX * pSrc, WAVEFORMATEX ** ppCoMemWFEX) { if (pSrc == NULL || pSrc->nAvgBytesPerSec == 0 || pSrc->nBlockAlign == 0 || pSrc->nChannels == 0) // There are other fields like wBitsPerSample but these can be zero in some formats. { return E_INVALIDARG; } ULONG cb = sizeof(WAVEFORMATEX); if (pSrc->wFormatTag != WAVE_FORMAT_PCM) { // Add the extra data size in but ignore for WAVE_FORMAT_PCM {accoring to MSDN this should be ignored}. cb += pSrc->cbSize; if (cb < pSrc->cbSize) { return E_INVALIDARG; } } *ppCoMemWFEX = (WAVEFORMATEX *)::CoTaskMemAlloc(cb); if (*ppCoMemWFEX) { memcpy(*ppCoMemWFEX, pSrc, cb); if ((*ppCoMemWFEX)->wFormatTag == WAVE_FORMAT_PCM) { (*ppCoMemWFEX)->cbSize = 0; // Always set cbSize to zero for WAVE_FORMAT_PCM. } return S_OK; } else { return E_OUTOFMEMORY; } } CSpStreamFormat() { m_guidFormatId = GUID_NULL; m_pCoMemWaveFormatEx = NULL; } CSpStreamFormat(SPSTREAMFORMAT eFormat, HRESULT * phr) { *phr = SpConvertStreamFormatEnum(eFormat, &m_guidFormatId, &m_pCoMemWaveFormatEx); } CSpStreamFormat(const WAVEFORMATEX * pWaveFormatEx, HRESULT * phr) { _ASSERT(pWaveFormatEx); *phr = CoMemCopyWFEX(pWaveFormatEx, &m_pCoMemWaveFormatEx); m_guidFormatId = SUCCEEDED(*phr) ? SPDFID_WaveFormatEx : GUID_NULL; } ~CSpStreamFormat() { ::CoTaskMemFree(m_pCoMemWaveFormatEx); } void Clear() { ::CoTaskMemFree(m_pCoMemWaveFormatEx); m_pCoMemWaveFormatEx = NULL; memset(&m_guidFormatId, 0, sizeof(m_guidFormatId)); } const GUID & FormatId() const { return m_guidFormatId; } const WAVEFORMATEX * WaveFormatExPtr() const { return m_pCoMemWaveFormatEx; } HRESULT AssignFormat(SPSTREAMFORMAT eFormat) { ::CoTaskMemFree(m_pCoMemWaveFormatEx); return SpConvertStreamFormatEnum(eFormat, &m_guidFormatId, &m_pCoMemWaveFormatEx); } HRESULT AssignFormat(ISpStreamFormat * pStream) { ::CoTaskMemFree(m_pCoMemWaveFormatEx); m_pCoMemWaveFormatEx = NULL; HRESULT hr = pStream->GetFormat(&m_guidFormatId, &m_pCoMemWaveFormatEx); if (SUCCEEDED(hr) && m_pCoMemWaveFormatEx) { if (m_pCoMemWaveFormatEx->wFormatTag == WAVE_FORMAT_PCM) { m_pCoMemWaveFormatEx->cbSize = 0; // Always set cbSize to zero for WAVE_FORMAT_PCM. } if (m_pCoMemWaveFormatEx->nAvgBytesPerSec == 0 || m_pCoMemWaveFormatEx->nBlockAlign == 0 || m_pCoMemWaveFormatEx->nChannels == 0) { Clear(); hr = E_INVALIDARG; } } return hr; } HRESULT AssignFormat(const WAVEFORMATEX * pWaveFormatEx) { if (pWaveFormatEx->nBlockAlign == 0) { return E_INVALIDARG; } ::CoTaskMemFree(m_pCoMemWaveFormatEx); HRESULT hr = CoMemCopyWFEX(pWaveFormatEx, &m_pCoMemWaveFormatEx); m_guidFormatId = SUCCEEDED(hr) ? SPDFID_WaveFormatEx : GUID_NULL; return hr; } HRESULT AssignFormat(REFGUID rguidFormatId, const WAVEFORMATEX * pWaveFormatEx) { HRESULT hr = S_OK; m_guidFormatId = rguidFormatId; ::CoTaskMemFree(m_pCoMemWaveFormatEx); m_pCoMemWaveFormatEx = NULL; if (rguidFormatId == SPDFID_WaveFormatEx) { if (pWaveFormatEx == NULL) { hr = E_INVALIDARG; } else { hr = CoMemCopyWFEX(pWaveFormatEx, &m_pCoMemWaveFormatEx); } if (FAILED(hr)) { m_guidFormatId = GUID_NULL; } } return hr; } BOOL IsEqual(REFGUID rguidFormatId, const WAVEFORMATEX * pwfex) const { if (rguidFormatId == m_guidFormatId) { if (m_pCoMemWaveFormatEx) { if (pwfex && pwfex->cbSize == m_pCoMemWaveFormatEx->cbSize && memcmp(m_pCoMemWaveFormatEx, pwfex, sizeof(WAVEFORMATEX) + pwfex->cbSize) == 0) { return TRUE; } } else { return (pwfex == NULL); } } return FALSE; } HRESULT ParamValidateAssignFormat(REFGUID rguidFormatId, const WAVEFORMATEX * pWaveFormatEx, BOOL fRequireWaveFormat = FALSE) { if ((pWaveFormatEx && (rguidFormatId != SPDFID_WaveFormatEx)) || (fRequireWaveFormat && pWaveFormatEx == NULL)) { return E_INVALIDARG; } return AssignFormat(rguidFormatId, pWaveFormatEx); } SPSTREAMFORMAT ComputeFormatEnum() { if (m_guidFormatId == GUID_NULL) { return SPSF_NoAssignedFormat; } if (m_guidFormatId == SPDFID_Text) { return SPSF_Text; } if ((m_guidFormatId != SPDFID_WaveFormatEx) || (m_pCoMemWaveFormatEx == NULL)) { return SPSF_NonStandardFormat; } // // It is a WAVEFORMATEX. Now determine which type it is and convert. // DWORD dwIndex = 0; switch (m_pCoMemWaveFormatEx->wFormatTag) { case WAVE_FORMAT_PCM: { switch (m_pCoMemWaveFormatEx->nChannels) { case 1: break; case 2: dwIndex |= 1; break; default: return SPSF_ExtendedAudioFormat; } switch (m_pCoMemWaveFormatEx->wBitsPerSample) { case 8: break; case 16: dwIndex |= 2; break; default: return SPSF_ExtendedAudioFormat; } switch (m_pCoMemWaveFormatEx->nSamplesPerSec) { case 48000: dwIndex += 4; // Fall through case 44100: dwIndex += 4; // Fall through case 32000: dwIndex += 4; // Fall through case 24000: dwIndex += 4; // Fall through case 22050: dwIndex += 4; // Fall through case 16000: dwIndex += 4; // Fall through case 12000: dwIndex += 4; // Fall through case 11025: dwIndex += 4; // Fall through case 8000: break; default: return SPSF_ExtendedAudioFormat; } return static_cast(SPSF_8kHz8BitMono + dwIndex); } case WAVE_FORMAT_DSPGROUP_TRUESPEECH: { return SPSF_TrueSpeech_8kHz1BitMono; } case WAVE_FORMAT_ALAW: // fall through case WAVE_FORMAT_MULAW: case WAVE_FORMAT_ADPCM: { switch (m_pCoMemWaveFormatEx->nChannels) { case 1: break; case 2: dwIndex |= 1; break; default: return SPSF_ExtendedAudioFormat; } if(m_pCoMemWaveFormatEx->wFormatTag == WAVE_FORMAT_ADPCM) { if(m_pCoMemWaveFormatEx->wBitsPerSample != 4) { return SPSF_ExtendedAudioFormat; } } else if(m_pCoMemWaveFormatEx->wBitsPerSample != 8) { return SPSF_ExtendedAudioFormat; } switch (m_pCoMemWaveFormatEx->nSamplesPerSec) { case 44100: dwIndex += 2; // Fall through case 22050: dwIndex += 2; // Fall through case 11025: dwIndex += 2; // Fall through case 8000: break; default: return SPSF_ExtendedAudioFormat; } switch( m_pCoMemWaveFormatEx->wFormatTag ) { case WAVE_FORMAT_ALAW: return static_cast(SPSF_CCITT_ALaw_8kHzMono + dwIndex); case WAVE_FORMAT_MULAW: return static_cast(SPSF_CCITT_uLaw_8kHzMono + dwIndex); case WAVE_FORMAT_ADPCM: return static_cast(SPSF_ADPCM_8kHzMono + dwIndex); } } case WAVE_FORMAT_GSM610: { if( m_pCoMemWaveFormatEx->nChannels != 1 ) { return SPSF_ExtendedAudioFormat; } switch (m_pCoMemWaveFormatEx->nSamplesPerSec) { case 44100: dwIndex = 3; break; case 22050: dwIndex = 2; break; case 11025: dwIndex = 1; break; case 8000: dwIndex = 0; break; default: return SPSF_ExtendedAudioFormat; } return static_cast(SPSF_GSM610_8kHzMono + dwIndex); } default: return SPSF_ExtendedAudioFormat; break; } } void DetachTo(CSpStreamFormat & Other) { ::CoTaskMemFree(Other.m_pCoMemWaveFormatEx); Other.m_guidFormatId = m_guidFormatId; Other.m_pCoMemWaveFormatEx = m_pCoMemWaveFormatEx; m_pCoMemWaveFormatEx = NULL; memset(&m_guidFormatId, 0, sizeof(m_guidFormatId)); } void DetachTo(GUID * pFormatId, WAVEFORMATEX ** ppCoMemWaveFormatEx) { *pFormatId = m_guidFormatId; *ppCoMemWaveFormatEx = m_pCoMemWaveFormatEx; m_pCoMemWaveFormatEx = NULL; memset(&m_guidFormatId, 0, sizeof(m_guidFormatId)); } HRESULT CopyTo(GUID * pFormatId, WAVEFORMATEX ** ppCoMemWFEX) const { HRESULT hr = S_OK; *pFormatId = m_guidFormatId; if (m_pCoMemWaveFormatEx) { hr = CoMemCopyWFEX(m_pCoMemWaveFormatEx, ppCoMemWFEX); if (FAILED(hr)) { memset(pFormatId, 0, sizeof(*pFormatId)); } } else { *ppCoMemWFEX = NULL; } return hr; } HRESULT CopyTo(CSpStreamFormat & Other) const { ::CoTaskMemFree(Other.m_pCoMemWaveFormatEx); return CopyTo(&Other.m_guidFormatId, &Other.m_pCoMemWaveFormatEx); } HRESULT AssignFormat(const CSpStreamFormat & Src) { return Src.CopyTo(*this); } HRESULT ParamValidateCopyTo(GUID * pFormatId, WAVEFORMATEX ** ppCoMemWFEX) const { if ((pFormatId == NULL) || (ppCoMemWFEX == NULL)) { return E_POINTER; } return CopyTo(pFormatId, ppCoMemWFEX); } BOOL operator==(const CSpStreamFormat & Other) const { return IsEqual(Other.m_guidFormatId, Other.m_pCoMemWaveFormatEx); } BOOL operator!=(const CSpStreamFormat & Other) const { return !IsEqual(Other.m_guidFormatId, Other.m_pCoMemWaveFormatEx); } ULONG SerializeSize() const { ULONG cb = sizeof(ULONG) + sizeof(m_guidFormatId); if (m_pCoMemWaveFormatEx) { if (m_pCoMemWaveFormatEx->cbSize != 0 && m_pCoMemWaveFormatEx->wFormatTag == WAVE_FORMAT_PCM) { _ASSERT(!"PCM wave format"); SetLastError(ERROR_INVALID_STATE); return 0; } cb += sizeof(WAVEFORMATEX) + m_pCoMemWaveFormatEx->cbSize + 3; // Add 3 to round up cb -= cb % 4; // Round to DWORD } return cb; } ULONG Serialize(BYTE * pBuffer) const { ULONG cb = SerializeSize(); *((UNALIGNED ULONG *)pBuffer) = cb; pBuffer += sizeof(ULONG); *((UNALIGNED GUID *)pBuffer) = m_guidFormatId; if (m_pCoMemWaveFormatEx) { pBuffer += sizeof(m_guidFormatId); memcpy(pBuffer, m_pCoMemWaveFormatEx, sizeof(WAVEFORMATEX) + m_pCoMemWaveFormatEx->cbSize); } return cb; } HRESULT Deserialize(const BYTE * pBuffer, ULONG * pcbUsed) { HRESULT hr = S_OK; // check pointer to pBuffer for size value if(pBuffer == NULL || pcbUsed == NULL) { return E_INVALIDARG; } *pcbUsed = *((UNALIGNED ULONG *)pBuffer); // check complete pBuffer from start if(*pcbUsed < sizeof(GUID) + sizeof(ULONG)) { return E_INVALIDARG; } pBuffer += sizeof(ULONG); ::CoTaskMemFree(m_pCoMemWaveFormatEx); m_pCoMemWaveFormatEx = NULL; memcpy(&m_guidFormatId, pBuffer, sizeof(GUID)); if (*pcbUsed > sizeof(GUID) + sizeof(ULONG)) { pBuffer += sizeof(m_guidFormatId); hr = CoMemCopyWFEX((const WAVEFORMATEX *)pBuffer, &m_pCoMemWaveFormatEx); if (FAILED(hr)) { m_guidFormatId = GUID_NULL; } } return hr; } }; // Return the default codepage given a LCID. // Note some of the newer locales do not have associated Windows codepages. For these, we return UTF-8. inline UINT SpCodePageFromLcid(LCID lcid) { WCHAR achCodePage[6]; return (0 != GetLocaleInfoW(lcid, LOCALE_IDEFAULTANSICODEPAGE, achCodePage, sizeof(achCodePage) / sizeof(*achCodePage))) ? _wtoi(achCodePage) : 65001; } inline HRESULT SPBindToFile( LPCWSTR pFileName, SPFILEMODE eMode, ISpStream ** ppStream, const GUID * pFormatId = NULL, const WAVEFORMATEX * pWaveFormatEx = NULL, ULONGLONG ullEventInterest = SPFEI_ALL_EVENTS) { HRESULT hr = ::CoCreateInstance(CLSID_SpStream, NULL, CLSCTX_ALL, __uuidof(*ppStream), (void **)ppStream); if (SUCCEEDED(hr)) { hr = (*ppStream)->BindToFile(pFileName, eMode, pFormatId, pWaveFormatEx, ullEventInterest); if (FAILED(hr)) { (*ppStream)->Release(); *ppStream = NULL; } } return hr; } /* SPBindToFile */ inline HRESULT SPBindToFile( const CHAR * pFileName, SPFILEMODE eMode, ISpStream** ppStream, const GUID * pFormatId = NULL, const WAVEFORMATEX * pWaveFormatEx = NULL, ULONGLONG ullEventInterest = SPFEI_ALL_EVENTS) { WCHAR szWcharFileName[MAX_PATH]; ::MultiByteToWideChar(CP_ACP, 0, pFileName, -1, szWcharFileName, sp_countof(szWcharFileName)); return SPBindToFile(szWcharFileName, eMode, ppStream, pFormatId, pWaveFormatEx, ullEventInterest); } /**************************************************************************** * CheckStringSizeBytes * *--------------* * Description: * Checks a string is not longer that the supplied size {IN BYTES}, including * the terminating char. If pcb is non-null then it is set to the size of * the string {IN BYTES}, including the terminating char. * *****************************************************************************/ inline HRESULT CheckStringSizeBytes(WCHAR const * psz, size_t cbMax, size_t* pcb) { HRESULT hr = S_OK; size_t cchMax = cbMax / sizeof(WCHAR); while (cchMax && (*psz != L'\0')) { psz++; cchMax--; } if (cchMax == 0) { // the string is longer than cchMax hr = E_INVALIDARG; } if (SUCCEEDED(hr) && pcb) { *pcb = cbMax - (cchMax - 1) * sizeof(WCHAR); } return hr; } /**************************************************************************** * SpClearEvent * *--------------* * Description: * Helper function that can be used by clients that do not use the CSpEvent * class. * * Returns: * *****************************************************************************/ inline void SpClearEvent(SPEVENT * pe) { if( pe->elParamType != SPEI_UNDEFINED) { if( pe->elParamType == SPET_LPARAM_IS_POINTER || pe->elParamType == SPET_LPARAM_IS_STRING) { ::CoTaskMemFree((void *)pe->lParam); } else if (pe->elParamType == SPET_LPARAM_IS_TOKEN || pe->elParamType == SPET_LPARAM_IS_OBJECT) { ((IUnknown*)pe->lParam)->Release(); } } memset(pe, 0, sizeof(*pe)); } /**************************************************************************** * SpInitEvent * *-------------* * Description: * * Returns: * *****************************************************************************/ inline void SpInitEvent(SPEVENT * pe) { memset(pe, 0, sizeof(*pe)); } /**************************************************************************** * SpEventSerializeSize * *----------------------* * Description: * Computes the required size of a buffer to serialize an event. The caller * must specify which type of serialized event is desired -- either SPSERIALIZEDEVENT * or SPSERIALIZEDEVENT64. * * Returns: * Size in bytes required to seriailze the event. * ****************************************************************************/ template inline ULONG SpEventSerializeSize(const SPEVENT * pEvent) { ULONG ulSize = sizeof(T); if( ( pEvent->elParamType == SPET_LPARAM_IS_POINTER ) && pEvent->lParam ) { ulSize += ULONG(pEvent->wParam); } else if ((pEvent->elParamType == SPET_LPARAM_IS_STRING) && pEvent->lParam != NULL) { // Would be better to check for overflow of string length. ulSize += ((ULONG) wcslen((WCHAR*)pEvent->lParam) + 1) * sizeof( WCHAR ); } else if( pEvent->elParamType == SPET_LPARAM_IS_TOKEN ) { CSpDynamicString dstrObjectId; if( ((ISpObjectToken*)(pEvent->lParam))->GetId( &dstrObjectId ) == S_OK ) { ulSize += (dstrObjectId.Length() + 1) * sizeof( WCHAR ); } else { return 0; } } // Round up to nearest DWORD ulSize += 3; ulSize -= ulSize % 4; return ulSize; } /**************************************************************************** * SpSerializedEventSize * *-----------------------* * Description: * Returns the size, in bytes, used by a serialized event. The caller can * pass a pointer to either a SPSERIAILZEDEVENT or SPSERIALIZEDEVENT64 structure. * * Returns: * Number of bytes used by serizlied event * *****************************************************************************/ template inline ULONG SpSerializedEventSize(const T * pSerEvent) { ULONG ulSize = sizeof(T); if( ( pSerEvent->elParamType == SPET_LPARAM_IS_POINTER ) && pSerEvent->SerializedlParam ) { ulSize += ULONG(pSerEvent->SerializedwParam); } else if ((pSerEvent->elParamType == SPET_LPARAM_IS_STRING || pSerEvent->elParamType == SPET_LPARAM_IS_TOKEN) && pSerEvent->SerializedlParam != NULL) { ulSize += ((ULONG)wcslen((WCHAR*)(pSerEvent + 1)) + 1) * sizeof( WCHAR ); } // Round up to nearest DWORD ulSize += 3; ulSize -= ulSize % 4; return ulSize; } /*** CSpEvent helper class * */ class CSpEvent : public SPEVENT { public: CSpEvent() { SpInitEvent(this); } ~CSpEvent() { SpClearEvent(this); } // If you need to take the address of a CSpEvent that is not const, use the AddrOf() method // which will do debug checking of parameters. If you encounter this problem when calling // GetEvents from an event source, you may want to use the GetFrom() method of this class. const SPEVENT * operator&() { return this; } CSpEvent * AddrOf() { // Note: This method does not ASSERT since we assume the caller knows what they are doing. return this; } void Clear() { SpClearEvent(this); } HRESULT CopyTo(SPEVENT * pDestEvent) const { memcpy(pDestEvent, this, sizeof(*pDestEvent)); if ((elParamType == SPET_LPARAM_IS_POINTER) && lParam) { pDestEvent->lParam = (LPARAM)::CoTaskMemAlloc(wParam); if (pDestEvent->lParam) { memcpy((void *)pDestEvent->lParam, (void *)lParam, wParam); } else { pDestEvent->eEventId = SPEI_UNDEFINED; return E_OUTOFMEMORY; } } else if (elParamType == SPET_LPARAM_IS_STRING && lParam != NULL) { size_t cLen = wcslen((WCHAR*)lParam) + 1; pDestEvent->lParam = (LPARAM)::CoTaskMemAlloc(cLen * sizeof(WCHAR)); if (pDestEvent->lParam) { StringCchCopyW ((WCHAR*)pDestEvent->lParam, cLen, (WCHAR*)lParam); } else { pDestEvent->eEventId = SPEI_UNDEFINED; return E_OUTOFMEMORY; } } else if (elParamType == SPET_LPARAM_IS_TOKEN || elParamType == SPET_LPARAM_IS_OBJECT) { ((IUnknown*)lParam)->AddRef(); } return S_OK; } HRESULT GetFrom(ISpEventSource * pEventSrc) { SpClearEvent(this); return pEventSrc->GetEvents(1, this, NULL); } HRESULT CopyFrom(const SPEVENT * pSrcEvent) { SpClearEvent(this); return static_cast(pSrcEvent)->CopyTo(this); } void Detach(SPEVENT * pDestEvent = NULL) { if (pDestEvent) { memcpy(pDestEvent, this, sizeof(*pDestEvent)); } memset(this, 0, sizeof(*this)); } template ULONG SerializeSize() const { return SpEventSerializeSize(this); } // Call this method with either SPSERIALIZEDEVENT or SPSERIALIZEDEVENT64 template HRESULT Serialize(T * pSerEvent) const { if (elParamType == SPET_LPARAM_IS_OBJECT) { _ASSERT(elParamType != SPET_LPARAM_IS_OBJECT); return E_UNEXPECTED; } HRESULT hr = S_OK; pSerEvent->eEventId = this->eEventId; pSerEvent->elParamType = this->elParamType; pSerEvent->ulStreamNum = this->ulStreamNum; pSerEvent->ullAudioStreamOffset = this->ullAudioStreamOffset; pSerEvent->SerializedwParam = static_cast(this->wParam); pSerEvent->SerializedlParam = static_cast(this->lParam); if (lParam) { switch(elParamType) { case SPET_LPARAM_IS_POINTER: memcpy(pSerEvent + 1, (void *)lParam, wParam); pSerEvent->SerializedlParam = sizeof(T); break; case SPET_LPARAM_IS_STRING: StringCchCopyW ((WCHAR *)(pSerEvent + 1), wcslen ((WCHAR*) lParam) + 1, (WCHAR*)lParam); pSerEvent->SerializedlParam = sizeof(T); break; case SPET_LPARAM_IS_TOKEN: { CSpDynamicString dstrObjectId; hr = ((ISpObjectToken*)lParam)->GetId( &dstrObjectId ); if( SUCCEEDED( hr ) ) { pSerEvent->SerializedwParam = (dstrObjectId.Length() + 1) * sizeof( WCHAR );; memcpy( pSerEvent + 1, (void *)dstrObjectId.m_psz, static_cast(pSerEvent->SerializedwParam) ); } pSerEvent->SerializedlParam = sizeof(T); } break; default: break; } } return hr; } template HRESULT Serialize(T ** ppCoMemSerEvent, ULONG * pcbSerEvent) const { *pcbSerEvent = SpEventSerializeSize(this); if (*pcbSerEvent == 0) { return E_FAIL; } *ppCoMemSerEvent = (T *)::CoTaskMemAlloc(*pcbSerEvent); if (*ppCoMemSerEvent) { return Serialize(*ppCoMemSerEvent); } else { *pcbSerEvent = 0; return E_OUTOFMEMORY; } } // Call this method with either SPSERIALIZEDEVENT or SPSERIALIZEDEVENT64 template HRESULT Deserialize(const T * pSerEvent, ULONG * pcbUsed = NULL, ULONG cbMaxLength = 0xFFFF) { Clear(); HRESULT hr = S_OK; const UNALIGNED T * pTemp = pSerEvent; if (cbMaxLength < sizeof(*pTemp)) { return E_INVALIDARG; } size_t cbExtraSize = cbMaxLength - sizeof(*pTemp); this->eEventId = pTemp->eEventId; this->elParamType = pTemp->elParamType; this->ulStreamNum = pTemp->ulStreamNum; this->ullAudioStreamOffset = pTemp->ullAudioStreamOffset; this->wParam = static_cast(pTemp->SerializedwParam); this->lParam = static_cast(pTemp->SerializedlParam); if (pTemp->SerializedlParam) { size_t cbAlloc = 0; switch (pTemp->elParamType) { case SPET_LPARAM_IS_POINTER: cbAlloc = static_cast(wParam); if (cbAlloc > cbExtraSize) { hr = E_INVALIDARG; } break; case SPET_LPARAM_IS_STRING: hr = CheckStringSizeBytes((WCHAR *)(pTemp + 1), cbExtraSize, &cbAlloc); break; case SPET_LPARAM_IS_TOKEN: { hr = CheckStringSizeBytes((WCHAR *)(pTemp + 1), cbExtraSize, NULL); if (SUCCEEDED(hr)) { hr = SpGetTokenFromId( (const WCHAR*)(pTemp + 1), (ISpObjectToken **)&lParam ); wParam = 0; } } break; case SPET_LPARAM_IS_UNDEFINED: break; case SPET_LPARAM_IS_OBJECT: hr = E_INVALIDARG; break; default: hr = E_INVALIDARG; break; } if (SUCCEEDED(hr) && cbAlloc) { void * pvBuff = ::CoTaskMemAlloc(cbAlloc); this->lParam = (LPARAM)pvBuff; if (pvBuff) { memcpy(pvBuff, pTemp + 1, cbAlloc); } else { hr = E_OUTOFMEMORY; } } } if( SUCCEEDED( hr ) && pcbUsed ) { *pcbUsed = SpEventSerializeSize(this); if(*pcbUsed == 0) { hr = E_FAIL; } } // Reset the data structure on failure. Otherwise, the destructor may AV. if (FAILED(hr)) { memset(this, 0, sizeof(*this)); } return hr; } // // Helpers for access to events. Performs run-time checks in debug and casts // data to the appropriate types // SPPHONEID Phoneme() const { _ASSERT(eEventId == SPEI_PHONEME); return (SPPHONEID)LOWORD(lParam); } SPVISEMES Viseme() const { _ASSERT(eEventId == SPEI_VISEME); return (SPVISEMES)LOWORD(lParam); } ULONG InputWordPos() const { _ASSERT(eEventId == SPEI_WORD_BOUNDARY); return ULONG(lParam); } ULONG InputWordLen() const { _ASSERT(eEventId == SPEI_WORD_BOUNDARY); return ULONG(wParam); } ULONG InputSentPos() const { _ASSERT(eEventId == SPEI_SENTENCE_BOUNDARY); return ULONG(lParam); } ULONG InputSentLen() const { _ASSERT(eEventId == SPEI_SENTENCE_BOUNDARY); return ULONG(wParam); } ISpObjectToken * ObjectToken() const { _ASSERT(elParamType == SPET_LPARAM_IS_TOKEN); return (ISpObjectToken *)lParam; } ISpObjectToken * VoiceToken() const // More explicit check than ObjectToken() { _ASSERT(eEventId == SPEI_VOICE_CHANGE); return ObjectToken(); } BOOL PersistVoiceChange() const { _ASSERT(eEventId == SPEI_VOICE_CHANGE); return (BOOL)wParam; } IUnknown * Object() const { _ASSERT(elParamType == SPET_LPARAM_IS_OBJECT); return (IUnknown*)lParam; } ISpRecoResult * RecoResult() const { return (ISpRecoResult *)Object(); } #if _SAPI_VER >= 0x053 ULONG RetainedAudioSize() const { _ASSERT(eEventId == SPEI_SR_RETAINEDAUDIO); return ULONG(wParam); } ISpStreamFormat * RetainedAudioStream() const { _ASSERT(eEventId == SPEI_SR_RETAINEDAUDIO); return (ISpStreamFormat *)Object(); } #endif BOOL IsPaused() { return (BOOL)(wParam & SPREF_AutoPause); } BOOL IsEmulated() { return (BOOL)(wParam & SPREF_Emulated); } #if _SAPI_VER >= 0x053 BOOL IsSMLTimeout() { return (BOOL)(wParam & SPREF_SMLTimeout); } #endif const WCHAR * String() const { _ASSERT(elParamType == SPET_LPARAM_IS_STRING); return (const WCHAR*)lParam; } const WCHAR * BookmarkName() const { _ASSERT(eEventId == SPEI_TTS_BOOKMARK); return String(); } const WCHAR * RequestTypeOfUI() const { _ASSERT(eEventId == SPEI_REQUEST_UI); return String(); } SPRECOSTATE RecoState() const { _ASSERT(eEventId == SPEI_RECO_STATE_CHANGE); return static_cast(wParam); } const WCHAR * PropertyName() const { _ASSERT((eEventId == SPEI_PROPERTY_NUM_CHANGE && elParamType == SPET_LPARAM_IS_STRING) || (eEventId == SPEI_PROPERTY_STRING_CHANGE && elParamType == SPET_LPARAM_IS_POINTER)); // Note: Don't use String() method here since in the case of string attributes, the elParamType // field specifies LPARAM_IS_POINTER, but the attribute name IS the first string in this buffer return (const WCHAR*)lParam; } const LONG PropertyNumValue() const { _ASSERT(eEventId == SPEI_PROPERTY_NUM_CHANGE); return static_cast(wParam); } const WCHAR * PropertyStringValue() const { // Search for the first NULL and return pointer to the char past it. _ASSERT(eEventId == SPEI_PROPERTY_STRING_CHANGE); const WCHAR * psz = (const WCHAR *)lParam; for (; *psz; psz++) {} return psz + 1; } SPINTERFERENCE Interference() const { _ASSERT(eEventId == SPEI_INTERFERENCE); return static_cast(lParam); } HRESULT EndStreamResult() const { _ASSERT(eEventId == SPEI_END_SR_STREAM); return static_cast(lParam); } BOOL InputStreamReleased() const { _ASSERT(eEventId == SPEI_END_SR_STREAM); return (wParam & SPESF_STREAM_RELEASED) ? TRUE : FALSE; } }; class CSpPhrasePtr { public: SPPHRASE * m_pPhrase; CSpPhrasePtr() : m_pPhrase(NULL) {} CSpPhrasePtr(ISpPhrase * pPhraseObj, HRESULT * phr) { *phr = pPhraseObj->GetPhrase(&m_pPhrase); } ~CSpPhrasePtr() { ::CoTaskMemFree(m_pPhrase); } //The assert on operator& usually indicates a bug. If this is really //what is needed, however, take the address of the m_pPhrase member explicitly. SPPHRASE ** operator&() { _ASSERT(m_pPhrase == NULL); return &m_pPhrase; } operator SPPHRASE *() const { return m_pPhrase; } SPPHRASE & operator*() const { _ASSERT(m_pPhrase); return *m_pPhrase; } SPPHRASE * operator->() const { return m_pPhrase; } bool operator!() const { return (m_pPhrase == NULL); } void Clear() { if (m_pPhrase) { ::CoTaskMemFree(m_pPhrase); m_pPhrase = NULL; } } HRESULT GetFrom(ISpPhrase * pPhraseObj) { Clear(); return pPhraseObj->GetPhrase(&m_pPhrase); } }; template class CSpCoTaskMemPtr { public: T * m_pT; CSpCoTaskMemPtr() : m_pT(NULL) {} CSpCoTaskMemPtr(void * pv) : m_pT((T *)pv) {} CSpCoTaskMemPtr(ULONG cElements, HRESULT * phr) { m_pT = (T *)::CoTaskMemAlloc(cElements * sizeof(T)); *phr = m_pT ? S_OK : E_OUTOFMEMORY; } ~CSpCoTaskMemPtr() { ::CoTaskMemFree(m_pT); } void Clear() { if (m_pT) { ::CoTaskMemFree(m_pT); m_pT = NULL; } } HRESULT Alloc(ULONG cArrayElements = 1) { T * pNewT = (T *)::CoTaskMemRealloc(m_pT, sizeof(T) * cArrayElements); if (pNewT == NULL) { // Keep the old behavior, the pointer becomes NULL if we fail above Clear(); return E_OUTOFMEMORY; } else { m_pT = pNewT; return S_OK; } } void Attach(void * pv) { Clear(); m_pT = (T *)pv; } T * Detatch() { T * pT = m_pT; m_pT = NULL; return pT; } //The assert on operator& usually indicates a bug. If this is really //what is needed, however, take the address of the m_pT member explicitly. T ** operator&() { _ASSERT(m_pT == NULL); return &m_pT; } T * operator->() { _ASSERT(m_pT != NULL); return m_pT; } operator T *() { return m_pT; } bool operator!() const { return (m_pT == NULL); } }; /**** Helper function used to create a new phrase object from an array of test words. Each word in the string is converted to a phrase element. This is useful to create a phrase to pass to the EmulateRecognition method. The method can convert standard words as well as words with the "/display_text/lexical_form/pronounciation;" word format. You can also specify the DisplayAttributes for each element if desired. If prgDispAttribs is NULL then the DisplayAttribs for each element default to SPAF_ONE_TRAILING_SPACE. ****/ inline HRESULT CreatePhraseFromWordArray(const WCHAR ** ppWords, ULONG cWords, SPDISPLYATTRIBUTES * prgDispAttribs, ISpPhraseBuilder **ppResultPhrase, LANGID LangId = 0, CComPtr cpPhoneConv = NULL, BOOL fNoSpecialCharacters = FALSE) { HRESULT hr = S_OK; if ( cWords == 0 || ppWords == NULL ) { return E_INVALIDARG; } size_t cTotalChars = 0; ULONG i; WCHAR** pStringPtrArray = (WCHAR**)::CoTaskMemAlloc( cWords * sizeof(WCHAR *)); if ( !pStringPtrArray ) { return E_OUTOFMEMORY; } for (i = 0; i < cWords; i++) { cTotalChars += wcslen(ppWords[i])+1; } if ((ULONG) cTotalChars != cTotalChars) { ::CoTaskMemFree(pStringPtrArray); return E_OUTOFMEMORY; } CSpDynamicString dsText((ULONG) cTotalChars); if(dsText.m_psz == NULL) { ::CoTaskMemFree(pStringPtrArray); return E_OUTOFMEMORY; } CSpDynamicString dsPhoneId((ULONG) cTotalChars); if(dsPhoneId.m_psz == NULL) { ::CoTaskMemFree(pStringPtrArray); return E_OUTOFMEMORY; } SPPHONEID* pphoneId = dsPhoneId; SPPHRASE Phrase; memset(&Phrase, 0, sizeof(Phrase)); #ifdef SP_SPPHRASESIZE_500 Phrase.cbSize = SP_SPPHRASESIZE_500; #else Phrase.cbSize = sizeof(Phrase); #endif if(LangId == 0) { LangId = SpGetUserDefaultUILanguage(); } SPPHRASEELEMENT *pPhraseElement = new SPPHRASEELEMENT[cWords]; if(pPhraseElement == NULL) { ::CoTaskMemFree(pStringPtrArray); return E_OUTOFMEMORY; } memset(pPhraseElement, 0, sizeof(SPPHRASEELEMENT) * cWords); WCHAR * pText = dsText; for (i = 0; SUCCEEDED(hr) && i < cWords; i++) { WCHAR *p = pText; pStringPtrArray[i] = pText; if (ppWords[i][0] == L'/' && !fNoSpecialCharacters) { const WCHAR *pszOriginalText = ppWords[i] + 1; //This is a compound word WCHAR* pszFirstPart = p; WCHAR* pszSecondPart = NULL; WCHAR* pszThirdPart = NULL; // Find the second separator slash. while (*pszOriginalText) { if (*pszOriginalText == L'\\') { pszOriginalText++; } else if (*pszOriginalText == L';' || *pszOriginalText == L'/') { break; } *p++ = *pszOriginalText++; } if (*pszOriginalText == L'/') { // We stopped at the second '/' *p = L'\0'; pszSecondPart = ++p; pszOriginalText++; while (*pszOriginalText) { if (*pszOriginalText == L'\\') { pszOriginalText++; } else if (*pszOriginalText == L';' || *pszOriginalText == L'/') { break; } *p++ = *pszOriginalText++; } if (*pszOriginalText == L'/') { // We stopped at the third '/' *p = L'\0'; pszThirdPart = ++p; pszOriginalText++; } } WCHAR *pBound = (WCHAR*) pszOriginalText + wcslen(pszOriginalText); while (pszOriginalText < pBound && *pszOriginalText != L';') { *p++ = *pszOriginalText++; } *p = L'\0'; pText = p + 1; pPhraseElement[i].pszDisplayText = pszFirstPart; pPhraseElement[i].pszLexicalForm = pszSecondPart ? pszSecondPart : pszFirstPart; if (pszThirdPart && *pszThirdPart != L'\0') { if(cpPhoneConv == NULL) { hr = SpCreatePhoneConverter(LangId, NULL, NULL, &cpPhoneConv); if(FAILED(hr)) { break; } } hr = cpPhoneConv->PhoneToId(pszThirdPart, pphoneId); if (SUCCEEDED(hr)) { pPhraseElement[i].pszPronunciation = pphoneId; pphoneId += wcslen(pphoneId) + 1; } } } else { //It is the simple format, only have one form, use it for everything. StringCchCopyW ( pText, cTotalChars - (pText - dsText), ppWords[i] ); pText += wcslen( p ) + 1; pPhraseElement[i].pszDisplayText = NULL; pPhraseElement[i].pszLexicalForm = p; pPhraseElement[i].pszPronunciation = NULL; } pPhraseElement[i].bDisplayAttributes = (BYTE)(prgDispAttribs ? prgDispAttribs[i] : SPAF_ONE_TRAILING_SPACE); pPhraseElement[i].RequiredConfidence = SP_NORMAL_CONFIDENCE; pPhraseElement[i].ActualConfidence = SP_NORMAL_CONFIDENCE; pPhraseElement[i].SREngineConfidence = 1.0f; // Emulated results give confidence of 1.0 } Phrase.Rule.ulCountOfElements = cWords; Phrase.Rule.SREngineConfidence = 1.0f; Phrase.pElements = pPhraseElement; Phrase.LangID = LangId; CComPtr cpPhrase; if (SUCCEEDED(hr)) { hr = cpPhrase.CoCreateInstance(CLSID_SpPhraseBuilder); } if (SUCCEEDED(hr)) { hr = cpPhrase->InitFromPhrase(&Phrase); } if (SUCCEEDED(hr)) { *ppResultPhrase = cpPhrase.Detach(); } delete[] pPhraseElement; ::CoTaskMemFree(pStringPtrArray); return hr; } /**** Helper function used to create a new phrase object from a test string. Each word in the string is converted to a phrase element. This is useful to create a phrase to pass to the EmulateRecognition method. The method can convert standard words as well as words with the "/display_text/lexical_form/pronounciation;" word format. If the emulation needs to match word sequence data (textbuffer) then the corresponding words need to be bracketed with '[' and ']' so they can be put into a single phrase element ****/ inline HRESULT CreatePhraseFromText(const WCHAR *pszOriginalText, ISpPhraseBuilder **ppResultPhrase, LANGID LangId = 0, CComPtr cpPhoneConv = NULL, BOOL fNoSpecialCharacters = FALSE) { HRESULT hr = S_OK; //We first trim the input text CSpDynamicString dsText((ULONG)wcslen(pszOriginalText) + 1); if(dsText.m_psz == NULL) { return E_OUTOFMEMORY; } ULONG cWords = 0; // Set first array pointer (if *p). WCHAR *p = dsText; while (*pszOriginalText != L'\0') { // Skip leading white spaces while (iswspace(*pszOriginalText)) { *p++ = L'\0'; pszOriginalText++; } // Skip over word if (*pszOriginalText != L'\0') { cWords++; if ((*pszOriginalText == L'/') && !fNoSpecialCharacters) { // Skip all non-semicolon characters and unescape escaped characters while (*pszOriginalText != L'\0') { if (*pszOriginalText == L'\\') { *p++ = *pszOriginalText++; } else if (*pszOriginalText == L';') { break; } *p++ = *pszOriginalText++; } if (*pszOriginalText == L';') { *p++ = *pszOriginalText++; } } else { // Skip all non-whitespace characters while ((*pszOriginalText != L'\0') && !iswspace(*pszOriginalText)) { *p++ = *pszOriginalText++; } } } } *p = L'\0'; WCHAR** pStringPtrArray = (WCHAR**)::CoTaskMemAlloc( cWords * sizeof(WCHAR *)); if ( !pStringPtrArray ) { hr = E_OUTOFMEMORY; } if ( SUCCEEDED( hr ) ) { p = dsText; for (ULONG i=0; i