> Thanks for your help.
>
[quoted text clipped - 6 lines]
> compile time (no errors). The problem we get happens at run-time. Would
> this situation be a symptom of the IDL being improper?
Hi Robert,
Your assessment makes good sense. But in looking through the code, I don't
seem to see the issue. Following is a step through of the code.
On a related issue (the reason we're using GetEnumerator). In unmanaged
C++, we have access to the GetItem( index ) method and can get members of the
collection by index. The GetItem method doesn't seem to be visible in C# as
a method, but the GetEnumerator is visible...so we are trying to use
GetEnumerator to index through the collection's objects. Do you know why
GetItem is not available in .NET, and GetEnumerator is (and GetItem is
available in unmanaged C++ and GetEnumerator is not available)?
Thanks!
Andy
------------------------------------------------------------------------------
------------------------------------------------------------------------------
------------------------------------------------------------------------------
Enumerator definition in header file:
STDMETHOD_(IUnknown *, get__NewEnum)(THIS) PURE;
------------------------------------------------------------------------------
------------------------------------------------------------------------------
------------------------------------------------------------------------------
Implementation of Enumerator:
/*
* CNodes::get_NewEnum
*
* Purpose:
* Returns an enumerator (IEnumVARIANT) for the items curently in the
collection.
* The NewEnum property is restricted and so is invisible to users of an
* automation controller's scripting language. Automation controllers that
support
* a 'For Each' statement to iterate through the elements of a collection
will use
* the enumerator returned by NewEnum. The enumerator creates a snapshot of
the
* the current state of the collection.
*
*/
STDMETHODIMP_(LPUNKNOWN)
CNodes::get__NewEnum(void)
{
return m_psa->GetNewEnum( &m_except );
}
------------------------------------------------------------------------------
------------------------------------------------------------------------------
------------------------------------------------------------------------------
Implementation of m_psa->GetNewEnum():
LPDISPATCH XSafeArray::GetNewEnum( XExcept *px ) const
{
PCEnumVariant penum = NULL;;
// LPUNKNOWN punkEnumVariant = NULL;
LPDISPATCH punkEnumVariant = NULL;
HRESULT hr;
// Create new enumerator for items currently in collection and QI for
IUnknown
hr = CEnumVariant::Create( m_psa, m_cElements, &penum);
if (FAILED(hr))
goto error;
hr = penum->QueryInterface(IID_IUnknown, (VOID FAR*
FAR*)&punkEnumVariant);
if (FAILED(hr))
{
delete penum;
goto error;
}
return punkEnumVariant;
error:
if (punkEnumVariant)
punkEnumVariant->Release();
// Signal CLinks::Invoke to raise exception.
if ( px )
{
px->excepinfo.scode = GetScode(hr);
px->excepinfo.bstrDescription = xSysAllocString("Enumerator creation
failed.");
px->bRaiseException = TRUE;
}
return NULL;
}
------------------------------------------------------------------------------
------------------------------------------------------------------------------
------------------------------------------------------------------------------
Implementation of CEnumVariant():
/*
* CEnumVariant::Create
*
* Purpose:
* Creates an instance of the IEnumVARIANT enumerator object and
initializes it.
*
* Parameters:
* psa Safe array containing items to be enumerated.
* cElements Number of items to be enumerated.
* ppenumvariant Returns enumerator object.
*
* Return Value:
* HRESULT
*
*/
HRESULT
CEnumVariant::Create(SAFEARRAY FAR* psa, ULONG cElements, PCEnumVariant FAR*
ppenumvariant)
{
HRESULT hr;
long lLBound;
*ppenumvariant = NULL;
PCEnumVariant penumvariant = new CEnumVariant();
if (penumvariant == NULL)
goto error;
penumvariant->m_cRef = 0;
// Copy elements into safe array that is used in enumerator
implemenatation and
// initialize state of enumerator.
hr = SafeArrayGetLBound(psa, 1, &lLBound);
if (FAILED(hr))
goto error;
penumvariant->m_cElements = cElements;
penumvariant->m_lLBound = lLBound;
penumvariant->m_lCurrent = lLBound;
hr = SafeArrayCopy(psa, &penumvariant->m_psa);
if (FAILED(hr))
goto error;
*ppenumvariant = penumvariant;
return NOERROR;
error:
if (penumvariant == NULL)
return ResultFromScode(E_OUTOFMEMORY);
if (penumvariant->m_psa)
SafeArrayDestroy(penumvariant->m_psa);
penumvariant->m_psa = NULL;
delete penumvariant;
return hr;
}
/*
* CEnumVariant::CEnumVariant
*
* Purpose:
* Constructor for CEnumVariant object. Initializes members to NULL.
*
*/
CEnumVariant::CEnumVariant()
{
m_psa = NULL;
ObjectCreated( _CENUMVARIANT );
}
/*
* CEnumVariant::~CEnumVariant
*
* Purpose:
* Destructor for CEnumVariant object.
*
*/
CEnumVariant::~CEnumVariant()
{
if (m_psa) SafeArrayDestroy(m_psa);
ObjectDeleted( _CENUMVARIANT );
}
/*
* CEnumVariant::QueryInterface, AddRef, Release
*
* Purpose:
* Implements IUnknown::QueryInterface, AddRef, Release
*
*/
STDMETHODIMP
CEnumVariant::QueryInterface(REFIID iid, void FAR* FAR* ppv)
{
*ppv = NULL;
if (iid == IID_IUnknown || iid == IID_IEnumVARIANT)
*ppv = this;
else return ResultFromScode(E_NOINTERFACE);
AddRef();
return NOERROR;
}
STDMETHODIMP_(ULONG)
CEnumVariant::AddRef(void)
{
return ++m_cRef; // AddRef Application Object if enumerator will
outlive application object
}
STDMETHODIMP_(ULONG)
CEnumVariant::Release(void)
{
if(--m_cRef == 0)
{
delete this;
return 0;
}
return m_cRef;
}
/*
* CEnumVariant::Next
*
* Purpose:
* Retrieves the next cElements elements. Implements IEnumVARIANT::Next.
*
*/
STDMETHODIMP
CEnumVariant::Next(ULONG cElements, VARIANT FAR* pvar, ULONG FAR*
pcElementFetched)
{
HRESULT hr;
ULONG l;
long l1;
ULONG l2;
if (pcElementFetched != NULL)
*pcElementFetched = 0;
for (l=0; l<cElements; l++)
VariantInit(&pvar[l]);
if (m_lCurrent >= (m_lLBound+m_cElements) )
{
// reached the end
return ResultFromScode(S_FALSE);
}
else
{
// Retrieve the next cElements elements.
for (l1=m_lCurrent, l2=0;
l1<(long)(m_lLBound+m_cElements) && l2<cElements;
l1++, l2++)
{
hr = SafeArrayGetElement(m_psa, &l1, &pvar[l2]);
if (FAILED(hr))
goto error;
}
// Set count of elements retrieved
if (pcElementFetched != NULL)
*pcElementFetched = l2;
m_lCurrent = l1;
// jgm 12/15/99
// THIS WAS TAKEN (by dan the consultant) FROM ONE OF
// THE OLE SAMPLES - not sure what the CORRECT behavior is.
//
// if user wants to get groups of 10 and there are 12
// in the collection - return TRUE first time then FALSE?
return (l2 < cElements) ? ResultFromScode(S_FALSE) : NOERROR;
}
error:
for (l=0; l<cElements; l++)
VariantClear(&pvar[l]);
return hr;
}
/*
* CEnumVariant::Skip
*
* Purpose:
* Skips the next cElements elements. Implements IEnumVARIANT::Skip.
*
*/
STDMETHODIMP
CEnumVariant::Skip(ULONG cElements)
{
m_lCurrent += cElements;
if (m_lCurrent > (long)(m_lLBound+m_cElements))
{
m_lCurrent = m_lLBound+m_cElements;
return ResultFromScode(S_FALSE);
}
else return NOERROR;
}
/*
* CEnumVariant::Reset
*
* Purpose:
* Resets the current element in the enumerator to the beginning.
Implements IEnumVARIANT::Reset.
*
*/
STDMETHODIMP
CEnumVariant::Reset()
{
m_lCurrent = m_lLBound;
return NOERROR;
}
/*
* CEnumVariant::Clone
*
* Purpose:
* Creates a copy of the current enumeration state. Implements
IEnumVARIANT::Clone.
*
*/
STDMETHODIMP
CEnumVariant::Clone(IEnumVARIANT FAR* FAR* ppenum)
{
PCEnumVariant penum = NULL;
HRESULT hr;
*ppenum = NULL;
hr = CEnumVariant::Create(m_psa, m_cElements, &penum);
if (FAILED(hr))
goto error;
penum->AddRef();
penum->m_lCurrent = m_lCurrent;
*ppenum = penum;
return NOERROR;
error:
if (penum)
penum->Release();
return hr;
}
#pragma warn +par
------------------------------------------------------------------------------
------------------------------------------------------------------------------
------------------------------------------------------------------------------
Robert Jordan - 12 Oct 2004 22:55 GMT
Hi,
> Your assessment makes good sense. But in looking through the code, I don't
> seem to see the issue. Following is a step through of the code.
The code looks ok, but I'm not sure whether CNodes::get__NewEnum()
has to return an IUnknown (like implemented) or if it has
to return an IEnumVARIANT pointer.
> On a related issue (the reason we're using GetEnumerator). In unmanaged
> C++, we have access to the GetItem( index ) method and can get members of the
[quoted text clipped - 3 lines]
> GetItem is not available in .NET, and GetEnumerator is (and GetItem is
> available in unmanaged C++ and GetEnumerator is not available)?
GetItems becomes the C#-indexer. You can access the elements of
your example with oWrkNodes[int].
bye
Rob