2755 lines
77 KiB
C
2755 lines
77 KiB
C
|
/*******************************************************************************
|
||
|
* SPHelper.h *
|
||
|
*------------*
|
||
|
* Description:
|
||
|
* This is the header file for core helper functions implementation.
|
||
|
*-------------------------------------------------------------------------------
|
||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
*******************************************************************************/
|
||
|
#ifndef SPHelper_h
|
||
|
#define SPHelper_h
|
||
|
|
||
|
#ifndef _INC_MALLOC
|
||
|
#include <malloc.h>
|
||
|
#endif
|
||
|
|
||
|
#ifndef _INC_CRTDBG
|
||
|
#include <crtdbg.h>
|
||
|
#endif
|
||
|
|
||
|
#ifndef __sapi_h__
|
||
|
#include <sapi.h>
|
||
|
#endif
|
||
|
|
||
|
#ifndef __sapiddk_h__
|
||
|
#include <sapiddk.h>
|
||
|
#endif
|
||
|
|
||
|
#ifndef SPError_h
|
||
|
#include <SPError.h>
|
||
|
#endif
|
||
|
|
||
|
#ifndef SPDebug_h
|
||
|
#include <SPDebug.h>
|
||
|
#endif
|
||
|
|
||
|
#ifndef _INC_LIMITS
|
||
|
#include <limits.h>
|
||
|
#endif
|
||
|
|
||
|
#ifndef _INC_MMSYSTEM
|
||
|
#include <mmsystem.h>
|
||
|
#endif
|
||
|
|
||
|
#ifndef __comcat_h__
|
||
|
#include <comcat.h>
|
||
|
#endif
|
||
|
|
||
|
#ifndef _INC_MMREG
|
||
|
#include <mmreg.h>
|
||
|
#endif
|
||
|
|
||
|
#ifndef __ATLBASE_H__
|
||
|
#include <atlbase.h>
|
||
|
#endif
|
||
|
|
||
|
//=== Constants ==============================================================
|
||
|
#define sp_countof(x) ((sizeof(x) / sizeof(*(x))))
|
||
|
|
||
|
/*** CSpDynamicString helper class
|
||
|
*
|
||
|
*/
|
||
|
class CSpDynamicString
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
WCHAR * m_psz;
|
||
|
CSpDynamicString()
|
||
|
{
|
||
|
m_psz = NULL;
|
||
|
}
|
||
|
CSpDynamicString(size_t cchReserve)
|
||
|
{
|
||
|
m_psz = (WCHAR *)::CoTaskMemAlloc(cchReserve * sizeof(WCHAR));
|
||
|
}
|
||
|
WCHAR * operator=(const CSpDynamicString& src)
|
||
|
{
|
||
|
if (m_psz != src.m_psz)
|
||
|
{
|
||
|
::CoTaskMemFree(m_psz);
|
||
|
m_psz = src.Copy();
|
||
|
}
|
||
|
return m_psz;
|
||
|
}
|
||
|
WCHAR * operator=(const WCHAR * pSrc)
|
||
|
{
|
||
|
Clear();
|
||
|
if (pSrc)
|
||
|
{
|
||
|
size_t cbNeeded = (wcslen(pSrc) + 1) * sizeof(WCHAR);
|
||
|
m_psz = (WCHAR *)::CoTaskMemAlloc(cbNeeded);
|
||
|
SPDBG_ASSERT(m_psz);
|
||
|
if (m_psz)
|
||
|
{
|
||
|
memcpy(m_psz, pSrc, cbNeeded);
|
||
|
}
|
||
|
}
|
||
|
return m_psz;
|
||
|
}
|
||
|
|
||
|
WCHAR * operator=(const char * pSrc)
|
||
|
{
|
||
|
Clear();
|
||
|
if (pSrc)
|
||
|
{
|
||
|
ULONG cbNeeded = (lstrlenA(pSrc) + 1) * sizeof(WCHAR);
|
||
|
m_psz = (WCHAR *)::CoTaskMemAlloc(cbNeeded);
|
||
|
SPDBG_ASSERT(m_psz);
|
||
|
if (m_psz)
|
||
|
{
|
||
|
::MultiByteToWideChar(CP_ACP, 0, pSrc, -1, m_psz, 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
|
||
|
{
|
||
|
return static_cast<unsigned int>( (m_psz == NULL)? 0 : wcslen(m_psz) );
|
||
|
}
|
||
|
|
||
|
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&()
|
||
|
{
|
||
|
SPDBG_ASSERT(m_psz == NULL);
|
||
|
return &m_psz;
|
||
|
}
|
||
|
|
||
|
WCHAR * Append(const WCHAR * pszSrc)
|
||
|
{
|
||
|
if (pszSrc)
|
||
|
{
|
||
|
size_t lenSrc = wcslen(pszSrc);
|
||
|
if (lenSrc)
|
||
|
{
|
||
|
ULONG lenMe = Length();
|
||
|
WCHAR *pszNew = (WCHAR *)::CoTaskMemAlloc((lenMe + lenSrc + 1) * sizeof(WCHAR));
|
||
|
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 + 1) * sizeof(WCHAR));
|
||
|
m_psz = pszNew;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SPDBG_ASSERT(FALSE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return m_psz;
|
||
|
}
|
||
|
|
||
|
WCHAR * Append(const WCHAR * pszSrc, const ULONG lenSrc)
|
||
|
{
|
||
|
if (pszSrc && lenSrc)
|
||
|
{
|
||
|
ULONG lenMe = Length();
|
||
|
WCHAR *pszNew = (WCHAR *)::CoTaskMemAlloc((lenMe + lenSrc + 1) * sizeof(WCHAR));
|
||
|
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
|
||
|
{
|
||
|
SPDBG_ASSERT(FALSE);
|
||
|
}
|
||
|
}
|
||
|
return m_psz;
|
||
|
}
|
||
|
|
||
|
WCHAR * Append2(const WCHAR * pszSrc1, const WCHAR * pszSrc2)
|
||
|
{
|
||
|
size_t lenSrc1 = pszSrc1 ? wcslen(pszSrc1) : 0;
|
||
|
size_t lenSrc2 = pszSrc2 ? wcslen(pszSrc2) : 0;
|
||
|
|
||
|
if (lenSrc1 || lenSrc2)
|
||
|
{
|
||
|
ULONG lenMe = Length();
|
||
|
WCHAR *pszNew = (WCHAR *)::CoTaskMemAlloc((lenMe + lenSrc1 + lenSrc2 + 1) * sizeof(WCHAR));
|
||
|
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
|
||
|
{
|
||
|
SPDBG_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);
|
||
|
SPDBG_ASSERT(psz);
|
||
|
if (psz)
|
||
|
{
|
||
|
::WideCharToMultiByte(CP_ACP, 0, m_psz, -1, psz, cbNeeded/sizeof(CHAR), NULL, NULL);
|
||
|
}
|
||
|
return psz;
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
void Attach(WCHAR * pszSrc)
|
||
|
{
|
||
|
SPDBG_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)
|
||
|
{
|
||
|
size_t cch = wcslen(m_psz);
|
||
|
m_psz = (WCHAR *)::CoTaskMemRealloc(m_psz, (cch + 1) * sizeof(WCHAR));
|
||
|
}
|
||
|
return m_psz;
|
||
|
}
|
||
|
WCHAR * ClearAndGrowTo(ULONG cch)
|
||
|
{
|
||
|
if (m_psz)
|
||
|
{
|
||
|
Clear();
|
||
|
}
|
||
|
m_psz = (WCHAR *)::CoTaskMemAlloc(cch * sizeof(WCHAR));
|
||
|
return m_psz;
|
||
|
}
|
||
|
WCHAR * LTrim()
|
||
|
{
|
||
|
if (m_psz)
|
||
|
{
|
||
|
WCHAR * pszRead = m_psz;
|
||
|
while (iswspace(*pszRead))
|
||
|
{
|
||
|
pszRead++;
|
||
|
}
|
||
|
if (pszRead != m_psz)
|
||
|
{
|
||
|
WCHAR * pszWrite = m_psz;
|
||
|
while (*pszRead)
|
||
|
{
|
||
|
*pszWrite++ = *pszRead++;
|
||
|
}
|
||
|
*pszWrite = '\0';
|
||
|
}
|
||
|
}
|
||
|
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(WCHAR * psz, ULONG ul)
|
||
|
{
|
||
|
const static WCHAR szHexChars[] = L"0123456789ABCDEF";
|
||
|
if (ul == 0)
|
||
|
{
|
||
|
psz[0] = L'0';
|
||
|
psz[1] = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ULONG ulChars = 1;
|
||
|
psz[0] = 0;
|
||
|
while (ul)
|
||
|
{
|
||
|
memmove(psz + 1, psz, ulChars * sizeof(WCHAR));
|
||
|
psz[0] = szHexChars[ul % 16];
|
||
|
ul /= 16;
|
||
|
ulChars++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//=== Token helpers
|
||
|
|
||
|
inline HRESULT SpGetTokenFromId(
|
||
|
const WCHAR * pszTokenId,
|
||
|
ISpObjectToken ** ppToken,
|
||
|
BOOL fCreateIfNotExist = FALSE)
|
||
|
{
|
||
|
SPDBG_FUNC("SpGetTokenFromId");
|
||
|
HRESULT hr;
|
||
|
|
||
|
CComPtr<ISpObjectToken> cpToken;
|
||
|
hr = cpToken.CoCreateInstance(CLSID_SpObjectToken);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = cpToken->SetId(NULL, pszTokenId, fCreateIfNotExist);
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
*ppToken = cpToken.Detach();
|
||
|
}
|
||
|
|
||
|
if (hr != SPERR_NOT_FOUND)
|
||
|
{
|
||
|
SPDBG_REPORT_ON_FAIL(hr);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
inline HRESULT SpGetCategoryFromId(
|
||
|
const WCHAR * pszCategoryId,
|
||
|
ISpObjectTokenCategory ** ppCategory,
|
||
|
BOOL fCreateIfNotExist = FALSE)
|
||
|
{
|
||
|
SPDBG_FUNC("SpGetCategoryFromId");
|
||
|
HRESULT hr;
|
||
|
|
||
|
CComPtr<ISpObjectTokenCategory> cpTokenCategory;
|
||
|
hr = cpTokenCategory.CoCreateInstance(CLSID_SpObjectTokenCategory);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = cpTokenCategory->SetId(pszCategoryId, fCreateIfNotExist);
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
*ppCategory = cpTokenCategory.Detach();
|
||
|
}
|
||
|
|
||
|
SPDBG_REPORT_ON_FAIL(hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
inline HRESULT SpGetDefaultTokenIdFromCategoryId(
|
||
|
const WCHAR * pszCategoryId,
|
||
|
WCHAR ** ppszTokenId)
|
||
|
{
|
||
|
SPDBG_FUNC("SpGetDefaultTokenFromCategoryId");
|
||
|
HRESULT hr;
|
||
|
|
||
|
CComPtr<ISpObjectTokenCategory> cpCategory;
|
||
|
hr = SpGetCategoryFromId(pszCategoryId, &cpCategory);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = cpCategory->GetDefaultTokenId(ppszTokenId);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
inline HRESULT SpSetDefaultTokenIdForCategoryId(
|
||
|
const WCHAR * pszCategoryId,
|
||
|
const WCHAR * pszTokenId)
|
||
|
{
|
||
|
SPDBG_FUNC("SpSetDefaultTokenIdForCategoryId");
|
||
|
HRESULT hr;
|
||
|
|
||
|
CComPtr<ISpObjectTokenCategory> 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)
|
||
|
{
|
||
|
SPDBG_FUNC("SpGetDefaultTokenFromCategoryId");
|
||
|
HRESULT hr;
|
||
|
|
||
|
CComPtr<ISpObjectTokenCategory> 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)
|
||
|
{
|
||
|
SPDBG_FUNC("SpSetDefaultTokenForCategoryId");
|
||
|
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)
|
||
|
{
|
||
|
SPDBG_FUNC("SpSetCommonTokenData");
|
||
|
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)
|
||
|
{
|
||
|
USES_CONVERSION;
|
||
|
|
||
|
TCHAR szLangId[10];
|
||
|
wsprintf(szLangId, _T("%x"), langid);
|
||
|
|
||
|
hr = pToken->SetStringValue(T2W(szLangId), pszLangDependentName);
|
||
|
}
|
||
|
|
||
|
// Open the attributes key if requested
|
||
|
if (SUCCEEDED(hr) && ppDataKeyAttribs)
|
||
|
{
|
||
|
hr = pToken->CreateKey(L"Attributes", ppDataKeyAttribs);
|
||
|
}
|
||
|
|
||
|
SPDBG_REPORT_ON_FAIL(hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
inline HRESULT SpCreateNewToken(
|
||
|
const WCHAR * pszTokenId,
|
||
|
ISpObjectToken ** ppToken)
|
||
|
{
|
||
|
SPDBG_FUNC("SpCreateNewToken");
|
||
|
HRESULT hr;
|
||
|
|
||
|
// Forcefully create the token
|
||
|
hr = SpGetTokenFromId(pszTokenId, ppToken, TRUE);
|
||
|
|
||
|
SPDBG_REPORT_ON_FAIL(hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
inline HRESULT SpCreateNewToken(
|
||
|
const WCHAR * pszCategoryId,
|
||
|
const WCHAR * pszTokenKeyName,
|
||
|
ISpObjectToken ** ppToken)
|
||
|
{
|
||
|
SPDBG_FUNC("SpCreateNewToken");
|
||
|
HRESULT hr;
|
||
|
|
||
|
// Forcefully create the category
|
||
|
CComPtr<ISpObjectTokenCategory> 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 = CoCreateGuid(&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);
|
||
|
}
|
||
|
|
||
|
SPDBG_REPORT_ON_FAIL(hr);
|
||
|
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)
|
||
|
{
|
||
|
SPDBG_FUNC("SpCreateNewTokenEx");
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
SPDBG_REPORT_ON_FAIL(hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
inline HRESULT SpCreateNewTokenEx(
|
||
|
const WCHAR * pszTokenId,
|
||
|
const CLSID * pclsid,
|
||
|
const WCHAR * pszLangIndependentName,
|
||
|
LANGID langid,
|
||
|
const WCHAR * pszLangDependentName,
|
||
|
ISpObjectToken ** ppToken,
|
||
|
ISpDataKey ** ppDataKeyAttribs)
|
||
|
{
|
||
|
SPDBG_FUNC("SpCreateNewTokenEx");
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
SPDBG_REPORT_ON_FAIL(hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
inline HRESULT SpEnumTokens(
|
||
|
const WCHAR * pszCategoryId,
|
||
|
const WCHAR * pszReqAttribs,
|
||
|
const WCHAR * pszOptAttribs,
|
||
|
IEnumSpObjectTokens ** ppEnum)
|
||
|
{
|
||
|
SPDBG_FUNC("SpEnumTokens");
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
CComPtr<ISpObjectTokenCategory> cpCategory;
|
||
|
hr = SpGetCategoryFromId(pszCategoryId, &cpCategory);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = cpCategory->EnumTokens(
|
||
|
pszReqAttribs,
|
||
|
pszOptAttribs,
|
||
|
ppEnum);
|
||
|
}
|
||
|
|
||
|
SPDBG_REPORT_ON_FAIL(hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
inline HRESULT SpFindBestToken(
|
||
|
const WCHAR * pszCategoryId,
|
||
|
const WCHAR * pszReqAttribs,
|
||
|
const WCHAR * pszOptAttribs,
|
||
|
ISpObjectToken **ppObjectToken)
|
||
|
{
|
||
|
SPDBG_FUNC("SpFindBestToken");
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
const WCHAR *pszVendorPreferred = L"VendorPreferred";
|
||
|
const size_t ulLenVendorPreferred = wcslen(pszVendorPreferred);
|
||
|
|
||
|
// append VendorPreferred to the end of pszOptAttribs to force this preference
|
||
|
size_t ulLen = pszOptAttribs ? wcslen(pszOptAttribs) + ulLenVendorPreferred + 1 : ulLenVendorPreferred;
|
||
|
WCHAR *pszOptAttribsVendorPref = (WCHAR*)_alloca((ulLen+1)*sizeof(WCHAR));
|
||
|
if (pszOptAttribsVendorPref)
|
||
|
{
|
||
|
if (pszOptAttribs)
|
||
|
{
|
||
|
wcscpy(pszOptAttribsVendorPref, pszOptAttribs);
|
||
|
wcscat(pszOptAttribsVendorPref, L";");
|
||
|
wcscat(pszOptAttribsVendorPref, pszVendorPreferred);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
wcscpy(pszOptAttribsVendorPref, pszVendorPreferred);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
CComPtr<IEnumSpObjectTokens> cpEnum;
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = SpEnumTokens(pszCategoryId, pszReqAttribs, pszOptAttribsVendorPref, &cpEnum);
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = cpEnum->Next(1, ppObjectToken, NULL);
|
||
|
if (hr == S_FALSE)
|
||
|
{
|
||
|
*ppObjectToken = NULL;
|
||
|
hr = SPERR_NOT_FOUND;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (hr != SPERR_NOT_FOUND)
|
||
|
{
|
||
|
SPDBG_REPORT_ON_FAIL(hr);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
template<class T>
|
||
|
HRESULT SpCreateObjectFromToken(ISpObjectToken * pToken, T ** ppObject,
|
||
|
IUnknown * pUnkOuter = NULL, DWORD dwClsCtxt = CLSCTX_ALL)
|
||
|
{
|
||
|
SPDBG_FUNC("SpCreateObjectFromToken");
|
||
|
HRESULT hr;
|
||
|
|
||
|
hr = pToken->CreateInstance(pUnkOuter, dwClsCtxt, __uuidof(T), (void **)ppObject);
|
||
|
|
||
|
SPDBG_REPORT_ON_FAIL(hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
template<class T>
|
||
|
HRESULT SpCreateObjectFromTokenId(const WCHAR * pszTokenId, T ** ppObject,
|
||
|
IUnknown * pUnkOuter = NULL, DWORD dwClsCtxt = CLSCTX_ALL)
|
||
|
{
|
||
|
SPDBG_FUNC("SpCreateObjectFromTokenId");
|
||
|
|
||
|
ISpObjectToken * pToken;
|
||
|
HRESULT hr = SpGetTokenFromId(pszTokenId, &pToken);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = SpCreateObjectFromToken(pToken, ppObject, pUnkOuter, dwClsCtxt);
|
||
|
pToken->Release();
|
||
|
}
|
||
|
|
||
|
SPDBG_REPORT_ON_FAIL(hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
template<class T>
|
||
|
HRESULT SpCreateDefaultObjectFromCategoryId(const WCHAR * pszCategoryId, T ** ppObject,
|
||
|
IUnknown * pUnkOuter = NULL, DWORD dwClsCtxt = CLSCTX_ALL)
|
||
|
{
|
||
|
SPDBG_FUNC("SpCreateObjectFromTokenId");
|
||
|
|
||
|
ISpObjectToken * pToken;
|
||
|
HRESULT hr = SpGetDefaultTokenFromCategoryId(pszCategoryId, &pToken);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = SpCreateObjectFromToken(pToken, ppObject, pUnkOuter, dwClsCtxt);
|
||
|
pToken->Release();
|
||
|
}
|
||
|
|
||
|
SPDBG_REPORT_ON_FAIL(hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
template<class T>
|
||
|
HRESULT SpCreateBestObject(
|
||
|
const WCHAR * pszCategoryId,
|
||
|
const WCHAR * pszReqAttribs,
|
||
|
const WCHAR * pszOptAttribs,
|
||
|
T ** ppObject,
|
||
|
IUnknown * pUnkOuter = NULL,
|
||
|
DWORD dwClsCtxt = CLSCTX_ALL)
|
||
|
{
|
||
|
SPDBG_FUNC("SpCreateBestObject");
|
||
|
HRESULT hr;
|
||
|
|
||
|
CComPtr<ISpObjectToken> cpToken;
|
||
|
hr = SpFindBestToken(pszCategoryId, pszReqAttribs, pszOptAttribs, &cpToken);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = SpCreateObjectFromToken(cpToken, ppObject, pUnkOuter, dwClsCtxt);
|
||
|
}
|
||
|
|
||
|
if (hr != SPERR_NOT_FOUND)
|
||
|
{
|
||
|
SPDBG_REPORT_ON_FAIL(hr);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
inline HRESULT SpCreatePhoneConverter(
|
||
|
LANGID LangID,
|
||
|
const WCHAR * pszReqAttribs,
|
||
|
const WCHAR * pszOptAttribs,
|
||
|
ISpPhoneConverter ** ppPhoneConverter)
|
||
|
{
|
||
|
SPDBG_FUNC("SpCreatePhoneConverter");
|
||
|
HRESULT hr;
|
||
|
|
||
|
if (LangID == 0)
|
||
|
{
|
||
|
hr = E_INVALIDARG;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CSpDynamicString dstrReqAttribs;
|
||
|
if (pszReqAttribs)
|
||
|
{
|
||
|
dstrReqAttribs = pszReqAttribs;
|
||
|
dstrReqAttribs.Append(L";");
|
||
|
}
|
||
|
|
||
|
WCHAR szLang[MAX_PATH];
|
||
|
|
||
|
SpHexFromUlong(szLang, LangID);
|
||
|
|
||
|
WCHAR szLangCondition[MAX_PATH];
|
||
|
wcscpy(szLangCondition, L"Language=");
|
||
|
wcscat(szLangCondition, szLang);
|
||
|
|
||
|
dstrReqAttribs.Append(szLangCondition);
|
||
|
|
||
|
hr = SpCreateBestObject(SPCAT_PHONECONVERTERS, dstrReqAttribs, pszOptAttribs, ppPhoneConverter);
|
||
|
}
|
||
|
|
||
|
if (hr != SPERR_NOT_FOUND)
|
||
|
{
|
||
|
SPDBG_REPORT_ON_FAIL(hr);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
* 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.
|
||
|
* On Win98 and WinMe ::GetLastError() returns 0 for some functions (see MSDN).
|
||
|
* We therefore check for that and return E_FAIL. This function should only be
|
||
|
* called in an error case since it will always return an error code!
|
||
|
*
|
||
|
* Returns:
|
||
|
* HRESULT for ::GetLastError()
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
inline HRESULT SpHrFromLastWin32Error()
|
||
|
{
|
||
|
DWORD dw = ::GetLastError();
|
||
|
return (dw == 0) ? E_FAIL : SpHrFromWin32(dw);
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************************************
|
||
|
* SpGetUserDefaultUILanguage *
|
||
|
*----------------------------*
|
||
|
* Description:
|
||
|
* Returns the default user interface language, using a method
|
||
|
* appropriate to the platform (Windows 9x, Windows NT, or Windows 2000)
|
||
|
*
|
||
|
* Returns:
|
||
|
* Default UI language
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
inline LANGID SpGetUserDefaultUILanguage(void)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
LANGID wUILang = 0;
|
||
|
|
||
|
OSVERSIONINFO Osv ;
|
||
|
Osv.dwOSVersionInfoSize = sizeof(Osv) ;
|
||
|
if(!GetVersionEx(&Osv))
|
||
|
{
|
||
|
hr = SpHrFromLastWin32Error();
|
||
|
}
|
||
|
// Get the UI language by one of three methods, depending on the system
|
||
|
else if(Osv.dwPlatformId != VER_PLATFORM_WIN32_NT)
|
||
|
{
|
||
|
// Case 1: Running on Windows 9x. Get the system UI language from registry:
|
||
|
CHAR szData[32];
|
||
|
DWORD dwSize = sizeof(szData) ;
|
||
|
HKEY hKey;
|
||
|
|
||
|
long lRet = RegOpenKeyEx(
|
||
|
HKEY_USERS,
|
||
|
_T(".Default\\Control Panel\\desktop\\ResourceLocale"),
|
||
|
0,
|
||
|
KEY_READ,
|
||
|
&hKey);
|
||
|
|
||
|
#ifdef _WIN32_WCE_BUG_10655
|
||
|
if (lRet == ERROR_INVALID_PARAMETER)
|
||
|
{
|
||
|
lRet = ERROR_FILE_NOT_FOUND;
|
||
|
}
|
||
|
#endif // _WIN32_WCE_BUG_10655
|
||
|
|
||
|
hr = SpHrFromWin32(lRet);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
lRet = RegQueryValueEx(
|
||
|
hKey,
|
||
|
_T(""),
|
||
|
NULL,
|
||
|
NULL,
|
||
|
(BYTE *)szData,
|
||
|
&dwSize);
|
||
|
|
||
|
#ifdef _WIN32_WCE_BUG_10655
|
||
|
if(lRet == ERROR_INVALID_PARAMETER)
|
||
|
{
|
||
|
lRet = ERROR_FILE_NOT_FOUND;
|
||
|
}
|
||
|
#endif //_WIN32_WCE_BUG_10655
|
||
|
|
||
|
hr = SpHrFromWin32(lRet);
|
||
|
::RegCloseKey(hKey) ;
|
||
|
}
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// Convert string to number
|
||
|
wUILang = (LANGID) strtol(szData, NULL, 16) ;
|
||
|
}
|
||
|
}
|
||
|
else if (Osv.dwMajorVersion >= 5.0)
|
||
|
{
|
||
|
// Case 2: Running on Windows 2000 or later. Use GetUserDefaultUILanguage to find
|
||
|
// the user's prefered UI language
|
||
|
|
||
|
|
||
|
HMODULE hMKernel32 = ::LoadLibraryW(L"kernel32.dll") ;
|
||
|
if (hMKernel32 == NULL)
|
||
|
{
|
||
|
hr = SpHrFromLastWin32Error();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
|
||
|
LANGID (WINAPI *pfnGetUserDefaultUILanguage) () =
|
||
|
(LANGID (WINAPI *)(void))
|
||
|
#ifdef _WIN32_WCE
|
||
|
GetProcAddress(hMKernel32, L"GetUserDefaultUILanguage") ;
|
||
|
#else
|
||
|
GetProcAddress(hMKernel32, "GetUserDefaultUILanguage") ;
|
||
|
#endif
|
||
|
|
||
|
if(NULL != pfnGetUserDefaultUILanguage)
|
||
|
{
|
||
|
wUILang = pfnGetUserDefaultUILanguage() ;
|
||
|
}
|
||
|
else
|
||
|
{ // GetProcAddress failed
|
||
|
hr = SpHrFromLastWin32Error();
|
||
|
}
|
||
|
::FreeLibrary(hMKernel32);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// Case 3: Running on Windows NT 4.0 or earlier. Get UI language
|
||
|
// from locale of .default user in registry:
|
||
|
// HKEY_USERS\.DEFAULT\Control Panel\International\Locale
|
||
|
|
||
|
WCHAR szData[32] ;
|
||
|
DWORD dwSize = sizeof(szData) ;
|
||
|
HKEY hKey ;
|
||
|
|
||
|
LONG lRet = RegOpenKeyEx(HKEY_USERS,
|
||
|
_T(".DEFAULT\\Control Panel\\International"),
|
||
|
0,
|
||
|
KEY_READ,
|
||
|
&hKey);
|
||
|
#ifdef _WIN32_WCE_BUG_10655
|
||
|
if(lRet == ERROR_INVALID_PARAMETER)
|
||
|
{
|
||
|
lRet = ERROR_FILE_NOT_FOUND;
|
||
|
}
|
||
|
#endif //_WIN32_WCE_BUG_10655
|
||
|
|
||
|
hr = SpHrFromWin32(lRet);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
lRet = RegQueryValueEx(
|
||
|
hKey,
|
||
|
_T("Locale"),
|
||
|
NULL,
|
||
|
NULL,
|
||
|
(BYTE *)szData,
|
||
|
&dwSize);
|
||
|
|
||
|
#ifdef _WIN32_WCE_BUG_10655
|
||
|
if(lRet == ERROR_INVALID_PARAMETER)
|
||
|
{
|
||
|
lRet = ERROR_FILE_NOT_FOUND;
|
||
|
}
|
||
|
#endif //_WIN32_WCE_BUG_10655
|
||
|
|
||
|
hr = SpHrFromWin32(lRet);
|
||
|
::RegCloseKey(hKey);
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// Convert string to number
|
||
|
wUILang = (LANGID) wcstol(szData, NULL, 16) ;
|
||
|
|
||
|
if(0x0401 == wUILang || // Arabic
|
||
|
0x040d == wUILang || // Hebrew
|
||
|
0x041e == wUILang // Thai
|
||
|
)
|
||
|
{
|
||
|
// Special case these to the English UI.
|
||
|
// These versions of Windows NT 4.0 were enabled only, i.e., the
|
||
|
// UI was English. However, the registry setting
|
||
|
// HKEY_USERS\.DEFAULT\Control Panel\International\Locale was set
|
||
|
// to the respective locale for application compatibility.
|
||
|
wUILang = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US) ;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (wUILang ? wUILang : ::GetUserDefaultLangID()); // In failure case, try our best!
|
||
|
}
|
||
|
|
||
|
|
||
|
inline HRESULT SpGetDescription(ISpObjectToken * pObjToken, WCHAR ** ppszDescription, LANGID Language = SpGetUserDefaultUILanguage())
|
||
|
{
|
||
|
WCHAR szLangId[10];
|
||
|
SpHexFromUlong(szLangId, Language);
|
||
|
HRESULT 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 || ::IsBadWritePtr(pFormatId, sizeof(*pFormatId))
|
||
|
|| ppCoMemWaveFormatEx==NULL || ::IsBadWritePtr(ppCoMemWaveFormatEx, sizeof(*ppCoMemWaveFormatEx)))
|
||
|
{
|
||
|
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 = adwKHZ[dwKHZ];
|
||
|
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 = adwKHZ[dwKHZ];
|
||
|
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 = adwKHZ[dwKHZ];
|
||
|
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 = adwKHZ[dwKHZ];
|
||
|
pwfex->nAvgBytesPerSec = BytesPerSec[dwIndex];
|
||
|
pwfex->nBlockAlign = (WORD)(BlockAlign[dwKHZ] * pwfex->nChannels);
|
||
|
pwfex->wBitsPerSample = 4;
|
||
|
pwfex->cbSize = 32;
|
||
|
BYTE* pExtra = ((BYTE*)pwfex) + sizeof( WAVEFORMATEX );
|
||
|
memcpy( pExtra, Extra[dwKHZ], 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 = adwKHZ[dwIndex];
|
||
|
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)
|
||
|
{
|
||
|
ULONG cb = sizeof(WAVEFORMATEX) + pSrc->cbSize;
|
||
|
*ppCoMemWFEX = (WAVEFORMATEX *)::CoTaskMemAlloc(cb);
|
||
|
if (*ppCoMemWFEX)
|
||
|
{
|
||
|
memcpy(*ppCoMemWFEX, pSrc, cb);
|
||
|
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)
|
||
|
{
|
||
|
SPDBG_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;
|
||
|
return pStream->GetFormat(&m_guidFormatId, &m_pCoMemWaveFormatEx);
|
||
|
}
|
||
|
|
||
|
HRESULT AssignFormat(const WAVEFORMATEX * pWaveFormatEx)
|
||
|
{
|
||
|
::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 (::IsBadReadPtr(pWaveFormatEx, sizeof(*pWaveFormatEx)))
|
||
|
{
|
||
|
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 && (::IsBadReadPtr(pWaveFormatEx, sizeof(*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)
|
||
|
{
|
||
|
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<SPSTREAMFORMAT>(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<SPSTREAMFORMAT>(SPSF_CCITT_ALaw_8kHzMono + dwIndex);
|
||
|
case WAVE_FORMAT_MULAW:
|
||
|
return static_cast<SPSTREAMFORMAT>(SPSF_CCITT_uLaw_8kHzMono + dwIndex);
|
||
|
case WAVE_FORMAT_ADPCM:
|
||
|
return static_cast<SPSTREAMFORMAT>(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<SPSTREAMFORMAT>(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 (::IsBadWritePtr(pFormatId, sizeof(*pFormatId)) ||
|
||
|
::IsBadWritePtr(ppCoMemWFEX, sizeof(*ppCoMemWFEX)))
|
||
|
{
|
||
|
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)
|
||
|
{
|
||
|
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;
|
||
|
::CoTaskMemFree(m_pCoMemWaveFormatEx);
|
||
|
m_pCoMemWaveFormatEx = NULL;
|
||
|
*pcbUsed = *((UNALIGNED ULONG *)pBuffer);
|
||
|
pBuffer += sizeof(ULONG);
|
||
|
// Misaligment exception is generated for SHx platform.
|
||
|
// Marking pointer as UNALIGNED does not help.
|
||
|
#ifndef _WIN32_WCE
|
||
|
m_guidFormatId = *((UNALIGNED GUID *)pBuffer);
|
||
|
#else
|
||
|
memcpy(&m_guidFormatId, pBuffer, sizeof(GUID));
|
||
|
#endif
|
||
|
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)
|
||
|
{
|
||
|
char achCodePage[6];
|
||
|
|
||
|
return (0 != GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE, achCodePage, sizeof(achCodePage))) ? atoi(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 */
|
||
|
|
||
|
#ifndef _UNICODE
|
||
|
inline HRESULT SPBindToFile( const TCHAR * 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);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/****************************************************************************
|
||
|
* 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.
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
// WCE compiler does not work propertly with template
|
||
|
#ifndef _WIN32_WCE
|
||
|
template <class T>
|
||
|
inline ULONG SpEventSerializeSize(const SPEVENT * pEvent)
|
||
|
|
||
|
{
|
||
|
ULONG ulSize = sizeof(T);
|
||
|
|
||
|
#else
|
||
|
|
||
|
inline ULONG SpEventSerializeSize(const SPEVENT * pEvent, ULONG ulSize)
|
||
|
{
|
||
|
#endif //_WIN32_WCE
|
||
|
|
||
|
if( ( pEvent->elParamType == SPET_LPARAM_IS_POINTER ) && pEvent->lParam )
|
||
|
{
|
||
|
ulSize += ULONG(pEvent->wParam);
|
||
|
}
|
||
|
else if ((pEvent->elParamType == SPET_LPARAM_IS_STRING) && pEvent->lParam != NULL)
|
||
|
{
|
||
|
ulSize += (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 );
|
||
|
}
|
||
|
}
|
||
|
// 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
|
||
|
*
|
||
|
********************************************************************* RAL ***/
|
||
|
|
||
|
// WCE compiler does not work propertly with template
|
||
|
#ifndef _WIN32_WCE
|
||
|
template <class T>
|
||
|
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 += (wcslen((WCHAR*)(pSerEvent + 1)) + 1) * sizeof( WCHAR );
|
||
|
}
|
||
|
// Round up to nearest DWORD
|
||
|
ulSize += 3;
|
||
|
ulSize -= ulSize % 4;
|
||
|
return ulSize;
|
||
|
}
|
||
|
|
||
|
#else //_WIN32_WCE
|
||
|
|
||
|
inline ULONG SpSerializedEventSize(const SPSERIALIZEDEVENT * pSerEvent, ULONG ulSize)
|
||
|
{
|
||
|
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 += (wcslen((WCHAR*)(pSerEvent + 1)) + 1) * sizeof( WCHAR );
|
||
|
}
|
||
|
// Round up to nearest DWORD
|
||
|
ulSize += 3;
|
||
|
ulSize -= ulSize % 4;
|
||
|
return ulSize;
|
||
|
}
|
||
|
|
||
|
inline ULONG SpSerializedEventSize(const SPSERIALIZEDEVENT64 * pSerEvent, ULONG ulSize)
|
||
|
{
|
||
|
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 += (wcslen((WCHAR*)(pSerEvent + 1)) + 1) * sizeof( WCHAR );
|
||
|
}
|
||
|
// Round up to nearest DWORD
|
||
|
ulSize += 3;
|
||
|
ulSize -= ulSize % 4;
|
||
|
return ulSize;
|
||
|
}
|
||
|
|
||
|
#endif //_WIN32_WCE
|
||
|
|
||
|
/*** 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)
|
||
|
{
|
||
|
SPDBG_ASSERT(wParam && (wParam < 0x100000)); // this is too big!
|
||
|
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)
|
||
|
{
|
||
|
pDestEvent->lParam = (LPARAM)::CoTaskMemAlloc((wcslen((WCHAR*)lParam) + 1) * sizeof(WCHAR));
|
||
|
if (pDestEvent->lParam)
|
||
|
{
|
||
|
wcscpy((WCHAR*)pDestEvent->lParam, (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<const CSpEvent *>(pSrcEvent)->CopyTo(this);
|
||
|
}
|
||
|
void Detach(SPEVENT * pDestEvent = NULL)
|
||
|
{
|
||
|
if (pDestEvent)
|
||
|
{
|
||
|
memcpy(pDestEvent, this, sizeof(*pDestEvent));
|
||
|
}
|
||
|
memset(this, 0, sizeof(*this));
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
ULONG SerializeSize() const
|
||
|
{
|
||
|
return SpEventSerializeSize<T>(this);
|
||
|
}
|
||
|
|
||
|
// Call this method with either SPSERIALIZEDEVENT or SPSERIALIZEDEVENT64
|
||
|
template <class T>
|
||
|
void Serialize(T * pSerEvent) const
|
||
|
{
|
||
|
SPDBG_ASSERT(elParamType != SPET_LPARAM_IS_OBJECT);
|
||
|
pSerEvent->eEventId = this->eEventId;
|
||
|
pSerEvent->elParamType = this->elParamType;
|
||
|
pSerEvent->ulStreamNum = this->ulStreamNum;
|
||
|
pSerEvent->ullAudioStreamOffset = this->ullAudioStreamOffset;
|
||
|
pSerEvent->SerializedwParam = static_cast<ULONG>(this->wParam);
|
||
|
pSerEvent->SerializedlParam = static_cast<LONG>(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:
|
||
|
wcscpy((WCHAR *)(pSerEvent + 1), (WCHAR*)lParam);
|
||
|
pSerEvent->SerializedlParam = sizeof(T);
|
||
|
break;
|
||
|
|
||
|
case SPET_LPARAM_IS_TOKEN:
|
||
|
{
|
||
|
CSpDynamicString dstrObjectId;
|
||
|
if( SUCCEEDED( ((ISpObjectToken*)lParam)->GetId( &dstrObjectId ) ) )
|
||
|
{
|
||
|
pSerEvent->SerializedwParam = (dstrObjectId.Length() + 1) * sizeof( WCHAR );;
|
||
|
memcpy( pSerEvent + 1, (void *)dstrObjectId.m_psz, static_cast<ULONG>(pSerEvent->SerializedwParam) );
|
||
|
}
|
||
|
pSerEvent->SerializedlParam = sizeof(T);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
HRESULT Serialize(T ** ppCoMemSerEvent, ULONG * pcbSerEvent) const
|
||
|
{
|
||
|
// WCE compiler does not work propertly with template
|
||
|
#ifndef _WIN32_WCE
|
||
|
*pcbSerEvent = SpEventSerializeSize<T>(this);
|
||
|
#else
|
||
|
*pcbSerEvent = SpEventSerializeSize(this, sizeof(** ppCoMemSerEvent));
|
||
|
#endif
|
||
|
*ppCoMemSerEvent = (T *)::CoTaskMemAlloc(*pcbSerEvent);
|
||
|
if (*ppCoMemSerEvent)
|
||
|
{
|
||
|
Serialize(*ppCoMemSerEvent);
|
||
|
return S_OK;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*pcbSerEvent = 0;
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Call this method with either SPSERIALIZEDEVENT or SPSERIALIZEDEVENT64
|
||
|
template <class T>
|
||
|
HRESULT Deserialize(const T * pSerEvent, ULONG * pcbUsed = NULL)
|
||
|
{
|
||
|
Clear();
|
||
|
HRESULT hr = S_OK;
|
||
|
const UNALIGNED T * pTemp = pSerEvent;
|
||
|
this->eEventId = pTemp->eEventId;
|
||
|
this->elParamType = pTemp->elParamType;
|
||
|
this->ulStreamNum = pTemp->ulStreamNum;
|
||
|
this->ullAudioStreamOffset = pTemp->ullAudioStreamOffset;
|
||
|
this->wParam = static_cast<WPARAM>(pTemp->SerializedwParam);
|
||
|
this->lParam = static_cast<LPARAM>(pTemp->SerializedlParam);
|
||
|
if (pTemp->SerializedlParam)
|
||
|
{
|
||
|
ULONG cbAlloc = 0;
|
||
|
switch (pTemp->elParamType)
|
||
|
{
|
||
|
case SPET_LPARAM_IS_POINTER:
|
||
|
cbAlloc = static_cast<ULONG>(wParam);
|
||
|
break;
|
||
|
|
||
|
case SPET_LPARAM_IS_STRING:
|
||
|
cbAlloc = sizeof(WCHAR) * (1 + wcslen((const WCHAR *)(pTemp + 1)));
|
||
|
break;
|
||
|
|
||
|
case SPET_LPARAM_IS_TOKEN:
|
||
|
{
|
||
|
ULONG ulDataOffset = ULONG(lParam);
|
||
|
hr = SpGetTokenFromId( (const WCHAR*)(pTemp + 1),
|
||
|
(ISpObjectToken **)&lParam );
|
||
|
wParam = 0;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
if (cbAlloc)
|
||
|
{
|
||
|
void * pvBuff = ::CoTaskMemAlloc(cbAlloc);
|
||
|
this->lParam = (LPARAM)pvBuff;
|
||
|
if (pvBuff)
|
||
|
{
|
||
|
memcpy(pvBuff, pTemp + 1, cbAlloc);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( SUCCEEDED( hr ) && pcbUsed )
|
||
|
{
|
||
|
// WCE compiler does not work propertly with template
|
||
|
#ifndef _WIN32_WCE
|
||
|
*pcbUsed = SpEventSerializeSize<T>(this);
|
||
|
#else
|
||
|
*pcbUsed = SpEventSerializeSize(this, sizeof(*pTemp));
|
||
|
#endif
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Helpers for access to events. Performs run-time checks in debug and casts
|
||
|
// data to the appropriate types
|
||
|
//
|
||
|
SPPHONEID Phoneme() const
|
||
|
{
|
||
|
SPDBG_ASSERT(eEventId == SPEI_PHONEME);
|
||
|
return (SPPHONEID)LOWORD(lParam);
|
||
|
}
|
||
|
SPVISEMES Viseme() const
|
||
|
{
|
||
|
SPDBG_ASSERT(eEventId == SPEI_VISEME);
|
||
|
return (SPVISEMES)LOWORD(lParam);
|
||
|
}
|
||
|
ULONG InputWordPos() const
|
||
|
{
|
||
|
SPDBG_ASSERT(eEventId == SPEI_WORD_BOUNDARY);
|
||
|
return ULONG(lParam);
|
||
|
}
|
||
|
ULONG InputWordLen() const
|
||
|
{
|
||
|
SPDBG_ASSERT(eEventId == SPEI_WORD_BOUNDARY);
|
||
|
return ULONG(wParam);
|
||
|
}
|
||
|
ULONG InputSentPos() const
|
||
|
{
|
||
|
SPDBG_ASSERT(eEventId == SPEI_SENTENCE_BOUNDARY);
|
||
|
return ULONG(lParam);
|
||
|
}
|
||
|
ULONG InputSentLen() const
|
||
|
{
|
||
|
SPDBG_ASSERT(eEventId == SPEI_SENTENCE_BOUNDARY);
|
||
|
return ULONG(wParam);
|
||
|
}
|
||
|
ISpObjectToken * ObjectToken() const
|
||
|
{
|
||
|
SPDBG_ASSERT(elParamType == SPET_LPARAM_IS_TOKEN);
|
||
|
return (ISpObjectToken *)lParam;
|
||
|
}
|
||
|
ISpObjectToken * VoiceToken() const // More explicit check than ObjectToken()
|
||
|
{
|
||
|
SPDBG_ASSERT(eEventId == SPEI_VOICE_CHANGE);
|
||
|
return ObjectToken();
|
||
|
}
|
||
|
BOOL PersistVoiceChange() const
|
||
|
{
|
||
|
SPDBG_ASSERT(eEventId == SPEI_VOICE_CHANGE);
|
||
|
return (BOOL)wParam;
|
||
|
}
|
||
|
IUnknown * Object() const
|
||
|
{
|
||
|
SPDBG_ASSERT(elParamType == SPET_LPARAM_IS_OBJECT);
|
||
|
return (IUnknown*)lParam;
|
||
|
}
|
||
|
ISpRecoResult * RecoResult() const
|
||
|
{
|
||
|
SPDBG_ASSERT(eEventId == SPEI_RECOGNITION || eEventId == SPEI_FALSE_RECOGNITION || eEventId == SPEI_HYPOTHESIS);
|
||
|
return (ISpRecoResult *)Object();
|
||
|
}
|
||
|
BOOL IsPaused()
|
||
|
{
|
||
|
SPDBG_ASSERT(eEventId == SPEI_RECOGNITION || eEventId == SPEI_SR_BOOKMARK);
|
||
|
return (BOOL)(wParam & SPREF_AutoPause);
|
||
|
}
|
||
|
BOOL IsEmulated()
|
||
|
{
|
||
|
SPDBG_ASSERT(eEventId == SPEI_RECOGNITION);
|
||
|
return (BOOL)(wParam & SPREF_Emulated);
|
||
|
}
|
||
|
const WCHAR * String() const
|
||
|
{
|
||
|
SPDBG_ASSERT(elParamType == SPET_LPARAM_IS_STRING);
|
||
|
return (const WCHAR*)lParam;
|
||
|
}
|
||
|
const WCHAR * BookmarkName() const
|
||
|
{
|
||
|
SPDBG_ASSERT(eEventId == SPEI_TTS_BOOKMARK);
|
||
|
return String();
|
||
|
}
|
||
|
const WCHAR * RequestTypeOfUI() const
|
||
|
{
|
||
|
SPDBG_ASSERT(eEventId == SPEI_REQUEST_UI);
|
||
|
return String();
|
||
|
}
|
||
|
SPRECOSTATE RecoState() const
|
||
|
{
|
||
|
SPDBG_ASSERT(eEventId == SPEI_RECO_STATE_CHANGE);
|
||
|
return static_cast<SPRECOSTATE>(wParam);
|
||
|
}
|
||
|
const WCHAR * PropertyName() const
|
||
|
{
|
||
|
SPDBG_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
|
||
|
{
|
||
|
SPDBG_ASSERT(eEventId == SPEI_PROPERTY_NUM_CHANGE);
|
||
|
return static_cast<LONG>(wParam);
|
||
|
}
|
||
|
const WCHAR * PropertyStringValue() const
|
||
|
{
|
||
|
// Search for the first NULL and return pointer to the char past it.
|
||
|
SPDBG_ASSERT(eEventId == SPEI_PROPERTY_STRING_CHANGE);
|
||
|
const WCHAR * psz;
|
||
|
for (psz = (const WCHAR *)lParam; *psz; psz++) {}
|
||
|
return psz + 1;
|
||
|
}
|
||
|
SPINTERFERENCE Interference() const
|
||
|
{
|
||
|
SPDBG_ASSERT(eEventId == SPEI_INTERFERENCE);
|
||
|
return static_cast<SPINTERFERENCE>(lParam);
|
||
|
}
|
||
|
HRESULT EndStreamResult() const
|
||
|
{
|
||
|
SPDBG_ASSERT(eEventId == SPEI_END_SR_STREAM);
|
||
|
return static_cast<HRESULT>(lParam);
|
||
|
}
|
||
|
BOOL InputStreamReleased() const
|
||
|
{
|
||
|
SPDBG_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&()
|
||
|
{
|
||
|
SPDBG_ASSERT(m_pPhrase == NULL);
|
||
|
return &m_pPhrase;
|
||
|
}
|
||
|
operator SPPHRASE *() const
|
||
|
{
|
||
|
return m_pPhrase;
|
||
|
}
|
||
|
SPPHRASE & operator*() const
|
||
|
{
|
||
|
SPDBG_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 T>
|
||
|
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)
|
||
|
{
|
||
|
m_pT = (T *)::CoTaskMemRealloc(m_pT, sizeof(T) * cArrayElements);
|
||
|
SPDBG_ASSERT(m_pT);
|
||
|
return (m_pT ? S_OK : E_OUTOFMEMORY);
|
||
|
}
|
||
|
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&()
|
||
|
{
|
||
|
SPDBG_ASSERT(m_pT == NULL);
|
||
|
return &m_pT;
|
||
|
}
|
||
|
T * operator->()
|
||
|
{
|
||
|
SPDBG_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<ISpPhoneConverter> cpPhoneConv = NULL)
|
||
|
{
|
||
|
SPDBG_FUNC("CreatePhraseFromWordArray");
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
if ( cWords == 0 || ppWords == NULL || ::IsBadReadPtr(ppWords, sizeof(*ppWords) * cWords ) )
|
||
|
{
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
|
||
|
if ( prgDispAttribs != NULL && ::IsBadReadPtr(prgDispAttribs, sizeof(*prgDispAttribs) * cWords ) )
|
||
|
{
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
CSpDynamicString dsText(cTotalChars);
|
||
|
if(dsText.m_psz == NULL)
|
||
|
{
|
||
|
::CoTaskMemFree(pStringPtrArray);
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
CSpDynamicString dsPhoneId(cTotalChars);
|
||
|
if(dsPhoneId.m_psz == NULL)
|
||
|
{
|
||
|
::CoTaskMemFree(pStringPtrArray);
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
SPPHONEID* pphoneId = (SPPHONEID*)dsPhoneId.m_psz;
|
||
|
|
||
|
SPPHRASE Phrase;
|
||
|
memset(&Phrase, 0, sizeof(Phrase));
|
||
|
Phrase.cbSize = sizeof(Phrase);
|
||
|
|
||
|
if(LangId == 0)
|
||
|
{
|
||
|
LangId = SpGetUserDefaultUILanguage();
|
||
|
}
|
||
|
|
||
|
if(cpPhoneConv == NULL)
|
||
|
{
|
||
|
hr = SpCreatePhoneConverter(LangId, NULL, NULL, &cpPhoneConv);
|
||
|
if(FAILED(hr))
|
||
|
{
|
||
|
::CoTaskMemFree(pStringPtrArray);
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
wcscpy( pText, ppWords[i] );
|
||
|
pText += wcslen( p ) + 1;
|
||
|
|
||
|
if (*p == L'/')
|
||
|
{
|
||
|
//This is a compound word
|
||
|
WCHAR* pszFirstPart = ++p;
|
||
|
WCHAR* pszSecondPart = NULL;
|
||
|
WCHAR* pszThirdPart = NULL;
|
||
|
|
||
|
while (*p && *p != L'/')
|
||
|
{
|
||
|
p++;
|
||
|
}
|
||
|
if (*p == L'/')
|
||
|
{
|
||
|
//It means we stop at the second '/'
|
||
|
*p = L'\0';
|
||
|
pszSecondPart = ++p;
|
||
|
while (*p && *p != L'/')
|
||
|
{
|
||
|
p++;
|
||
|
}
|
||
|
if (*p == L'/')
|
||
|
{
|
||
|
//It means we stop at the third '/'
|
||
|
*p = L'\0';
|
||
|
pszThirdPart = ++p;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pPhraseElement[i].pszDisplayText = pszFirstPart;
|
||
|
pPhraseElement[i].pszLexicalForm = pszSecondPart ? pszSecondPart : pszFirstPart;
|
||
|
|
||
|
if ( pszThirdPart)
|
||
|
{
|
||
|
hr = cpPhoneConv->PhoneToId(pszThirdPart, pphoneId);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
pPhraseElement[i].pszPronunciation = pphoneId;
|
||
|
pphoneId += wcslen( (wchar_t*)pphoneId ) + 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//It is the simple format, only have one form, use it for everything.
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
Phrase.Rule.ulCountOfElements = cWords;
|
||
|
Phrase.pElements = pPhraseElement;
|
||
|
Phrase.LangID = LangId;
|
||
|
|
||
|
CComPtr<ISpPhraseBuilder> 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 ****/
|
||
|
inline HRESULT CreatePhraseFromText(const WCHAR *pszOriginalText,
|
||
|
ISpPhraseBuilder **ppResultPhrase,
|
||
|
LANGID LangId = 0,
|
||
|
CComPtr<ISpPhoneConverter> cpPhoneConv = NULL)
|
||
|
{
|
||
|
SPDBG_FUNC("CreatePhraseFromText");
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
//We first trim the input text
|
||
|
CSpDynamicString dsText(pszOriginalText);
|
||
|
if(dsText.m_psz == NULL)
|
||
|
{
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
dsText.TrimBoth();
|
||
|
|
||
|
ULONG cWords = 0;
|
||
|
BOOL fInCompoundword = FALSE;
|
||
|
|
||
|
// Set first array pointer (if *p).
|
||
|
WCHAR *p = dsText;
|
||
|
while (*p)
|
||
|
{
|
||
|
if( iswspace(*p) && !fInCompoundword)
|
||
|
{
|
||
|
cWords++;
|
||
|
*p++ = L'\0';
|
||
|
while (*p && iswspace(*p))
|
||
|
{
|
||
|
*p++ = L'\0';
|
||
|
}
|
||
|
// Add new array pointer. Use vector.
|
||
|
}
|
||
|
else if (*p == L'/' && !fInCompoundword)
|
||
|
{
|
||
|
fInCompoundword = TRUE;
|
||
|
}
|
||
|
else if (*p == L';' && fInCompoundword)
|
||
|
{
|
||
|
fInCompoundword = FALSE;
|
||
|
*p++ = L'\0';
|
||
|
// Add new array element.
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
p++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cWords++;
|
||
|
|
||
|
WCHAR** pStringPtrArray = (WCHAR**)::CoTaskMemAlloc( cWords * sizeof(WCHAR *));
|
||
|
if ( !pStringPtrArray )
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
if ( SUCCEEDED( hr ) )
|
||
|
{
|
||
|
p = dsText;
|
||
|
for (ULONG i=0; i<cWords; i++)
|
||
|
{
|
||
|
pStringPtrArray[i] = p;
|
||
|
p += wcslen(p)+1;
|
||
|
}
|
||
|
|
||
|
hr = CreatePhraseFromWordArray((const WCHAR **)pStringPtrArray, cWords, NULL, ppResultPhrase, LangId, cpPhoneConv);
|
||
|
|
||
|
::CoTaskMemFree(pStringPtrArray);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
#endif /* This must be the last line in the file */
|