nim_duilib/duilib/Core/Markup.cpp
jiajia_deng 4933d1f2bc Remove dependency on shared
Signed-off-by: jiajia_deng <2894220@gmail.com>
2019-09-20 16:27:58 +08:00

606 lines
19 KiB
C++
Raw Permalink Blame History

#include "StdAfx.h"
#include "shlwapi.h"
#ifndef TRACE
#define TRACE
#endif
///////////////////////////////////////////////////////////////////////////////////////
namespace ui
{
CMarkupNode::CMarkupNode() : m_pOwner(NULL)
{
}
CMarkupNode::CMarkupNode(CMarkup* pOwner, int iPos) : m_pOwner(pOwner), m_iPos(iPos), m_nAttributes(0)
{
}
CMarkupNode CMarkupNode::GetSibling()
{
if( m_pOwner == NULL ) return CMarkupNode();
ULONG iPos = m_pOwner->m_pElements[m_iPos].iNext;
if( iPos == 0 ) return CMarkupNode();
return CMarkupNode(m_pOwner, iPos);
}
bool CMarkupNode::HasSiblings() const
{
if( m_pOwner == NULL ) return false;
ULONG iPos = m_pOwner->m_pElements[m_iPos].iNext;
return iPos > 0;
}
CMarkupNode CMarkupNode::GetChild()
{
if( m_pOwner == NULL ) return CMarkupNode();
ULONG iPos = m_pOwner->m_pElements[m_iPos].iChild;
if( iPos == 0 ) return CMarkupNode();
return CMarkupNode(m_pOwner, iPos);
}
CMarkupNode CMarkupNode::GetChild(LPCTSTR pstrName)
{
if( m_pOwner == NULL ) return CMarkupNode();
ULONG iPos = m_pOwner->m_pElements[m_iPos].iChild;
while( iPos != 0 ) {
if( _tcscmp(m_pOwner->m_pstrXML + m_pOwner->m_pElements[iPos].iStart, pstrName) == 0 ) {
return CMarkupNode(m_pOwner, iPos);
}
iPos = m_pOwner->m_pElements[iPos].iNext;
}
return CMarkupNode();
}
bool CMarkupNode::HasChildren() const
{
if( m_pOwner == NULL ) return false;
return m_pOwner->m_pElements[m_iPos].iChild != 0;
}
CMarkupNode CMarkupNode::GetParent()
{
if( m_pOwner == NULL ) return CMarkupNode();
ULONG iPos = m_pOwner->m_pElements[m_iPos].iParent;
if( iPos == 0 ) return CMarkupNode();
return CMarkupNode(m_pOwner, iPos);
}
bool CMarkupNode::IsValid() const
{
return m_pOwner != NULL;
}
LPCTSTR CMarkupNode::GetName() const
{
if( m_pOwner == NULL ) return NULL;
return m_pOwner->m_pstrXML + m_pOwner->m_pElements[m_iPos].iStart;
}
LPCTSTR CMarkupNode::GetValue() const
{
if( m_pOwner == NULL ) return NULL;
return m_pOwner->m_pstrXML + m_pOwner->m_pElements[m_iPos].iData;
}
LPCTSTR CMarkupNode::GetAttributeName(int iIndex)
{
if( m_pOwner == NULL ) return NULL;
if( m_nAttributes == 0 ) _MapAttributes();
if( iIndex < 0 || iIndex >= m_nAttributes ) return _T("");
return m_pOwner->m_pstrXML + m_aAttributes[iIndex].iName;
}
LPCTSTR CMarkupNode::GetAttributeValue(int iIndex)
{
if( m_pOwner == NULL ) return NULL;
if( m_nAttributes == 0 ) _MapAttributes();
if( iIndex < 0 || iIndex >= m_nAttributes ) return _T("");
return m_pOwner->m_pstrXML + m_aAttributes[iIndex].iValue;
}
LPCTSTR CMarkupNode::GetAttributeValue(LPCTSTR pstrName)
{
if( m_pOwner == NULL ) return NULL;
if( m_nAttributes == 0 ) _MapAttributes();
for( int i = 0; i < m_nAttributes; i++ ) {
if( _tcscmp(m_pOwner->m_pstrXML + m_aAttributes[i].iName, pstrName) == 0 ) return m_pOwner->m_pstrXML + m_aAttributes[i].iValue;
}
return _T("");
}
bool CMarkupNode::GetAttributeValue(int iIndex, LPTSTR pstrValue, SIZE_T cchMax)
{
if( m_pOwner == NULL ) return false;
if( m_nAttributes == 0 ) _MapAttributes();
if( iIndex < 0 || iIndex >= m_nAttributes ) return false;
_tcsncpy(pstrValue, m_pOwner->m_pstrXML + m_aAttributes[iIndex].iValue, cchMax);
return true;
}
bool CMarkupNode::GetAttributeValue(LPCTSTR pstrName, LPTSTR pstrValue, SIZE_T cchMax)
{
if( m_pOwner == NULL ) return false;
if( m_nAttributes == 0 ) _MapAttributes();
for( int i = 0; i < m_nAttributes; i++ ) {
if( _tcscmp(m_pOwner->m_pstrXML + m_aAttributes[i].iName, pstrName) == 0 ) {
_tcsncpy(pstrValue, m_pOwner->m_pstrXML + m_aAttributes[i].iValue, cchMax);
return true;
}
}
return false;
}
int CMarkupNode::GetAttributeCount()
{
if( m_pOwner == NULL ) return 0;
if( m_nAttributes == 0 ) _MapAttributes();
return m_nAttributes;
}
bool CMarkupNode::HasAttributes()
{
if( m_pOwner == NULL ) return false;
if( m_nAttributes == 0 ) _MapAttributes();
return m_nAttributes > 0;
}
bool CMarkupNode::HasAttribute(LPCTSTR pstrName)
{
if( m_pOwner == NULL ) return false;
if( m_nAttributes == 0 ) _MapAttributes();
for( int i = 0; i < m_nAttributes; i++ ) {
if( _tcscmp(m_pOwner->m_pstrXML + m_aAttributes[i].iName, pstrName) == 0 ) return true;
}
return false;
}
void CMarkupNode::_MapAttributes()
{
m_nAttributes = 0;
LPCTSTR pstr = m_pOwner->m_pstrXML + m_pOwner->m_pElements[m_iPos].iStart;
LPCTSTR pstrEnd = m_pOwner->m_pstrXML + m_pOwner->m_pElements[m_iPos].iData;
pstr += _tcslen(pstr) + 1;
while( pstr < pstrEnd ) {
m_pOwner->_SkipWhitespace(pstr);
m_aAttributes[m_nAttributes].iName = pstr - m_pOwner->m_pstrXML;
pstr += _tcslen(pstr) + 1;
m_pOwner->_SkipWhitespace(pstr);
if( *pstr++ != _T('\"') ) return; // if( *pstr != _T('\"') ) { pstr = ::CharNext(pstr); return; }
m_aAttributes[m_nAttributes++].iValue = pstr - m_pOwner->m_pstrXML;
if( m_nAttributes >= MAX_XML_ATTRIBUTES ) return;
pstr += _tcslen(pstr) + 1;
}
}
///////////////////////////////////////////////////////////////////////////////////////
//
//
//
CMarkup::CMarkup(LPCTSTR pstrXML)
{
m_pstrXML = NULL;
m_pElements = NULL;
m_nElements = 0;
m_bPreserveWhitespace = true;
if( pstrXML != NULL ) Load(pstrXML);
}
CMarkup::~CMarkup()
{
Release();
}
bool CMarkup::IsValid() const
{
return m_pElements != NULL;
}
void CMarkup::SetPreserveWhitespace(bool bPreserve)
{
m_bPreserveWhitespace = bPreserve;
}
bool CMarkup::Load(LPCTSTR pstrXML)
{
Release();
SIZE_T cchLen = _tcslen(pstrXML) + 1;
m_pstrXML = static_cast<LPTSTR>(malloc(cchLen * sizeof(TCHAR)));
::CopyMemory(m_pstrXML, pstrXML, cchLen * sizeof(TCHAR));
bool bRes = _Parse();
if( !bRes ) Release();
return bRes;
}
bool CMarkup::LoadFromMem(BYTE* pByte, DWORD dwSize, int encoding)
{
#ifdef _UNICODE
if (encoding == XMLFILE_ENCODING_UTF8)
{
if( dwSize >= 3 && pByte[0] == 0xEF && pByte[1] == 0xBB && pByte[2] == 0xBF )
{
pByte += 3; dwSize -= 3;
}
DWORD nWide = ::MultiByteToWideChar( CP_UTF8, 0, (LPCSTR)pByte, dwSize, NULL, 0 );
m_pstrXML = static_cast<LPTSTR>(malloc((nWide + 1)*sizeof(TCHAR)));
::MultiByteToWideChar( CP_UTF8, 0, (LPCSTR)pByte, dwSize, m_pstrXML, nWide );
m_pstrXML[nWide] = _T('\0');
}
else if (encoding == XMLFILE_ENCODING_ASNI)
{
DWORD nWide = ::MultiByteToWideChar( CP_ACP, 0, (LPCSTR)pByte, dwSize, NULL, 0 );
m_pstrXML = static_cast<LPTSTR>(malloc((nWide + 1)*sizeof(TCHAR)));
::MultiByteToWideChar( CP_ACP, 0, (LPCSTR)pByte, dwSize, m_pstrXML, nWide );
m_pstrXML[nWide] = _T('\0');
}
else
{
if ( dwSize >= 2 && ( ( pByte[0] == 0xFE && pByte[1] == 0xFF ) || ( pByte[0] == 0xFF && pByte[1] == 0xFE ) ) )
{
dwSize = dwSize / 2 - 1;
if ( pByte[0] == 0xFE && pByte[1] == 0xFF )
{
pByte += 2;
for ( DWORD nSwap = 0 ; nSwap < dwSize ; nSwap ++ )
{
register CHAR nTemp = pByte[ ( nSwap << 1 ) + 0 ];
pByte[ ( nSwap << 1 ) + 0 ] = pByte[ ( nSwap << 1 ) + 1 ];
pByte[ ( nSwap << 1 ) + 1 ] = nTemp;
}
}
else
{
pByte += 2;
}
m_pstrXML = static_cast<LPTSTR>(malloc((dwSize + 1)*sizeof(TCHAR)));
::CopyMemory( m_pstrXML, pByte, dwSize * sizeof(TCHAR) );
m_pstrXML[dwSize] = _T('\0');
pByte -= 2;
}
}
#else // !_UNICODE
if (encoding == XMLFILE_ENCODING_UTF8)
{
if( dwSize >= 3 && pByte[0] == 0xEF && pByte[1] == 0xBB && pByte[2] == 0xBF )
{
pByte += 3; dwSize -= 3;
}
DWORD nWide = ::MultiByteToWideChar( CP_UTF8, 0, (LPCSTR)pByte, dwSize, NULL, 0 );
LPWSTR w_str = static_cast<LPWSTR>(malloc((nWide + 1)*sizeof(WCHAR)));
::MultiByteToWideChar( CP_UTF8, 0, (LPCSTR)pByte, dwSize, w_str, nWide );
w_str[nWide] = L'\0';
DWORD wide = ::WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)w_str, nWide, NULL, 0, NULL, NULL);
m_pstrXML = static_cast<LPTSTR>(malloc((wide + 1)*sizeof(TCHAR)));
::WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)w_str, nWide, m_pstrXML, wide, NULL, NULL);
m_pstrXML[wide] = _T('\0');
free(w_str);
}
else if (encoding == XMLFILE_ENCODING_UNICODE)
{
if ( dwSize >= 2 && ( ( pByte[0] == 0xFE && pByte[1] == 0xFF ) || ( pByte[0] == 0xFF && pByte[1] == 0xFE ) ) )
{
dwSize = dwSize / 2 - 1;
if ( pByte[0] == 0xFE && pByte[1] == 0xFF )
{
pByte += 2;
for ( DWORD nSwap = 0 ; nSwap < dwSize ; nSwap ++ )
{
register CHAR nTemp = pByte[ ( nSwap << 1 ) + 0 ];
pByte[ ( nSwap << 1 ) + 0 ] = pByte[ ( nSwap << 1 ) + 1 ];
pByte[ ( nSwap << 1 ) + 1 ] = nTemp;
}
}
else
{
pByte += 2;
}
DWORD nWide = ::WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)pByte, dwSize, NULL, 0, NULL, NULL);
m_pstrXML = static_cast<LPTSTR>(malloc((nWide + 1)*sizeof(TCHAR)));
::WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)pByte, dwSize, m_pstrXML, nWide, NULL, NULL);
m_pstrXML[nWide] = _T('\0');
pByte -= 2;
}
}
else
{
m_pstrXML = static_cast<LPTSTR>(malloc((dwSize + 1)*sizeof(TCHAR)));
::CopyMemory( m_pstrXML, pByte, dwSize * sizeof(TCHAR) );
m_pstrXML[dwSize] = _T('\0');
}
#endif // _UNICODE
bool bRes = _Parse();
if( !bRes ) Release();
return bRes;
}
bool CMarkup::LoadFromFile(LPCTSTR pstrFilename, int encoding)
{
Release();
std::wstring sFile = GlobalManager::GetResourcePath();
if (::PathIsRelative(pstrFilename))
{
sFile += pstrFilename;
}
else
{
sFile = pstrFilename;
}
HANDLE hFile = ::CreateFile(sFile.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile == INVALID_HANDLE_VALUE ) return _Failed(_T("Error opening file"));
DWORD dwSize = ::GetFileSize(hFile, NULL);
if( dwSize == 0 ) return _Failed(_T("File is empty"));
if ( dwSize > 4096*1024 ) return _Failed(_T("File too large"));
DWORD dwRead = 0;
BYTE* pByte = new BYTE[ dwSize ];
::ReadFile( hFile, pByte, dwSize, &dwRead, NULL );
::CloseHandle( hFile );
if( dwRead != dwSize ) {
delete[] pByte;
Release();
return _Failed(_T("Could not read file"));
}
bool ret = LoadFromMem(pByte, dwSize, encoding);
delete[] pByte;
return ret;
}
void CMarkup::Release()
{
if( m_pstrXML != NULL ) free(m_pstrXML);
if( m_pElements != NULL ) free(m_pElements);
m_pstrXML = NULL;
m_pElements = NULL;
m_nElements;
}
void CMarkup::GetLastErrorMessage(LPTSTR pstrMessage, SIZE_T cchMax) const
{
_tcsncpy(pstrMessage, m_szErrorMsg, cchMax);
}
void CMarkup::GetLastErrorLocation(LPTSTR pstrSource, SIZE_T cchMax) const
{
_tcsncpy(pstrSource, m_szErrorXML, cchMax);
}
CMarkupNode CMarkup::GetRoot()
{
if( m_nElements == 0 ) return CMarkupNode();
return CMarkupNode(this, 1);
}
bool CMarkup::_Parse()
{
_ReserveElement(); // Reserve index 0 for errors
::ZeroMemory(m_szErrorMsg, sizeof(m_szErrorMsg));
::ZeroMemory(m_szErrorXML, sizeof(m_szErrorXML));
LPTSTR pstrXML = m_pstrXML;
return _Parse(pstrXML, 0);
}
bool CMarkup::_Parse(LPTSTR& pstrText, ULONG iParent)
{
_SkipWhitespace(pstrText);
ULONG iPrevious = 0;
for( ; ; )
{
if( *pstrText == _T('\0') && iParent <= 1 ) return true;
_SkipWhitespace(pstrText);
if( *pstrText != _T('<') ) return _Failed(_T("Expected start tag"), pstrText);
if( pstrText[1] == _T('/') ) return true;
*pstrText++ = _T('\0');
_SkipWhitespace(pstrText);
// Skip comment or processing directive
if( *pstrText == _T('!') || *pstrText == _T('?') ) {
TCHAR ch = *pstrText;
if( *pstrText == _T('!') ) ch = _T('-');
while( *pstrText != _T('\0') && !(*pstrText == ch && *(pstrText + 1) == _T('>')) ) pstrText = ::CharNext(pstrText);
if( *pstrText != _T('\0') ) pstrText += 2;
_SkipWhitespace(pstrText);
continue;
}
_SkipWhitespace(pstrText);
// Fill out element structure
XMLELEMENT* pEl = _ReserveElement();
ULONG iPos = pEl - m_pElements;
pEl->iStart = pstrText - m_pstrXML;
pEl->iParent = iParent;
pEl->iNext = pEl->iChild = 0;
if( iPrevious != 0 ) m_pElements[iPrevious].iNext = iPos;
else if( iParent > 0 ) m_pElements[iParent].iChild = iPos;
iPrevious = iPos;
// Parse name
LPCTSTR pstrName = pstrText;
_SkipIdentifier(pstrText);
LPTSTR pstrNameEnd = pstrText;
if( *pstrText == _T('\0') ) return _Failed(_T("Error parsing element name"), pstrText);
// Parse attributes
if( !_ParseAttributes(pstrText) ) return false;
_SkipWhitespace(pstrText);
if( pstrText[0] == _T('/') && pstrText[1] == _T('>') )
{
pEl->iData = pstrText - m_pstrXML;
*pstrText = _T('\0');
pstrText += 2;
}
else
{
if( *pstrText != _T('>') ) return _Failed(_T("Expected start-tag closing"), pstrText);
// Parse node data
pEl->iData = ++pstrText - m_pstrXML;
LPTSTR pstrDest = pstrText;
if( !_ParseData(pstrText, pstrDest, _T('<')) ) return false;
// Determine type of next element
if( *pstrText == _T('\0') && iParent <= 1 ) return true;
if( *pstrText != _T('<') ) return _Failed(_T("Expected end-tag start"), pstrText);
if( pstrText[0] == _T('<') && pstrText[1] != _T('/') )
{
if( !_Parse(pstrText, iPos) ) return false;
}
if( pstrText[0] == _T('<') && pstrText[1] == _T('/') )
{
*pstrDest = _T('\0');
*pstrText = _T('\0');
pstrText += 2;
_SkipWhitespace(pstrText);
SIZE_T cchName = pstrNameEnd - pstrName;
if( _tcsncmp(pstrText, pstrName, cchName) != 0 ) return _Failed(_T("Unmatched closing tag"), pstrText);
pstrText += cchName;
_SkipWhitespace(pstrText);
if( *pstrText++ != _T('>') ) return _Failed(_T("Unmatched closing tag"), pstrText);
}
}
*pstrNameEnd = _T('\0');
_SkipWhitespace(pstrText);
}
}
CMarkup::XMLELEMENT* CMarkup::_ReserveElement()
{
if( m_nElements == 0 ) m_nReservedElements = 0;
if( m_nElements >= m_nReservedElements ) {
m_nReservedElements += (m_nReservedElements / 2) + 500;
m_pElements = static_cast<XMLELEMENT*>(realloc(m_pElements, m_nReservedElements * sizeof(XMLELEMENT)));
}
return &m_pElements[m_nElements++];
}
void CMarkup::_SkipWhitespace(LPCTSTR& pstr) const
{
while( *pstr > _T('\0') && *pstr <= _T(' ') ) pstr = ::CharNext(pstr);
}
void CMarkup::_SkipWhitespace(LPTSTR& pstr) const
{
while( *pstr > _T('\0') && *pstr <= _T(' ') ) pstr = ::CharNext(pstr);
}
void CMarkup::_SkipIdentifier(LPCTSTR& pstr) const
{
// <20><><EFBFBD><EFBFBD>ֻ<EFBFBD><D6BB><EFBFBD><EFBFBD>Ӣ<EFBFBD>ģ<EFBFBD><C4A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
while( *pstr != _T('\0') && (*pstr == _T('_') || *pstr == _T(':') || _istalnum(*pstr)) ) pstr = ::CharNext(pstr);
}
void CMarkup::_SkipIdentifier(LPTSTR& pstr) const
{
// <20><><EFBFBD><EFBFBD>ֻ<EFBFBD><D6BB><EFBFBD><EFBFBD>Ӣ<EFBFBD>ģ<EFBFBD><C4A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
while( *pstr != _T('\0') && (*pstr == _T('_') || *pstr == _T(':') || _istalnum(*pstr)) ) pstr = ::CharNext(pstr);
}
bool CMarkup::_ParseAttributes(LPTSTR& pstrText)
{
if( *pstrText == _T('>') ) return true;
*pstrText++ = _T('\0');
_SkipWhitespace(pstrText);
while( *pstrText != _T('\0') && *pstrText != _T('>') && *pstrText != _T('/') ) {
_SkipIdentifier(pstrText);
LPTSTR pstrIdentifierEnd = pstrText;
_SkipWhitespace(pstrText);
if( *pstrText != _T('=') ) return _Failed(_T("Error while parsing attributes"), pstrText);
*pstrText++ = _T(' ');
*pstrIdentifierEnd = _T('\0');
_SkipWhitespace(pstrText);
if( *pstrText++ != _T('\"') ) return _Failed(_T("Expected attribute value"), pstrText);
LPTSTR pstrDest = pstrText;
if( !_ParseData(pstrText, pstrDest, _T('\"')) ) return false;
if( *pstrText == _T('\0') ) return _Failed(_T("Error while parsing attribute string"), pstrText);
*pstrDest = _T('\0');
if( pstrText != pstrDest ) *pstrText = _T(' ');
pstrText++;
_SkipWhitespace(pstrText);
}
return true;
}
bool CMarkup::_ParseData(LPTSTR& pstrText, LPTSTR& pstrDest, char cEnd)
{
while( *pstrText != _T('\0') && *pstrText != cEnd ) {
if( *pstrText == _T('&') ) {
while( *pstrText == _T('&') ) {
_ParseMetaChar(++pstrText, pstrDest);
}
if (*pstrText == cEnd)
break;
}
if( *pstrText == _T(' ') ) {
*pstrDest++ = *pstrText++;
if( !m_bPreserveWhitespace ) _SkipWhitespace(pstrText);
}
else {
LPTSTR pstrTemp = ::CharNext(pstrText);
while( pstrText < pstrTemp) {
*pstrDest++ = *pstrText++;
}
}
}
// Make sure that MapAttributes() works correctly when it parses
// over a value that has been transformed.
LPTSTR pstrFill = pstrDest + 1;
while( pstrFill < pstrText ) *pstrFill++ = _T(' ');
return true;
}
void CMarkup::_ParseMetaChar(LPTSTR& pstrText, LPTSTR& pstrDest)
{
if( pstrText[0] == _T('a') && pstrText[1] == _T('m') && pstrText[2] == _T('p') && pstrText[3] == _T(';') ) {
*pstrDest++ = _T('&');
pstrText += 4;
}
else if( pstrText[0] == _T('l') && pstrText[1] == _T('t') && pstrText[2] == _T(';') ) {
*pstrDest++ = _T('<');
pstrText += 3;
}
else if( pstrText[0] == _T('g') && pstrText[1] == _T('t') && pstrText[2] == _T(';') ) {
*pstrDest++ = _T('>');
pstrText += 3;
}
else if( pstrText[0] == _T('q') && pstrText[1] == _T('u') && pstrText[2] == _T('o') && pstrText[3] == _T('t') && pstrText[4] == _T(';') ) {
*pstrDest++ = _T('\"');
pstrText += 5;
}
else if( pstrText[0] == _T('a') && pstrText[1] == _T('p') && pstrText[2] == _T('o') && pstrText[3] == _T('s') && pstrText[4] == _T(';') ) {
*pstrDest++ = _T('\'');
pstrText += 5;
}
else {
*pstrDest++ = _T('&');
}
}
bool CMarkup::_Failed(LPCTSTR pstrError, LPCTSTR pstrLocation)
{
// Register last error
TRACE(_T("XML Error: %s"), pstrError);
if( pstrLocation != NULL ) TRACE(pstrLocation);
_tcsncpy(m_szErrorMsg, pstrError, (sizeof(m_szErrorMsg) / sizeof(m_szErrorMsg[0])) - 1);
_tcsncpy(m_szErrorXML, pstrLocation != NULL ? pstrLocation : _T(""), lengthof(m_szErrorXML) - 1);
return false; // Always return 'false'
}
} // namespace ui