基于Windows Sdk 与visual C++2008 在微软平台上构架自己的语音朗读引擎
本人闲来无事,自行开发了一个小型的语音朗读引擎,搭建起在微软平台上的语音朗读框架服务体系,
鉴于本人个人力量有限,为了将语音朗读引擎做的功能更加强悍,强大,
现在将该系统开源,需要源码的请在本人博客下留下EMail,
本系统属于系统框架,搭建起一个语音朗读的引擎服务框架,
在微软平台上畅通无阻,
现在将本系统构架公布一下,
并贴出相关核心源码,源码体积为27M,编译后为1M,
适用于windows 2000/xp2003/vista windows CE /mobile
#include "stdafx.h"#include #include int wmain(int argc, __in_ecount(argc) WCHAR* argv[]){ static const DWORD dwVersion = { 1 }; ULONG ulNumWords = 0; HRESULT hr = S_OK; if( argc != 4 ) { printf( "%s", "Usage: > MakeVoice [[in]word list file] [[out]voice file] [voice name]/n" ); hr = E_INVALIDARG; } else { ::CoInitialize( NULL ); FILE *hWordList = NULL, *hVoiceFile = NULL; if ( fopen_s( &hWordList, CW2A(argv[1]), "r" ) != 0 ) { hWordList = NULL; hr = E_FAIL; } if ( SUCCEEDED( hr ) && fopen_s( &hVoiceFile, CW2A(argv[2]), "wb" ) != 0 ) { hVoiceFile = NULL; hr = E_FAIL; } if( SUCCEEDED( hr ) ) { if( !fwrite( &dwVersion, sizeof(dwVersion), 1, hVoiceFile ) || fseek( hVoiceFile, 4, SEEK_CUR ) ) { hr = E_FAIL; } WCHAR WordFileName[MAX_PATH]; while( SUCCEEDED( hr ) && fgetws( WordFileName, MAX_PATH, hWordList ) ) { ULONG ulTextLen = (ULONG)wcslen( WordFileName ); if( WordFileName[ulTextLen-1] == '/n' ) { WordFileName[--ulTextLen] = NULL; } ulTextLen = (ulTextLen+1) * sizeof(WCHAR); if( fwrite( &ulTextLen, sizeof(ulTextLen), 1, hVoiceFile ) && fwrite( WordFileName, ulTextLen, 1, hVoiceFile ) ) { ++ulNumWords; ISpStream* pStream; wcscat_s( WordFileName, _countof(WordFileName), L".wav" ); hr = SPBindToFile( WordFileName, SPFM_OPEN_READONLY, &pStream ); if( SUCCEEDED( hr ) ) { CSpStreamFormat Fmt; Fmt.AssignFormat(pStream); if( Fmt.ComputeFormatEnum() == SPSF_11kHz16BitMono ) { STATSTG Stat; hr = pStream->Stat( &Stat, STATFLAG_NONAME ); ULONG ulNumBytes = Stat.cbSize.LowPart; if( ulNumBytes > MAXLONG ) { hr = E_OUTOFMEMORY; } if( SUCCEEDED( hr ) && fwrite( &ulNumBytes, sizeof(ulNumBytes), 1, hVoiceFile ) ) { BYTE* Buff = (BYTE*)_malloca( ulNumBytes ); if( SUCCEEDED( hr = pStream->Read( Buff, ulNumBytes, NULL ) ) ) { if( !fwrite( Buff, 1, ulNumBytes, hVoiceFile ) ) { hr = E_FAIL; } } _freea( Buff ); } else { hr = E_FAIL; } } else { printf( "Input file: %s has wrong wav format.", (LPSTR)CW2A( WordFileName ) ); } pStream->Release(); } } else { hr = E_FAIL; } } } else { hr = E_FAIL; } if( SUCCEEDED( hr ) ) { if( fseek( hVoiceFile, sizeof(dwVersion), SEEK_SET ) || !fwrite( &ulNumWords, sizeof(ulNumWords), 1, hVoiceFile ) ) { hr = E_FAIL; } } if( SUCCEEDED( hr ) ) { CComPtr cpToken; CComPtr cpDataKeyAttribs; hr = SpCreateNewTokenEx( SPCAT_VOICES, argv[3], &CLSID_SampleTTSEngine, L"Sample TTS Voice", 0x409, L"Sample TTS Voice", &cpToken, &cpDataKeyAttribs); if (SUCCEEDED(hr)) { hr = cpDataKeyAttribs->SetStringValue(L"Gender", L"Male"); if (SUCCEEDED(hr)) { hr = cpDataKeyAttribs->SetStringValue(L"Name", L"SampleTTSVoice"); } if (SUCCEEDED(hr)) { hr = cpDataKeyAttribs->SetStringValue(L"Language", L"409"); } if (SUCCEEDED(hr)) { hr = cpDataKeyAttribs->SetStringValue(L"Age", L"Adult"); } if (SUCCEEDED(hr)) { hr = cpDataKeyAttribs->SetStringValue(L"Vendor", L"Microsoft"); } CHAR szFullPath[MAX_PATH * 2]; if (SUCCEEDED(hr) && _fullpath(szFullPath, CW2A(argv[2]), sizeof(szFullPath)/sizeof(szFullPath[0])) == NULL) { hr = SPERR_NOT_FOUND; } if (SUCCEEDED(hr)) { hr = cpToken->SetStringValue(L"VoiceData", CA2W(szFullPath)); } } } if( hWordList ) { fclose( hWordList ); } if( hVoiceFile ) { fclose( hVoiceFile ); } ::CoUninitialize(); } return FAILED( hr );} TTS引擎头文件#ifndef TtsEngObj_h#define TtsEngObj_h#ifndef __SampleTtsEngine_h__#include "SampleTtsEngine.h"#endif#ifndef SPDDKHLP_h#include #endif#ifndef SPCollec_h#include #endif#include "resource.h"class CSentItem{ public: CSentItem() { memset( this, 0, sizeof(*this) ); } CSentItem( CSentItem& Other ) { memcpy( this, &Other, sizeof( Other ) ); } const SPVSTATE* pXmlState; LPCWSTR pItem; ULONG ulItemLen; ULONG ulItemSrcOffset; ULONG ulItemSrcLen; };typedef CSPList CItemList;/*** CTTSEngObj COM 接口 *********************************/class ATL_NO_VTABLE CTTSEngObj : public CComObjectRootEx, public CComCoClass, public ISpTTSEngine, public ISpObjectWithToken{ public: DECLARE_REGISTRY_RESOURCEID(IDR_SAMPLETTSENGINE) DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_COM_MAP(CTTSEngObj) COM_INTERFACE_ENTRY(ISpTTSEngine) COM_INTERFACE_ENTRY(ISpObjectWithToken) END_COM_MAP() public: HRESULT FinalConstruct(); void FinalRelease(); public: STDMETHODIMP SetObjectToken( ISpObjectToken * pToken ); STDMETHODIMP GetObjectToken( ISpObjectToken ** ppToken ) { return SpGenericGetObjectToken( ppToken, m_cpToken ); } STDMETHOD(Speak)( DWORD dwSpeakFlags, REFGUID rguidFormatId, const WAVEFORMATEX * pWaveFormatEx, const SPVTEXTFRAG* pTextFragList, ISpTTSEngineSite* pOutputSite ); STDMETHOD(GetOutputFormat)( const GUID * pTargetFormatId, const WAVEFORMATEX * pTargetWaveFormatEx, GUID * pDesiredFormatId, WAVEFORMATEX ** ppCoMemDesiredWaveFormatEx ); private: HRESULT MapFile(const WCHAR * pszTokenValName, HANDLE * phMapping, void ** ppvData ); HRESULT GetNextSentence( CItemList& ItemList ); BOOL AddNextSentItem( CItemList& ItemList ); HRESULT OutputSentence( CItemList& ItemList, ISpTTSEngineSite* pOutputSite ); private: CComPtr m_cpToken; HANDLE m_hVoiceData; void* m_pVoiceData; VOICEITEM* m_pWordList; ULONG m_ulNumWords; const SPVTEXTFRAG* m_pCurrFrag; const WCHAR* m_pNextChar; const WCHAR* m_pEndChar; ULONGLONG m_ullAudioOff;};#endif TTS资源调用 import "oaidl.idl";import "ocidl.idl";import "sapiddk.idl";typedef struct VOICEITEM{ LPCWSTR pText; ULONG ulTextLen; ULONG ulNumAudioBytes; BYTE* pAudio;} VOICEITEM;[ uuid(7192AA2F-F759-43e9-91E7-226371EF6B2F), version(1.0), helpstring("Sample TTS Engine 1.0 Type Library")]library SAMPLETTSENGLib{ importlib("stdole32.tlb"); importlib("stdole2.tlb"); [ uuid(A832755E-9C2A-40b4-89B2-3A92EE705852), helpstring("SampleTTSEngine Class") ] coclass SampleTTSEngine { [default] interface ISpTTSEngine; interface ISpObjectWithToken; };}; TTS服务框架 #include "stdafx.h"#include "resource.h"#include #include "SampleTtsEngine.h"#include "SampleTtsEngine_i.c"#include "TtsEngObj.h" CComModule _Module;BEGIN_OBJECT_MAP(ObjectMap) OBJECT_ENTRY( CLSID_SampleTTSEngine , CTTSEngObj )END_OBJECT_MAP() #ifdef _WIN32_WCEextern "C" BOOL WINAPI DllMain(HANDLE hInstance, ULONG dwReason, LPVOID)#elseextern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID #endif{ if (dwReason == DLL_PROCESS_ATTACH) { _Module.Init(ObjectMap, (HINSTANCE)hInstance, &LIBID_SAMPLETTSENGLib); } else if (dwReason == DLL_PROCESS_DETACH) _Module.Term(); return TRUE; } STDAPI DllCanUnloadNow(void){ return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;} STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv){ return _Module.GetClassObject(rclsid, riid, ppv);} STDAPI DllRegisterServer(void){ return _Module.RegisterServer(TRUE);} STDAPI DllUnregisterServer(void){ return _Module.UnregisterServer(TRUE);}TTS预留接口EXPORTS DllCanUnloadNow PRIVATE DllGetClassObject PRIVATE DllRegisterServer PRIVATE DllUnregisterServer PRIVATE /********************************************************************************TTS引擎实现********************************************************************************/#include "stdafx.h"#include "TtsEngObj.h" /******************************************************************************----------------------------* *描述: *构造*****************************************************************************/HRESULT CTTSEngObj::FinalConstruct(){ HRESULT hr = S_OK; m_hVoiceData = NULL; m_pVoiceData = NULL; m_pWordList = NULL; m_ulNumWords = 0; return hr;} void CTTSEngObj::FinalRelease(){ delete m_pWordList; if( m_pVoiceData ) { ::UnmapViewOfFile( (void*)m_pVoiceData ); } if( m_hVoiceData ) { ::CloseHandle( m_hVoiceData ); }} HRESULT CTTSEngObj::MapFile( const WCHAR * pszTokenVal, HANDLE * phMapping, void ** ppvData ) { HRESULT hr = S_OK; CSpDynamicString dstrFilePath; hr = m_cpToken->GetStringValue( pszTokenVal, &dstrFilePath ); if ( SUCCEEDED( hr ) ) { bool fWorked = false; *phMapping = NULL; *ppvData = NULL; HANDLE hFile;#ifdef _WIN32_WCE hFile = CreateFileForMapping( dstrFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );#else hFile = CreateFile( CW2T(dstrFilePath), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );#endif if (hFile != INVALID_HANDLE_VALUE) { *phMapping = ::CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, 0, NULL ); if (*phMapping) { *ppvData = ::MapViewOfFile( *phMapping, FILE_MAP_READ, 0, 0, 0 ); if (*ppvData) { fWorked = true; } } ::CloseHandle( hFile ); } if (!fWorked) { hr = HRESULT_FROM_WIN32(::GetLastError()); if (*phMapping) { ::CloseHandle(*phMapping); *phMapping = NULL; } } } return hr;}STDMETHODIMP CTTSEngObj::SetObjectToken(ISpObjectToken * pToken){ HRESULT hr = SpGenericSetObjectToken(pToken, m_cpToken); if( SUCCEEDED( hr ) ) { hr = MapFile( L"VoiceData", &m_hVoiceData, &m_pVoiceData ); } if( SUCCEEDED( hr ) ) { delete m_pWordList; UNALIGNED DWORD* pdwPtr = (UNALIGNED DWORD*)m_pVoiceData; if( *pdwPtr++ != 1 ) { hr = E_INVALIDARG; _ASSERT(0); } if( SUCCEEDED( hr ) ) { m_ulNumWords = *pdwPtr++; } m_pWordList = new VOICEITEM[m_ulNumWords]; if( !m_pWordList ) { hr = E_OUTOFMEMORY; } else { for( ULONG i = 0; i < m_ulNumWords; ++i ) { ULONG ulTextByteLen = *pdwPtr++; m_pWordList[i].pText = (UNALIGNED LPCWSTR)pdwPtr; m_pWordList[i].ulTextLen = (ulTextByteLen / sizeof(WCHAR)) - 1; pdwPtr = (UNALIGNED DWORD*)(((BYTE*)pdwPtr) + ulTextByteLen); m_pWordList[i].ulNumAudioBytes = *pdwPtr++; m_pWordList[i].pAudio = (BYTE*)pdwPtr; pdwPtr = (UNALIGNED DWORD*)(((BYTE*)pdwPtr) + m_pWordList[i].ulNumAudioBytes); } } } return hr;} /****************************************************************************** CTTSEngObj : :说话* *-------------------* *描述: *这是主要的方法的SAPI要求提供的案文。 *------------------------------------------------- ---------------------------- *输入参数 * * pUser *指针当前的用户配置文件对象。这个对象包含 *信息,如哪些语言和正在使用此对象 *还提供获得资源,如掌握词汇的SAPI对象。 * * dwSpeakFlags *这是一套用来控制旗帜的行为 *的SAPI语音对象和相关的发动机。 * * VoiceFmtIndex *零基础的索引指定的输出格式,应 *用于在渲染。 * * pTextFragList *一个链表的文字片段将会作出。有 *每一个片段的XML状态的变化。如果输入文本并 *不包含任何的XML标记,将只有一个单一的片段。 * * pOutputSite *该接口返回的SAPI ,所有输出的音频样本和事件是书面。 * *返回值 * S_OK -这应该是成功的渲染后返回,或者如果 *渲染被打断,因为* pfContinue更改为FALSE 。 * E_INVALIDARG ******************************************************************************/STDMETHODIMP CTTSEngObj::Speak( DWORD dwSpeakFlags, REFGUID rguidFormatId, const WAVEFORMATEX * pWaveFormatEx, const SPVTEXTFRAG* pTextFragList, ISpTTSEngineSite* pOutputSite ){ HRESULT hr = S_OK; if( SP_IS_BAD_INTERFACE_PTR( pOutputSite ) || SP_IS_BAD_READ_PTR( pTextFragList ) ) { hr = E_INVALIDARG; } else { m_pCurrFrag = pTextFragList; m_pNextChar = m_pCurrFrag->pTextStart; m_pEndChar = m_pNextChar + m_pCurrFrag->ulTextLen; m_ullAudioOff = 0; CItemList ItemList; while( SUCCEEDED( hr ) && !(pOutputSite->GetActions() & SPVES_ABORT) ) { if( pOutputSite->GetActions() & SPVES_SKIP ) { long lSkipCnt; SPVSKIPTYPE eType; hr = pOutputSite->GetSkipInfo( &eType, &lSkipCnt ); if( SUCCEEDED( hr ) ) { hr = pOutputSite->CompleteSkip( 0 ); } } if( SUCCEEDED( hr ) && (hr = GetNextSentence( ItemList )) != S_OK ) { break; } if( !(pOutputSite->GetActions() & SPVES_ABORT) ) { CSentItem& FirstItem = ItemList.GetHead(); CSentItem& LastItem = ItemList.GetTail(); CSpEvent Event; Event.eEventId = SPEI_SENTENCE_BOUNDARY; Event.elParamType = SPET_LPARAM_IS_UNDEFINED; Event.ullAudioStreamOffset = m_ullAudioOff; Event.lParam = (LPARAM)FirstItem.ulItemSrcOffset; Event.wParam = (WPARAM)LastItem.ulItemSrcOffset + LastItem.ulItemSrcLen - FirstItem.ulItemSrcOffset; hr = pOutputSite->AddEvents( &Event, 1 ); if( SUCCEEDED( hr ) ) { hr = OutputSentence( ItemList, pOutputSite ); } } } if( hr == S_FALSE ) { hr = S_OK; } } return hr;}HRESULT CTTSEngObj::OutputSentence( CItemList& ItemList, ISpTTSEngineSite* pOutputSite ){ HRESULT hr = S_OK; ULONG WordIndex; SPLISTPOS ListPos = ItemList.GetHeadPosition(); while( ListPos && !(pOutputSite->GetActions() & SPVES_ABORT) ) { CSentItem& Item = ItemList.GetNext( ListPos ); switch( Item.pXmlState->eAction ) { case SPVA_Speak: { if( iswalpha( Item.pItem[0] ) || iswdigit( Item.pItem[0] ) ) { for( WordIndex = 0; WordIndex < m_ulNumWords; ++WordIndex ) { if( ( m_pWordList[WordIndex].ulTextLen == Item.ulItemLen ) && ( !_wcsnicmp( m_pWordList[WordIndex].pText, Item.pItem, Item.ulItemLen )) ) { break; } } if( WordIndex == m_ulNumWords ) { WordIndex = 0; } CSpEvent Event; Event.eEventId = SPEI_WORD_BOUNDARY; Event.elParamType = SPET_LPARAM_IS_UNDEFINED; Event.ullAudioStreamOffset = m_ullAudioOff; Event.lParam = Item.ulItemSrcOffset; Event.wParam = Item.ulItemSrcLen; pOutputSite->AddEvents( &Event, 1 ); hr = pOutputSite->Write( m_pWordList[WordIndex].pAudio, m_pWordList[WordIndex].ulNumAudioBytes, NULL ); m_ullAudioOff += m_pWordList[WordIndex].ulNumAudioBytes; } } break; case SPVA_Silence: { BYTE Buff[1000]; memset( Buff, 0, 1000 ); ULONG NumSilenceBytes = Item.pXmlState->SilenceMSecs * 22; while( !(pOutputSite->GetActions() & SPVES_ABORT) ) { if( NumSilenceBytes > 1000 ) { hr = pOutputSite->Write( Buff, 1000, NULL ); NumSilenceBytes -= 1000; } else { hr = pOutputSite->Write( Buff, NumSilenceBytes, NULL ); break; } } m_ullAudioOff += NumSilenceBytes; } break; case SPVA_Bookmark: { WCHAR * pszBookmark = (WCHAR *)_malloca((Item.ulItemLen + 1) * sizeof(WCHAR)); memcpy(pszBookmark, Item.pItem, Item.ulItemLen * sizeof(WCHAR)); pszBookmark[Item.ulItemLen] = 0; SPEVENT Event; Event.eEventId = SPEI_TTS_BOOKMARK; Event.elParamType = SPET_LPARAM_IS_STRING; Event.ullAudioStreamOffset = m_ullAudioOff; Event.lParam = (LPARAM)pszBookmark; Event.wParam = _wtol(pszBookmark); hr = pOutputSite->AddEvents( &Event, 1 ); _freea(pszBookmark); } break; case SPVA_Pronounce: break; case SPVA_ParseUnknownTag: break; } } return hr;} /* CTTSEngObj::OutputSentence */STDMETHODIMP CTTSEngObj::GetOutputFormat( const GUID * pTargetFormatId, const WAVEFORMATEX * pTargetWaveFormatEx, GUID * pDesiredFormatId, WAVEFORMATEX ** ppCoMemDesiredWaveFormatEx ){ HRESULT hr = S_OK; hr = SpConvertStreamFormatEnum(SPSF_11kHz16BitMono, pDesiredFormatId, ppCoMemDesiredWaveFormatEx); return hr;}HRESULT CTTSEngObj::GetNextSentence( CItemList& ItemList ){ HRESULT hr = S_OK; ItemList.RemoveAll(); if( m_pCurrFrag == NULL ) { hr = S_FALSE; } else { BOOL fSentDone = false; BOOL fGoToNextFrag = false; while( m_pCurrFrag && !fSentDone ) { if( m_pCurrFrag->State.eAction == SPVA_Speak ) { fSentDone = AddNextSentItem( ItemList ); if( m_pNextChar >= m_pEndChar ) { fGoToNextFrag = true; } } else { CSentItem Item; Item.pItem = m_pCurrFrag->pTextStart; Item.ulItemLen = m_pCurrFrag->ulTextLen; Item.ulItemSrcOffset = m_pCurrFrag->ulTextSrcOffset; Item.ulItemSrcLen = Item.ulItemLen; Item.pXmlState = &m_pCurrFrag->State; ItemList.AddTail( Item ); fGoToNextFrag = true; } if( fGoToNextFrag ) { fGoToNextFrag = false; m_pCurrFrag = m_pCurrFrag->pNext; if( m_pCurrFrag ) { m_pNextChar = m_pCurrFrag->pTextStart; m_pEndChar = m_pNextChar + m_pCurrFrag->ulTextLen; } else { m_pNextChar = NULL; m_pEndChar = NULL; } } } if( ItemList.IsEmpty() ) { hr = S_FALSE; } } return hr;} static BOOL IsSpace( WCHAR wc ){ return ( ( wc == 0x20 ) || ( wc == 0x9 ) || ( wc == 0xD ) || ( wc == 0xA ) );}static const WCHAR* SkipWhiteSpace( const WCHAR* pPos ){ while( IsSpace( *pPos ) ) ++pPos; return pPos;}static const WCHAR* FindNextToken( const WCHAR* pStart, const WCHAR* pEnd, const WCHAR*& pNext ){ const WCHAR* pPos = SkipWhiteSpace( pStart ); pNext = pPos; if( pNext == pEnd ) { pPos = NULL; } else { while( *pNext && !IsSpace( *pNext ) ) { if( ++pNext == pEnd ) { break; } } } return pPos;} BOOL SearchSet( WCHAR wc, const WCHAR* Set, ULONG Count, ULONG* pIndex ){ for( ULONG i = 0; i < Count; ++i ) { if( wc == Set[i] ) { *pIndex = i; return true; } } return false;}BOOL CTTSEngObj::AddNextSentItem( CItemList& ItemList ){ ULONG ulIndex; CSentItem Item; Item.pItem = FindNextToken( m_pNextChar, m_pEndChar, m_pNextChar ); if( Item.pItem == NULL ) { return false; } const WCHAR* pTrailChar = m_pNextChar-1; ULONG TokenLen = (ULONG)(m_pNextChar - Item.pItem); static const WCHAR LeadItems[] = { L'(', L'/"', L'{', L'/'', L'[' }; while( TokenLen > 1 ) { if( SearchSet( Item.pItem[0], LeadItems, sp_countof(LeadItems), &ulIndex ) ) { CSentItem LItem; LItem.pItem = Item.pItem; LItem.ulItemLen = 1; LItem.pXmlState = &m_pCurrFrag->State; LItem.ulItemSrcLen = LItem.ulItemLen; LItem.ulItemSrcOffset = m_pCurrFrag->ulTextSrcOffset + (ULONG)( LItem.pItem - m_pCurrFrag->pTextStart ); ItemList.AddTail( LItem ); ++Item.pItem; --TokenLen; } else { break; } } SPLISTPOS ItemPos = ItemList.AddTail( Item ); static const WCHAR EOSItems[] = { L'.', L'!', L'?' }; static const WCHAR TrailItems[] = { L',', L'/"', L';', L':', L')', L'}', L'/'', L']' }; BOOL fIsEOS = false; while( TokenLen > 1 ) { BOOL fAddTrailItem = false; if( SearchSet( *pTrailChar, EOSItems, sp_countof(EOSItems), &ulIndex ) ) { fIsEOS = true; fAddTrailItem = true; } else if( SearchSet( *pTrailChar, TrailItems, sp_countof(TrailItems), &ulIndex ) ) { fAddTrailItem = true; } if( fAddTrailItem ) { CSentItem TItem; TItem.pItem = pTrailChar; TItem.ulItemLen = 1; TItem.pXmlState = &m_pCurrFrag->State; TItem.ulItemSrcLen = TItem.ulItemLen; TItem.ulItemSrcOffset = m_pCurrFrag->ulTextSrcOffset + (ULONG)( TItem.pItem - m_pCurrFrag->pTextStart ); ItemList.InsertAfter( ItemPos, TItem ); --TokenLen; --pTrailChar; } else { break; } } if( *m_pNextChar == NULL ) { fIsEOS = true; if( !SearchSet( *(m_pNextChar-1), EOSItems, sp_countof(EOSItems), &ulIndex ) ) { static const WCHAR* pPeriod = L"."; CSentItem EOSItem; EOSItem.pItem = pPeriod; EOSItem.ulItemLen = 1; EOSItem.pXmlState = &m_pCurrFrag->State; EOSItem.ulItemSrcLen = EOSItem.ulItemLen; EOSItem.ulItemSrcOffset = m_pCurrFrag->ulTextSrcOffset + (ULONG)( (m_pNextChar-1) - m_pCurrFrag->pTextStart ); ItemList.AddTail( EOSItem ); } } else if( pTrailChar[1] == L'.' ) { } for( ULONG i = 0; i < TokenLen; ++i ) { if( Item.pItem[i] == L'/'' ) { ((WCHAR)Item.pItem[i]) = L'_'; } } if( TokenLen > 0 ) { Item.ulItemLen = TokenLen; Item.pXmlState = &m_pCurrFrag->State; Item.ulItemSrcLen = Item.ulItemLen; Item.ulItemSrcOffset = m_pCurrFrag->ulTextSrcOffset + (ULONG)( Item.pItem - m_pCurrFrag->pTextStart ); ItemList.SetAt( ItemPos, Item ); } return fIsEOS;}
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
暂时没有评论,来抢沙发吧~