1819 lines
48 KiB
C++
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
|