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.
353 lines
11 KiB
353 lines
11 KiB
/*-----------------------------------------------------------------------------
|
|
*
|
|
* File: collect.cpp
|
|
* Author: Samuel Clement (samclem)
|
|
* Date: Fri Aug 13 14:40:17 1999
|
|
* Description:
|
|
* Implementation of the CCollection object helper class.
|
|
*
|
|
* History:
|
|
* 13 Aug 1999: Created.
|
|
*----------------------------------------------------------------------------*/
|
|
|
|
#include "stdafx.h"
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CCollection::CCollection
|
|
*
|
|
* Create a new CCollection object. this initializes the collection to be
|
|
* an empty collection, a collection with no elements.
|
|
*---------------------------------------------------------------------------*/
|
|
CCollection::CCollection()
|
|
: m_lLength( 0 ), m_rgpDispatch( NULL ), m_lCursor( 0 )
|
|
{
|
|
TRACK_OBJECT( "CCollection" );
|
|
}
|
|
|
|
STDMETHODIMP_(void)
|
|
CCollection::FinalRelease()
|
|
{
|
|
// free our dispatch array, release our referance
|
|
// back to our owner
|
|
FreeDispatchArray();
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CCollection::FreeDispatchArray()
|
|
*
|
|
* This handles freeing the array of IDispatch pointers we have. this
|
|
* will free all the pointers, then delete the array.
|
|
*---------------------------------------------------------------------------*/
|
|
void CCollection::FreeDispatchArray()
|
|
{
|
|
// step 1, call release on all the pointers
|
|
for ( unsigned long i = 0; i < m_lLength; i++ )
|
|
{
|
|
m_rgpDispatch[i]->Release();
|
|
}
|
|
|
|
// step 2, free the array
|
|
{
|
|
CoTaskMemFree( m_rgpDispatch );
|
|
m_rgpDispatch = NULL;
|
|
m_lLength = 0;
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CCollection:SetDispatchArray
|
|
*
|
|
* This handles setting the dispatch array for this collection. You cannot
|
|
* call this unless you don't have an array yet. The array must be allocated
|
|
* with CoTaskMemAlloc.
|
|
*
|
|
* rgpDispatch: the array of IDispatch pointers
|
|
* lSize: the number of elements within the array.
|
|
*---------------------------------------------------------------------------*/
|
|
bool CCollection::SetDispatchArray( IDispatch** rgpDispatch, unsigned long lSize )
|
|
{
|
|
Assert( m_rgpDispatch == NULL );
|
|
|
|
if ( NULL == rgpDispatch )
|
|
{
|
|
TraceTag((tagError, "Invalid argument passed to SetDispatchArray"));
|
|
return false;
|
|
}
|
|
|
|
// assign the pointers. It is assumed that the caller has
|
|
// already addref'd the pointers
|
|
m_rgpDispatch = rgpDispatch;
|
|
m_lLength = lSize;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* Collection::AllocateDispatchArray
|
|
*
|
|
* This handles the allocation of the Dispatch array. This will allocate
|
|
* an array with lSize elements and initialize it to NULL, This cannot be
|
|
* called after the array has been set.
|
|
*
|
|
* lSize: the size of the array to allocate.
|
|
*---------------------------------------------------------------------------*/
|
|
HRESULT CCollection::AllocateDispatchArray( unsigned long lSize )
|
|
{
|
|
Assert( m_rgpDispatch == NULL );
|
|
|
|
// if the array is zero in length we are done.
|
|
if ( lSize == 0 )
|
|
return S_OK;
|
|
|
|
ULONG cb = sizeof( IDispatch* ) * lSize;
|
|
m_rgpDispatch = static_cast<IDispatch**>(CoTaskMemAlloc( cb ));
|
|
if ( !m_rgpDispatch )
|
|
return E_OUTOFMEMORY;
|
|
|
|
// clear the memory, set the length
|
|
ZeroMemory( m_rgpDispatch, cb );
|
|
m_lLength = lSize;
|
|
return S_OK;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CCollection::CopyFrom
|
|
*
|
|
* This handles creating this collection from an existing collection, this
|
|
* copies the members from pCollection, and then sets punkToRelease so that
|
|
* the owner will live.
|
|
*
|
|
* pCollection: the collection to copy from
|
|
*---------------------------------------------------------------------------*/
|
|
HRESULT CCollection::CopyFrom( CCollection* pCollection )
|
|
{
|
|
Assert( m_rgpDispatch == NULL );
|
|
Assert( pCollection != NULL );
|
|
|
|
HRESULT hr;
|
|
// Allocate the array
|
|
hr = AllocateDispatchArray( pCollection->m_lLength );
|
|
if ( FAILED(hr) ) {
|
|
return hr;
|
|
}
|
|
|
|
// copy the fields
|
|
m_lLength = pCollection->m_lLength;
|
|
m_lCursor = pCollection->m_lCursor;
|
|
|
|
// Copy and AddRef the elements in the collection
|
|
for ( int i = 0; i < m_lLength; i++ ) {
|
|
m_rgpDispatch[i] = pCollection->m_rgpDispatch[i];
|
|
m_rgpDispatch[i]->AddRef();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CCollection::get_Count() [ICollection]
|
|
*
|
|
* This returns the length of the collection.
|
|
*
|
|
* plLength: our param, to recieve the length of the collection
|
|
*---------------------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CCollection::get_Count( /*out*/ long* plLength )
|
|
{
|
|
if ( NULL == plLength )
|
|
return E_POINTER;
|
|
|
|
*plLength = m_lLength;
|
|
return S_OK;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CCollection::get_Length() [ICollection]
|
|
*
|
|
* This returns the length of the collection.
|
|
*
|
|
* plLength: our param, to recieve the length of the collection
|
|
*---------------------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CCollection::get_Length( /*out*/ unsigned long* plLength )
|
|
{
|
|
if ( NULL == plLength )
|
|
return E_POINTER;
|
|
|
|
*plLength = m_lLength;
|
|
return S_OK;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CCollection::get_Item() [ICollection]
|
|
*
|
|
* This returns the desired item from our dispatch array. If the index
|
|
* is invalid then will put NULl into the out param.
|
|
*
|
|
* lItem: the item that we want to retrieve
|
|
* ppDispItem: Out param to recieve the item's IDispatch
|
|
*---------------------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CCollection::get_Item( long Index, /*out*/ IDispatch** ppDispItem )
|
|
{
|
|
if ( NULL == ppDispItem )
|
|
return E_POINTER;
|
|
|
|
// initialize the out param
|
|
*ppDispItem = NULL;
|
|
if ( Index >= m_lLength || Index < 0)
|
|
{
|
|
TraceTag((tagError, "CCollection: access item %ld, only %ld items", Index, m_lLength ));
|
|
return S_OK;
|
|
}
|
|
|
|
*ppDispItem = m_rgpDispatch[Index];
|
|
Assert( *ppDispItem );
|
|
(*ppDispItem)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CCollection::get_NewEnum() [ICollection]
|
|
*
|
|
* This create a new enumeration which is a copy of this one. this creates
|
|
* an exact copy of this enumeration and returns it.
|
|
*---------------------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CCollection::get__NewEnum( /*out*/ IUnknown** ppEnum )
|
|
{
|
|
HRESULT hr;
|
|
CComObject<CCollection>* pCollection = NULL;
|
|
|
|
if ( NULL == ppEnum )
|
|
return E_POINTER;
|
|
|
|
// initialize the out param
|
|
*ppEnum = NULL;
|
|
|
|
// attempt to create a new collection object
|
|
hr = THR( CComObject<CCollection>::CreateInstance( &pCollection ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
|
|
// attempt to copy this collection
|
|
hr = THR( pCollection->CopyFrom( this ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
|
|
// fill the our param
|
|
hr = THR( pCollection->QueryInterface( IID_IUnknown,
|
|
reinterpret_cast<void**>(ppEnum) ) );
|
|
|
|
Cleanup:
|
|
if ( FAILED( hr ) )
|
|
delete pCollection;
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CCollection::Next() [IEnumVARIANT]
|
|
*
|
|
* Copies celt elements int the rgvar array. returns the number of elements
|
|
* retrieved.
|
|
*
|
|
* celt: the number of elements the caller wants
|
|
* rgvar: a place to put these elements
|
|
* pceltFetched: How many elements we actually we able to get.
|
|
*---------------------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CCollection::Next( unsigned long celt, VARIANT* rgvar, unsigned long* pceltFetched )
|
|
{
|
|
unsigned long celtFetched = 0;
|
|
|
|
// verify the argments
|
|
if ( NULL == rgvar && celt )
|
|
return E_POINTER;
|
|
|
|
// figure out how many we can return
|
|
celtFetched = celt;
|
|
if ( m_lCursor + celtFetched >= m_lLength )
|
|
celtFetched = m_lLength - m_lCursor;
|
|
|
|
// Init, and copy the results
|
|
for ( unsigned long i = 0; i < celt; i++ )
|
|
VariantInit( &rgvar[i] );
|
|
|
|
for ( i = 0; i < celtFetched; i++ )
|
|
{
|
|
rgvar[i].vt = VT_DISPATCH;
|
|
rgvar[i].pdispVal = m_rgpDispatch[m_lCursor+i];
|
|
rgvar[i].pdispVal->AddRef();
|
|
}
|
|
|
|
// Return the number of elements fetched, if required
|
|
if ( pceltFetched ) {
|
|
*pceltFetched = celtFetched;
|
|
}
|
|
m_lCursor += celtFetched;
|
|
return( celt == celtFetched ? S_OK : S_FALSE );
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CCollection::Skip() [IEnumVARIANT]
|
|
*
|
|
* Skips celt elements in the array.
|
|
*
|
|
* celt: the number of elements that we want to skip.
|
|
*---------------------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CCollection::Skip( unsigned long celt )
|
|
{
|
|
m_lCursor += celt;
|
|
if ( m_lCursor >= m_lLength )
|
|
{
|
|
m_lCursor = m_lLength;
|
|
return S_FALSE; // no more left
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CCollection::Reset() [IEnumVARIANT]
|
|
*
|
|
* Resets the cursor to the start of the collection
|
|
*---------------------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CCollection::Reset()
|
|
{
|
|
// simply point to element 0, I don't know how this can fail.
|
|
m_lCursor = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CCollection::Clone() [IEnumVARIANT]
|
|
*
|
|
* Copies this collection including its current position
|
|
*
|
|
* ppEnum: Out, recieves a pointer to the new enumeration
|
|
*---------------------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CCollection::Clone( /*out*/ IEnumVARIANT** ppEnum )
|
|
{
|
|
// delegate the work to get_NewEnum()
|
|
IUnknown* pUnk = NULL;
|
|
HRESULT hr;
|
|
|
|
if ( NULL == ppEnum )
|
|
return E_POINTER;
|
|
*ppEnum = NULL;
|
|
|
|
hr = THR( get__NewEnum( &pUnk ) );
|
|
if ( FAILED( hr ) )
|
|
return hr;
|
|
|
|
hr = THR( pUnk->QueryInterface( IID_IEnumVARIANT,
|
|
reinterpret_cast<void**>(ppEnum) ) );
|
|
|
|
// release the temporary pointer
|
|
pUnk->Release();
|
|
return hr;
|
|
}
|