windows-nt/Source/XPSP1/NT/shell/ext/mlang/mlstr.cpp
2020-09-26 16:20:57 +08:00

1819 lines
48 KiB
C++

// MLStr.cpp : Implementation of CMLStr
#include "private.h"
#ifndef NEWMLSTR
#include "mlstr.h"
#ifdef ASTRIMPL
#include "mlsbwalk.h"
#endif
/////////////////////////////////////////////////////////////////////////////
// CMLStr Helper functions
HRESULT RegularizePosLen(long lStrLen, long* plPos, long* plLen)
{
ASSERT_WRITE_PTR(plPos);
ASSERT_WRITE_PTR(plLen);
long lPos = *plPos;
long lLen = *plLen;
if (lPos < 0)
lPos = lStrLen;
else
lPos = min(lPos, lStrLen);
if (lLen < 0)
lLen = lStrLen - lPos;
else
lLen = min(lLen, lStrLen - lPos);
*plPos = lPos;
*plLen = lLen;
return S_OK;
}
#ifdef ASTRIMPL
HRESULT LocaleToCodePage(LCID locale, UINT* puCodePage)
{
HRESULT hr = S_OK;
if (puCodePage)
{
TCHAR szCodePage[8];
if (::GetLocaleInfo(locale, LOCALE_IDEFAULTANSICODEPAGE, szCodePage, ARRAYSIZE(szCodePage)) > 0)
*puCodePage = _ttoi(szCodePage);
else
hr = E_FAIL; // NLS failed
}
return hr;
}
#endif
/////////////////////////////////////////////////////////////////////////////
// CMLStr
CMLStr::CMLStr(void) :
m_pMLStrBufW(NULL),
m_pMLStrBufA(NULL),
m_lBufFlags(0),
m_cchBuf(0),
m_locale(0),
#ifdef ASTRIMPL
m_LockInfo(this)
#else
m_lLockFlags(0)
#endif
{
DllAddRef();
m_dwThreadID = ::GetCurrentThreadId();
}
CMLStr::~CMLStr(void)
{
if (m_pMLStrBufW)
m_pMLStrBufW->Release();
if (m_pMLStrBufA)
m_pMLStrBufA->Release();
DllRelease();
}
STDMETHODIMP CMLStr::Sync(BOOL)
{
ASSERT_THIS;
return S_OK; // No multithread supported; Always synchronized
}
STDMETHODIMP CMLStr::GetLength(long* plLen)
{
ASSERT_THIS;
ASSERT_WRITE_PTR_OR_NULL(plLen);
HRESULT hr = CheckThread();
#ifdef ASTRIMPL
CLock Lock(FALSE, this, hr);
#endif
long lLen;
if (SUCCEEDED(hr))
hr = GetLen(0, GetBufCCh(), &lLen);
if (plLen)
{
if (SUCCEEDED(hr))
*plLen = lLen;
else
*plLen = 0;
}
return hr;
}
STDMETHODIMP CMLStr::SetMLStr(long, long, IUnknown*, long, long)
{
return E_NOTIMPL; // IMLangString::SetMLStr()
}
STDMETHODIMP CMLStr::GetMLStr(long, long, IUnknown*, DWORD, const IID*, IUnknown**, long*, long*)
{
return E_NOTIMPL; // IMLangString::GetMLStr()
}
#ifndef ASTRIMPL
STDMETHODIMP CMLStr::SetWStr(long lDestPos, long lDestLen, const WCHAR* pszSrc, long cchSrc, long* pcchActual, long* plActualLen)
{
return E_NOTIMPL; // !ASTRIMPL
}
STDMETHODIMP CMLStr::SetStrBufW(long lDestPos, long lDestLen, IMLangStringBufW* pSrcBuf, long* pcchActual, long* plActualLen)
{
return SetStrBufCommon(NULL, lDestPos, lDestLen, 0, pSrcBuf, NULL, pcchActual, plActualLen);
}
#endif
HRESULT CMLStr::SetStrBufCommon(void* pMLStrX, long lDestPos, long lDestLen, UINT uCodePage, IMLangStringBufW* pSrcBufW, IMLangStringBufA* pSrcBufA, long* pcchActual, long* plActualLen)
{
ASSERT_THIS;
ASSERT_READ_PTR_OR_NULL(pSrcBufW);
ASSERT_READ_PTR_OR_NULL(pSrcBufA);
ASSERT(!pSrcBufW || !pSrcBufA); // Either one or both should be NULL
ASSERT_WRITE_PTR_OR_NULL(pcchActual);
ASSERT_WRITE_PTR_OR_NULL(plActualLen);
HRESULT hr = CheckThread();
#ifdef ASTRIMPL
CLock Lock(TRUE, this, hr);
#endif
long lBufFlags = 0; // '= 0' for in case of both of pSrcBufW and pSrcBufA are NULL
long cchBuf = 0;
long cchDestPos;
long cchDestLen;
long lActualLen = 0;
#ifndef ASTRIMPL
if (SUCCEEDED(hr) && IsLocked())
hr = E_INVALIDARG; // This MLStr is locked
#endif
if (SUCCEEDED(hr) &&
(!pSrcBufW || SUCCEEDED(hr = pSrcBufW->GetStatus(&lBufFlags, &cchBuf))) &&
(!pSrcBufA || SUCCEEDED(hr = pSrcBufA->GetStatus(&lBufFlags, &cchBuf))) &&
SUCCEEDED(hr = RegularizePosLen(&lDestPos, &lDestLen)) &&
SUCCEEDED(hr = GetCCh(0, lDestPos, &cchDestPos)) &&
SUCCEEDED(hr = GetCCh(cchDestPos, lDestLen, &cchDestLen)))
{
if (!cchDestPos && cchDestLen == GetBufCCh()) // Replacing entire string
{
IMLangStringBufW* const pOldBufW = GetMLStrBufW();
IMLangStringBufA* const pOldBufA = GetMLStrBufA();
if (pOldBufW)
pOldBufW->Release();
else if (pOldBufA)
pOldBufA->Release();
if (pSrcBufW)
pSrcBufW->AddRef();
else if (pSrcBufA)
pSrcBufA->AddRef();
SetMLStrBufW(pSrcBufW);
SetMLStrBufA(pSrcBufA);
SetCodePage(uCodePage);
SetBufFlags(lBufFlags);
SetBufCCh(cchBuf);
if (plActualLen)
hr = GetLen(0, GetBufCCh(), &lActualLen);
}
else
{
#ifdef ASTRIMPL
if (pSrcBufW)
{
CMLStrBufWalkW BufWalk(pSrcBufW, 0, cchBuf, (pcchActual || plActualLen));
while (BufWalk.Lock(hr))
{
long cchSet;
long lSetLen;
hr = ((IMLangStringWStr*)pMLStrX)->SetWStr(lDestPos, lDestLen, BufWalk.GetStr(), BufWalk.GetCCh(), &cchSet, (plActualLen) ? &lSetLen : NULL);
lActualLen += lSetLen;
BufWalk.Unlock(hr, cchSet);
}
cchBuf = BufWalk.GetDoneCCh();
pSrcBufW->Release();
}
else if (pSrcBufA && pMLStrX)
{
CMLStrBufWalkA BufWalk(pSrcBufA, 0, cchBuf, (pcchActual || plActualLen));
while (BufWalk.Lock(hr))
{
long cchSet;
long lSetLen;
hr = ((IMLangStringAStr*)pMLStrX)->SetAStr(lDestPos, lDestLen, uCodePage, BufWalk.GetStr(), BufWalk.GetCCh(), &cchSet, (plActualLen) ? &lSetLen : NULL);
lActualLen += lSetLen;
BufWalk.Unlock(hr, cchSet);
}
cchBuf = BufWalk.GetDoneCCh();
pSrcBufA->Release();
}
else
{
hr = SetMLStr(lDestPos, lDestLen, NULL, 0, 0);
}
#else
hr = E_INVALIDARG; // !ASTRIMPL
#endif
}
}
if (SUCCEEDED(hr))
{
if (pcchActual)
*pcchActual = cchBuf;
if (plActualLen)
*plActualLen = lActualLen;
}
else
{
if (pcchActual)
*pcchActual = 0;
if (plActualLen)
*plActualLen = 0;
}
return hr;
}
#ifndef ASTRIMPL
STDMETHODIMP CMLStr::GetWStr(long lSrcPos, long lSrcLen, WCHAR* pszDest, long cchDest, long* pcchActual, long* plActualLen)
{
ASSERT_THIS;
ASSERT_WRITE_BLOCK_OR_NULL(pszDest, cchDest);
ASSERT_WRITE_PTR_OR_NULL(pcchActual);
ASSERT_WRITE_PTR_OR_NULL(plActualLen);
HRESULT hr = CheckThread();
long cchSrcPos;
long cchSrcLen;
long cchActual;
long lActualLen;
if (SUCCEEDED(hr) && IsLocked())
hr = E_INVALIDARG; // This MLStr is locked
if (SUCCEEDED(hr) &&
SUCCEEDED(hr = RegularizePosLen(&lSrcPos, &lSrcLen)) &&
SUCCEEDED(hr = GetCCh(0, lSrcPos, &cchSrcPos)) &&
SUCCEEDED(hr = GetCCh(cchSrcPos, lSrcLen, &cchSrcLen)))
{
if (pszDest)
{
long cchActualTemp = min(cchSrcLen, cchDest);
cchActual = cchActualTemp;
while (SUCCEEDED(hr) && cchActualTemp > 0)
{
WCHAR* pszBuf;
long cchBuf;
if (m_pMLStrBufW)
{
if (SUCCEEDED(hr = m_pMLStrBufW->LockBuf(cchSrcPos, cchActualTemp, &pszBuf, &cchBuf)))
{
::memcpy(pszDest, pszBuf, sizeof(WCHAR) * cchBuf);
hr = m_pMLStrBufW->UnlockBuf(pszBuf, 0, 0);
cchSrcPos += cchBuf;
cchActualTemp -= cchBuf;
pszDest += cchBuf;
}
}
else // m_pMLStrBufW
{
hr = E_FAIL; // !ASTRIMPL
}
}
if (FAILED(hr) && cchActualTemp < cchActual && (pcchActual || plActualLen))
{
cchActual -= cchActualTemp;
hr = S_OK;
}
}
else
{
cchActual = cchSrcLen;
}
}
if (SUCCEEDED(hr) && plActualLen)
hr = CalcLenW(0, cchActual, &lActualLen);
if (SUCCEEDED(hr))
{
if (pcchActual)
*pcchActual = cchActual;
if (plActualLen)
*plActualLen = lActualLen;
}
else
{
if (pcchActual)
*pcchActual = 0;
if (plActualLen)
*plActualLen = 0;
}
return hr;
}
STDMETHODIMP CMLStr::GetStrBufW(long, long, IMLangStringBufW**, long*)
{
return E_NOTIMPL; // !ASTRIMPL
}
STDMETHODIMP CMLStr::LockWStr(long lSrcPos, long lSrcLen, long lFlags, long cchRequest, WCHAR** ppszDest, long* pcchDest, long* plDestLen)
{
ASSERT_THIS;
ASSERT_WRITE_PTR_OR_NULL(ppszDest);
ASSERT_WRITE_PTR_OR_NULL(pcchDest);
ASSERT_WRITE_PTR_OR_NULL(plDestLen);
HRESULT hr = CheckThread();
long cchSrcPos;
long cchSrcLen;
WCHAR* pszBuf = NULL;
long cchBuf;
long lLockLen;
if (SUCCEEDED(hr) && (IsLocked() || !lFlags || (lFlags & ~GetBufFlags() & MLSTR_WRITE)))
hr = E_INVALIDARG; // This MLStr is locked, no flags specified or not writable
if (!(lFlags & MLSTR_WRITE))
cchRequest = 0;
if (SUCCEEDED(hr) &&
SUCCEEDED(hr = PrepareMLStrBuf()) &&
SUCCEEDED(hr = RegularizePosLen(&lSrcPos, &lSrcLen)) &&
SUCCEEDED(hr = GetCCh(0, lSrcPos, &cchSrcPos)) &&
SUCCEEDED(hr = GetCCh(cchSrcPos, lSrcLen, &cchSrcLen)))
{
IMLangStringBufW* const pMLStrBufW = GetMLStrBufW();
SetDirectLockFlag(pMLStrBufW != 0);
if (IsDirectLock())
{
long cchInserted;
long cchLockLen = cchSrcLen;
if (cchRequest > cchSrcLen &&
SUCCEEDED(hr = pMLStrBufW->Insert(cchSrcPos + cchSrcLen, cchRequest - cchSrcLen, &cchInserted)))
{
SetBufCCh(GetBufCCh() + cchInserted);
cchLockLen += cchInserted;
if (!pcchDest && cchLockLen < cchRequest)
hr = E_OUTOFMEMORY; // Can't insert in StrBuf
}
if (SUCCEEDED(hr) &&
SUCCEEDED(hr = pMLStrBufW->LockBuf(cchSrcPos, cchLockLen, &pszBuf, &cchBuf)) &&
!pcchDest && cchBuf < max(cchSrcLen, cchRequest))
{
hr = E_OUTOFMEMORY; // Can't lock StrBuf
}
}
else if (m_pMLStrBufA)
{
long cchSize;
if (SUCCEEDED(hr = CalcBufSizeW(lSrcLen, &cchSize)))
{
cchBuf = max(cchSize, cchRequest);
hr = MemAlloc(sizeof(*pszBuf) * cchBuf, (void**)&pszBuf);
}
if (SUCCEEDED(hr) && (lFlags & MLSTR_READ))
hr = ConvertMLStrBufAToWStr(m_uCodePage, m_pMLStrBufA, cchSrcPos, cchSrcLen, pszBuf, cchBuf, (pcchDest) ? &cchBuf : NULL);
}
else
{
hr = E_FAIL; // !ASTRIMPL
}
}
if (plDestLen && SUCCEEDED(hr))
hr = CalcLenW(pszBuf, cchBuf, &lLockLen);
if (SUCCEEDED(hr))
{
SetLockFlags(lFlags);
m_pszLockBuf = pszBuf;
m_cchLockPos = cchSrcPos;
m_cchLockLen = cchBuf;
m_lLockPos = lSrcPos;
m_lLockLen = lSrcLen;
if (ppszDest)
*ppszDest = pszBuf;
if (pcchDest)
*pcchDest = cchBuf;
if (plDestLen)
*plDestLen = lLockLen;
}
else
{
if (pszBuf)
{
if (IsDirectLock())
GetMLStrBufW()->UnlockBuf(pszBuf, 0, 0);
else
MemFree(pszBuf);
}
if (ppszDest)
*ppszDest = NULL;
if (pcchDest)
*pcchDest = 0;
if (plDestLen)
*plDestLen = 0;
}
return hr;
}
#endif
#ifdef ASTRIMPL
HRESULT CMLStr::UnlockWStrDirect(void* pKey, const void* pszSrc, long cchSrc, long* pcchActual, long* plActualLen)
{
HRESULT hr;
IMLangStringBufW* const pMLStrBufW = GetMLStrBufW();
const long cchLockLen = GetLockInfo()->GetCChLen(pKey);
if (SUCCEEDED(hr = pMLStrBufW->UnlockBuf((WCHAR*)pszSrc, 0, cchSrc)) &&
(GetLockInfo()->GetFlags(pKey) & MLSTR_WRITE))
{
if (cchSrc < cchLockLen)
{
if (SUCCEEDED(hr = pMLStrBufW->Delete(GetLockInfo()->GetCChPos(pKey) + cchSrc, cchLockLen - cchSrc)))
SetBufCCh(GetBufCCh() - (cchLockLen - cchSrc));
}
if (SUCCEEDED(hr) && plActualLen)
hr = CalcLenW((WCHAR*)pszSrc, cchSrc, plActualLen);
if (pcchActual)
*pcchActual = cchSrc;
}
return hr;
}
HRESULT CMLStr::UnlockWStrIndirect(void* pKey, const void* pszSrc, long cchSrc, long* pcchActual, long* plActualLen)
{
HRESULT hr = S_OK;
if (GetLockInfo()->GetFlags(pKey) & MLSTR_WRITE)
{
CComQIPtr<IMLangStringWStr, &IID_IMLangStringWStr> pMLStrW(this);
ASSERT(pMLStrW);
hr = pMLStrW->SetWStr(GetLockInfo()->GetPos(pKey), GetLockInfo()->GetLen(pKey), (WCHAR*)pszSrc, cchSrc, pcchActual, plActualLen);
}
ASSIGN_IF_FAILED(hr, MemFree((void*)pszSrc));
return hr;
}
HRESULT CMLStr::UnlockAStrDirect(void* pKey, const void* pszSrc, long cchSrc, long* pcchActual, long* plActualLen)
{
HRESULT hr;
IMLangStringBufA* const pMLStrBufA = GetMLStrBufA();
const long cchLockLen = GetLockInfo()->GetCChLen(pKey);
if (SUCCEEDED(hr = pMLStrBufA->UnlockBuf((CHAR*)pszSrc, 0, cchSrc)) &&
(GetLockInfo()->GetFlags(pKey) & MLSTR_WRITE))
{
if (cchSrc < cchLockLen)
{
if (SUCCEEDED(hr = pMLStrBufA->Delete(GetLockInfo()->GetCChPos(pKey) + cchSrc, cchLockLen - cchSrc)))
SetBufCCh(GetBufCCh() - (cchLockLen - cchSrc));
}
if (SUCCEEDED(hr) && plActualLen)
hr = CalcLenA(GetCodePage(), (CHAR*)pszSrc, cchSrc, plActualLen);
if (pcchActual)
*pcchActual = cchSrc;
}
return hr;
}
HRESULT CMLStr::UnlockAStrIndirect(void* pKey, const void* pszSrc, long cchSrc, long* pcchActual, long* plActualLen)
{
HRESULT hr = S_OK;
if (GetLockInfo()->GetFlags(pKey) & MLSTR_WRITE)
{
CComQIPtr<IMLangStringAStr, &IID_IMLangStringAStr> pMLStrA(this);
ASSERT(pMLStrA);
hr = pMLStrA->SetAStr(GetLockInfo()->GetPos(pKey), GetLockInfo()->GetLen(pKey), GetLockInfo()->GetCodePage(pKey), (CHAR*)pszSrc, cchSrc, pcchActual, plActualLen);
}
ASSIGN_IF_FAILED(hr, MemFree((void*)pszSrc));
return hr;
}
#endif
#ifndef ASTRIMPL
STDMETHODIMP CMLStr::UnlockWStr(const WCHAR* pszSrc, long cchSrc, long* pcchActual, long* plActualLen)
{
ASSERT_THIS;
ASSERT_READ_BLOCK(pszSrc, cchSrc);
ASSERT_WRITE_PTR_OR_NULL(pcchActual);
ASSERT_WRITE_PTR_OR_NULL(plActualLen);
HRESULT hr = CheckThread();
long lSrcLen = 0;
const long lLockFlags = GetLockFlags();
if (SUCCEEDED(hr) && (!IsLocked() || pszSrc != m_pszLockBuf))
hr = E_INVALIDARG; // This MLStr is not locked
if (!(lLockFlags & MLSTR_WRITE))
{
cchSrc = 0;
lSrcLen = 0;
}
if (SUCCEEDED(hr))
{
IMLangStringBufW* const pMLStrBufW = GetMLStrBufW();
if (IsDirectLock())
{
if (SUCCEEDED(hr = pMLStrBufW->UnlockBuf(pszSrc, 0, cchSrc)) &&
(lLockFlags & MLSTR_WRITE))
{
if (cchSrc < m_cchLockLen)
{
if (SUCCEEDED(hr = pMLStrBufW->Delete(m_cchLockPos + cchSrc, m_cchLockLen - cchSrc)))
SetBufCCh(GetBufCCh() - (m_cchLockLen - cchSrc));
}
if (SUCCEEDED(hr) && plActualLen)
hr = CalcLenW(pszSrc, cchSrc, &lSrcLen);
}
}
else
{
if (lLockFlags & MLSTR_WRITE)
hr = SetWStr(m_lLockPos, m_lLockLen, pszSrc, cchSrc, (pcchActual) ? &cchSrc : NULL, (plActualLen) ? &lSrcLen : NULL);
HRESULT hrTemp = MemFree((void*)pszSrc);
if (FAILED(hrTemp) && SUCCEEDED(hr))
hr = hrTemp;
}
}
if (SUCCEEDED(hr))
{
if (pcchActual)
*pcchActual = cchSrc;
if (plActualLen)
*plActualLen = lSrcLen;
}
else
{
if (pcchActual)
*pcchActual = 0;
if (plActualLen)
*plActualLen = 0;
}
SetLockFlags(0); // Unlock it anyway
return hr;
}
#endif
#ifdef ASTRIMPL
HRESULT CMLStr::UnlockStrCommon(const void* pszSrc, long cchSrc, long* pcchActual, long* plActualLen)
{
HRESULT hr = CheckThread();
void* pLockKey;
long lSrcLen;
if (SUCCEEDED(hr))
hr = GetLockInfo()->Find(pszSrc, cchSrc, &pLockKey);
if (SUCCEEDED(hr))
hr = GetLockInfo()->Unlock(pLockKey, pszSrc, cchSrc, (pcchActual) ? &cchSrc : NULL, (plActualLen) ? &lSrcLen : NULL);
if (SUCCEEDED(hr))
{
if (pcchActual)
*pcchActual = cchSrc;
if (plActualLen)
*plActualLen = lSrcLen;
}
else
{
if (pcchActual)
*pcchActual = 0;
if (plActualLen)
*plActualLen = 0;
}
return hr;
}
#endif
STDMETHODIMP CMLStr::SetLocale(long lDestPos, long lDestLen, LCID locale)
{
ASSERT_THIS;
HRESULT hr = CheckThread();
#ifdef ASTRIMPL
CLock Lock(TRUE, this, hr);
#endif
long cchDestPos;
long cchDestLen;
if (SUCCEEDED(hr) &&
SUCCEEDED(hr = RegularizePosLen(&lDestPos, &lDestLen)) &&
SUCCEEDED(hr = GetCCh(0, lDestPos, &cchDestPos)) &&
SUCCEEDED(hr = GetCCh(cchDestPos, lDestLen, &cchDestLen)))
{
//if (!cchDestPos && cchDestLen == GetBufCCh())
SetLocale(locale);
//else
// hr = E_NOTIMPL; // Cannot set the locale to a part of string in this version.
}
return hr;
}
STDMETHODIMP CMLStr::GetLocale(long lSrcPos, long lSrcMaxLen, LCID* plocale, long* plLocalePos, long* plLocaleLen)
{
ASSERT_THIS;
ASSERT_WRITE_PTR_OR_NULL(plocale);
ASSERT_WRITE_PTR_OR_NULL(plLocalePos);
ASSERT_WRITE_PTR_OR_NULL(plLocaleLen);
HRESULT hr = CheckThread();
#ifdef ASTRIMPL
CLock Lock(FALSE, this, hr);
#endif
long lStrLen;
if (SUCCEEDED(hr) &&
SUCCEEDED(hr = GetLen(0, GetBufCCh(), &lStrLen)) &&
SUCCEEDED(hr = ::RegularizePosLen(lStrLen, &lSrcPos, &lSrcMaxLen)))
{
if (plocale)
*plocale = GetLocale();
if (plLocalePos)
*plLocalePos = 0;
if (plLocaleLen)
{
if (plLocalePos)
*plLocaleLen = lStrLen;
else
*plLocaleLen = lSrcMaxLen;
}
}
else
{
if (plocale)
*plocale = 0;
if (plLocalePos)
*plLocalePos = 0;
if (plLocaleLen)
*plLocaleLen = 0;
}
return hr;
}
HRESULT CMLStr::PrepareMLStrBuf(void)
{
if (GetMLStrBufW() || GetMLStrBufA())
return S_OK;
#ifdef ASTRIMPL
IMLangStringBufW* pBuf = new CMLStr::CMLStrBufStandardW;
if (pBuf)
{
SetMLStrBufW(pBuf);
return S_OK;
}
else
{
return E_OUTOFMEMORY;
}
#else
else
return E_NOTIMPL; //!ASTRIMPL
#endif
}
HRESULT CMLStr::RegularizePosLen(long* plPos, long* plLen)
{
HRESULT hr;
long lStrLen;
if (SUCCEEDED(hr = GetLen(0, GetBufCCh(), &lStrLen)))
hr = ::RegularizePosLen(lStrLen, plPos, plLen);
return hr;
}
HRESULT CMLStr::GetCCh(long cchOffset, long lLen, long* pcchLen)
{
if (GetMLStrBufW())
{
if (pcchLen)
*pcchLen = lLen; // The number of characters is equal to the length
return S_OK;
}
else if (GetMLStrBufA())
{
HRESULT hr = S_OK;
#ifdef ASTRIMPL
CMLStrBufWalkA BufWalk(GetMLStrBufA(), cchOffset, GetBufCCh() - cchOffset);
while (lLen > 0 && BufWalk.Lock(hr))
{
for (LPCSTR pszTemp = BufWalk.GetStr(); lLen > 0 && *pszTemp; lLen--)
pszTemp = ::CharNextExA((WORD)GetCodePage(), pszTemp, 0);
if (!*pszTemp)
lLen = 0; // String terminated
BufWalk.Unlock(hr);
}
#else
long cchDone = 0;
long cchRest = GetBufCCh() - cchOffset;
while (SUCCEEDED(hr) && lLen > 0)
{
CHAR* pszBuf;
long cchBuf;
if (SUCCEEDED(hr = m_pMLStrBufA->LockBuf(cchOffset, cchRest, &pszBuf, &cchBuf)))
{
for (LPCSTR pszTemp = pszBuf; lLen > 0 && *pszTemp; lLen--)
pszTemp = ::CharNextExA((WORD)m_uCodePage, pszTemp, 0);
if (!*pszBuf)
lLen = 0; // String terminated
hr = m_pMLStrBufA->UnlockBuf(pszBuf, 0, 0);
cchOffset += cchBuf;
cchRest -= cchBuf;
cchDone += (int)(pszTemp - pszBuf);
}
}
#endif
if (pcchLen)
{
if (SUCCEEDED(hr))
#ifdef ASTRIMPL
*pcchLen = BufWalk.GetDoneCCh();
#else
*pcchLen = cchDone;
#endif
else
*pcchLen = 0;
}
return hr;
}
else
{
if (pcchLen)
*pcchLen = 0; // No string
return S_OK;
}
}
HRESULT CMLStr::GetLen(long cchOffset, long cchLen, long* plLen)
{
if (GetMLStrBufW())
{
if (plLen)
*plLen = cchLen; // The length is equal to the number of characters
return S_OK;
}
else if (GetMLStrBufA())
{
HRESULT hr = S_OK;
long lDoneLen = 0;
#ifdef ASTRIMPL
CMLStrBufWalkA BufWalk(GetMLStrBufA(), cchOffset, cchLen);
while (BufWalk.Lock(hr))
{
long lTempLen;
hr = CalcLenA(GetCodePage(), BufWalk.GetStr(), BufWalk.GetCCh(), &lTempLen);
if (hr == S_FALSE)
cchLen = 0; // String terminated
lDoneLen += lTempLen;
BufWalk.Unlock(hr);
}
#else
while (SUCCEEDED(hr) && cchLen > 0)
{
CHAR* pszBuf;
long cchBuf;
if (SUCCEEDED(hr = m_pMLStrBufA->LockBuf(cchOffset, cchLen, &pszBuf, &cchBuf)))
{
long lTempLen;
hr = CalcLenA(GetCodePage(), pszBuf, cchBuf, &lTempLen);
if (hr == S_FALSE)
cchLen = 0; // String terminated
lDoneLen += lTempLen;
hr = m_pMLStrBufA->UnlockBuf(pszBuf, 0, 0);
cchOffset += cchBuf;
cchLen -= cchBuf;
}
}
#endif
if (plLen)
{
if (SUCCEEDED(hr))
*plLen = lDoneLen;
else
*plLen = 0;
}
return hr;
}
else
{
if (plLen)
*plLen = 0; // No string
return S_OK;
}
}
HRESULT CMLStr::CalcLenA(UINT uCodePage, const CHAR* psz, long cchLen, long* plLen)
{
long lLen = 0;
const CHAR* const pszEnd = psz + cchLen;
for (; psz < pszEnd && *psz; lLen++)
{
const CHAR* const pszNew = ::CharNextExA((WORD)uCodePage, psz, 0);
if (pszNew > pszEnd) // Overrun out of buffer
break;
psz = pszNew;
}
if (plLen)
*plLen = lLen;
if (*psz)
return S_OK;
else
return S_FALSE;
}
#ifdef ASTRIMPL
HRESULT CMLStr::CalcCChA(UINT uCodePage, const CHAR* psz, long lLen, long* pcchLen)
{
const CHAR* const pszStart = psz;
for (; lLen > 0 && *psz; lLen--)
psz = ::CharNextExA((WORD)uCodePage, psz, 0);
if (pcchLen)
*pcchLen = psz - pszStart;
if (*psz)
return S_OK;
else
return S_FALSE;
}
HRESULT CMLStr::ConvAStrToWStr(UINT uCodePage, const CHAR* pszSrc, long cchSrc, WCHAR* pszDest, long cchDest, long* pcchActualA, long* pcchActualW, long* plActualLen)
{
HRESULT hr = S_OK;
long lWrittenLen;
long cchWrittenA;
long cchWrittenW = ::MultiByteToWideChar(uCodePage, 0, pszSrc, cchSrc, pszDest, (pszDest) ? cchDest : 0);
if (!cchWrittenW)
hr = E_FAIL; // NLS failed
if ((pcchActualA || plActualLen) && SUCCEEDED(hr))
hr = CalcLenW(pszDest, cchWrittenW, &lWrittenLen); // BOGUS: pszDest may be NULL
if (pcchActualA && SUCCEEDED(hr))
hr = CalcCChA(uCodePage, pszSrc, lWrittenLen, &cchWrittenA);
if (SUCCEEDED(hr))
{
if (pcchActualA)
*pcchActualA = cchWrittenA;
if (pcchActualW)
*pcchActualW = cchWrittenW;
if (plActualLen)
*plActualLen = lWrittenLen;
}
else
{
if (pcchActualA)
*pcchActualA = 0;
if (pcchActualW)
*pcchActualW = 0;
if (plActualLen)
*plActualLen = 0;
}
return hr;
}
HRESULT CMLStr::ConvWStrToAStr(BOOL fCanStopAtMiddle, UINT uCodePage, const WCHAR* pszSrc, long cchSrc, CHAR* pszDest, long cchDest, long* pcchActualA, long* pcchActualW, long* plActualLen)
{
HRESULT hr = S_OK;
long lWrittenLen;
long cchWrittenW;
long cchWrittenA = ::WideCharToMultiByte(uCodePage, (fCanStopAtMiddle) ? 0 : WC_DEFAULTCHAR, pszSrc, cchSrc, pszDest, (pszDest) ? cchDest : 0, NULL, NULL);
if (!cchWrittenA)
hr = E_FAIL; // NLS failed
if ((pcchActualW || plActualLen) && SUCCEEDED(hr))
{
if (pszDest)
hr = CalcLenA(uCodePage, pszDest, cchWrittenA, &lWrittenLen);
else
hr = E_NOTIMPL; // Can't retrieve pcchActualW and plActualLen
}
if (pcchActualW && SUCCEEDED(hr))
hr = CalcCChW(pszSrc, lWrittenLen, &cchWrittenW);
if (SUCCEEDED(hr))
{
if (pcchActualA)
*pcchActualA = cchWrittenA;
if (pcchActualW)
*pcchActualW = cchWrittenW;
if (plActualLen)
*plActualLen = lWrittenLen;
}
else
{
if (pcchActualA)
*pcchActualA = 0;
if (pcchActualW)
*pcchActualW = 0;
if (plActualLen)
*plActualLen = 0;
}
return hr;
}
#endif
#ifndef ASTRIMPL
HRESULT CMLStr::ConvertMLStrBufAToWStr(UINT uCodePage, IMLangStringBufA* pMLStrBufA, long cchSrcPos, long cchSrcLen, WCHAR* pszBuf, long cchBuf, long* pcchActual)
{
HRESULT hr = S_OK;
long cchDone = 0;
while (SUCCEEDED(hr) && cchSrcLen > 0)
{
CHAR* pszBufA;
long cchBufA;
if (SUCCEEDED(hr = pMLStrBufA->LockBuf(cchSrcPos, cchSrcLen, &pszBufA, &cchBufA)))
{
long cchWritten = ::MultiByteToWideChar(uCodePage, 0, pszBufA, cchBufA, pszBuf, cchBuf);
if (!cchWritten)
hr = E_FAIL; // NLS failed
HRESULT hrTemp = pMLStrBufA->UnlockBuf(pszBufA, 0, 0);
if (FAILED(hrTemp) && SUCCEEDED(hr))
hr = hrTemp;
cchSrcPos += cchBufA;
cchSrcLen -= cchBufA;
pszBuf += cchWritten;
cchBuf -= cchWritten;
cchDone += cchWritten;
ASSERT(cchBuf >= 0);
}
}
if (pcchActual)
{
*pcchActual = cchDone;
if (FAILED(hr) && cchDone > 0)
hr = S_OK;
}
return hr;
}
HRESULT CMLStr::ConvertWStrToMLStrBufA(const WCHAR*, long, UINT, IMLangStringBufA*, long, long)
{
return E_NOTIMPL; // !ASTRIMPL
}
#endif
#ifdef ASTRIMPL
/////////////////////////////////////////////////////////////////////////////
// CMLStr::CLockInfo
HRESULT CMLStr::CLockInfo::UnlockAll(void)
{
if (m_pLockArray)
{
for (int n = 0; n < MAX_LOCK_COUNT; n++)
{
if (m_pLockArray[n].m_psz)
Unlock(&m_pLockArray[n], m_pLockArray[n].m_psz, m_pLockArray[n].m_cchLen, NULL, NULL);
}
}
return S_OK;
}
HRESULT CMLStr::CLockInfo::Lock(PFNUNLOCKPROC pfnUnlockProc, long lFlags, UINT uCodePage, void* psz, long lPos, long lLen, long cchPos, long cchLen)
{
HRESULT hr = S_OK;
int nIndex;
if (!m_pLockArray)
{
m_pLockArray = new CLockInfoEntry[MAX_LOCK_COUNT];
if (m_pLockArray)
{
for (nIndex = 0; nIndex < MAX_LOCK_COUNT; nIndex++)
m_pLockArray[nIndex].m_psz = NULL;
}
else
{
hr = E_OUTOFMEMORY;
}
}
if (SUCCEEDED(hr))
{
for (nIndex = 0; nIndex < MAX_LOCK_COUNT; nIndex++)
{
if (!m_pLockArray[nIndex].m_psz)
break;
}
if (nIndex >= MAX_LOCK_COUNT)
hr = MLSTR_E_TOOMANYNESTOFLOCK;
}
if (SUCCEEDED(hr))
{
m_pLockArray[nIndex].m_psz = psz;
m_pLockArray[nIndex].m_pfnUnlockProc = pfnUnlockProc;
m_pLockArray[nIndex].m_lFlags = lFlags;
m_pLockArray[nIndex].m_uCodePage = uCodePage;
m_pLockArray[nIndex].m_lPos = lPos;
m_pLockArray[nIndex].m_lLen = lLen;
m_pLockArray[nIndex].m_cchPos = cchPos;
m_pLockArray[nIndex].m_cchLen = cchLen;
}
return hr;
}
HRESULT CMLStr::CLockInfo::Find(const void* psz, long, void** ppKey)
{
HRESULT hr = S_OK;
int nIndex;
if (m_pLockArray)
{
for (nIndex = 0; nIndex < MAX_LOCK_COUNT; nIndex++)
{
if (psz == m_pLockArray[nIndex].m_psz)
break;
}
}
if (!m_pLockArray || nIndex >= MAX_LOCK_COUNT)
hr = E_INVALIDARG;
if (ppKey)
{
if (SUCCEEDED(hr))
*ppKey = &m_pLockArray[nIndex];
else
*ppKey = NULL;
}
return hr;
}
HRESULT CMLStr::CLockInfo::Unlock(void* pKey, const void* psz, long cch, long* pcchActual, long* plActualLen)
{
CLockInfoEntry* const pEntry = (CLockInfoEntry*)pKey;
HRESULT hr;
if (!(pEntry->m_lFlags & MLSTR_WRITE))
{
cch = 0;
if (plActualLen)
*plActualLen = 0;
}
hr = (m_pMLStr->*(pEntry->m_pfnUnlockProc))(pKey, psz, cch, pcchActual, plActualLen);
if (SUCCEEDED(hr))
hr = EndLock(pEntry->m_lFlags & MLSTR_WRITE);
pEntry->m_psz = NULL; // Remove from lock array anyway
if (FAILED(hr))
{
if (pcchActual)
*pcchActual = 0;
if (plActualLen)
*plActualLen = 0;
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
// CMLStr::CMLStrBufStandardW
long CMLStr::CMLStrBufStandardW::RoundBufSize(long cchStr)
{
for (int n = 8; n < 12; n++)
{
if (cchStr < (1L << n))
break;
}
const long cchTick = (1L << (n - 4));
return (cchStr + cchTick - 1) / cchTick * cchTick;
}
#endif
#else // NEWMLSTR
#include "mlstr.h"
/////////////////////////////////////////////////////////////////////////////
// CMLStr
CMLStr::CMLStr(void) :
m_lLen(0),
m_hUnlockEvent(NULL),
m_hZeroEvent(NULL)
{
DllAddRef();
}
CMLStr::~CMLStr(void)
{
void* pv;
if (m_hZeroEvent)
::CloseHandle(m_hZeroEvent);
if (m_hUnlockEvent)
::CloseHandle(m_hUnlockEvent);
// m_lock should be empty
ASSERT(SUCCEEDED(m_lock.Top(&pv)));
ASSERT(!pv);
// Release all attributes in m_attr
VERIFY(SUCCEEDED(m_attr.Top(&pv)));
while (pv)
{
IMLStrAttr* const pAttr = m_attr.GetAttr(pv);
ASSERT(pAttr);
VERIFY(SUCCEEDED(pAttr->SetClient(NULL))); // Reset
VERIFY(SUCCEEDED(StartEndConnectionAttr(pAttr, NULL, m_attr.GetCookie(pv)))); // Disconnect
pAttr->Release();
VERIFY(SUCCEEDED(m_attr.Next(pv, &pv)));
}
DllRelease();
}
STDMETHODIMP CMLStr::LockMLStr(long lPos, long lLen, DWORD dwFlags, DWORD* pdwCookie, long* plActualPos, long* plActualLen)
{
ASSERT_WRITE_PTR_OR_NULL(pdwCookie);
ASSERT_WRITE_PTR_OR_NULL(plActualPos);
ASSERT_WRITE_PTR_OR_NULL(plActualLen);
HRESULT hr;
void* pv;
Lock();
if (SUCCEEDED(hr = ::RegularizePosLen(m_lLen, &lPos, &lLen)))
{
const DWORD dwThrd = ::GetCurrentThreadId();
if (SUCCEEDED(hr = CheckAccessValidation(lPos, lLen, dwFlags, dwThrd, plActualPos, plActualLen)) &&
SUCCEEDED(hr = m_lock.Add(&pv)))
{
if (plActualPos && !plActualLen)
lLen -= *plActualPos - lPos;
else if (plActualLen)
lLen = *plActualLen;
if (plActualPos)
lPos = *plActualPos;
hr = m_lock.SetLock(pv, lPos, lLen, dwFlags, dwThrd);
if (FAILED(hr))
VERIFY(SUCCEEDED(m_lock.Remove(pv)));
}
}
else
{
if (plActualPos)
*plActualPos = 0;
if (plActualLen)
*plActualLen = 0;
}
Unlock();
if (pdwCookie)
{
if (SUCCEEDED(hr))
*pdwCookie = (DWORD)pv;
else
*pdwCookie = 0;
}
return hr;
}
HRESULT CMLStr::CheckAccessValidation(long lPos, long lLen, DWORD dwFlags, DWORD dwThrd, long* plActualPos, long* plActualLen)
{
HRESULT hr;
DWORD dwStartTime = 0;
long lActualPos;
long lActualLen;
for (;;) // Waiting unlock loop
{
void* pv;
HRESULT hrValidation = S_OK;
lActualPos = lPos;
lActualLen = lLen;
hr = m_lock.Top(&pv);
while (SUCCEEDED(hr) && pv) // Enumerate all locks
{
LOCKINFO* plinfo;
if (SUCCEEDED(hr = m_lock.GetLockInfo(pv, &plinfo))) // Retrieve info of a lock
{
if ((dwFlags & MLSTR_MOVE) && // Moving this lock
lPos < plinfo->lPos + plinfo->lLen && // Overwrap or left of this lock
(dwThrd != plinfo->dwThrd || // Another thread
(plinfo->dwFlags & (MLSTR_READ | MLSTR_WRITE)))) // Same thread and has read or write access
{
if (dwThrd == plinfo->dwThrd)
hr = MLSTR_E_ACCESSDENIED;
else
hr = MLSTR_E_BUSY;
}
if (SUCCEEDED(hr) &&
lActualPos < plinfo->lPos + plinfo->lLen &&
lActualPos + lActualLen >= plinfo->lPos) // Overwraping with this lock
{
DWORD dwShareMask = 0;
if (dwThrd == plinfo->dwThrd) // Same thread
dwShareMask = ~(MLSTR_SHARE_DENYREAD | MLSTR_SHARE_DENYWRITE); // Ignore share flags
if (((dwFlags & MLSTR_WRITE) && (plinfo->dwFlags & (MLSTR_READ | MLSTR_WRITE | MLSTR_SHARE_DENYWRITE) & dwShareMask)) || // Write on read/write
((dwFlags & MLSTR_READ) && (plinfo->dwFlags & ( MLSTR_WRITE | MLSTR_SHARE_DENYREAD ) & dwShareMask)) || // Read on write
((dwFlags & MLSTR_SHARE_DENYWRITE & dwShareMask) && (plinfo->dwFlags & MLSTR_WRITE)) || // Share deny on write
((dwFlags & MLSTR_SHARE_DENYREAD & dwShareMask) && (plinfo->dwFlags & MLSTR_READ))) // Share deny on read
{
// Conflicting access
if ((plinfo->lPos <= lActualPos && plinfo->lPos + plinfo->lLen >= lActualPos + lActualLen) || // No valid range left
(!plActualPos && !plActualLen)) // Needs to lock entire range
{
lActualPos = 0;
lActualLen = 0;
if (dwThrd == plinfo->dwThrd)
hr = MLSTR_E_ACCESSDENIED;
else
hr = MLSTR_E_BUSY;
}
else if ((!plActualPos && plinfo->lPos <= lActualPos) || // Forward processing, Starting from invalid range
(!plActualLen && plinfo->lPos + plinfo->lLen < lActualPos + lActualLen) || // Backward processing, Trancate valid range
(plActualPos && plActualLen && plinfo->lPos - lActualPos >= (lActualPos + lActualLen) - (plinfo->lPos + plinfo->lLen))) // Maximum valid range, Right valid range is bigger
{
lActualLen += lActualPos;
lActualPos = plinfo->lPos + plinfo->lLen;
lActualLen -= lActualPos;
if (!plActualPos) // Forward processing
{
if (dwThrd == plinfo->dwThrd)
hrValidation = MLSTR_E_ACCESSDENIED;
else
hrValidation = MLSTR_E_BUSY;
}
}
else
{
lActualLen = plinfo->lPos - lActualPos;
if (!plActualLen) // Backward processing
{
if (dwThrd == plinfo->dwThrd)
hrValidation = MLSTR_E_ACCESSDENIED;
else
hrValidation = MLSTR_E_BUSY;
}
}
}
}
}
if (SUCCEEDED(hr))
hr = m_lock.Next(pv, &pv);
}
if (SUCCEEDED(hr) && FAILED(hrValidation))
{
hr = hrValidation;
if (plActualLen && lPos < lActualPos) // Forward processing
{
lActualLen = lActualPos - lPos;
lActualPos = lPos;
}
else if (plActualPos && lPos + lLen != lActualPos + lActualLen) // Backward processing
{
lActualPos += lActualLen;
lActualLen = lPos + lLen - lActualPos;
}
}
if (hr != MLSTR_E_BUSY || (dwFlags | MLSTR_NOWAIT)) // No busy state, or don't want to wait even if busy
break;
// Now, let's wait another thread run UnlockMLStr. Then, try validation again.
if (!dwStartTime) // Not initialized yet
dwStartTime = ::GetTickCount(); // Remember starting time
const DWORD dwElapsedTime = ::GetTickCount() - dwStartTime;
if (dwElapsedTime >= MLSTR_LOCK_TIMELIMIT) // Already elapsed long time
break;
if (!m_hUnlockEvent) // We don't have event object yet
{
m_hUnlockEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL); // Manual reset, initial reset
if (!m_hUnlockEvent)
break;
m_cWaitUnlock = -1; // Initialize
}
else // After second time
{
ASSERT(m_cWaitUnlock == 0 || m_cWaitUnlock == -1 || m_cWaitUnlock >= 1);
if (m_cWaitUnlock == 0) // Don't reset if m_cWaitUnlock is not zero
{
::ResetEvent(m_hUnlockEvent);
m_cWaitUnlock = -1;
}
else
{
if (!m_hZeroEvent)
{
m_hZeroEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL); // Auto-reset, initial reset
if (!m_hZeroEvent)
break;
}
if (m_cWaitUnlock == -1)
m_cWaitUnlock = 2;
else
m_cWaitUnlock++;
}
}
ASSERT(m_cWaitUnlock == -1 || m_cWaitUnlock >= 2);
// CAUTION: Don't leave here until we make sure m_cWaitUnlock gets zero.
Unlock();
// === The story of m_cWaitUnlock ===
// If we don't have m_cWaitUnlock, the following scenario can be considered.
// (1) Thread A: ResetEvent(m_hUnlockEvent)
// (2) Thread A: Unlock()
// (3) Thread B: SetEvent(m_hUnlockEvent) // UnlockMLStr!!!
// (4) Thread C: Lock()
// (5) Thread C: ResetEvent(m_hUnlockEvent) // Problem!!!
// (6) Thread C: Unlock()
// (7) Thread A: WaitForSingleObject(m_hUnlockEvent)
// In this scenario, thread A is missing a event of (3). This situation should not happen.
// m_cWaitUnlock solves the problem.
const DWORD dwWaitResult = ::WaitForSingleObject(m_hUnlockEvent, MLSTR_LOCK_TIMELIMIT - dwElapsedTime); // Now wait unlock
Lock();
ASSERT(m_cWaitUnlock == -1 || m_cWaitUnlock >= 1);
if (m_cWaitUnlock == -1)
{
m_cWaitUnlock = 0;
}
else // m_cWaitUnlock >= 1
{
m_cWaitUnlock--;
// Here, let's wait until m_cWaitUnlock gets zero.
// Unless this, it may not good for performance.
// In worst case, it makes thousands of loops in this function because it never reset m_hUnlockEvent.
// m_hUnlockEvent will be signaled even though UnlockMLStr is called yet.
if (m_cWaitUnlock > 0)
{
Unlock();
::WaitForSingleObject(m_hZeroEvent, INFINITE); // Wait until m_cWaitUnlock gets zero, auto-reset
Lock();
}
else // Now it's zero! Yeah!
{
::SetEvent(m_hZeroEvent); // Release other threads
}
}
// ASSERT(m_cWaitUnlock == 0); This is not true. Maybe non-zero for next time.
// Now we may leave here.
if (dwWaitResult != WAIT_OBJECT_0) // Time expired or an error occurred
break;
}
if (plActualPos)
*plActualPos = lActualPos;
if (plActualLen)
*plActualLen = lActualLen;
return hr;
}
STDMETHODIMP CMLStr::UnlockMLStr(DWORD dwCookie)
{
Lock();
void* const pv = (void*)dwCookie;
const HRESULT hr = m_lock.Remove(pv);
if (m_hUnlockEvent)
::SetEvent(m_hUnlockEvent);
Unlock();
return hr;
}
STDMETHODIMP CMLStr::GetLength(long* plLen)
{
ASSERT_THIS;
ASSERT_WRITE_PTR_OR_NULL(plLen);
if (plLen)
*plLen = m_lLen;
return S_OK;
}
STDMETHODIMP CMLStr::SetMLStr(long, long, IUnknown*, long, long)
{
return E_NOTIMPL; // IMLangString::SetMLStr()
}
STDMETHODIMP CMLStr::RegisterAttr(IUnknown* pUnk, DWORD* pdwCookie)
{
ASSERT_THIS;
ASSERT_READ_PTR(pUnk);
ASSERT_WRITE_PTR_OR_NULL(pdwCookie);
HRESULT hr;
void* pv;
IMLStrAttr* pAttr = NULL;
BOOL fConnStarted = FALSE;
DWORD dwConnCookie;
Lock();
if (SUCCEEDED(hr = m_attr.Add(&pv)) &&
SUCCEEDED(hr = pUnk->QueryInterface(IID_IMLStrAttr, (void**)&pAttr)))
{
ASSERT_READ_PTR(pAttr);
}
if (SUCCEEDED(hr) &&
SUCCEEDED(hr = StartEndConnectionAttr(pAttr, &dwConnCookie, 0))) // Connect
{
fConnStarted = TRUE;
if (SUCCEEDED(hr = pAttr->SetClient((IMLangString*)this)))
{
CFire fire(hr, this);
while (fire.Next())
hr = fire.Sink()->OnRegisterAttr(pAttr);
}
}
if (SUCCEEDED(hr) &&
SUCCEEDED(hr = pAttr->SetMLStr(0, -1, (IMLangString*)this, 0, m_lLen)))
{
m_attr.SetAttr(pv, pAttr);
m_attr.SetCookie(pv, dwConnCookie);
if (pdwCookie)
*pdwCookie = (DWORD)pv;
}
else
{
if (pAttr)
{
pAttr->SetClient(NULL);
if (fConnStarted)
VERIFY(SUCCEEDED(StartEndConnectionAttr(pAttr, NULL, dwConnCookie))); // Disconnect
pAttr->Release();
}
if (pv)
m_attr.Remove(pv);
if (pdwCookie)
*pdwCookie = NULL;
}
Unlock();
return hr;
}
STDMETHODIMP CMLStr::UnregisterAttr(DWORD dwCookie)
{
ASSERT_THIS;
void* const pv = (void*)dwCookie;
Lock();
IMLStrAttr* const pAttr = m_attr.GetAttr(pv);
ASSERT(pAttr);
// Fire OnUnregisterAttr
HRESULT hr;
CFire fire(hr, this);
while (fire.Next())
hr = fire.Sink()->OnUnregisterAttr(pAttr);
// Release attribute
if (SUCCEEDED(hr) &&
SUCCEEDED(hr = pAttr->SetClient(NULL))) // Reset
{
VERIFY(SUCCEEDED(hr = StartEndConnectionAttr(pAttr, NULL, m_attr.GetCookie(pv)))); // Disconnect
pAttr->Release();
// Remove entry from attr table
m_attr.Remove(pv);
}
Unlock();
return hr;
}
STDMETHODIMP CMLStr::EnumAttr(IEnumUnknown** ppEnumUnk)
{
ASSERT_THIS;
ASSERT_WRITE_PTR_OR_NULL(ppEnumUnk);
if (!ppEnumUnk)
return S_OK;
CEnumAttr* const pEnum = new CComObject<CEnumAttr>;
*ppEnumUnk = pEnum;
if (pEnum)
{
pEnum->Init(this);
return S_OK;
}
else
{
return E_OUTOFMEMORY;
}
}
STDMETHODIMP CMLStr::FindAttr(REFIID riid, LPARAM lParam, IUnknown** ppUnk)
{
ASSERT_THIS;
ASSERT_WRITE_PTR_OR_NULL(ppUnk);
HRESULT hr;
void* pv;
IUnknown* pMaxUnk = NULL;
long lMaxConf = 0;
Lock();
for (hr = m_attr.Top(&pv); SUCCEEDED(hr) && pv; hr = m_attr.Next(pv, &pv))
{
IMLStrAttr* const pIMLStrAttr = m_attr.GetAttr(pv);
IUnknown* pUnk;
long lConf;
hr = pIMLStrAttr->QueryAttr(riid, lParam, &pUnk, &lConf);
if (SUCCEEDED(hr))
{
if (lConf > lMaxConf)
{
lMaxConf = lConf;
if (pMaxUnk)
pMaxUnk->Release();
pMaxUnk = pUnk;
}
else
{
if (pUnk)
pUnk->Release();
}
if (lMaxConf == MLSTR_CONF_MAX)
break;
}
}
if (SUCCEEDED(hr))
{
if (ppUnk)
*ppUnk = pMaxUnk;
else if (pMaxUnk)
pMaxUnk->Release();
}
else
{
if (pMaxUnk)
pMaxUnk->Release();
if (ppUnk)
*ppUnk = NULL;
}
Unlock();
return hr;
}
STDMETHODIMP CMLStr::OnRequestEdit(long lDestPos, long lDestLen, long lNewLen, REFIID riid, LPARAM lParam, IUnknown* pUnk)
{
HRESULT hr;
CFire fire(hr, this);
while (fire.Next())
hr = fire.Sink()->OnRequestEdit(lDestPos, lDestLen, lNewLen, riid, lParam, pUnk);
return hr;
}
STDMETHODIMP CMLStr::OnCanceledEdit(long lDestPos, long lDestLen, long lNewLen, REFIID riid, LPARAM lParam, IUnknown* pUnk)
{
HRESULT hr;
CFire fire(hr, this);
while (fire.Next())
hr = fire.Sink()->OnCanceledEdit(lDestPos, lDestLen, lNewLen, riid, lParam, pUnk);
return hr;
}
STDMETHODIMP CMLStr::OnChanged(long lDestPos, long lDestLen, long lNewLen, REFIID riid, LPARAM lParam, IUnknown* pUnk)
{
HRESULT hr;
CFire fire(hr, this);
while (fire.Next())
hr = fire.Sink()->OnChanged(lDestPos, lDestLen, lNewLen, riid, lParam, pUnk);
return hr;
}
/////////////////////////////////////////////////////////////////////////////
// CMLStr::CEnumAttr
CMLStr::CEnumAttr::CEnumAttr(void) :
m_pMLStr(NULL),
m_pv(NULL)
{
}
CMLStr::CEnumAttr::~CEnumAttr(void)
{
if (m_pMLStr)
m_pMLStr->Unlock();
}
void CMLStr::CEnumAttr::Init(CMLStr* pMLStr)
{
ASSERT_THIS;
ASSERT_READ_PTR(pMLStr);
if (m_pMLStr)
m_pMLStr->Unlock();
m_pMLStr = pMLStr;
m_pMLStr->Lock();
VERIFY(SUCCEEDED(Reset()));
}
HRESULT CMLStr::CEnumAttr::Next(ULONG celt, IUnknown** rgelt, ULONG* pceltFetched)
{
ASSERT_THIS;
ASSERT_WRITE_BLOCK_OR_NULL(rgelt, celt);
ASSERT_WRITE_PTR_OR_NULL(pceltFetched);
ULONG c = 0;
if (rgelt && m_pMLStr)
{
for (; m_pv && c < celt; c++)
{
*rgelt = m_pMLStr->m_attr.GetAttr(m_pv);
ASSERT(*rgelt);
(*rgelt)->AddRef();
VERIFY(SUCCEEDED(m_pMLStr->m_attr.Next(m_pv, &m_pv)));
rgelt++;
}
}
if (pceltFetched)
*pceltFetched = c;
return S_OK;
}
HRESULT CMLStr::CEnumAttr::Skip(ULONG celt)
{
ASSERT_THIS;
for (ULONG c = 0; m_pv && c < celt; c++)
VERIFY(SUCCEEDED(m_pMLStr->m_attr.Next(m_pv, &m_pv)));
return S_OK;
}
HRESULT CMLStr::CEnumAttr::Reset(void)
{
ASSERT_THIS;
ASSERT_READ_PTR(m_pMLStr);
VERIFY(SUCCEEDED(m_pMLStr->m_attr.Top(&m_pv)));
return S_OK;
}
HRESULT CMLStr::CEnumAttr::Clone(IEnumUnknown** ppEnum)
{
ASSERT_THIS;
ASSERT_WRITE_PTR_OR_NULL(ppEnum);
ASSERT_READ_PTR(m_pMLStr);
return m_pMLStr->EnumAttr(ppEnum);
}
#endif // NEWMLSTR