//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1997.
//
//  File:       N C C O M . H
//
//  Contents:   Common routines for dealing with COM.
//
//  Notes:
//
//  Author:     shaunco   25 Jan 1998
//
//----------------------------------------------------------------------------

#pragma once
#ifndef _NCCOM_H_
#define _NCCOM_H_

#include "ncdefine.h"   // for NOTHROW
#include "ncstlstr.h"

HRESULT
HrCoTaskMemAlloc (
    ULONG   cb,
    VOID**  ppv);

HRESULT
HrCoTaskMemAllocAndDupSzLen (
    IN PCWSTR pszwSrc,
    IN ULONG cchSrc,
    OUT PWSTR* ppszwDst);

HRESULT
HrCoTaskMemAllocAndDupSz (
    IN PCWSTR pszwSrc,
    OUT PWSTR* ppszwDst);

VOID
NcSetProxyBlanket (
    IN IUnknown* pUnk);

//+---------------------------------------------------------------------------
//
//  Function:   ReleaseIUnknownArray
//
//  Purpose:    Releases an array of IUnknown pointers.
//
//  Arguments:
//      cpunk [in]  count of pointers to release
//      apunk [in]  array of pointers to release
//
//  Returns:    Nothing
//
//  Author:     shaunco   23 Mar 1997
//
//  Notes:      Any of the pointers in the array can be NULL.
//
inline
NOTHROW
VOID
ReleaseIUnknownArray (
    ULONG       cpunk,
    IUnknown**  apunk)
{
    AssertH (apunk);

    while (cpunk--)
    {
        ReleaseObj (*apunk);
        apunk++;
    }
}

//------------------------------------------------------------------------
// CIEnumIter - template iterator for IEnumIUnknown
//
//  Tenum is of type IEnumXXX (the enumeration interface)
//  Telt is of type XXX (the type of the element being enumerated)
//
//  HrNext(Telt* pelt) retreives next interface pointer and returns S_OK
//  if it is non-null.  S_FALSE is returned if *pelt is null (at end of list).
//  An error code will be returned for other failures (and *pelt will be
//  null of course.)
//
template <class Tenum, class Telt>
class CIEnumIter
{
public:
    NOTHROW CIEnumIter (Tenum* penum);
    NOTHROW ~CIEnumIter () { ReleaseRemainingBatch (); }

    NOTHROW HRESULT HrNext(Telt* pelt);
    NOTHROW VOID    SetEnumerator(Tenum* penum)
                { AssertSzH(!m_penum, "Enumerator already set.");
                  m_penum = penum;
                  AssertSzH(m_penum, "Can't use a null enumerator."); }

protected:
    NOTHROW VOID ReleaseRemainingBatch ();

    Tenum*  m_penum;        // pointer to the enumerator.  not addref'd.
    Telt*   m_aelt;         // array of enumerated types.
    Telt*   m_peltNext;     // pointer to next type to be returned.
    ULONG   m_celtFetched;  // number of elements fetched.
    HRESULT m_hrLast;       // last error
};


//------------------------------------------------------------------------
// CIEnumIter - template iterator for IEnumXXX
//
template <class Tenum, class Telt>
inline CIEnumIter<Tenum, Telt>::CIEnumIter(Tenum* penum)
{
    m_penum         = penum;
    m_aelt          = NULL;
    m_peltNext      = NULL;
    m_celtFetched   = NULL;
    m_hrLast        = S_OK;
}

//+---------------------------------------------------------------------------
//
//  Member:     CIEnumIter<Tenum, Telt>::HrNext
//
//  Purpose:    Returns the next item in the enumeration.
//
//  Arguments:
//      pelt [out]  Pointer to the returned elemnt.  Null if not available.
//
//  Returns:    S_OK if *pelt is valid.  S_FALSE if it is NULL.  Error
//              otherwise.
//
//  Author:     shaunco   24 Mar 1997
//
//  Notes:
//
template <class Tenum, class Telt>
inline NOTHROW HRESULT CIEnumIter<Tenum, Telt>::HrNext(Telt* pelt)
{
    AssertH(pelt);

    const ULONG c_celtBatch = 256;

    // If we failed for any reason before, return that failure.
    //
    if (FAILED(m_hrLast))
    {
        *pelt = NULL;
        goto error;
    }

    AssertSzH(m_penum, "m_penum is null.  Did you forget to call SetEnumerator()?");
    AssertSzH(c_celtBatch, "c_celtBatch can't be zero.");

    // If we already have the next interface pointer, and we're
    // not at the end of the batch, return it and advance.
    // This if should be caught most of the time.
    //
    if (m_peltNext && (m_peltNext < m_aelt + m_celtFetched))
    {
        *pelt = *m_peltNext;
        m_peltNext++;
    }

    // Otherwise, if we don't have the next interface pointer (first time),
    // or we're at the end of the batch, get the next batch and return
    // the first pointer in it.
    // This if should be caught the first time through.
    //
    else if (!m_peltNext || (m_celtFetched == c_celtBatch))
    {
        // Indicate that m_peltNext is invalid.
        //
        m_peltNext = NULL;

        // Free the old block of pointers
        MemFree(m_aelt);

        // Allocate the next block of pointers
        m_aelt = reinterpret_cast<Telt *>(MemAlloc(c_celtBatch * sizeof(Telt *)));
        if (!m_aelt)
        {
            *pelt = NULL;
            m_hrLast = E_OUTOFMEMORY;
            goto error;
        }

        AssertH (m_aelt);

        // Get the next batch.
        //
        m_hrLast = m_penum->Next(c_celtBatch, m_aelt, &m_celtFetched);

        // Make sure the implementor of Next is obeying the rules.
        AssertH (FImplies((S_OK == m_hrLast), (m_celtFetched == c_celtBatch)));
        AssertH (FImplies((SUCCEEDED(m_hrLast) && (0 == m_celtFetched)), (NULL == *m_aelt)));

        // If we were successful, set the next pointer and return
        // S_OK if we returned a valid pointer or S_FALSE if we
        // returned NULL.
        //
        if (SUCCEEDED(m_hrLast))
        {
            m_peltNext = m_aelt + 1;
            if (m_celtFetched)
            {
                *pelt = *m_aelt;
                m_hrLast = S_OK;
            }
            else
            {
                *pelt = NULL;
                m_hrLast = S_FALSE;
            }
        }
        else
        {
            *pelt = NULL;
        }
    }

    // Otherwise we've completely iterated the last batch and there are
    // no more batches.
    //
    else
    {
        AssertH(m_peltNext >= m_aelt + m_celtFetched);
        AssertH(m_celtFetched != c_celtBatch);

        *pelt = NULL;
        m_hrLast = S_FALSE;
    }

error:
    AssertH(FIff(S_OK == m_hrLast, NULL != *pelt));
    AssertH(FImplies(S_FALSE == m_hrLast, NULL == *pelt));

    TraceError("CIEnumIter<Tenum, Telt>::HrNext(Telt* pelt)",
               (S_FALSE == m_hrLast) ? S_OK : m_hrLast);
    return m_hrLast;
}

template <class Tenum, class Telt>
inline NOTHROW VOID CIEnumIter<Tenum, Telt>::ReleaseRemainingBatch ()
{
    // This needs to be run if the user doesn't completely iterate the
    // batch.  Finish releasing the interface pointers and free the batch.
    //
    if (m_peltNext && m_aelt)
    {
        while (m_peltNext < m_aelt + m_celtFetched)
        {
            ReleaseObj (*m_peltNext);
            m_peltNext++;
        }

        MemFree (m_aelt);
    }

    // If this method is ever called from anywhere other than just
    // the destructor, uncomment the following lines.
    // m_peltNext = NULL;
    // m_aelt = NULL;
}


#endif // _NCCOM_H_
