
//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1993.
//
//  File:       defutil.cpp
//
//  Contents:   Implementations of utility functions for the default
//              handler and default link objects
//
//  Classes:    none
//
//  Functions:  DuLockContainer
//              DuSetClientSite
//              DuGetClientSite
//              DuCacheDelegate
//
//  History:    dd-mmm-yy Author    Comment
//              11-Jan-94 alexgo    added VDATEHEAP macros to every function
//              20-Nov-93 alexgo    32bit port
//
//--------------------------------------------------------------------------

#include <le2int.h>
#pragma SEG(defutil)

#include <olerem.h>
#include <ole2dbg.h>

ASSERTDATA
NAME_SEG(defutil)


//+-------------------------------------------------------------------------
//
//  Function:   DuLockContainer
//
//  Synopsis:   Calls IOleContainer->LockContainer from the given client site
//
//  Effects:    Unlocking the container may release the calling object.
//
//  Arguments:  [pCS]           -- the client site from which to get
//                                 the IOleContainer pointer
//              [fLockNew]      -- TRUE == lock, FALSE == unlock
//              [pfLockCur]     -- pointer to a flag with the current lock
//                                 state
//
//  Requires:
//
//  Returns:    void
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              20-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

#pragma SEG(DuLockContainer)
INTERNAL_(void) DuLockContainer(IOleClientSite FAR* pCS, BOOL fLockNew,
        BOOL FAR*pfLockCur)
{
        VDATEHEAP();

#ifdef _DEBUG
        BOOL fLocked = FALSE;   // used only for debugging so don't waste
                                // the code space in the retail version
#endif // _DEBUG

        IOleContainer FAR* pContainer;

        //the double bang turns each into a true boolean
        if (!!fLockNew == !!*pfLockCur)
        {
                // already locked as needed
                return;
        }

        // set flag to false first since unlocking container may release obj;
        // we can just set to false since it is either already false or going
        // to become false (don't set to true until we know the lock completed).
        *pfLockCur = FALSE;

        if (pCS == NULL)
        {
                pContainer = NULL;
        }
        else
        {
                HRESULT hresult = pCS->GetContainer(&pContainer);

		// Excel 5 can return S_FALSE, pContainer == NULL
		// so we can't use AssertOutPtrIface here since it
		// expects all successful returns to provide a
		// valid interface

                if (hresult != NOERROR)
                {
                        pContainer = NULL; // just in case
                }
        }
        if (pContainer != NULL)
        {
                // we assume that LockContainer will succeed first and
                // and set the locked flag that was passed into us.  This
                // way, if LockContainer succeeeds, we won't access memory
                // that could have potentially been blown away.
                // If it *fails*, then we handle reset the flag (as our
                // memory would not have been free'd)

                BOOL fLockOld = *pfLockCur;
                *pfLockCur = fLockNew;

                if( pContainer->LockContainer(fLockNew) != NOERROR )
                {
                        //failure case, we were not deleted
                        *pfLockCur = fLockOld;
                        //fLocked is FALSE
                }
#ifdef _DEBUG
                else
                {
                        fLocked = TRUE;
                }
#endif // _DEBUG

                pContainer->Release();
        }

#ifdef _DEBUG
        if (!fLocked)
        {
                Puts(
                "WARNING: couldn't lock/unlock container for clientsite:\n");
                DbgDumpObject(pCS,0);
        }
#endif // _DEBUG
}


//+-------------------------------------------------------------------------
//
//  Function:   DuSetClientSite
//
//  Synopsis:   Called by the default handler and deflink SetClientSite
//              implemenations; Releases the old client site (and unlocks
//              its container), stores the client site (locking its
//              container).
//
//  Effects:
//
//  Arguments:  [fRunning]      -- whether or not the delegate is running
//              [pCSNew]        -- the new client site
//              [ppCSCur]       -- a pointer to the original client site
//                                 pointer.  [*ppCSCur] will be reset
//                                 to the new client site pointer.
//              [pfLockCur]     -- pointer to the fLocked flag, used by
//                                 DuLockContainer.
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              22-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

#pragma SEG(DuSetClientSite)
INTERNAL DuSetClientSite(BOOL fRunning, IOleClientSite FAR* pCSNew,
        IOleClientSite FAR* FAR* ppCSCur, BOOL FAR*pfLockCur)
{
        VDATEHEAP();

        if (pCSNew)
        {
                VDATEIFACE( pCSNew );
        }

        IOleClientSite FAR* pCSCur = *ppCSCur;

        if (pCSCur != NULL)
        {
                // Unlock the old container
                if (fRunning)
                {
                        DuLockContainer(pCSCur, FALSE, pfLockCur);
                }

                pCSCur->Release();
        }

        // we've decided to keep the pointer that's been passed to us. So we
        // must AddRef()
        if ((pCSCur = pCSNew) != NULL)
        {
                pCSNew->AddRef();

                // Lock the newcontainer
                if (fRunning)
                {
                        DuLockContainer(pCSNew, TRUE, pfLockCur);
                }
        }

        *ppCSCur = pCSCur;

        return NOERROR;
}

//+-------------------------------------------------------------------------
//
//  Function:   DuCacheDelegate
//
//  Synopsis:   Retrieves the requested interface from [pUnk].  If [fAgg] is
//              true, we release the pointer (so ref counts to not get
//              obfuscated ;-)
//
//  Effects:
//
//  Arguments:  [ppUnk]  -- the object to QueryInterface on
//              [iid]   -- the requested interface
//              [ppv]   -- where to put the pointer to the interface
//              [pUnkOuter]     -- controlling unknown, if non-NULL indicates
//                                 aggregation and release is called on it
//
//
//
//  Requires:
//
//  Returns:    void *, the requested interface pointer
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//		29-Jun-94 alexgo    better handle re-entrancy
//              20-Jun-94 alexgo    updated to May '94 aggregation rules
//              22-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

#pragma SEG(DuCacheDelegate)
INTERNAL_(void FAR*) DuCacheDelegate(IUnknown FAR** ppUnk,
        REFIID iid, LPVOID FAR* ppv, IUnknown *pUnkOuter)
{
        VDATEHEAP();

        if (*ppUnk != NULL && *ppv == NULL)
        {
                if ((*ppUnk)->QueryInterface (iid, ppv) == NOERROR)
                {
			// the QI may actually be an outgoing call so it
			// is possible that ppUnk was released and set to
			// NULL during our call.  To make the default link
			// and handler simpler, we check for that case and
			// release any pointer we may have obtained
			// from the QI

			if( *ppUnk == NULL )
			{
				LEDebugOut((DEB_WARN, "WARNING: Delegate "
					"released during QI, should be OK\n"));
				if( *ppv )
				{
					// this should never be a final
					// release on the default handler
					// since we are calling it from
					// within a method in the default
					// link object. Therefore,
					// we do not need to guard this
					// release
					//
					// in the case of the link object,
					// this may be the final release
					// on the proxies, but since they are
					// not aggregated into the link
					// object, that's OK.

					(*(IUnknown **)ppv)->Release();
					*ppv = NULL;
				}
			}
                        if( pUnkOuter && *ppv)
                        {
                                // we will keep the pointer but we don't want
                                // to bump the ref count of the aggregate,
                                // so we gotta do Release() on the controlling
                                // unknown.
                                pUnkOuter->Release();
                        }
                }
        }

        return *ppv;
}


