You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
664 lines
16 KiB
664 lines
16 KiB
/*===================================================================
|
|
Microsoft Denali
|
|
|
|
Microsoft Confidential.
|
|
Copyright 1996 Microsoft Corporation. All Rights Reserved.
|
|
|
|
Component: StringList object
|
|
|
|
File: strlist.cpp
|
|
|
|
Owner: DGottner
|
|
|
|
This file contains the code for the implementation of the String List object.
|
|
===================================================================*/
|
|
|
|
#include "denpre.h"
|
|
#pragma hdrstop
|
|
|
|
#include "strlist.h"
|
|
#include "MemChk.h"
|
|
|
|
#pragma warning (disable: 4355) // ignore: "'this' used in base member init
|
|
|
|
|
|
/*===================================================================
|
|
CStringListElem::CStringListElem
|
|
|
|
Constructor
|
|
===================================================================*/
|
|
CStringListElem::CStringListElem()
|
|
:
|
|
m_fBufferInUse(FALSE),
|
|
m_fAllocated(FALSE),
|
|
m_pNext(NULL),
|
|
m_szPointer(NULL)
|
|
{
|
|
}
|
|
|
|
/*===================================================================
|
|
CStringListElem::~CStringListElem
|
|
|
|
Destructor
|
|
===================================================================*/
|
|
CStringListElem::~CStringListElem()
|
|
{
|
|
if (m_fAllocated)
|
|
delete [] m_szPointer;
|
|
|
|
if (m_pNext)
|
|
delete m_pNext;
|
|
}
|
|
|
|
/*===================================================================
|
|
CStringListElem::Init
|
|
|
|
Init CStringListElem
|
|
|
|
Parameters
|
|
szValue the string
|
|
fMakeCopy if FALSE - just store the pointer
|
|
lCodePage codepage to use to convert to UNICODE
|
|
===================================================================*/
|
|
HRESULT CStringListElem::Init(
|
|
char *szValue,
|
|
BOOL fMakeCopy,
|
|
UINT lCodePage)
|
|
{
|
|
// for now, always make a copy of the string. This is to ensure
|
|
// that any string lists placed in session state via a dictionary
|
|
// object do not have their elements freed from under them when
|
|
// the request completes.
|
|
|
|
if (1 /*fMakeCopy*/) {
|
|
|
|
CMBCSToWChar convStr;
|
|
HRESULT hr = S_OK;
|
|
|
|
if (FAILED(hr = convStr.Init(szValue, lCodePage))) {
|
|
return hr;
|
|
}
|
|
|
|
// now we will move the string into the elements memory. If the
|
|
// converted string is bigger than the internal buffer, then
|
|
// set the element's pointer to an allocated copy of the converted
|
|
// string.
|
|
|
|
if ((convStr.GetStringLen() + 1) > (sizeof(m_szBuffer)/sizeof(WCHAR))) {
|
|
m_szPointer = convStr.GetString(TRUE);
|
|
if (!m_szPointer)
|
|
return E_OUTOFMEMORY;
|
|
m_fBufferInUse = FALSE;
|
|
m_fAllocated = TRUE;
|
|
}
|
|
else {
|
|
|
|
// if it fits, simply copy it into the internal buffer.
|
|
|
|
wcscpy(m_szBuffer, convStr.GetString());
|
|
m_fBufferInUse = TRUE;
|
|
m_fAllocated = FALSE;
|
|
}
|
|
}
|
|
#if 0
|
|
else {
|
|
m_szPointer = szValue;
|
|
m_fBufferInUse = FALSE;
|
|
m_fAllocated = FALSE;
|
|
}
|
|
#endif
|
|
|
|
m_pNext = NULL;
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CStringListElem::Init
|
|
|
|
Init CStringListElem
|
|
|
|
Parameters
|
|
szValue the string
|
|
fMakeCopy if FALSE - just store the pointer
|
|
===================================================================*/
|
|
HRESULT CStringListElem::Init(
|
|
WCHAR *wszValue,
|
|
BOOL fMakeCopy)
|
|
{
|
|
// for now, always make a copy of the string. This is to ensure
|
|
// that any string lists placed in session state via a dictionary
|
|
// object do not have their elements freed from under them when
|
|
// the request completes.
|
|
|
|
if (1 /*fMakeCopy*/) {
|
|
|
|
// now we will move the string into the elements memory. If the
|
|
// converted string is bigger than the internal buffer, then
|
|
// set the element's pointer to an allocated copy
|
|
|
|
if ((wcslen(wszValue) + 1) > (sizeof(m_szBuffer)/sizeof(WCHAR))) {
|
|
m_szPointer = StringDupW(wszValue);
|
|
if (!m_szPointer)
|
|
return E_OUTOFMEMORY;
|
|
m_fBufferInUse = FALSE;
|
|
m_fAllocated = TRUE;
|
|
}
|
|
else {
|
|
|
|
// if it fits, simply copy it into the internal buffer.
|
|
|
|
wcscpy(m_szBuffer, wszValue);
|
|
m_fBufferInUse = TRUE;
|
|
m_fAllocated = FALSE;
|
|
}
|
|
}
|
|
#if 0
|
|
else {
|
|
m_szPointer = szValue;
|
|
m_fBufferInUse = FALSE;
|
|
m_fAllocated = FALSE;
|
|
}
|
|
#endif
|
|
|
|
m_pNext = NULL;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*===================================================================
|
|
CStringList::CStringList
|
|
|
|
Constructor
|
|
===================================================================*/
|
|
|
|
CStringList::CStringList(IUnknown *pUnkOuter, PFNDESTROYED pfnDestroy)
|
|
: m_ISupportErrImp(this, pUnkOuter, IID_IStringList)
|
|
{
|
|
m_pBegin = m_pEnd = NULL;
|
|
m_cValues = 0;
|
|
m_cRefs = 1;
|
|
m_pfnDestroy = pfnDestroy;
|
|
CDispatch::Init(IID_IStringList);
|
|
m_lCodePage = GetACP();
|
|
}
|
|
|
|
|
|
|
|
/*===================================================================
|
|
CStringList::~CStringList
|
|
|
|
Destructor
|
|
===================================================================*/
|
|
|
|
CStringList::~CStringList()
|
|
{
|
|
if (m_pBegin)
|
|
delete m_pBegin;
|
|
}
|
|
|
|
|
|
|
|
/*===================================================================
|
|
CStringList::AddValue
|
|
|
|
Parameters:
|
|
szValue - value to add to the string list
|
|
lCodePage - the CodePage used when construct return value
|
|
===================================================================*/
|
|
|
|
HRESULT CStringList::AddValue(char *szValue, BOOL fDuplicate, UINT lCodePage)
|
|
{
|
|
CStringListElem *pElem = new CStringListElem;
|
|
if (!pElem)
|
|
return E_OUTOFMEMORY;
|
|
|
|
m_lCodePage = lCodePage;
|
|
|
|
HRESULT hr = pElem->Init(szValue, fDuplicate, lCodePage);
|
|
if (FAILED(hr)) {
|
|
delete pElem;
|
|
return hr;
|
|
}
|
|
|
|
if (m_pBegin == NULL)
|
|
{
|
|
m_pBegin = m_pEnd = pElem;
|
|
}
|
|
else
|
|
{
|
|
m_pEnd->SetNext(pElem);
|
|
m_pEnd = pElem;
|
|
}
|
|
|
|
++m_cValues;
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CStringList::AddValue
|
|
|
|
Parameters:
|
|
szValue - value to add to the string list
|
|
lCodePage - the CodePage used when construct return value
|
|
===================================================================*/
|
|
|
|
HRESULT CStringList::AddValue(WCHAR *szValue, BOOL fDuplicate)
|
|
{
|
|
CStringListElem *pElem = new CStringListElem;
|
|
if (!pElem)
|
|
return E_OUTOFMEMORY;
|
|
|
|
HRESULT hr = pElem->Init(szValue, fDuplicate);
|
|
|
|
if (FAILED(hr)) {
|
|
delete pElem;
|
|
return hr;
|
|
}
|
|
|
|
if (m_pBegin == NULL)
|
|
{
|
|
m_pBegin = m_pEnd = pElem;
|
|
}
|
|
else
|
|
{
|
|
m_pEnd->SetNext(pElem);
|
|
m_pEnd = pElem;
|
|
}
|
|
|
|
++m_cValues;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
/*===================================================================
|
|
CStringList::QueryInterface
|
|
CStringList::AddRef
|
|
CStringList::Release
|
|
|
|
IUnknown members for CStringList object.
|
|
===================================================================*/
|
|
|
|
STDMETHODIMP CStringList::QueryInterface(const IID &iid, void **ppvObj)
|
|
{
|
|
*ppvObj = NULL;
|
|
|
|
if (iid == IID_IUnknown || iid == IID_IDispatch ||
|
|
iid == IID_IStringList || iid == IID_IDenaliIntrinsic)
|
|
{
|
|
*ppvObj = this;
|
|
}
|
|
|
|
if (iid == IID_ISupportErrorInfo)
|
|
*ppvObj = &m_ISupportErrImp;
|
|
|
|
if (*ppvObj != NULL)
|
|
{
|
|
static_cast<IUnknown *>(*ppvObj)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
return ResultFromScode(E_NOINTERFACE);
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CStringList::AddRef()
|
|
{
|
|
return ++m_cRefs;
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CStringList::Release()
|
|
{
|
|
if (--m_cRefs != 0)
|
|
return m_cRefs;
|
|
|
|
if (m_pfnDestroy != NULL)
|
|
(*m_pfnDestroy)();
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/*===================================================================
|
|
CStringList::get_Count
|
|
|
|
Parameters:
|
|
pcValues - count is stored in *pcValues
|
|
===================================================================*/
|
|
|
|
STDMETHODIMP CStringList::get_Count(int *pcValues)
|
|
{
|
|
*pcValues = m_cValues;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
/*===================================================================
|
|
CStringList::ConstructDefaultReturn
|
|
|
|
Return comma-separated list for the case where the CStringList
|
|
is not indexed.
|
|
===================================================================*/
|
|
|
|
HRESULT CStringList::ConstructDefaultReturn(VARIANT *pvarOut) {
|
|
VariantClear(pvarOut);
|
|
|
|
//
|
|
// NEW SEMANTIC: we now return Empty (and not "") if nothing is in the collection
|
|
//
|
|
if (m_cValues == 0)
|
|
return S_OK; // VariantClear set pvarOut to Empty
|
|
|
|
STACK_BUFFER( tempValues, 1024 );
|
|
|
|
register CStringListElem *pElem;
|
|
int cBytes = 0;
|
|
|
|
for (pElem = m_pBegin; pElem != NULL; pElem = pElem->QueryNext())
|
|
cBytes += (wcslen(pElem->QueryValue()) * sizeof(WCHAR));
|
|
|
|
// need to account for the ", " and NULL Termination
|
|
|
|
cBytes += sizeof(WCHAR) + ((2*(m_cValues - 1)) * sizeof(WCHAR));
|
|
|
|
if (!tempValues.Resize(cBytes)) {
|
|
ExceptionId(IID_IStringList, IDE_REQUEST, IDE_OOM);
|
|
return E_FAIL;
|
|
}
|
|
|
|
WCHAR *szReturn = (WCHAR *)tempValues.QueryPtr();
|
|
szReturn[0] = L'\0';
|
|
WCHAR *szNext = szReturn;
|
|
|
|
for (pElem = m_pBegin; pElem != NULL; pElem = pElem->QueryNext()) {
|
|
szNext = strcpyExW(szNext, pElem->QueryValue());
|
|
if (pElem->QueryNext() != NULL)
|
|
szNext = strcpyExW(szNext, L", ");
|
|
}
|
|
|
|
BSTR bstrT;
|
|
if ((bstrT = SysAllocString(szReturn)) == NULL) {
|
|
ExceptionId(IID_IStringList, IDE_REQUEST, IDE_OOM);
|
|
return E_FAIL;
|
|
}
|
|
|
|
V_VT(pvarOut) = VT_BSTR;
|
|
V_BSTR(pvarOut) = bstrT;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
/*===================================================================
|
|
CStringList::get_Item
|
|
===================================================================*/
|
|
|
|
STDMETHODIMP CStringList::get_Item(VARIANT varIndex, VARIANT *pvarOut)
|
|
{
|
|
long i;
|
|
VariantInit(pvarOut);
|
|
|
|
|
|
if (V_VT(&varIndex) == VT_ERROR) {
|
|
return ConstructDefaultReturn(pvarOut);
|
|
}
|
|
|
|
// BUG 937: VBScript passes VT_VARIANT|VT_BYREF when passing variants
|
|
// Loop through while we have a VT_BYREF until we get the real variant.
|
|
//
|
|
// and changed again...
|
|
//
|
|
// BUG 1609 the prior code was only checking for VT_I4 and jscript passed in a
|
|
// VT_R8 and it failed so now we use the VariantChangeType call to solve the
|
|
// problem
|
|
|
|
VARIANT var;
|
|
VariantInit(&var);
|
|
|
|
|
|
HRESULT hr = S_OK;
|
|
if((hr = VariantChangeType(&var, &varIndex ,0,VT_I4)) != S_OK) {
|
|
ExceptionId(IID_IStringList, IDE_REQUEST, IDE_EXPECTING_INT);
|
|
return E_FAIL;
|
|
}
|
|
|
|
i = V_I4(&var);
|
|
VariantClear(&var);
|
|
|
|
// END bug 1609
|
|
|
|
if (i <= 0 || i > m_cValues) {
|
|
ExceptionId(IID_IStringList, IDE_REQUEST, IDE_BAD_ARRAY_INDEX);
|
|
return E_FAIL;
|
|
}
|
|
|
|
register CStringListElem *pElem = m_pBegin;
|
|
while (--i > 0)
|
|
pElem = pElem->QueryNext();
|
|
|
|
BSTR bstrT;
|
|
if ((bstrT = SysAllocString(pElem->QueryValue())) == NULL ) {
|
|
ExceptionId(IID_IStringList, IDE_REQUEST, IDE_OOM);
|
|
return E_FAIL;
|
|
}
|
|
|
|
V_VT(pvarOut) = VT_BSTR;
|
|
V_BSTR(pvarOut) = bstrT;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*===================================================================
|
|
CStringList::get__NewEnum
|
|
===================================================================*/
|
|
|
|
STDMETHODIMP CStringList::get__NewEnum(IUnknown **ppEnumReturn)
|
|
{
|
|
*ppEnumReturn = new CStrListIterator(this);
|
|
if (*ppEnumReturn == NULL)
|
|
{
|
|
ExceptionId(IID_IStringList, IDE_REQUEST, IDE_OOM);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
* C S t r L i s t I t e r a t o r
|
|
*/
|
|
|
|
/*===================================================================
|
|
CStrListIterator::CStrListIterator
|
|
|
|
Constructor
|
|
|
|
NOTE: CRequest is (currently) not refcounted. AddRef/Release
|
|
added to protect against future changes.
|
|
===================================================================*/
|
|
|
|
CStrListIterator::CStrListIterator(CStringList *pStrings)
|
|
{
|
|
Assert (pStrings != NULL);
|
|
|
|
m_pStringList = pStrings;
|
|
m_pCurrent = m_pStringList->m_pBegin;
|
|
m_cRefs = 1;
|
|
|
|
m_pStringList->AddRef();
|
|
}
|
|
|
|
|
|
|
|
/*===================================================================
|
|
CStrListIterator::CStrListIterator
|
|
|
|
Destructor
|
|
===================================================================*/
|
|
|
|
CStrListIterator::~CStrListIterator()
|
|
{
|
|
m_pStringList->Release();
|
|
}
|
|
|
|
|
|
|
|
/*===================================================================
|
|
CStrListIterator::QueryInterface
|
|
CStrListIterator::AddRef
|
|
CStrListIterator::Release
|
|
|
|
IUnknown members for CServVarsIterator object.
|
|
===================================================================*/
|
|
|
|
STDMETHODIMP CStrListIterator::QueryInterface(REFIID iid, void **ppvObj)
|
|
{
|
|
if (iid == IID_IUnknown || iid == IID_IEnumVARIANT)
|
|
{
|
|
AddRef();
|
|
*ppvObj = this;
|
|
return S_OK;
|
|
}
|
|
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CStrListIterator::AddRef()
|
|
{
|
|
return ++m_cRefs;
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CStrListIterator::Release()
|
|
{
|
|
if (--m_cRefs > 0)
|
|
return m_cRefs;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/*===================================================================
|
|
CStrListIterator::Clone
|
|
|
|
Clone this iterator (standard method)
|
|
===================================================================*/
|
|
|
|
STDMETHODIMP CStrListIterator::Clone(IEnumVARIANT **ppEnumReturn)
|
|
{
|
|
CStrListIterator *pNewIterator = new CStrListIterator(m_pStringList);
|
|
if (pNewIterator == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// new iterator should point to same location as this.
|
|
pNewIterator->m_pCurrent = m_pCurrent;
|
|
|
|
*ppEnumReturn = pNewIterator;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
/*===================================================================
|
|
CStrListIterator::Next
|
|
|
|
Get next value (standard method)
|
|
|
|
To rehash standard OLE semantics:
|
|
|
|
We get the next "cElements" from the collection and store them
|
|
in "rgVariant" which holds at least "cElements" items. On
|
|
return "*pcElementsFetched" contains the actual number of elements
|
|
stored. Returns S_FALSE if less than "cElements" were stored, S_OK
|
|
otherwise.
|
|
===================================================================*/
|
|
|
|
STDMETHODIMP CStrListIterator::Next(unsigned long cElementsRequested, VARIANT *rgVariant, unsigned long *pcElementsFetched)
|
|
{
|
|
// give a valid pointer value to 'pcElementsFetched'
|
|
//
|
|
unsigned long cElementsFetched;
|
|
if (pcElementsFetched == NULL)
|
|
pcElementsFetched = &cElementsFetched;
|
|
|
|
// Loop through the collection until either we reach the end or
|
|
// cElements becomes zero
|
|
//
|
|
unsigned long cElements = cElementsRequested;
|
|
*pcElementsFetched = 0;
|
|
|
|
while (cElements > 0 && m_pCurrent != NULL)
|
|
{
|
|
BSTR bstrT = SysAllocString(m_pCurrent->QueryValue());
|
|
if (bstrT == NULL)
|
|
return E_OUTOFMEMORY;
|
|
V_VT(rgVariant) = VT_BSTR;
|
|
V_BSTR(rgVariant) = bstrT;
|
|
|
|
++rgVariant;
|
|
--cElements;
|
|
++*pcElementsFetched;
|
|
m_pCurrent = m_pCurrent->QueryNext();
|
|
}
|
|
|
|
// initialize the remaining variants
|
|
//
|
|
while (cElements-- > 0)
|
|
VariantInit(rgVariant++);
|
|
|
|
return (*pcElementsFetched == cElementsRequested)? S_OK : S_FALSE;
|
|
}
|
|
|
|
|
|
|
|
/*===================================================================
|
|
CStrListIterator::Skip
|
|
|
|
Skip items (standard method)
|
|
|
|
To rehash standard OLE semantics:
|
|
|
|
We skip over the next "cElements" from the collection.
|
|
Returns S_FALSE if less than "cElements" were skipped, S_OK
|
|
otherwise.
|
|
===================================================================*/
|
|
|
|
STDMETHODIMP CStrListIterator::Skip(unsigned long cElements)
|
|
{
|
|
/* Loop through the collection until either we reach the end or
|
|
* cElements becomes zero
|
|
*/
|
|
while (cElements > 0 && m_pCurrent != NULL)
|
|
{
|
|
--cElements;
|
|
m_pCurrent = m_pCurrent->QueryNext();
|
|
}
|
|
|
|
return (cElements == 0)? S_OK : S_FALSE;
|
|
}
|
|
|
|
|
|
|
|
/*===================================================================
|
|
CStrListIterator::Reset
|
|
|
|
Reset the iterator (standard method)
|
|
===================================================================*/
|
|
|
|
STDMETHODIMP CStrListIterator::Reset()
|
|
{
|
|
m_pCurrent = m_pStringList->m_pBegin;
|
|
return S_OK;
|
|
}
|