2730 lines
85 KiB
Plaintext
2730 lines
85 KiB
Plaintext
/*******************************************************************************
|
|
* SPHelper.h *
|
|
*------------*
|
|
* Description:
|
|
* This is the header file for core helper functions implementation.
|
|
*-------------------------------------------------------------------------------
|
|
* Created By: EDC Date: 08/14/98
|
|
* Copyright (C) 1998 Microsoft Corporation
|
|
* All Rights Reserved
|
|
*
|
|
*******************************************************************************/
|
|
#ifndef SPHelper_h
|
|
#define SPHelper_h
|
|
|
|
#ifndef __sapi_h__
|
|
#include <sapi.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_CRTDBG
|
|
#include <crtdbg.h>
|
|
#endif
|
|
|
|
#ifndef _INC_MALLOC
|
|
#include <malloc.h>
|
|
#endif
|
|
|
|
#ifndef _INC_MMSYSTEM
|
|
#include <mmsystem.h>
|
|
#endif
|
|
|
|
#ifndef __comcat_h__
|
|
#include <comcat.h>
|
|
#endif
|
|
|
|
|
|
//=== Constants ==============================================================
|
|
#define sp_countof(x) ((sizeof(x) / sizeof(*(x))))
|
|
#define DEF_SPLSTR( s ) { L##s , sp_countof( s ) - 1 }
|
|
|
|
//
|
|
// String handling and conversion classes
|
|
//
|
|
/*** SPLSTR
|
|
* This structure is for managing strings with known lengths
|
|
*/
|
|
struct SPLSTR
|
|
{
|
|
WCHAR* pStr;
|
|
int Len;
|
|
};
|
|
|
|
//
|
|
// Define a few new ATL OLE2T and W2T to deal with const strings (unicode only)
|
|
//
|
|
#ifdef _UNICODE
|
|
inline const WCHAR * OLE2T(const WCHAR * lp) { return lp; }
|
|
inline const WCHAR * W2T(const WCHAR * lp) { return lp; }
|
|
#endif
|
|
|
|
//
|
|
// Helper template class used to convert WCHAR strings to fixed-size buffers
|
|
//
|
|
template <const int i = MAX_PATH>
|
|
class CSpTcharString
|
|
{
|
|
#ifdef _UNICODE
|
|
private:
|
|
const WCHAR * m_psz;
|
|
public:
|
|
CSpTcharString(const WCHAR * psz) : m_psz(psz) {}
|
|
operator const WCHAR *() { return m_psz; }
|
|
const WCHAR * operator =(const WCHAR * psz) { m_psz = psz; return psz; }
|
|
#else
|
|
private:
|
|
TCHAR m_aString[i];
|
|
public:
|
|
CSpTcharString(const WCHAR * psz)
|
|
{
|
|
::WideCharToMultiByte(CP_ACP, 0, psz, -1, m_aString, i, NULL, NULL);
|
|
}
|
|
operator const TCHAR *() { return m_aString; }
|
|
const TCHAR * operator =(const WCHAR * psz)
|
|
{
|
|
::WideCharToMultiByte(CP_ACP, 0, psz, -1, m_aString, i, NULL, NULL);
|
|
return m_aString;
|
|
}
|
|
#endif
|
|
};
|
|
|
|
template <const int i = MAX_PATH>
|
|
class CSpToAnsiString
|
|
{
|
|
private:
|
|
CHAR * m_pStr;
|
|
CHAR m_aString[i];
|
|
public:
|
|
CSpToAnsiString(const WCHAR * psz)
|
|
{
|
|
if (psz)
|
|
{
|
|
m_pStr = m_aString;
|
|
::WideCharToMultiByte(CP_ACP, 0, psz, -1, m_aString, i, NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
m_pStr = NULL;
|
|
}
|
|
}
|
|
operator CHAR *() { return m_pStr; }
|
|
CHAR * operator =(const WCHAR * psz)
|
|
{
|
|
if (psz)
|
|
{
|
|
m_pStr = m_aString;
|
|
::WideCharToMultiByte(CP_ACP, 0, psz, -1, m_aString, i, NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
m_pStr = NULL;
|
|
}
|
|
return m_pStr;
|
|
}
|
|
};
|
|
|
|
// BUGBUG - This class isn't tested and isn't used a whole lot, maybe it should be removed.
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4100) // unused formal paramater
|
|
class CSpFileStream : public IStream
|
|
{
|
|
private:
|
|
HANDLE m_hFile;
|
|
ULONG m_ulRef;
|
|
public:
|
|
CSpFileStream(HRESULT * pHR, const TCHAR * pFileName, DWORD dwDesiredAccess = GENERIC_READ, DWORD dwShareMode = FILE_SHARE_READ, DWORD dwCreationDisposition = OPEN_EXISTING)
|
|
{
|
|
m_hFile = ::CreateFile(pFileName, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, 0, NULL);
|
|
m_ulRef = 1;
|
|
*pHR = (m_hFile != INVALID_HANDLE_VALUE) ? S_OK : HRESULT_FROM_WIN32(::GetLastError());
|
|
}
|
|
CSpFileStream(HANDLE hFile) : m_hFile(hFile), m_ulRef(1) {}
|
|
~CSpFileStream()
|
|
{
|
|
if (m_hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
::CloseHandle(m_hFile);
|
|
m_hFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
STDMETHODIMP QueryInterface(REFIID riid, void ** ppv)
|
|
{
|
|
if (riid == __uuidof(IStream) ||
|
|
riid == IID_ISequentialStream ||
|
|
riid == __uuidof(IUnknown))
|
|
{
|
|
*ppv = (IStream *)this;
|
|
m_ulRef++;
|
|
return S_OK;
|
|
}
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
STDMETHODIMP_(ULONG) AddRef()
|
|
{
|
|
return ++m_ulRef;
|
|
}
|
|
STDMETHODIMP_(ULONG) Release()
|
|
{
|
|
--m_ulRef;
|
|
if (m_ulRef)
|
|
{
|
|
return m_ulRef;
|
|
}
|
|
delete this;
|
|
return 0;
|
|
}
|
|
STDMETHODIMP Read(void * pv, ULONG cb, ULONG * pcbRead)
|
|
{
|
|
ULONG ulRead;
|
|
if (::ReadFile(m_hFile, pv, cb, &ulRead, NULL))
|
|
{
|
|
if (pcbRead) *pcbRead = ulRead;
|
|
return S_OK;
|
|
}
|
|
return HRESULT_FROM_WIN32(::GetLastError());
|
|
}
|
|
STDMETHODIMP Write(const void * pv, ULONG cb, ULONG * pcbWritten)
|
|
{
|
|
ULONG ulWritten;
|
|
if (::WriteFile(m_hFile, pv, cb, &ulWritten, NULL))
|
|
{
|
|
if (pcbWritten) *pcbWritten = ulWritten;
|
|
return S_OK;
|
|
}
|
|
return HRESULT_FROM_WIN32(::GetLastError());
|
|
}
|
|
|
|
STDMETHODIMP Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
|
|
{
|
|
long lMoveHigh = dlibMove.HighPart;
|
|
DWORD dwNewPos = ::SetFilePointer(m_hFile, dlibMove.LowPart, &lMoveHigh, dwOrigin);
|
|
if (dwNewPos == 0xFFFFFFFF && ::GetLastError() != NO_ERROR)
|
|
{
|
|
return HRESULT_FROM_WIN32(::GetLastError());
|
|
}
|
|
if (plibNewPosition)
|
|
{
|
|
plibNewPosition->LowPart = dwNewPos;
|
|
plibNewPosition->HighPart = lMoveHigh;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP SetSize(ULARGE_INTEGER libNewSize)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LARGE_INTEGER Move = {0};
|
|
ULARGE_INTEGER Cur;
|
|
hr = Seek(Move, STREAM_SEEK_CUR, &Cur);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LARGE_INTEGER li;
|
|
li.QuadPart = libNewSize.QuadPart;
|
|
hr = Seek(li, STREAM_SEEK_SET, NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (!::SetEndOfFile(m_hFile))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(::GetLastError());
|
|
}
|
|
li.QuadPart = Cur.QuadPart;
|
|
Seek(li, STREAM_SEEK_SET, NULL);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CopyTo(IStream *pStreamDest, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER __RPC_FAR *pcbWritten)
|
|
{
|
|
// BUGBUG -- For now do it the slacker way!
|
|
void * pData = ::HeapAlloc(::GetProcessHeap(), 0, (DWORD)cb.QuadPart);
|
|
if (pData == NULL) return E_OUTOFMEMORY;
|
|
ULONG cbRead;
|
|
Read(pData, (DWORD)cb.QuadPart, &cbRead);
|
|
if (pcbRead) pcbRead->QuadPart = cbRead;
|
|
ULONG cbWritten;
|
|
pStreamDest->Write(pData, cbRead, &cbWritten);
|
|
if (pcbWritten) pcbWritten->QuadPart = cbWritten;
|
|
::HeapFree(::GetProcessHeap(), 0, pData);
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP Commit(DWORD grfCommitFlags)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP Revert(void)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
|
|
{
|
|
return E_NOTIMPL; // BUGBUG -- This could be supported!
|
|
}
|
|
|
|
STDMETHODIMP UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
|
|
{
|
|
return E_NOTIMPL; // BUGBUG -- This too!
|
|
}
|
|
|
|
STDMETHODIMP Stat(STATSTG *pstatstg, DWORD grfStatFlag)
|
|
{
|
|
//
|
|
// BUGBUG -- This needs to be thought out some more. Name, etc
|
|
//
|
|
HRESULT hr = S_OK;
|
|
if (grfStatFlag & (~STATFLAG_NONAME))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It is acceptable to simply fill in the size and type fields and zero the rest.
|
|
// This is what streams created by CreateStreamOnHGlobal return.
|
|
//
|
|
ZeroMemory(pstatstg, sizeof(*pstatstg));
|
|
pstatstg->type = STGTY_STREAM;
|
|
pstatstg->cbSize.LowPart = ::GetFileSize(m_hFile, &(pstatstg->cbSize.HighPart));
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP Clone(IStream ** ppstm)
|
|
{
|
|
return E_NOTIMPL; // BUGBUG -- This should work just fine!
|
|
}
|
|
|
|
|
|
};
|
|
#pragma warning(pop)
|
|
|
|
|
|
//=== Object creation helpers ================================================
|
|
|
|
template<class T>
|
|
HRESULT SpCreateObject(ISpObjectToken * pToken, T ** ppObject,
|
|
IUnknown * pUnkOuter = NULL, DWORD dwClsCtxt = CLSCTX_ALL)
|
|
{
|
|
return pToken->CreateInstance(pUnkOuter, dwClsCtxt, __uuidof(T), (void **)ppObject);
|
|
}
|
|
|
|
|
|
template<class T>
|
|
HRESULT SpCreateObject(ISpResourceManager * pResMgr, const WCHAR * pszObjectId, T ** ppObject,
|
|
IUnknown * pUnkOuter = NULL, DWORD dwClsCtxt = CLSCTX_ALL)
|
|
{
|
|
ISpObjectToken * pToken;
|
|
HRESULT hr = pResMgr->GetObjectToken(pszObjectId, &pToken);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SpCreateObject(pToken, ppObject, pUnkOuter, dwClsCtxt);
|
|
pToken->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
template<class T>
|
|
HRESULT SpCreateBestObject(ISpResourceManager * pResMgr, const WCHAR * pszCategory, T ** ppObject,
|
|
const WCHAR * pszReqAttrs = NULL, const WCHAR * pszOptAttrs = NULL,
|
|
IUnknown * pUnkOuter = NULL, DWORD dwClsCtxt = CLSCTX_ALL)
|
|
{
|
|
IEnumSpObjectTokens * pEnum;
|
|
HRESULT hr = pResMgr->EnumTokens(pszCategory, pszReqAttrs, pszOptAttrs, &pEnum);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ISpObjectToken * pToken;
|
|
hr = pEnum->Next(1, &pToken, NULL);
|
|
if (hr == S_OK)
|
|
{
|
|
hr = SpCreateObject(pToken, ppObject, pUnkOuter, dwClsCtxt);
|
|
pToken->Release();
|
|
}
|
|
pEnum->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
template<class T>
|
|
T * SpInterlockedExchangePointer(T ** pTarget, void * pNew) // Use VOID for pNew so NULL will work.
|
|
{
|
|
#ifdef InterlockedExchangePointer
|
|
return (T *) InterlockedExchangePointer(pTarget, (T *)pNew);
|
|
#else
|
|
return (T *)::InterlockedExchange((LPLONG)pTarget, (LONG)pNew);
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
* 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
|
|
*
|
|
********************************************************************* RAL ***/
|
|
|
|
inline LANGID SpGetUserDefaultUILanguage(void)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LANGID wUILang = 0;
|
|
|
|
OSVERSIONINFO Osv ;
|
|
Osv.dwOSVersionInfoSize = sizeof(Osv) ;
|
|
if(!GetVersionEx(&Osv))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(::GetLastError());
|
|
}
|
|
// Get the UI language by one of three methods, depending on the system
|
|
// BUGBUG -- What about Windows CE???
|
|
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 dwErr, dwSize = sizeof(szData) ;
|
|
HKEY hKey;
|
|
|
|
dwErr = HRESULT_FROM_WIN32(::RegOpenKeyExA(HKEY_USERS, ".Default\\Control Panel\\desktop\\ResourceLocale",
|
|
0, KEY_READ, &hKey));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(::RegQueryValueExA(hKey, "", NULL, NULL, (BYTE *)szData, &dwSize));
|
|
::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 = HRESULT_FROM_WIN32(::GetLastError());
|
|
}
|
|
else
|
|
{
|
|
LANGID (WINAPI *pfnGetUserDefaultUILanguage) () =
|
|
(LANGID (WINAPI *)(void))
|
|
GetProcAddress(hMKernel32, "GetUserDefaultUILanguage") ;
|
|
|
|
if(NULL != pfnGetUserDefaultUILanguage)
|
|
{
|
|
wUILang = pfnGetUserDefaultUILanguage() ;
|
|
}
|
|
else
|
|
{ // GetProcAddress failed
|
|
hr = HRESULT_FROM_WIN32(::GetLastError());
|
|
}
|
|
::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 ;
|
|
|
|
hr = HRESULT_FROM_WIN32(::RegOpenKeyExW(HKEY_USERS, L".DEFAULT\\Control Panel\\International",
|
|
0, KEY_READ, &hKey));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(::RegQueryValueExW(hKey, L"Locale", NULL, NULL, (BYTE *)szData, &dwSize));
|
|
::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!
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* SpConvertLanguageId *
|
|
*---------------------*
|
|
* Description:
|
|
* Given a language ID in pLangId, this function will convert the ID as follows:
|
|
* 0 - Returns the default UI language ID
|
|
* LANG_SYSTEM_DEFAULT - Returns the system default language ID
|
|
* LANG_USER_DEFAULT - Returns the user default language ID
|
|
* Other - Function will not change language ID
|
|
*
|
|
* Returns:
|
|
* Converted language ID
|
|
*
|
|
********************************************************************* RAL ***/
|
|
|
|
inline LANGID SpConvertLanguageId(LANGID LangId)
|
|
{
|
|
switch (LangId)
|
|
{
|
|
case 0:
|
|
return ::SpGetUserDefaultUILanguage();
|
|
case LANG_SYSTEM_DEFAULT:
|
|
return ::GetSystemDefaultLangID();
|
|
case LANG_USER_DEFAULT:
|
|
return ::GetUserDefaultLangID();
|
|
default:
|
|
return LangId;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// 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++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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:
|
|
*
|
|
* Returns:
|
|
*
|
|
********************************************************************* RAL ***/
|
|
|
|
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 & 0x1c) >> 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
|
|
{
|
|
*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 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()
|
|
{
|
|
memset(&m_guidFormatId, 0, sizeof(m_guidFormatId));
|
|
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 if it's a standard enum or not.
|
|
//
|
|
DWORD dwIndex = 0;
|
|
if (m_pCoMemWaveFormatEx->wFormatTag != WAVE_FORMAT_PCM)
|
|
{
|
|
return SPSF_ExtendedAudioFormat;
|
|
}
|
|
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);
|
|
}
|
|
|
|
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();
|
|
*((ULONG *)pBuffer) = cb;
|
|
pBuffer += sizeof(ULONG);
|
|
*((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 = *((ULONG *)pBuffer);
|
|
pBuffer += sizeof(ULONG);
|
|
m_guidFormatId = *((GUID *)pBuffer);
|
|
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;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
/*
|
|
inline BOOL SPGuidToWaveFormatEx(REFGUID rguidFmt, WAVEFORMATEX * pWFEX)
|
|
{
|
|
if (!SPIsValidPCMFormat(rguidFmt))
|
|
{
|
|
return FALSE;
|
|
}
|
|
BOOL bIsStereo = rguidFmt.Data1 & 0x1;
|
|
BOOL bIs16 = rguidFmt.Data1 & 0x2;
|
|
DWORD dwKHZ = (rguidFmt.Data1 & 0x1c) >> 2;
|
|
static const DWORD aKHZ[] = { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 };
|
|
pWFEX->wFormatTag = WAVE_FORMAT_PCM;
|
|
pWFEX->nChannels = pWFEX->nBlockAlign = bIsStereo ? 2 : 1;
|
|
pWFEX->nSamplesPerSec = aKHZ[dwKHZ];
|
|
pWFEX->wBitsPerSample = 8;
|
|
if (bIs16)
|
|
{
|
|
pWFEX->wBitsPerSample *= 2;
|
|
pWFEX->nBlockAlign *= 2;
|
|
}
|
|
pWFEX->nAvgBytesPerSec = pWFEX->nSamplesPerSec * pWFEX->nBlockAlign;
|
|
pWFEX->cbSize = 0;
|
|
return TRUE;
|
|
}
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* SPOpenWavFile *
|
|
*---------------*
|
|
* Description:
|
|
* This method
|
|
********************************************************************* RAL ***
|
|
inline HRESULT SPOpenWavFile(const WCHAR * pszFileName, ISpWavStream ** ppStream, ULONGLONG ullEventInterest = SPFEI_ALL_EVENTS)
|
|
{
|
|
HRESULT hr = ::CoCreateInstance(CLSID_SpWavStream, NULL, CLSCTX_ALL, __uuidof(*ppStream), (void **)ppStream);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = (*ppStream)->Open(pszFileName, ullEventInterest);
|
|
if (FAILED(hr))
|
|
{
|
|
(*ppStream)->Release();
|
|
*ppStream = NULL;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* SPCreateWavFile *
|
|
*-----------------*
|
|
* Description:
|
|
* This method
|
|
********************************************************************* RAL ***
|
|
inline HRESULT SPCreateWavFile( const WCHAR * pszFileName, REFGUID rguidFormatId, const WAVEFORMATEX * pWFEX,
|
|
ISpWavStream ** ppStream, ULONGLONG ullEventInterest = SPFEI_ALL_EVENTS)
|
|
{
|
|
HRESULT hr = ::CoCreateInstance(CLSID_SpWavStream, NULL, CLSCTX_ALL, __uuidof(*ppStream), (void **)ppStream);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = (*ppStream)->Create(pszFileName, rguidFormatId, pWFEX, ullEventInterest);
|
|
}
|
|
if (FAILED(hr))
|
|
{
|
|
(*ppStream)->Release();
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
// This tweaks the ATL conversion macros A2W, W2T, etc., to use a designated codepage
|
|
|
|
#define USES_CODEPAGE_CONVERSION(cp) USES_CONVERSION; _acp=(cp);
|
|
|
|
|
|
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
|
|
|
|
/****************************************************************************
|
|
* SPCreateStreamOnHGlobal *
|
|
*-------------------------*
|
|
* Description:
|
|
* Similar to CreateStreamOnHGlobal Win32 API, but allows a stream to be
|
|
* created
|
|
*
|
|
* Returns:
|
|
*
|
|
********************************************************************* RAL ***/
|
|
|
|
inline HRESULT SPCreateStreamOnHGlobal(
|
|
HGLOBAL hGlobal, //Memory handle for the stream object
|
|
BOOL fDeleteOnRelease, //Whether to free memory when the object is released
|
|
REFGUID rguidFormatId, //Format ID for stream
|
|
const WAVEFORMATEX * pwfex, //WaveFormatEx for stream
|
|
ISpStream ** ppStream) //Address of variable to receive ISpStream pointer
|
|
{
|
|
HRESULT hr;
|
|
IStream * pMemStream;
|
|
*ppStream = NULL;
|
|
hr = ::CreateStreamOnHGlobal(hGlobal, fDeleteOnRelease, &pMemStream);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ::CoCreateInstance(CLSID_SpStream, NULL, CLSCTX_ALL, __uuidof(*ppStream), (void **)ppStream);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = (*ppStream)->SetBaseStream(pMemStream, rguidFormatId, pwfex);
|
|
if (FAILED(hr))
|
|
{
|
|
(*ppStream)->Release();
|
|
*ppStream = NULL;
|
|
}
|
|
}
|
|
pMemStream->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* SPGetHGlobalFromStream *
|
|
*------------------------*
|
|
* Description:
|
|
*
|
|
* Returns:
|
|
*
|
|
********************************************************************* RAL ***/
|
|
|
|
inline HRESULT SPGetHGlobalFromStream(ISpStream * pStream, HGLOBAL * phGlobal)
|
|
{
|
|
HRESULT hr;
|
|
IStream * pMemStream;
|
|
*phGlobal = NULL;
|
|
hr = pStream->GetBaseStream(&pMemStream);
|
|
if (hr == S_OK)
|
|
{
|
|
hr = ::GetHGlobalFromStream(pMemStream, phGlobal);
|
|
pMemStream->Release();
|
|
}
|
|
else
|
|
{
|
|
if (SUCCEEDED(hr)) // Convert S_FALSE from GetBaseStream into an error for this method
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
inline HRESULT SPGetRelativePath( HMODULE hModule, const TCHAR* pCmnRoot,
|
|
const TCHAR* pRelPath, TCHAR* pAbsPath )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
TCHAR Buff[MAX_PATH];
|
|
|
|
ULONG CmnLen = ::GetModuleFileName( hModule, Buff, sizeof( Buff ) );
|
|
if( !CmnLen )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( ::GetLastError() );
|
|
}
|
|
else
|
|
{
|
|
//--- Get last occurence of common root
|
|
BOOL fRootFound = false;
|
|
size_t Count = _tcslen( pCmnRoot );
|
|
TCHAR *pRoot = _tcsrchr( Buff, '\\' );
|
|
while( --pRoot > Buff )
|
|
{
|
|
if( ( tolower(*pRoot) == tolower(*pCmnRoot) ) &&
|
|
!_tcsnicmp( pRoot, pCmnRoot, Count ) )
|
|
{
|
|
fRootFound = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( fRootFound )
|
|
{
|
|
CmnLen = (pRoot + Count) - Buff;
|
|
_tcsncpy( pAbsPath, Buff, CmnLen );
|
|
pAbsPath[CmnLen++] = '\\';
|
|
pAbsPath[CmnLen] = 0;
|
|
if( pRelPath )
|
|
{
|
|
_tcscpy( &pAbsPath[CmnLen], pRelPath );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
} /* SPGetRelativePath */
|
|
|
|
|
|
inline HRESULT SpOpenCLSIDKey(REFCLSID rcid, HKEY * phk)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WCHAR szKey[50];
|
|
memcpy(szKey, L"CLSID\\", 6*sizeof(szKey[0]));
|
|
::StringFromGUID2(rcid, szKey + 6, sp_countof(szKey) - 6);
|
|
CSpTcharString<sizeof(szKey)> tcszKey(szKey);
|
|
LONG rr = ::RegOpenKeyEx(HKEY_CLASSES_ROOT, tcszKey, 0, KEY_READ, phk);
|
|
if (rr != ERROR_SUCCESS)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(::GetLastError());
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*** CSpDynamicString helper class
|
|
*
|
|
*/
|
|
class CSpDynamicString
|
|
{
|
|
public:
|
|
WCHAR * m_psz;
|
|
CSpDynamicString()
|
|
{
|
|
m_psz = NULL;
|
|
}
|
|
CSpDynamicString(ULONG 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)
|
|
{
|
|
ULONG 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 (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)
|
|
{
|
|
ULONG 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 * Append2(const WCHAR * pszSrc1, const WCHAR * pszSrc2)
|
|
{
|
|
ULONG lenSrc1 = wcslen(pszSrc1);
|
|
ULONG lenSrc2 = wcslen(pszSrc2);
|
|
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;
|
|
}
|
|
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)
|
|
{
|
|
SPDBG_ASSERT(m_psz);
|
|
SPDBG_ASSERT(Length() <= ulNumChars);
|
|
m_psz[ulNumChars] = 0;
|
|
}
|
|
WCHAR * Compact()
|
|
{
|
|
if (m_psz)
|
|
{
|
|
ULONG 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;
|
|
}
|
|
};
|
|
/****************************************************************************
|
|
* SpClearEvent *
|
|
*--------------*
|
|
* Description:
|
|
* Helper function that can be used by clients that do not use the CSpEvent
|
|
* class.
|
|
*
|
|
* Returns:
|
|
*
|
|
********************************************************************* RAL ***/
|
|
|
|
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:
|
|
*
|
|
********************************************************************* RAL ***/
|
|
|
|
inline void SpInitEvent(SPEVENT * pe)
|
|
{
|
|
memset(pe, 0, sizeof(*pe));
|
|
}
|
|
|
|
/*** 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;
|
|
}
|
|
SPEVENT * 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));
|
|
}
|
|
|
|
//
|
|
// NOTE: It is OK to call this function with an event that is serialized -- It will return
|
|
// the size of the serialized data. It is also OK to call this for events that are
|
|
// not serialized.
|
|
//
|
|
static ULONG SerializeSize(const SPEVENT *pEvent)
|
|
{
|
|
SPDBG_ASSERT(pEvent->elParamType != SPET_LPARAM_IS_OBJECT);
|
|
ULONG ulSize = sizeof(*pEvent);
|
|
if( ( pEvent->elParamType == SPET_LPARAM_IS_POINTER ) && pEvent->lParam )
|
|
{
|
|
//--- Round up to nearest DWORD multiple
|
|
ulSize += ((pEvent->wParam + 3) / 4) * 4;
|
|
}
|
|
else if ((pEvent->elParamType == SPET_LPARAM_IS_STRING) && pEvent->lParam != NULL)
|
|
{
|
|
//--- Round up to nearest DWORD multiple
|
|
ulSize += (((wcslen((WCHAR*)pEvent->lParam) + 1) * 2 + 3) / 4) * 4;
|
|
}
|
|
else if( pEvent->elParamType == SPET_LPARAM_IS_TOKEN )
|
|
{
|
|
CSpDynamicString dstrObjectId;
|
|
if( ((ISpObjectToken*)(pEvent->lParam))->GetID( &dstrObjectId ) == S_OK )
|
|
{
|
|
ulSize += (dstrObjectId.Length() + 1) * sizeof( WCHAR );
|
|
}
|
|
}
|
|
return ulSize;
|
|
} /* SerializeSize */
|
|
|
|
ULONG SerializeSize(void) const
|
|
{
|
|
return SerializeSize(this);
|
|
}
|
|
ULONG Serialize(BYTE * pBuffer) const
|
|
{
|
|
SPDBG_ASSERT(elParamType != SPET_LPARAM_IS_OBJECT);
|
|
SPEVENT * pSerEvent = (SPEVENT *)pBuffer;
|
|
memcpy(pBuffer, this, sizeof(*this));
|
|
if ((elParamType == SPET_LPARAM_IS_POINTER) && lParam)
|
|
{
|
|
memcpy(pBuffer + sizeof(*this), (void *)lParam, wParam);
|
|
pSerEvent->lParam = sizeof(*this);
|
|
}
|
|
else if ((elParamType == SPET_LPARAM_IS_STRING) && lParam)
|
|
{
|
|
wcscpy((WCHAR *)pBuffer + sizeof(*this), (WCHAR*)lParam);
|
|
pSerEvent->lParam = sizeof(*this);
|
|
}
|
|
else if( elParamType == SPET_LPARAM_IS_TOKEN )
|
|
{
|
|
CSpDynamicString dstrObjectId;
|
|
if( SUCCEEDED( ((ISpObjectToken*)lParam)->GetID( &dstrObjectId ) ) )
|
|
{
|
|
pSerEvent->wParam = (dstrObjectId.Length() + 1) * sizeof( WCHAR );;
|
|
memcpy( pBuffer + sizeof(*this), (void *)dstrObjectId.m_psz, pSerEvent->wParam );
|
|
}
|
|
pSerEvent->lParam = sizeof(*this);
|
|
}
|
|
return SerializeSize();
|
|
}
|
|
|
|
HRESULT Deserialize(ISpResourceManager* pResMgr, const BYTE * pBuffer, ULONG * pcbUsed)
|
|
{
|
|
SPDBG_ASSERT(eEventId == SPEI_UNDEFINED);
|
|
HRESULT hr = S_OK;
|
|
memcpy(this, pBuffer, sizeof(*this));
|
|
if( elParamType == SPET_LPARAM_IS_POINTER )
|
|
{
|
|
ULONG ulDataOffset = lParam;
|
|
lParam = (LPARAM)::CoTaskMemAlloc(wParam);
|
|
if (lParam)
|
|
{
|
|
memcpy((void *)lParam, pBuffer + ulDataOffset, wParam);
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else if (elParamType == SPET_LPARAM_IS_STRING)
|
|
{
|
|
const WCHAR * psz = (const WCHAR*)(pBuffer + lParam);
|
|
ULONG cch = wcslen(psz) + 1;
|
|
|
|
lParam = (LPARAM)::CoTaskMemAlloc(cch * sizeof(WCHAR));
|
|
if (lParam)
|
|
{
|
|
wcscpy((WCHAR*)lParam, psz);
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else if( elParamType == SPET_LPARAM_IS_TOKEN )
|
|
{
|
|
ULONG ulDataOffset = lParam;
|
|
hr = pResMgr->GetObjectToken( (const WCHAR*)(pBuffer + ulDataOffset),
|
|
(ISpObjectToken **)&lParam );
|
|
wParam = 0;
|
|
}
|
|
|
|
if( SUCCEEDED( hr ) && pcbUsed )
|
|
{
|
|
*pcbUsed = SerializeSize();
|
|
}
|
|
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_WORDBOUNDARY);
|
|
return lParam;
|
|
}
|
|
ULONG InputWordLen() const
|
|
{
|
|
SPDBG_ASSERT(eEventId == SPEI_WORDBOUNDARY);
|
|
return wParam;
|
|
}
|
|
ULONG InputSentPos() const
|
|
{
|
|
SPDBG_ASSERT(eEventId == SPEI_SENTENCEBOUNDARY);
|
|
return lParam;
|
|
}
|
|
ULONG InputSentLen() const
|
|
{
|
|
SPDBG_ASSERT(eEventId == SPEI_SENTENCEBOUNDARY);
|
|
return wParam;
|
|
}
|
|
void BookmarkName( CSpDynamicString& outString) const
|
|
{
|
|
SPDBG_ASSERT(eEventId == SPEI_BOOKMARK);
|
|
|
|
WCHAR* pszTemp;
|
|
ULONG cbNeeded = wParam + sizeof(WCHAR);
|
|
|
|
pszTemp = (WCHAR *)::CoTaskMemAlloc(cbNeeded);
|
|
SPDBG_ASSERT(pszTemp);
|
|
if (pszTemp)
|
|
{
|
|
memcpy(pszTemp, (void*)lParam, cbNeeded);
|
|
pszTemp[wParam/sizeof(WCHAR)] = 0;
|
|
|
|
outString = pszTemp; //Automatic conversion to SpDynamicString
|
|
|
|
::CoTaskMemFree( pszTemp );
|
|
}
|
|
}
|
|
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_FALSERECOGNITION || eEventId == SPEI_HYPOTHESIS);
|
|
return (ISpRecoResult *)Object();
|
|
}
|
|
const WCHAR * String() const
|
|
{
|
|
SPDBG_ASSERT(elParamType == SPET_LPARAM_IS_STRING);
|
|
return (const WCHAR*)lParam;
|
|
}
|
|
const WCHAR * RequestTypeOfUI() const
|
|
{
|
|
SPDBG_ASSERT(eEventId == SPEI_REQUESTUI);
|
|
return String();
|
|
}
|
|
};
|
|
|
|
|
|
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);
|
|
}
|
|
/*
|
|
const WCHAR * TextValueOfId(ULONG IdProp)
|
|
{
|
|
SPDBG_ASSERT(m_pPhrase);
|
|
SPPROPERTY * pCur = m_pPhrase->pProperties;
|
|
SPPROPERTY * pPastEnd = pCur + m_pPhrase->cProperties;
|
|
while (pCur < pPastEnd)
|
|
{
|
|
if (pCur->ulNameId == IdProp)
|
|
{
|
|
return pCur->pszValue;
|
|
}
|
|
pCur++;
|
|
}
|
|
return NULL;
|
|
}
|
|
HRESULT ElementInfoOfId(const ULONG IdProp, ULONG *pulFirstElement, ULONG *pulCountOfElements)
|
|
{
|
|
SPDBG_ASSERT(m_pPhrase);
|
|
*pulFirstElement = *pulCountOfElements = 0;
|
|
SPPROPERTY * pCur = m_pPhrase->pProperties;
|
|
SPPROPERTY * pPastEnd = pCur + m_pPhrase->cProperties;
|
|
while (pCur < pPastEnd)
|
|
{
|
|
if (pCur->ulNameId == IdProp)
|
|
{
|
|
*pulFirstElement = pCur->ulFirstElement;
|
|
*pulCountOfElements = pCur->ulCountOfElements;
|
|
return S_OK;
|
|
}
|
|
pCur++;
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
*/
|
|
};
|
|
|
|
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
T * operator =(const T & val)
|
|
{
|
|
Clear();
|
|
m_pT = (T *)::CoTaskMemAlloc(sizeof(T));
|
|
SPDBG_ASSERT(m_pT);
|
|
if (m_pT)
|
|
{
|
|
memcpy(m_pT, &val, sizeof(val));
|
|
}
|
|
return m_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;
|
|
}
|
|
operator T *()
|
|
{
|
|
return m_pT;
|
|
}
|
|
bool operator!() const
|
|
{
|
|
return (m_pT == NULL);
|
|
}
|
|
};
|
|
|
|
#ifndef _WIN32_WCE
|
|
|
|
//
|
|
// The compiler will automatically throw out the inline functions if _UNICODE is defined and simply
|
|
// directly call the Win32 function. Unfortunately, this requires two classes since simply defining
|
|
// const m_bUnicodeSupport does not force the functions to be inlined when built with _UNICODE.
|
|
//
|
|
template <BOOL bUnicodeOnly>
|
|
class CSpUnicodeSupportT
|
|
{
|
|
BOOL m_bUnicodeSupport;
|
|
public:
|
|
CSpUnicodeSupportT()
|
|
{
|
|
if (!bUnicodeOnly)
|
|
{
|
|
m_bUnicodeSupport = ::IsWindowUnicode(::GetDesktopWindow());
|
|
}
|
|
}
|
|
CSpUnicodeSupportT(BOOL bUnicodeSupport)
|
|
{
|
|
if (bUnicodeOnly)
|
|
{
|
|
SPDBG_ASSERT(bUnicodeSupport);
|
|
}
|
|
else
|
|
{
|
|
m_bUnicodeSupport = bUnicodeSupport;
|
|
}
|
|
}
|
|
BOOL UnicodeSystem(void) const
|
|
{
|
|
if (bUnicodeOnly)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return m_bUnicodeSupport;
|
|
}
|
|
}
|
|
HANDLE CreateFile(const WCHAR * lpFileName,
|
|
DWORD dwDesiredAccess,
|
|
DWORD dwShareMode,
|
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
|
|
DWORD dwCreationDisposition,
|
|
DWORD dwFlagsAndAttributes,
|
|
HANDLE hTemplateFile) const
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::CreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
|
dwFlagsAndAttributes, hTemplateFile);
|
|
}
|
|
else
|
|
{
|
|
return ::CreateFileA(CSpToAnsiString<>(lpFileName), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
|
dwFlagsAndAttributes, hTemplateFile);
|
|
}
|
|
}
|
|
BOOL DeleteFile(LPCWSTR lpFileName)
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::DeleteFileW(lpFileName);
|
|
}
|
|
else
|
|
{
|
|
return ::DeleteFileA(CSpToAnsiString<>(lpFileName));
|
|
}
|
|
}
|
|
BOOL MoveFile(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName)
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::MoveFileW(lpExistingFileName, lpNewFileName);
|
|
}
|
|
else
|
|
{
|
|
return ::MoveFileA(CSpToAnsiString<>(lpExistingFileName), CSpToAnsiString<>(lpNewFileName));
|
|
}
|
|
}
|
|
BOOL CreateDirectory(const WCHAR * lpPathName,
|
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes) const
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::CreateDirectoryW(lpPathName, lpSecurityAttributes);
|
|
}
|
|
else
|
|
{
|
|
return ::CreateDirectoryA(CSpToAnsiString<>(lpPathName), lpSecurityAttributes);
|
|
}
|
|
}
|
|
HANDLE CreateFileMapping(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect,
|
|
DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, const WCHAR *lpName)
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::CreateFileMappingW(hFile, lpFileMappingAttributes, flProtect, dwMaximumSizeHigh,
|
|
dwMaximumSizeLow, lpName);
|
|
}
|
|
else
|
|
{
|
|
return ::CreateFileMappingA(hFile, lpFileMappingAttributes, flProtect, dwMaximumSizeHigh,
|
|
dwMaximumSizeLow, CSpToAnsiString<>(lpName));
|
|
}
|
|
}
|
|
LONG RegOpenKeyEx(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult) const
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::RegOpenKeyExW(hKey, lpSubKey, ulOptions, samDesired, phkResult);
|
|
}
|
|
else
|
|
{
|
|
return ::RegOpenKeyExA(hKey, CSpToAnsiString<>(lpSubKey), ulOptions, samDesired, phkResult);
|
|
}
|
|
}
|
|
LONG RegCreateKeyEx(HKEY hk, LPCWSTR lpSubKey, DWORD dwReserved, LPCWSTR lpClass, DWORD dwOptions,
|
|
REGSAM samDesired, LPSECURITY_ATTRIBUTES lpSecurityAttributes, PHKEY phkResult,
|
|
LPDWORD lpdwDisposition) const
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::RegCreateKeyExW(hk, lpSubKey, dwReserved, const_cast<WCHAR *>(lpClass), dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition);
|
|
}
|
|
else
|
|
{
|
|
return ::RegCreateKeyExA(hk, CSpToAnsiString<>(lpSubKey), dwReserved, CSpToAnsiString<>(lpClass), dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition);
|
|
}
|
|
}
|
|
LONG RegDeleteKey(HKEY hKey, LPCWSTR lpSubKey) const
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::RegDeleteKeyW(hKey, lpSubKey);
|
|
}
|
|
else
|
|
{
|
|
return ::RegDeleteKeyA(hKey, CSpToAnsiString<>(lpSubKey));
|
|
}
|
|
}
|
|
LONG RegDeleteValue(HKEY hKey, LPCWSTR lpSubKey) const
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::RegDeleteValueW(hKey, lpSubKey);
|
|
}
|
|
else
|
|
{
|
|
return ::RegDeleteValueA(hKey, CSpToAnsiString<>(lpSubKey));
|
|
}
|
|
}
|
|
//
|
|
// Use RegQueryStringValue for strings. Use this for binary data.
|
|
//
|
|
LONG RegQueryValueEx(HKEY hk, LPCWSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData) const
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::RegQueryValueExW(hk, lpValueName, NULL, lpType, lpData, lpcbData);
|
|
}
|
|
else
|
|
{
|
|
return ::RegQueryValueExA(hk, CSpToAnsiString<>(lpValueName), NULL, lpType, lpData, lpcbData);
|
|
}
|
|
}
|
|
//
|
|
// NOTE: The size parameter is in CHARACTERS! Even though the registry API sizes are
|
|
// in bytes, this function uses character counts.
|
|
//
|
|
LONG RegQueryStringValue(HKEY hKey, LPCWSTR lpValueName, LPWSTR lpData, LPDWORD lpcchData) const
|
|
{
|
|
DWORD dwType;
|
|
LONG rr;
|
|
if (UnicodeSystem())
|
|
{
|
|
*lpcchData *= sizeof(WCHAR);
|
|
rr = ::RegQueryValueExW(hKey, lpValueName, NULL, &dwType, (BYTE *)lpData, lpcchData);
|
|
*lpcchData /= sizeof(WCHAR);
|
|
}
|
|
else
|
|
{
|
|
DWORD dwOrigCharCount = *lpcchData;
|
|
char * pszScratch = lpData ? (char *)_alloca(dwOrigCharCount) : NULL;
|
|
rr = ::RegQueryValueExA(hKey, CSpToAnsiString<>(lpValueName), NULL, &dwType, (BYTE *)pszScratch, lpcchData);
|
|
if (lpData)
|
|
{
|
|
if (rr == ERROR_SUCCESS)
|
|
{
|
|
*lpcchData = ::MultiByteToWideChar(CP_ACP, 0, pszScratch, -1, lpData, dwOrigCharCount);
|
|
if (*lpcchData == 0)
|
|
{
|
|
rr = ::GetLastError();
|
|
*lpcchData = ::MultiByteToWideChar(CP_ACP, 0, pszScratch, -1, NULL, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
SPDBG_ASSERT((rr != ERROR_SUCCESS) || (dwType == REG_SZ));
|
|
return rr;
|
|
}
|
|
//
|
|
// NOTES: Size is in Characters for lpcchName. Although this function uses RegEnumKeyEx, we chose to simply
|
|
// implement the ReqEnumKey functionality since the Ex functionality is not used
|
|
// by most programs (this saves a bunch of string conversion code).
|
|
//
|
|
LONG RegEnumKey(HKEY hk, DWORD dwIndex, LPWSTR lpName, LPDWORD lpcchName) const
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::RegEnumKeyExW(hk, dwIndex, lpName, lpcchName, NULL, NULL, NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
DWORD dwSize = *lpcchName;
|
|
char * pszScratch = lpName ? (char *)_alloca(dwSize) : NULL;
|
|
LONG rr = ::RegEnumKeyExA(hk, dwIndex, pszScratch, &dwSize, NULL, NULL, NULL, NULL);
|
|
if (lpName)
|
|
{
|
|
if (rr == ERROR_SUCCESS)
|
|
{
|
|
*lpcchName = ::MultiByteToWideChar(CP_ACP, 0, pszScratch, -1, lpName, *lpcchName);
|
|
if (*lpcchName == 0)
|
|
{
|
|
*lpcchName = ::MultiByteToWideChar(CP_ACP, 0, pszScratch, -1, NULL, 0);
|
|
rr = ::GetLastError();
|
|
}
|
|
*lpcchName *= sizeof(WCHAR);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*lpcchName = dwSize;
|
|
}
|
|
return rr;
|
|
}
|
|
}
|
|
//
|
|
// NOTES: Size is in Characters for lpcchName. Although this function uses RegEnumValue
|
|
// it will only return the names, not the data. cbValueName is the count of characters
|
|
//
|
|
LONG RegEnumValueName(HKEY hk, DWORD dwIndex, LPWSTR lpName, LPDWORD lpcchName) const
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::RegEnumValueW(hk, dwIndex, lpName, lpcchName, NULL, NULL, NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
DWORD dwSize = *lpcchName;
|
|
char * pszScratch = lpName ? (char *)_alloca(dwSize) : NULL;
|
|
LONG rr = ::RegEnumValueA(hk, dwIndex, pszScratch, &dwSize, NULL, NULL, NULL, NULL);
|
|
if (lpName)
|
|
{
|
|
if (rr == ERROR_SUCCESS)
|
|
{
|
|
*lpcchName = ::MultiByteToWideChar(CP_ACP, 0, pszScratch, -1, lpName, *lpcchName);
|
|
if (*lpcchName == 0)
|
|
{
|
|
*lpcchName = ::MultiByteToWideChar(CP_ACP, 0, pszScratch, -1, NULL, 0);
|
|
rr = ::GetLastError();
|
|
}
|
|
*lpcchName *= sizeof(WCHAR);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*lpcchName = dwSize;
|
|
}
|
|
return rr;
|
|
}
|
|
}
|
|
//
|
|
// Don't use this for strings. Use RegSetStringValue instead.
|
|
//
|
|
LONG RegSetValueEx(HKEY hKey, LPCWSTR lpValueName, DWORD Reserved, DWORD dwType, const BYTE * lpData, DWORD cbData) const
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::RegSetValueExW(hKey, lpValueName, Reserved, dwType, lpData, cbData);
|
|
}
|
|
else
|
|
{
|
|
return ::RegSetValueExA(hKey, CSpToAnsiString<>(lpValueName), Reserved, dwType, lpData, cbData);
|
|
}
|
|
}
|
|
LONG RegSetStringValue(HKEY hKey, LPCWSTR lpValueName, LPCWSTR lpData) const
|
|
{
|
|
LONG rr;
|
|
DWORD dwSize = (wcslen(lpData)+1) * sizeof(WCHAR);
|
|
if (UnicodeSystem())
|
|
{
|
|
rr = ::RegSetValueExW(hKey, lpValueName, NULL, REG_SZ, (const BYTE *)lpData, dwSize);
|
|
}
|
|
else
|
|
{
|
|
char * pszScratch = (char *)_alloca(dwSize);
|
|
dwSize = ::WideCharToMultiByte(CP_ACP, 0, lpData, -1, pszScratch, dwSize, NULL, NULL);
|
|
rr = ::RegSetValueExA(hKey, CSpToAnsiString<>(lpValueName), NULL, REG_SZ, (BYTE *)pszScratch, dwSize);
|
|
}
|
|
return rr;
|
|
}
|
|
HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCWSTR lpName) const
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::CreateEventW(lpEventAttributes, bManualReset, bInitialState, lpName);
|
|
}
|
|
else
|
|
{
|
|
return ::CreateEventA(lpEventAttributes, bManualReset, bInitialState, CSpToAnsiString<>(lpName));
|
|
}
|
|
}
|
|
HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCWSTR lpName) const
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::CreateMutexW(lpMutexAttributes, bInitialOwner, lpName);
|
|
}
|
|
else
|
|
{
|
|
return ::CreateMutexA(lpMutexAttributes, bInitialOwner, CSpToAnsiString<>(lpName));
|
|
}
|
|
}
|
|
int LoadString(HINSTANCE hInstance, UINT uID, LPWSTR lpBuffer, int nBuffer) const
|
|
{
|
|
if (bUnicodeOnly) // NOTE: If the DLL is built ANSI then use ANSI load!
|
|
{
|
|
return ::LoadStringW(hInstance, uID, lpBuffer, nBuffer);
|
|
}
|
|
else
|
|
{
|
|
char * pszScratch = (char *)_alloca(nBuffer * 2);
|
|
int r = ::LoadStringA(hInstance, uID, pszScratch, nBuffer * 2);
|
|
if (r)
|
|
{
|
|
r = ::MultiByteToWideChar(CP_ACP, 0, pszScratch, -1, lpBuffer, nBuffer);
|
|
}
|
|
else
|
|
{
|
|
*lpBuffer = 0;
|
|
}
|
|
return r;
|
|
}
|
|
}
|
|
HMODULE LoadLibraryEx(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::LoadLibraryExW(lpLibFileName, hFile, dwFlags);
|
|
}
|
|
else
|
|
{
|
|
return ::LoadLibraryExA(CSpToAnsiString<>(lpLibFileName), hFile, dwFlags);
|
|
}
|
|
}
|
|
HRSRC FindResourceEx(HMODULE hModule, LPCWSTR lpType, LPCWSTR lpName, WORD wLanguage)
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::FindResourceExW(hModule, lpType, lpName, wLanguage);
|
|
}
|
|
else
|
|
{
|
|
return ::FindResourceExA(hModule,
|
|
HIWORD(lpType) ? CSpToAnsiString<>(lpType) : (const CHAR *) lpType,
|
|
HIWORD(lpName) ? CSpToAnsiString<>(lpName) : (const CHAR *) lpName,
|
|
wLanguage);
|
|
}
|
|
}
|
|
DWORD GetModuleFileName(HMODULE hModule, LPWSTR lpFileName, DWORD nSize) const
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::GetModuleFileNameW(hModule, lpFileName, nSize);
|
|
}
|
|
else
|
|
{
|
|
TCHAR szFileName[MAX_PATH];
|
|
DWORD r = ::GetModuleFileNameA(hModule, szFileName, sp_countof(szFileName));
|
|
if (r)
|
|
{
|
|
r = ::MultiByteToWideChar(CP_ACP, 0, szFileName, r, lpFileName, nSize - 1);
|
|
lpFileName[r] = 0;
|
|
}
|
|
return r;
|
|
}
|
|
}
|
|
int CompareString(LCID Locale, DWORD dwCmpFlags, LPCWSTR lpString1, int cchCount1, LPCWSTR lpString2, int cchCount2)
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::CompareStringW(Locale, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2);
|
|
}
|
|
else
|
|
{
|
|
return ::CompareStringA(Locale, dwCmpFlags, CSpToAnsiString<>(lpString1), cchCount1,
|
|
CSpToAnsiString<>(lpString2), cchCount2);
|
|
}
|
|
}
|
|
BOOL GetUserName(LPWSTR lpBuffer, LPDWORD pnSize)
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::GetUserNameW(lpBuffer, pnSize);
|
|
}
|
|
else
|
|
{
|
|
DWORD cchWideCharBuff = *pnSize;
|
|
CHAR * psz = (CHAR *)_alloca(cchWideCharBuff * sizeof(CHAR));
|
|
BOOL fWorked = ::GetUserNameA(psz, pnSize);
|
|
if (fWorked)
|
|
{
|
|
*pnSize = ::MultiByteToWideChar(CP_ACP, 0, psz, -1, lpBuffer, cchWideCharBuff);
|
|
if (*pnSize == 0)
|
|
{
|
|
fWorked = FALSE;
|
|
*pnSize = ::MultiByteToWideChar(CP_ACP, 0, psz, -1, NULL, 0);
|
|
}
|
|
}
|
|
return fWorked;
|
|
}
|
|
}
|
|
#if defined(mmioOpen)
|
|
HMMIO mmioOpen(LPCWSTR szFileName, LPMMIOINFO lpmmioinfo, DWORD dwOpenFlags) const
|
|
{
|
|
if (UnicodeSystem())
|
|
{
|
|
return ::mmioOpenW((WCHAR *)szFileName, lpmmioinfo, dwOpenFlags);
|
|
}
|
|
else
|
|
{
|
|
return ::mmioOpenA(CSpToAnsiString<>(szFileName), lpmmioinfo, dwOpenFlags);
|
|
}
|
|
}
|
|
MMRESULT waveOutGetDevCaps(UINT uDeviceId, LPWAVEOUTCAPSW pwoc, UINT cbwoc) const
|
|
{
|
|
// BUGBUG: SB 16 drivers overwrite the WAVEINCAPS buffer by a DWORD. So they probably do it for
|
|
// WAVEOUTCAPS too
|
|
MMRESULT mmr = MMSYSERR_NOERROR;
|
|
if (UnicodeSystem())
|
|
{
|
|
BYTE *pBuffer = new BYTE[sizeof(WAVEOUTCAPSW) + sizeof(DWORD)];
|
|
WAVEOUTCAPSW *pwocw = reinterpret_cast<WAVEOUTCAPSW *>(pBuffer);
|
|
if (pwocw)
|
|
{
|
|
mmr = ::waveOutGetDevCapsW(uDeviceId, pwocw, cbwoc);
|
|
if (mmr == MMSYSERR_NOERROR)
|
|
{
|
|
*pwoc = *pwocw;
|
|
}
|
|
delete [] pBuffer;
|
|
}
|
|
else
|
|
{
|
|
mmr = MMSYSERR_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BYTE *pBuffer = new BYTE[sizeof(WAVEOUTCAPSA) + sizeof(DWORD)];
|
|
WAVEOUTCAPSA *pwoca = reinterpret_cast<WAVEOUTCAPSA *>(pBuffer);
|
|
if (pwoca)
|
|
{
|
|
mmr = ::waveOutGetDevCapsA(uDeviceId, pwoca, sizeof(*pwoca));
|
|
if (mmr == MMSYSERR_NOERROR)
|
|
{
|
|
pwoc->wMid = pwoca->wMid;
|
|
pwoc->wPid = pwoca->wPid;
|
|
pwoc->vDriverVersion = pwoca->vDriverVersion;
|
|
pwoc->dwFormats = pwoca->dwFormats;
|
|
pwoc->wChannels = pwoca->wChannels;
|
|
pwoc->wReserved1 = pwoca->wReserved1;
|
|
pwoc->dwSupport = pwoca->dwSupport;
|
|
::MultiByteToWideChar(CP_ACP, 0, pwoca->szPname, -1, pwoc->szPname, sp_countof(pwoc->szPname));
|
|
}
|
|
else
|
|
{
|
|
mmr = MMSYSERR_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mmr = MMSYSERR_ERROR;
|
|
}
|
|
}
|
|
return mmr;
|
|
}
|
|
MMRESULT waveInGetDevCaps(UINT uDeviceId, LPWAVEINCAPSW pwic, UINT cbwic) const
|
|
{
|
|
// BUGBUG: SB 16 drivers overwrite the WAVEINCAPS buffer by a DWORD
|
|
MMRESULT mmr = MMSYSERR_NOERROR;
|
|
if (UnicodeSystem())
|
|
{
|
|
BYTE *pBuffer = new BYTE[sizeof(WAVEINCAPSW) + sizeof(DWORD)];
|
|
WAVEINCAPSW *pwicw = reinterpret_cast<WAVEINCAPSW *>(pBuffer);
|
|
if (pwicw)
|
|
{
|
|
mmr = ::waveInGetDevCapsW(uDeviceId, pwicw, cbwic);
|
|
if (mmr == MMSYSERR_NOERROR)
|
|
{
|
|
*pwic = *pwicw;
|
|
}
|
|
delete [] pBuffer;
|
|
}
|
|
else
|
|
{
|
|
mmr = MMSYSERR_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BYTE *pBuffer = new BYTE[sizeof(WAVEINCAPSA) + sizeof(DWORD)];
|
|
WAVEINCAPSA *pwica = reinterpret_cast<WAVEINCAPSA *>(pBuffer);
|
|
if (pwica)
|
|
{
|
|
mmr = ::waveInGetDevCapsA(uDeviceId, pwica, sizeof(*pwica));
|
|
if (mmr == MMSYSERR_NOERROR)
|
|
{
|
|
pwic->wMid = pwica->wMid;
|
|
pwic->wPid = pwica->wPid;
|
|
pwic->vDriverVersion = pwica->vDriverVersion;
|
|
pwic->dwFormats = pwica->dwFormats;
|
|
pwic->wChannels = pwica->wChannels;
|
|
pwic->wReserved1 = pwica->wReserved1;
|
|
::MultiByteToWideChar(CP_ACP, 0, pwica->szPname, -1, pwic->szPname, sp_countof(pwic->szPname));
|
|
}
|
|
delete [] pBuffer;
|
|
}
|
|
else
|
|
{
|
|
mmr = MMSYSERR_ERROR;
|
|
}
|
|
}
|
|
return mmr;
|
|
}
|
|
#endif // defined(mmioOpen)
|
|
};
|
|
|
|
#ifdef _UNICODE
|
|
typedef CSpUnicodeSupportT<TRUE> CSpUnicodeSupport;
|
|
#else
|
|
typedef CSpUnicodeSupportT<FALSE> CSpUnicodeSupport;
|
|
#endif
|
|
|
|
#else
|
|
|
|
|
|
class CSpUnicodeSupport
|
|
{
|
|
public:
|
|
HANDLE CreateFile(const WCHAR * lpFileName,
|
|
DWORD dwDesiredAccess,
|
|
DWORD dwShareMode,
|
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
|
|
DWORD dwCreationDisposition,
|
|
DWORD dwFlagsAndAttributes,
|
|
HANDLE hTemplateFile) const
|
|
{
|
|
return ::CreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
|
dwFlagsAndAttributes, hTemplateFile);
|
|
}
|
|
HANDLE CreateFileForMapping(const WCHAR * lpFileName,
|
|
DWORD dwDesiredAccess,
|
|
DWORD dwShareMode,
|
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
|
|
DWORD dwCreationDisposition,
|
|
DWORD dwFlagsAndAttributes,
|
|
HANDLE hTemplateFile) const
|
|
{
|
|
return ::CreateFileForMappingW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
|
dwFlagsAndAttributes, hTemplateFile);
|
|
}
|
|
BOOL DeleteFile(LPCWSTR lpFileName)
|
|
{
|
|
return ::DeleteFileW(lpFileName);
|
|
}
|
|
BOOL CreateDirectory(const WCHAR * lpPathName,
|
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes) const
|
|
{
|
|
return ::CreateDirectoryW(lpPathName, lpSecurityAttributes);
|
|
}
|
|
BOOL MoveFile(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName)
|
|
{
|
|
return ::MoveFileW(lpExistingFileName, lpNewFileName);
|
|
}
|
|
HANDLE CreateFileMapping(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect,
|
|
DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, const WCHAR *lpName)
|
|
{
|
|
return ::CreateFileMappingW(hFile, lpFileMappingAttributes, flProtect, dwMaximumSizeHigh,
|
|
dwMaximumSizeLow, lpName);
|
|
}
|
|
LONG RegOpenKeyEx(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult) const
|
|
{
|
|
return ::RegOpenKeyExW(hKey, lpSubKey, ulOptions, samDesired, phkResult);
|
|
}
|
|
LONG RegCreateKeyEx(HKEY hk, LPCWSTR lpSubKey, DWORD dwReserved, LPCWSTR lpClass, DWORD dwOptions,
|
|
REGSAM samDesired, LPSECURITY_ATTRIBUTES lpSecurityAttributes, PHKEY phkResult,
|
|
LPDWORD lpdwDisposition) const
|
|
{
|
|
return ::RegCreateKeyExW(hk, lpSubKey, dwReserved, (WCHAR *)lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition);
|
|
}
|
|
LONG RegDeleteKey(HKEY hKey, LPCWSTR lpSubKey) const
|
|
{
|
|
return ::RegDeleteKeyW(hKey, lpSubKey);
|
|
}
|
|
LONG RegDeleteValue(HKEY hKey, LPCWSTR lpSubKey) const
|
|
{
|
|
return ::RegDeleteValueW(hKey, lpSubKey);
|
|
}
|
|
LONG RegQueryValueEx(HKEY hk, LPCWSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData) const
|
|
{
|
|
return ::RegQueryValueExW(hk, lpValueName, NULL, lpType, lpData, lpcbData);
|
|
}
|
|
//
|
|
// NOTE: The size parameter is in CHARACTERS! Even though the registry API sizes are
|
|
// in bytes, this function uses character counts.
|
|
//
|
|
LONG RegQueryStringValue(HKEY hKey, LPCWSTR lpValueName, LPWSTR lpData, LPDWORD lpcchData) const
|
|
{
|
|
DWORD dwType;
|
|
*lpcchData *= sizeof(WCHAR);
|
|
LONG rr = ::RegQueryValueExW(hKey, lpValueName, NULL, &dwType, (BYTE *)lpData, lpcchData);
|
|
*lpcchData /= sizeof(WCHAR);
|
|
return rr;
|
|
}
|
|
//
|
|
// NOTES: Size is in bytes. Although this function uses RegEnumKeyEx, we chose to simply
|
|
// implement the ReqEnumKey functionality since the Ex functionality is not used
|
|
// by most programs (this saves a bunch of string conversion code).
|
|
//
|
|
LONG RegEnumKey(HKEY hk, DWORD dwIndex, LPWSTR lpName, LPDWORD lpcbName) const
|
|
{
|
|
return ::RegEnumKeyExW(hk, dwIndex, lpName, lpcbName, NULL, NULL, NULL, NULL);
|
|
}
|
|
//
|
|
// NOTES: Size is in Characters for lpcchName. Although this function uses RegEnumValue
|
|
// it will only return the names, not the data. cbValueName is the count of characters
|
|
//
|
|
LONG RegEnumValueName(HKEY hk, DWORD dwIndex, LPWSTR lpName, LPDWORD lpcchName) const
|
|
{
|
|
return ::RegEnumValueW(hk, dwIndex, lpName, lpcchName, NULL, NULL, NULL, NULL);
|
|
}
|
|
LONG RegSetValueEx(HKEY hKey, LPCWSTR lpValueName, DWORD Reserved, DWORD dwType, const BYTE * lpData, DWORD cbData) const
|
|
{
|
|
return ::RegSetValueExW(hKey, lpValueName, Reserved, dwType, lpData, cbData);
|
|
}
|
|
LONG RegSetStringValue(HKEY hKey, LPCWSTR lpValueName, LPCWSTR lpData) const
|
|
{
|
|
DWORD dwSize = (wcslen(lpData)+1) * sizeof(WCHAR);
|
|
return ::RegSetValueExW(hKey, lpValueName, NULL, REG_SZ, (const BYTE *)lpData, dwSize);
|
|
}
|
|
HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCWSTR lpName) const
|
|
{
|
|
return ::CreateEventW(lpEventAttributes, bManualReset, bInitialState, lpName);
|
|
}
|
|
HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCWSTR lpName) const
|
|
{
|
|
return ::CreateMutexW(lpMutexAttributes, bInitialOwner, lpName);
|
|
}
|
|
int LoadString(HINSTANCE hInstance, UINT uID, LPWSTR lpBuffer, int nBuffer) const
|
|
{
|
|
return ::LoadStringW(hInstance, uID, lpBuffer, nBuffer);
|
|
}
|
|
HMODULE LoadLibrary(LPCWSTR lpLibFileName)
|
|
{
|
|
return ::LoadLibraryW(lpLibFileName);
|
|
}
|
|
HMODULE LoadLibraryEx(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
|
|
{
|
|
return ::LoadLibraryExW(lpLibFileName, hFile, dwFlags);
|
|
}
|
|
HRSRC FindResource(HMODULE hModule, LPCWSTR lpName, LPCWSTR lpType)
|
|
{
|
|
return ::FindResource(hModule, lpName, lpType);
|
|
}
|
|
DWORD GetModuleFileName(HMODULE hModule, LPWSTR lpFileName, DWORD nSize) const
|
|
{
|
|
return ::GetModuleFileNameW(hModule, lpFileName, nSize);
|
|
}
|
|
int CompareString(LCID Locale, DWORD dwCmpFlags, LPCWSTR lpString1, int cchCount1, LPCWSTR lpString2, int cchCount2)
|
|
{
|
|
return ::CompareStringW(Locale, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2);
|
|
}
|
|
BOOL GetUserName(LPWSTR lpBuffer, LPDWORD nSize)
|
|
{
|
|
return GetUserNameW(lpBuffer, nSize);
|
|
}
|
|
#if defined(mmioOpen)
|
|
HMMIO mmioOpen(LPCWSTR szFileName, LPMMIOINFO lpmmioinfo, DWORD dwOpenFlags) const
|
|
{
|
|
return ::mmioOpenW((WCHAR *)szFileName, lpmmioinfo, dwOpenFlags);
|
|
}
|
|
#endif // defined(mmioOpen)
|
|
MMRESULT waveOutGetDevCaps(UINT uDeviceId, LPWAVEOUTCAPS pwoc, UINT cbwoc) const
|
|
{
|
|
return ::waveOutGetDevCaps(uDeviceId, pwoc, cbwoc);
|
|
}
|
|
MMRESULT waveInGetDevCaps(UINT uDeviceId, LPWAVEINCAPS pwic, UINT cbwic) const
|
|
{
|
|
return ::waveInGetDevCaps(uDeviceId, pwic, cbwic);
|
|
}
|
|
};
|
|
|
|
#endif
|
|
|
|
//
|
|
// BUGBUG -- RICH: Move to SAPI.IDL...
|
|
//
|
|
typedef struct SPEVENTINTEREST
|
|
{
|
|
ULONG cbSize;
|
|
ULONGLONG ullInterestFlags;
|
|
} SPEVENTINTEREST;
|
|
|
|
|
|
class SpEventInterest : public SPEVENTINTEREST
|
|
{
|
|
public:
|
|
void SetInterest(SPEVENTENUM e0, SPEVENTENUM e1 = SPEI_UNDEFINED, SPEVENTENUM e2 = SPEI_UNDEFINED, SPEVENTENUM e3 = SPEI_UNDEFINED,
|
|
SPEVENTENUM e4 = SPEI_UNDEFINED, SPEVENTENUM e5 = SPEI_UNDEFINED, SPEVENTENUM e6 = SPEI_UNDEFINED, SPEVENTENUM e7 = SPEI_UNDEFINED)
|
|
{
|
|
ullInterestFlags = ((1ui64 << e0) | (1ui64 << e1) | (1ui64 << e2) | (1ui64 << e3) |
|
|
(1ui64 << e4) | (1ui64 << e5) | (1ui64 << e6) | (1ui64 << e7));
|
|
}
|
|
void SetInterest(ULONGLONG ullInitialMask,
|
|
SPEVENTENUM e0 = SPEI_UNDEFINED, SPEVENTENUM e1 = SPEI_UNDEFINED, SPEVENTENUM e2 = SPEI_UNDEFINED, SPEVENTENUM e3 = SPEI_UNDEFINED,
|
|
SPEVENTENUM e4 = SPEI_UNDEFINED, SPEVENTENUM e5 = SPEI_UNDEFINED, SPEVENTENUM e6 = SPEI_UNDEFINED, SPEVENTENUM e7 = SPEI_UNDEFINED)
|
|
{
|
|
ullInterestFlags = (ullInitialMask |
|
|
(1ui64 << e0) | (1ui64 << e1) | (1ui64 << e2) | (1ui64 << e3) |
|
|
(1ui64 << e4) | (1ui64 << e5) | (1ui64 << e6) | (1ui64 << e7));
|
|
}
|
|
SpEventInterest()
|
|
{
|
|
cbSize = sizeof(*this);
|
|
ullInterestFlags = 0;
|
|
}
|
|
SpEventInterest(SPEVENTENUM e0, SPEVENTENUM e1 = SPEI_UNDEFINED, SPEVENTENUM e2 = SPEI_UNDEFINED, SPEVENTENUM e3 = SPEI_UNDEFINED,
|
|
SPEVENTENUM e4 = SPEI_UNDEFINED, SPEVENTENUM e5 = SPEI_UNDEFINED, SPEVENTENUM e6 = SPEI_UNDEFINED, SPEVENTENUM e7 = SPEI_UNDEFINED)
|
|
{
|
|
cbSize = sizeof(*this);
|
|
SetInterest(e0, e1, e2, e3, e4, e5, e6, e7);
|
|
}
|
|
SpEventInterest(ULONGLONG ullInitialMask,
|
|
SPEVENTENUM e0 = SPEI_UNDEFINED, SPEVENTENUM e1 = SPEI_UNDEFINED, SPEVENTENUM e2 = SPEI_UNDEFINED, SPEVENTENUM e3 = SPEI_UNDEFINED,
|
|
SPEVENTENUM e4 = SPEI_UNDEFINED, SPEVENTENUM e5 = SPEI_UNDEFINED, SPEVENTENUM e6 = SPEI_UNDEFINED, SPEVENTENUM e7 = SPEI_UNDEFINED)
|
|
{
|
|
cbSize = sizeof(*this);
|
|
SetInterest(ullInitialMask, e0, e1, e2, e3, e4, e5, e6, e7);
|
|
}
|
|
void AddInterest(SPEVENTENUM e)
|
|
{
|
|
ullInterestFlags |= (1ui64 << e);
|
|
}
|
|
void ClearInterest(SPEVENTENUM e)
|
|
{
|
|
ullInterestFlags &= (~(1ui64 << e));
|
|
}
|
|
bool IsInterestSet(SPEVENTENUM e) const
|
|
{
|
|
return (ullInterestFlags & (1i64 << e)) ? true : false;
|
|
}
|
|
operator const SPEVENTINTEREST *() const // Allow direct cast to a pointer
|
|
{
|
|
return this;
|
|
}
|
|
};
|
|
|
|
#endif /* This must be the last line in the file */
|