//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 2000.
//
//  File:       hpalette.cxx
//
//  Contents:   Support for Windows/OLE data types for oleprx32.dll.
//              Used to be transmit_as routines, now user_marshal routines.
//
//              This file contains support for STGMEDIUM, FLAG_STGMEDIUM, and 
//               ASYNC_STGMEDIUM.
//
//  Functions:  
//              STGMEDIUM_UserSize
//              STGMEDIUM_UserMarshal
//              STGMEDIUM_UserUnmarshal
//              STGMEDIUM_UserFree
//              STGMEDIUM_UserSize64
//              STGMEDIUM_UserMarshal64
//              STGMEDIUM_UserUnmarshal64
//              STGMEDIUM_UserFree64
//              FLAG_STGMEDIUM_UserSize
//              FLAG_STGMEDIUM_UserMarshal
//              FLAG_STGMEDIUM_UserUnmarshal
//              FLAG_STGMEDIUM_UserFree
//              FLAG_STGMEDIUM_UserSize64
//              FLAG_STGMEDIUM_UserMarshal64
//              FLAG_STGMEDIUM_UserUnmarshal64
//              FLAG_STGMEDIUM_UserFree64
//              ASYNC_STGMEDIUM_UserSize
//              ASYNC_STGMEDIUM_UserMarshal
//              ASYNC_STGMEDIUM_UserUnmarshal
//              ASYNC_STGMEDIUM_UserFree
//              ASYNC_STGMEDIUM_UserSize64
//              ASYNC_STGMEDIUM_UserMarshal64
//              ASYNC_STGMEDIUM_UserUnmarshal64
//              ASYNC_STGMEDIUM_UserFree64
//
//  History:    13-Dec-00   JohnDoty    Migrated from transmit.cxx,
//                                      created NDR64 functions
//
//--------------------------------------------------------------------------
#include "stdrpc.hxx"
#pragma hdrstop

#include <oleauto.h>
#include <objbase.h>
#include "transmit.hxx"
#include <rpcwdt.h>
#include <storext.h>
#include "widewrap.h"
#include <valid.h>
#include <obase.h>
#include <stream.hxx>

#include "carefulreader.hxx"

// PROTOTYPES FOR OTHER USERMARSHAL ROUTINES, TO HELP US!
EXTERN_C unsigned long __stdcall __RPC_USER WdtpInterfacePointer_UserSize (USER_MARSHAL_CB * pContext, unsigned long Flags, unsigned long Offset, IUnknown *pIf, const IID &IId );
EXTERN_C unsigned char __RPC_FAR * __RPC_USER __stdcall WdtpInterfacePointer_UserMarshal (USER_MARSHAL_CB * pContext, unsigned long Flags, unsigned char *pBuffer, IUnknown *pIf, const IID &IId );
EXTERN_C unsigned long __stdcall __RPC_USER WdtpInterfacePointer_UserSize64 (USER_MARSHAL_CB * pContext, unsigned long Flags, unsigned long Offset, IUnknown *pIf, const IID &IId );
EXTERN_C unsigned char __RPC_FAR * __RPC_USER __stdcall WdtpInterfacePointer_UserMarshal64 (USER_MARSHAL_CB * pContext, unsigned long Flags, unsigned char *pBuffer, IUnknown *pIf, const IID &IId );
unsigned char __RPC_FAR * __RPC_USER WdtpInterfacePointer_UserUnmarshalWorker (USER_MARSHAL_CB * pContext,  unsigned char * pBuffer, IUnknown ** ppIf, const IID &IId, ULONG_PTR BufferSize, BOOL fNDR64 );


unsigned char __RPC_FAR * __RPC_USER HMETAFILEPICT_UserUnmarshalWorker (unsigned long * pFlags, unsigned char * pBuffer, HMETAFILEPICT * pHMetaFilePict, ULONG_PTR BufferSize);
unsigned char __RPC_FAR * __RPC_USER HMETAFILEPICT_UserUnmarshalWorker64 (unsigned long * pFlags, unsigned char * pBuffer, HMETAFILEPICT * pHMetaFilePict, ULONG_PTR BufferSize);

unsigned char __RPC_FAR * __RPC_USER HENHMETAFILE_UserUnmarshalWorker (unsigned long * pFlags, unsigned char * pBuffer, HENHMETAFILE * pHMetaFilePict, ULONG_PTR BufferSize);
unsigned char __RPC_FAR * __RPC_USER HENHMETAFILE_UserUnmarshalWorker64 (unsigned long * pFlags, unsigned char * pBuffer, HENHMETAFILE * pHMetaFilePict, ULONG_PTR BufferSize);

unsigned char __RPC_FAR * __RPC_USER HBITMAP_UserUnmarshalWorker (unsigned long * pFlags, unsigned char * pBuffer, HBITMAP * pHMetaFilePict, ULONG_PTR BufferSize);
unsigned char __RPC_FAR * __RPC_USER HBITMAP_UserUnmarshalWorker64 (unsigned long * pFlags, unsigned char * pBuffer, HBITMAP * pHMetaFilePict, ULONG_PTR BufferSize);

unsigned char __RPC_FAR * __RPC_USER HPALETTE_UserUnmarshalWorker (unsigned long * pFlags, unsigned char * pBuffer, HPALETTE * pHMetaFilePict, ULONG_PTR BufferSize);
unsigned char __RPC_FAR * __RPC_USER HPALETTE_UserUnmarshalWorker64 (unsigned long * pFlags, unsigned char * pBuffer, HPALETTE * pHMetaFilePict, ULONG_PTR BufferSize);

unsigned char __RPC_FAR * __RPC_USER WdtpGlobalUnmarshal (unsigned long * pFlags, unsigned char * pBuffer, HGLOBAL * pGlobal, BOOL fCanReallocate, ULONG_PTR BufferSize);
unsigned char __RPC_FAR * __RPC_USER WdtpGlobalUnmarshal64 (unsigned long * pFlags, unsigned char * pBuffer, HGLOBAL * pGlobal, BOOL fCanReallocate, ULONG_PTR BufferSize);


//+-------------------------------------------------------------------------
//
//  class:  CPunkForRelease
//
//  purpose:    special IUnknown for remoted STGMEDIUMs
//
//  history:    02-Mar-94   Rickhi      Created
//
//  notes:  This class is used to do the cleanup correctly when certain
//      types of storages are passed between processes or machines
//      in Nt and Chicago.
//
//      On NT, GLOBAL, GDI, and BITMAP handles cannot be passed between
//      processes, so we actually copy the whole data and create a
//      new handle in the receiving process. However, STGMEDIUMs have
//      this weird behaviour where if PunkForRelease is non-NULL then
//      the sender is responsible for cleanup, not the receiver. Since
//      we create a new handle in the receiver, we would leak handles
//      if we didnt do some special processing.  So, we do the
//      following...
//
//          During STGMEDIUM_UserUnmarshal, if there is a pUnkForRelease
//          replace it with a CPunkForRelease.  When Release is called
//          on the CPunkForRelease, do the necessary cleanup work,
//          then call the real PunkForRelease.
//
//+-------------------------------------------------------------------------

class   CPunkForRelease : public IUnknown
{
public:
    CPunkForRelease( STGMEDIUM *pStgMed, ulong fTopLayerOnly);

    //  IUnknown Methods
    STDMETHOD(QueryInterface)(REFIID riid, void **ppunk);
    STDMETHOD_(ULONG, AddRef)(void);
    STDMETHOD_(ULONG, Release)(void);

private:
    ~CPunkForRelease(void);

    ULONG       _cRefs;                 //  reference count
    ULONG       _fTopLayerMFP:1;        //  optimisation flag for Chicago
    STGMEDIUM   _stgmed;                //  storage medium
    IUnknown  * _pUnkForRelease;        //  real pUnkForRelease
};


inline CPunkForRelease::CPunkForRelease(
    STGMEDIUM * pStgMed,
    ulong       fTopLayerMFPict
    ) :
    _cRefs(1),
    _fTopLayerMFP( fTopLayerMFPict),
    _stgmed(*pStgMed)
{
    //  NOTE: we assume the caller has verified pStgMed is not NULL,
    //  and the pUnkForRelease is non-null, otherwise there is no
    //  point in constructing this object.  The tymed must also be
    //  one of the special ones.

    UserNdrAssert(pStgMed);
    UserNdrAssert(pStgMed->tymed == TYMED_HGLOBAL ||
       pStgMed->tymed == TYMED_GDI  ||
       pStgMed->tymed == TYMED_MFPICT  ||
       pStgMed->tymed == TYMED_ENHMF);

    _pUnkForRelease = pStgMed->pUnkForRelease;
}


inline CPunkForRelease::~CPunkForRelease()
{
    //  since we really have our own copies of these handles' data, just
    //  pretend like the callee is responsible for the release, and
    //  recurse into ReleaseStgMedium to do the cleanup.

    _stgmed.pUnkForRelease = NULL;

    // There is this weird optimized case of Chicago MFPICT when we have two
    // layers with a HENHMETAFILE handle inside. Top layer is an HGLOBAL.

    if ( _fTopLayerMFP )
        GlobalFree( _stgmed.hGlobal );
    else
        ReleaseStgMedium( &_stgmed );

    //  release the callers punk
    _pUnkForRelease->Release();
}

STDMETHODIMP_(ULONG) CPunkForRelease::AddRef(void)
{
    InterlockedIncrement((LONG *)&_cRefs);
    return _cRefs;
}

STDMETHODIMP_(ULONG) CPunkForRelease::Release(void)
{
    long Ref = _cRefs - 1;

    UserNdrAssert( _cRefs );

    if (InterlockedDecrement((LONG *)&_cRefs) == 0)
        {
        // null out the vtable.
        long * p = (long *)this;
        *p = 0;

        delete this;
        return 0;
        }
    else
        return Ref;
}

STDMETHODIMP CPunkForRelease::QueryInterface(REFIID riid, void **ppv)
{
    if (IsEqualIID(riid, IID_IUnknown))
    {
    *ppv = (void *)(IUnknown *) this;
    AddRef();
    return S_OK;
    }
    else
    {
    *ppv = NULL;
    return E_NOINTERFACE;
    }
}

//+-------------------------------------------------------------------------
//
//  Function:   SetContextFlagsForAsyncCall
//
//  Synopsis:   Forces correct flags for an async stgmed call.
//
//  history:    May-97   Ryszardk      Created.
//
//  We used to force MSHCTX_DIFFERENTMACHINE context on every
//  async stgmedium call because of problems with handles - for a truly
//  async call passing a handle, the client may free the handle before
//  the server can use it.
//  That is still needed. However, we cannot force the different machine
//  flags on IStream and IStorage as that prevents custom marshaler from
//  running.
//
//--------------------------------------------------------------------------

void inline
SetContextFlagsForAsyncCall(
    unsigned long * pFlags,
    STGMEDIUM     * pStgmed )
{
    if ( *pFlags & USER_CALL_IS_ASYNC )
        {
        // Additional considerations for async calls.

        switch( pStgmed->tymed )
            {
            case TYMED_NULL:
            case TYMED_MFPICT:
            case TYMED_ENHMF:
            case TYMED_GDI:
            case TYMED_HGLOBAL:
            case TYMED_FILE:
            default:
                if (!REMOTE_CALL(*pFlags))
                    {
                    *pFlags &= ~0xff;
                    *pFlags |= MSHCTX_DIFFERENTMACHINE;
                    }
                break;

            case TYMED_ISTREAM:
            case TYMED_ISTORAGE:
                // Dont force remote.
                break;

            }
        }
}


//+-------------------------------------------------------------------------
//
//  Function:   STGMEDIUM_UserSize
//
//  Synopsis:   Sizes a stgmedium pbject for RPC marshalling.
//
//  history:    May-95   Ryszardk      Created.
//
//--------------------------------------------------------------------------

unsigned long  __RPC_USER
STGMEDIUM_UserSize(
    unsigned long * pFlags,
    unsigned long   Offset,
    STGMEDIUM     * pStgmed )
{
    if ( ! pStgmed )
        return Offset;

    LENGTH_ALIGN( Offset, 3 );

    // Both handle and pUnk are represented by a long.
    if ( pStgmed->tymed == TYMED_NULL )
        Offset += sizeof(long) + sizeof(long);  // switch, (empty arm), pUnk
    else
        Offset += sizeof(long) + 2 * sizeof(long); // switch, handle, pUnk

    // Pointee of the union arm.
    // Only if the handle/pointer field is non-null.

    if ( pStgmed->hGlobal )
        {
        SetContextFlagsForAsyncCall( pFlags, pStgmed );

        switch( pStgmed->tymed )
            {
            case TYMED_NULL:
                break;
            case TYMED_MFPICT:
                Offset = HMETAFILEPICT_UserSize( pFlags,
                                                  Offset,
                                                  &pStgmed->hMetaFilePict );
                break;
            case TYMED_ENHMF:
                Offset = HENHMETAFILE_UserSize( pFlags,
                                                  Offset,
                                                  &pStgmed->hEnhMetaFile );
                break;
            case TYMED_GDI:

                // A GDI object is not necesarrily a BITMAP.  Therefore, we handle
                // those types we know about based on the object type, and reject
                // those which we do not support.

                // switch for object type.

                Offset += sizeof(long);

                switch( GetObjectType( (HGDIOBJ)pStgmed->hBitmap ) )
                    {
                    case OBJ_BITMAP:
                        Offset = HBITMAP_UserSize( pFlags,
                                                   Offset,
                                                   &pStgmed->hBitmap );
                        break;

                    case OBJ_PAL:
                        Offset = HPALETTE_UserSize( pFlags,
                                                    Offset,
                                       (HPALETTE *) & pStgmed->hBitmap );
                        break;

                    default:
                        RAISE_RPC_EXCEPTION(DV_E_TYMED);
                        break;
                    }
                break;

            case TYMED_HGLOBAL:
                Offset = HGLOBAL_UserSize( pFlags,
                                            Offset,
                                            &pStgmed->hGlobal );
                break;
            case TYMED_FILE:
                {
                ulong ulDataSize = lstrlenW(pStgmed->lpszFileName) + 1;
                Offset += 3 * sizeof(long); // [string]
                Offset += ulDataSize * sizeof(wchar_t);
                }
                break;

            case TYMED_ISTREAM:
            case TYMED_ISTORAGE:
                Offset = WdtpInterfacePointer_UserSize(
                            (USER_MARSHAL_CB *)pFlags,
                            *pFlags,
                            Offset,
                            pStgmed->pstg,
                            ((pStgmed->tymed == TYMED_ISTREAM)  ? IID_IStream
                                                                : IID_IStorage));
                break;

            default:
                break;

            }
        }

    // pUnkForRelease, if not null.

    if ( pStgmed->pUnkForRelease )
        Offset = WdtpInterfacePointer_UserSize( (USER_MARSHAL_CB *)pFlags,
                                                 *pFlags,
                                                 Offset,
                                                 pStgmed->pUnkForRelease,
                                                 IID_IUnknown );

    return( Offset );
}

//+-------------------------------------------------------------------------
//
//  Function:   STGMEDIUM_UserMarshal
//
//  Synopsis:   Marshals a stgmedium pbject for RPC.
//
//  history:    May-95   Ryszardk      Created.
//
//--------------------------------------------------------------------------

unsigned char __RPC_FAR * __RPC_USER
STGMEDIUM_UserMarshal(
    unsigned long * pFlags,
    unsigned char * pBufferStart,
    STGMEDIUM     * pStgmed )
{
    unsigned char * pBuffer;
    unsigned char * pUnionArmMark;

    if ( ! pStgmed )
        return pBufferStart;

    UserNdrDebugOut((
        UNDR_FORCE,
        "--STGMEDIUM_UserMarshal: %s\n",
        WdtpGetStgmedName(pStgmed)));

    pBuffer = pBufferStart;
    ALIGN( pBuffer, 3 );

    // userSTGMEDIUM: switch, union arm, pUnk ptr.

    *( PULONG_LV_CAST pBuffer)++ = pStgmed->tymed;
    pUnionArmMark = pBuffer;
    if ( pStgmed->tymed != TYMED_NULL )
        {
        // hGlobal stands for any of these handles.

        *( PLONG_LV_CAST pBuffer)++ = HandleToLong( pStgmed->hGlobal );
        }

    *( PLONG_LV_CAST pBuffer)++ = HandleToLong( pStgmed->pUnkForRelease );

    // Now the pointee of the union arm.
    // We need to marshal only if the handle/pointer field is non null.
    // Otherwise it is already in the buffer.

    if ( pStgmed->hGlobal )
        {
        SetContextFlagsForAsyncCall( pFlags, pStgmed );

        switch( pStgmed->tymed )
            {
            case TYMED_NULL:
                break;
            case TYMED_MFPICT:
                pBuffer = HMETAFILEPICT_UserMarshal( pFlags,
                                                     pBuffer,
                                                     &pStgmed->hMetaFilePict );
                break;
            case TYMED_ENHMF:
                pBuffer = HENHMETAFILE_UserMarshal( pFlags,
                                                    pBuffer,
                                                    &pStgmed->hEnhMetaFile );
                break;
            case TYMED_GDI:

                {
                // A GDI object is not necesarrily a BITMAP.  Therefore, we handle
                // those types we know about based on the object type, and reject
                // those which we do not support.

                ulong GdiObjectType = GetObjectType( (HGDIOBJ)pStgmed->hBitmap );

                // GDI_OBJECT

                *( PULONG_LV_CAST pBuffer)++ = GdiObjectType;

                switch( GdiObjectType )
                    {
                    case OBJ_BITMAP:
                        pBuffer = HBITMAP_UserMarshal( pFlags,
                                                       pBuffer,
                                                       &pStgmed->hBitmap );
                        break;

                    case OBJ_PAL:
                        pBuffer = HPALETTE_UserMarshal( pFlags,
                                                        pBuffer,
                                           (HPALETTE *) & pStgmed->hBitmap );
                        break;

                    default:
                        RpcRaiseException(DV_E_TYMED);
                    }
                }
                break;

            case TYMED_HGLOBAL:
                pBuffer = HGLOBAL_UserMarshal( pFlags,
                                               pBuffer,
                                               & pStgmed->hGlobal );
                break;
            case TYMED_FILE:
                {
                // We marshal it as a [string].

                ulong Count = (pStgmed->lpszFileName)
                                    ?  lstrlenW(pStgmed->lpszFileName) + 1
                                    :  0;

                *( PULONG_LV_CAST pBuffer)++ = Count;
                *( PULONG_LV_CAST pBuffer)++ = 0;
                *( PULONG_LV_CAST pBuffer)++ = Count;
                memcpy( pBuffer, pStgmed->lpszFileName, Count * sizeof(wchar_t) );
                pBuffer += Count * sizeof(wchar_t);
                }
                break;

            case TYMED_ISTREAM:
            case TYMED_ISTORAGE:
                pBuffer = WdtpInterfacePointer_UserMarshal(
                               ((USER_MARSHAL_CB *)pFlags),
                               *pFlags,
                               pBuffer,
                               pStgmed->pstg,
                               ((pStgmed->tymed == TYMED_ISTREAM)  ? IID_IStream
                                                                   : IID_IStorage));
                break;

            default:
                break;
            }
        }

    // Marker for this pointer is already in the buffer.

    if ( pStgmed->pUnkForRelease )
        pBuffer = WdtpInterfacePointer_UserMarshal( ((USER_MARSHAL_CB *)pFlags
),
                                                    *pFlags,
                                                    pBuffer,
                                                    pStgmed->pUnkForRelease,
                                                    IID_IUnknown );

    return( pBuffer );
}

//+-------------------------------------------------------------------------
//
//  Function:   STGMEDIUM_UserUnmarshalWorker
//
//  Synopsis:   Unmarshals a stgmedium object for RPC.
//
//  history:    Aug-99   JohnStra      Created.
//
//--------------------------------------------------------------------------

unsigned char __RPC_FAR * __RPC_USER
STGMEDIUM_UserUnmarshalWorker(
    unsigned long * pFlags,
    unsigned char * pBuffer,
    STGMEDIUM     * pStgmed,
    ULONG_PTR       BufferSize )
{
    unsigned long   fUnkForRelease;
    LONG_PTR        Handle = 0;

    // Align the buffer and save the fixup size.
    UCHAR* pBufferStart = pBuffer;
    ALIGN( pBuffer, 3 );
    ULONG_PTR cbFixup = (ULONG_PTR) (pBuffer - pBufferStart);

    // Check for EOB.
    CHECK_BUFFER_SIZE( BufferSize, cbFixup + sizeof( ULONG ) );

    // switch, union arm, pUnk.
    pStgmed->tymed = *( PULONG_LV_CAST pBuffer)++;

    UserNdrDebugOut((UNDR_FORCE, "--STGMEDIUM_UserUnmarshal: %s\n",
                     WdtpGetStgmedName(pStgmed) ));

    ULONG_PTR cbData = cbFixup + (2 * sizeof( ULONG ));
    if ( pStgmed->tymed != TYMED_NULL )
        {
        cbData += sizeof( ULONG );

        CHECK_BUFFER_SIZE( BufferSize, cbData );

        // This value is just a marker for the handle - a long.
        Handle =  *( PLONG_LV_CAST pBuffer)++;
        }
    else
        {
        CHECK_BUFFER_SIZE( BufferSize, cbData );
        }

    // pUnkForRelease pointer marker.
    fUnkForRelease = *( PULONG_LV_CAST pBuffer)++;

    // First pointee

    // Union arm pointee.
    // We need to unmarshal only if the handle/pointer field was not NULL.

    if ( Handle )
        {
        SetContextFlagsForAsyncCall( pFlags, pStgmed );

        LONG* pBuf = (LONG*)pBuffer;

        switch( pStgmed->tymed )
            {
            case TYMED_NULL:
                break;
            case TYMED_MFPICT:

#if defined(_WIN64)
                if ( IS_DATA_MARKER( pBuf[0] ) )
                {
                    CHECK_BUFFER_SIZE( BufferSize, cbData + (2 * sizeof( ULONG )) );

                    if ( Handle != pBuf[1] )
                    {
                        RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );
                    }
                }
                else
                {
                    //Inproc should always have marked this as handle64....
                    //Out-of-proc should always have marked this with a data marker....
                    if (!IS_HANDLE64_MARKER(pBuf[0]))
                        RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );

                    // Align to 64b.
                    PBYTE pbBuf = (PBYTE)( pBuf + 1 );
                    PBYTE pbBufStart = pbBuf;
                    ALIGN( pbBuf, 7 );

                    // Make sure we don't step off the end of the buffer.
                    CHECK_BUFFER_SIZE( BufferSize, cbData +
                                        (ULONG_PTR)(pbBuf - pbBufStart) +
                                                   (sizeof(__int64)) );

                    // Verify that the handle put on the wire matches the
                    // first instance of the handle on the wire.
                    if ( Handle != (LONG_PTR) (*(LONG *)pbBuf ) )
                        RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );
                }
#else
                CHECK_BUFFER_SIZE( BufferSize, cbData + (2 * sizeof(ULONG)) );

                if ( Handle != pBuf[1] )
                    RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );
#endif

                pBuffer = HMETAFILEPICT_UserUnmarshalWorker(
                                      pFlags,
                                      pBuffer,
                                     &pStgmed->hMetaFilePict,
                                      BufferSize - cbData );
                break;
            case TYMED_ENHMF:
                // Must be room in buffer to do lookahead check.
                CHECK_BUFFER_SIZE( BufferSize, cbData + (2 * sizeof( ULONG )) );

                // validate the handle.
                if ( Handle != pBuf[1] )
                    RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );

                pBuffer = HENHMETAFILE_UserUnmarshalWorker( pFlags,
                                                            pBuffer,
                                                        &pStgmed->hEnhMetaFile,
                                                            BufferSize - cbData );
                break;
            case TYMED_GDI:
                {
                // A GDI object is not necesarrily a BITMAP.  Therefore, we
                // handle those types we know about based on the object type,
                // and reject those which we do not support.

                // Make sure we don't walk off the end of the buffer.
                CHECK_BUFFER_SIZE( BufferSize, cbData + (3 * sizeof( ULONG )) );

                cbData += sizeof( ULONG );

                DWORD GdiObjectType = *( PULONG_LV_CAST pBuffer)++;

                switch( GdiObjectType )
                    {
                    case OBJ_BITMAP:
                        // Lookahead validaton of the handle.  We look at
                        // the 3rd DWORD: GDI type, DISC, Handle.
                        if ( Handle != pBuf[2] )
                            RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );

                        pBuffer = HBITMAP_UserUnmarshalWorker( pFlags,
                                                               pBuffer,
                                                              &pStgmed->hBitmap,
                                                               BufferSize - cbData );
                        break;

                    case OBJ_PAL:
                        // Lookahead validaton of the handle.
                        if ( Handle != pBuf[2] )
                            RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );

                        pBuffer = HPALETTE_UserUnmarshalWorker( pFlags,
                                                                pBuffer,
                                               (HPALETTE *) & pStgmed->hBitmap,
                                                                BufferSize - cbData );
                        break;

                    default:
                        RAISE_RPC_EXCEPTION(DV_E_TYMED);
                    }
                }
                break;

            case TYMED_HGLOBAL:
                {
                // reallocation is forbidden for [in-out] hglobal in STGMEDIUM.

#if defined(_WIN64)
                if ( IS_DATA_MARKER( pBuf[0] ) )
                {
                    CHECK_BUFFER_SIZE( BufferSize, cbData + (2 * sizeof( ULONG )) );

                    if ( Handle != pBuf[1] )
                    {
                        RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );
                    }
                }
                else
                {
                    // Align to 64b.
                    PBYTE pbBuf = (PBYTE)( pBuf + 1 );
                    PBYTE pbBufStart = pbBuf;
                    ALIGN( pbBuf, 7 );

                    // Make sure we don't step off the end of the buffer.
                    CHECK_BUFFER_SIZE( BufferSize, cbData +
                                        (ULONG_PTR)(pbBuf - pbBufStart) +
                                                   (sizeof(__int64)) );

                    // Verify that the handle put on the wire matches the
                    // first instance of the handle on the wire.
                    if ( Handle != (LONG_PTR) (*(LONG *)pbBuf ) )
                        RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );
                }
#else
                CHECK_BUFFER_SIZE( BufferSize, cbData + (2 * sizeof(ULONG)) );

                if ( Handle != pBuf[1] )
                    RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );
#endif

                pBuffer = WdtpGlobalUnmarshal( pFlags,
                                               pBuffer,
                                               & pStgmed->hGlobal,
                                               FALSE,
                                               BufferSize - cbData);
                break;
                }
            case TYMED_FILE:
                {
                // Must be room in buffer for header.
                CHECK_BUFFER_SIZE( BufferSize, cbData + (3 * sizeof(ULONG)) );

                // We marshal it as a [string].

                ulong Count = *( PULONG_LV_CAST pBuffer)++;
                if ( *( PULONG_LV_CAST pBuffer)++ != 0 ||
                     *( PULONG_LV_CAST pBuffer)++ != Count )
                    RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );

                if ( ! pStgmed->lpszFileName )
                    pStgmed->lpszFileName = (LPOLESTR)
                                        WdtpAllocate( pFlags,
                                                      Count * sizeof(wchar_t) );

                // Must be room in the buffer for the string.
                CHECK_BUFFER_SIZE(
                    BufferSize,
                    cbData + (3 * sizeof( ULONG )) + (Count * sizeof( wchar_t )) );

                memcpy(pStgmed->lpszFileName, pBuffer, Count * sizeof(wchar_t));
                pBuffer += Count * sizeof(wchar_t);
                }
                break;

            case TYMED_ISTREAM:
            case TYMED_ISTORAGE:
                // Non null pointer, retrieve the interface pointer

                pBuffer = WdtpInterfacePointer_UserUnmarshalWorker(
                              (USER_MARSHAL_CB *)pFlags,
                              pBuffer,
                              (IUnknown **) &pStgmed->pstm,
                              ((pStgmed->tymed == TYMED_ISTREAM)
                                    ? IID_IStream
                                    : IID_IStorage),
                               BufferSize - cbData,
                              FALSE );
                break;

            default:
                break;
            }
        }
    else
        {
        // New handle/pointer field is null, so release the previous one
        // if it wasn't null.

        if ( pStgmed->hGlobal )
            {
            // This should never happen for GetDataHere.

            // Note, that we release the handle field, not the stgmedium itself.
            // Accordingly, we don't follow punkForRelease.

            UserNdrDebugOut((
                  UNDR_FORCE,
                  "--STGMEDIUM_UserUnmarshal: %s: NULL in, freeing old one\n",
                  WdtpGetStgmedName(pStgmed)));

            STGMEDIUM  TmpStg = *pStgmed;
            TmpStg.pUnkForRelease = NULL;

            if ( pStgmed->tymed == TYMED_HGLOBAL )
                {
                // Cannot reallocate.
                RAISE_RPC_EXCEPTION(DV_E_TYMED);
                }
            else
                {
                ReleaseStgMedium( &TmpStg );
                }
            }

        pStgmed->hGlobal = 0;
        }

    // Fixup the buffer size so if fUnkForRelease is set, we
    // pass the correct BufferSize to the unmarshal routine.
    BufferSize -= (ULONG_PTR)(pBuffer - pBufferStart);

    if ( fUnkForRelease )
        {
        // There is an interface pointer on the wire.

        pBuffer = WdtpInterfacePointer_UserUnmarshalWorker(
                        (USER_MARSHAL_CB *)pFlags,
                        pBuffer,
                        &pStgmed->pUnkForRelease,
                        IID_IUnknown,
                        BufferSize,
                        FALSE );
        }

    if ( pStgmed->pUnkForRelease )
        {
        // Replace the app's punkForRelease with our custom release
        // handler for special situations.

        // The special situation is when a handle is remoted with data
        // and so we have to clean up a side effect of having a data copy
        // around. UserFree does it properly but we need that for the callee.
        // When the callee releases a stgmed, it would invoke
        // ReleaseStgMedium and this API doesn't do anything for handles
        // when the punkForRelease is not NULL.

        ULONG fHandleWithData = 0;
        ULONG fTopLevelOnly = 0;

        switch ( pStgmed->tymed )
            {
            case TYMED_HGLOBAL:
                fHandleWithData = HGLOBAL_DATA_PASSING( *pFlags );
                break;

            case TYMED_ENHMF:
            case TYMED_GDI:
                fHandleWithData = GDI_DATA_PASSING( *pFlags );
                break;

            case TYMED_MFPICT:
                fHandleWithData = HGLOBAL_DATA_PASSING( *pFlags );
                fTopLevelOnly   = fHandleWithData  &&
                                        ! GDI_DATA_PASSING( *pFlags );
                break;

            default:
                break;
            }

        if ( fHandleWithData )
            {
            IUnknown *
            punkTmp = (IUnknown *) new CPunkForRelease( pStgmed,
                                                        fTopLevelOnly );
            if (!punkTmp)
                {
                RAISE_RPC_EXCEPTION(E_OUTOFMEMORY);
                }

            pStgmed->pUnkForRelease = punkTmp;
            }
        }

    return( pBuffer );
}

//+-------------------------------------------------------------------------
//
//  Function:   STGMEDIUM_UserUnmarshal
//
//  Synopsis:   Unmarshals a stgmedium object for RPC.
//
//  history:    May-95   Ryszardk      Created.
//              Aug-99   JohnStra      Factored bulk of code out into a
//                                     worker routine in order to add
//                                     consistency checks.
//
//--------------------------------------------------------------------------

unsigned char __RPC_FAR * __RPC_USER
STGMEDIUM_UserUnmarshal(
    unsigned long * pFlags,
    unsigned char * pBuffer,
    STGMEDIUM     * pStgmed )
{
    // Init buffer size and ptr to buffer.
    CUserMarshalInfo MarshalInfo( pFlags, pBuffer );
    ULONG_PTR BufferSize   = MarshalInfo.GetBufferSize();
    UCHAR*    pBufferStart = MarshalInfo.GetBuffer();

    pBuffer = STGMEDIUM_UserUnmarshalWorker( pFlags,
                                             pBufferStart,
                                             pStgmed,
                                             BufferSize );
    return( pBuffer );
}


//+-------------------------------------------------------------------------
//
//  Function:   STGMEDIUM_UserFree
//
//  Synopsis:   Frees a stgmedium object for RPC.
//
//  history:    May-95   Ryszardk      Created.
//
//  Note:       This routine is called from the freeing walk at server
//              or from the SetData *proxy*, when ownership has been passed.
//
//--------------------------------------------------------------------------

EXTERN_C
void NukeHandleAndReleasePunk(
    STGMEDIUM * pStgmed )
{
    pStgmed->hGlobal = NULL;
    pStgmed->tymed = TYMED_NULL;

    if (pStgmed->pUnkForRelease)
        {
        pStgmed->pUnkForRelease->Release();
        pStgmed->pUnkForRelease = 0;
        }
}

void __RPC_USER
STGMEDIUM_UserFree(
    unsigned long * pFlags,
    STGMEDIUM * pStgmed )
{
    UserNdrDebugOut((UNDR_FORCE, "--STGMEDIUM_UserFree: %s\n", WdtpGetStgmedName(pStgmed)));

    if( pStgmed )
        {
        SetContextFlagsForAsyncCall( pFlags, pStgmed );

        switch ( pStgmed->tymed )
            {
            case TYMED_FILE:
                WdtpFree( pFlags, pStgmed->lpszFileName);
                NukeHandleAndReleasePunk( pStgmed );
                break;

            case TYMED_NULL:
            case TYMED_ISTREAM:
            case TYMED_ISTORAGE:
                ReleaseStgMedium( pStgmed );
                break;

            case TYMED_GDI:
            case TYMED_ENHMF:

                if ( GDI_HANDLE_PASSING(*pFlags) )
                    {
                    NukeHandleAndReleasePunk( pStgmed );
                    }
                else
                    {
                    // Handle w/data: there is a side effect to clean up.
                    // For punk !=0, this will go to our CPunk object.

                    ReleaseStgMedium( pStgmed );
                    }
                break;

            case TYMED_HGLOBAL:

                if ( HGLOBAL_HANDLE_PASSING(*pFlags) )
                    {
                    NukeHandleAndReleasePunk( pStgmed );
                    }
                else
                    {
                    // Handle w/data: there is a side effect to clean up.
                    // For punk ==0, this will just release the data.
                    // For punk !=0, this will go to our CPunk object,
                    // release the data, and then call the original punk.

                    ReleaseStgMedium( pStgmed );
                    }
                break;

            case TYMED_MFPICT:

                if ( HGLOBAL_HANDLE_PASSING(*pFlags) )
                    {
                    NukeHandleAndReleasePunk( pStgmed );
                    }
                else if ( GDI_HANDLE_PASSING(*pFlags) )
                    {
                    if ( pStgmed->pUnkForRelease )
                        {
                        pStgmed->pUnkForRelease->Release();
                        }
                    else
                        {
                        if ( pStgmed->hGlobal )
                            GlobalFree( pStgmed->hGlobal );
                        pStgmed->hGlobal = NULL;
                        pStgmed->tymed = TYMED_NULL;
                        }
                    }
                else
                    {
                    // Handle w/data: there is a side effect to clean up.
                    // For punk !=0, this will go to our CPunk object.

                    ReleaseStgMedium( pStgmed );
                    }
                break;

            default:
                RAISE_RPC_EXCEPTION( E_INVALIDARG );
                break;
            }
        }
}

//+-------------------------------------------------------------------------
//
//  Function:   FLAG_STGMEDIUM_UserSize
//
//  Synopsis:   Sizes a wrapper for stgmedium.
//
//  history:    May-95   Ryszardk      Created.
//
//--------------------------------------------------------------------------

unsigned long  __RPC_USER
FLAG_STGMEDIUM_UserSize(
    unsigned long * pFlags,
    unsigned long   Offset,
    FLAG_STGMEDIUM* pFlagStgmed )
{
    if ( ! pFlagStgmed )
        return Offset;

    LENGTH_ALIGN( Offset, 3 );

    Offset += 2 * sizeof(long);
    Offset  = STGMEDIUM_UserSize( pFlags, Offset, & pFlagStgmed->Stgmed );

    return( Offset );
}

//+-------------------------------------------------------------------------
//
//  Function:   FLAG_STGMEDIUM_UserMarshal
//
//  Synopsis:   Marshals a wrapper for stgmedium. Used in SetData.
//
//  history:    May-95   Ryszardk      Created.
//
//--------------------------------------------------------------------------

unsigned char __RPC_FAR * __RPC_USER
FLAG_STGMEDIUM_UserMarshal(
    unsigned long * pFlags,
    unsigned char * pBuffer,
    FLAG_STGMEDIUM* pFlagStgmed )
{
    if ( ! pFlagStgmed )
        return pBuffer;

    ALIGN( pBuffer, 3 );

    // Flags: we need them when freeing in the client call_as routine

    pFlagStgmed->ContextFlags = *pFlags;

    *( PULONG_LV_CAST pBuffer)++ = *pFlags;
    *( PULONG_LV_CAST pBuffer)++ = pFlagStgmed->fPassOwnership;

    pBuffer = STGMEDIUM_UserMarshal( pFlags,
                                     pBuffer,
                                     & pFlagStgmed->Stgmed );

    return( pBuffer );
}

//+-------------------------------------------------------------------------
//
//  Function:   FLAG_STGMEDIUM_UserUnmarshal
//
//  Synopsis:   Unmarshals a wrapper for stgmedium.
//
//  history:    May-95   Ryszardk      Created.
//
//--------------------------------------------------------------------------

unsigned char __RPC_FAR * __RPC_USER
FLAG_STGMEDIUM_UserUnmarshal(
    unsigned long * pFlags,
    unsigned char * pBuffer,
    FLAG_STGMEDIUM* pFlagStgmed )
{
    // Init buffer size.
    CUserMarshalInfo MarshalInfo( pFlags, pBuffer );
    ULONG_PTR BufferSize   = MarshalInfo.GetBufferSize();
    UCHAR*    pBufferStart = MarshalInfo.GetBuffer();
    UCHAR*    pBufferPtr   = pBufferStart;

    // Align the buffer.
    ALIGN( pBufferPtr, 3 );
    ULONG_PTR cbFixup = (ULONG_PTR)(pBufferPtr - pBufferStart);

    // BufferSize must not be less than the
    // alignment fixup + ContextFlags + fPassOwnership + tymed.
    CHECK_BUFFER_SIZE( BufferSize, cbFixup + (2 * sizeof( ULONG )) );

    // Flags and buffer marker

    pFlagStgmed->ContextFlags = *( PULONG_LV_CAST pBufferPtr)++;

    // Flags: we need them when freeing in the client call_as routine
    // We need that in the Proxy, when we call the user free routine.

    pFlagStgmed->fPassOwnership = *( PULONG_LV_CAST pBufferPtr)++;
    pFlagStgmed->ContextFlags   = *pFlags;

    // Needed to handle both GDI handles and Istream/IStorage correctly.

    // Subtract alignment fixup + 2 DWORDs from BufferSize.
    BufferSize -= cbFixup + (2 * sizeof( ULONG ));

    pBuffer = STGMEDIUM_UserUnmarshalWorker( pFlags,
                                             pBufferPtr,
                                           & pFlagStgmed->Stgmed,
                                             BufferSize );
    return( pBuffer );
}

//+-------------------------------------------------------------------------
//
//  Function:   FLAG_STGMEDIUM_UserFree
//
//  Synopsis:   Freess a wrapper for stgmedium.
//
//  history:    May-95   Ryszardk      Created.
//
//--------------------------------------------------------------------------

void __RPC_USER
FLAG_STGMEDIUM_UserFree(
    unsigned long * pFlags,
    FLAG_STGMEDIUM* pFlagsStgmed )
{
    if ( ! pFlagsStgmed->fPassOwnership )
        STGMEDIUM_UserFree( pFlags, & pFlagsStgmed->Stgmed );

    // else the callee is supposed to release the stg medium.
}


//+-------------------------------------------------------------------------
//
//  Function:   ASYNC_STGMEDIUM_UserSize
//
//  Synopsis:   Sizes a wrapper for stgmedium.
//
//  history:    May-95   Ryszardk      Created.
//              May-97   Ryszardk      introduced the async flag to optimize
//
//--------------------------------------------------------------------------

unsigned long  __RPC_USER
ASYNC_STGMEDIUM_UserSize(
    unsigned long * pFlags,
    unsigned long   Offset,
    ASYNC_STGMEDIUM* pAsyncStgmed )
{
    if ( ! pAsyncStgmed )
        return Offset;

    // Needed to handle both GDI handles and Istream/IStorage correctly.

    // BTW: This is needed only as a workaround because the [async] attr
    // has been temporarily removed from objidl.idl. (May 1997).
    // After we have the new [async] in place, this code is unnecessary
    // as the NDR engine is setting the same flag for every async call.

    *pFlags |= USER_CALL_IS_ASYNC;

    Offset  = STGMEDIUM_UserSize( pFlags, Offset, pAsyncStgmed );

    return( Offset );
}

//+-------------------------------------------------------------------------
//
//  Function:   ASYNC_STGMEDIUM_UserMarshal
//
//  Synopsis:   Marshals a wrapper for stgmedium. Used in SetData.
//
//  history:    May-95   Ryszardk      Created.
//
//--------------------------------------------------------------------------

unsigned char __RPC_FAR * __RPC_USER
ASYNC_STGMEDIUM_UserMarshal(
    unsigned long * pFlags,
    unsigned char * pBuffer,
    ASYNC_STGMEDIUM* pAsyncStgmed )
{
    if ( ! pAsyncStgmed )
        return pBuffer;

    // Needed to handle both GDI handles and Istream/IStorage correctly.

    *pFlags |= USER_CALL_IS_ASYNC;

    pBuffer = STGMEDIUM_UserMarshal( pFlags,
                                     pBuffer,
                                     pAsyncStgmed );

    return( pBuffer );
}

//+-------------------------------------------------------------------------
//
//  Function:   ASYNC_STGMEDIUM_UserUnmarshal
//
//  Synopsis:   Unmarshals a wrapper for stgmedium.
//
//  history:    May-95   Ryszardk      Created.
//
//--------------------------------------------------------------------------

unsigned char __RPC_FAR * __RPC_USER
ASYNC_STGMEDIUM_UserUnmarshal(
    unsigned long * pFlags,
    unsigned char * pBuffer,
    ASYNC_STGMEDIUM* pAsyncStgmed )
{
    // Init buffer size.
    CUserMarshalInfo MarshalInfo( pFlags, pBuffer );
    ULONG_PTR BufferSize   = MarshalInfo.GetBufferSize();
    UCHAR*    pBufferStart = MarshalInfo.GetBuffer();

    // Needed to handle both GDI handles and Istream/IStorage correctly.

    *pFlags |= USER_CALL_IS_ASYNC;

    pBuffer =  STGMEDIUM_UserUnmarshalWorker( pFlags,
                                              pBufferStart,
                                              pAsyncStgmed,
                                              BufferSize );

    return( pBuffer );
}

//+-------------------------------------------------------------------------
//
//  Function:   ASYNC_STGMEDIUM_UserFree
//
//  Synopsis:   Freess a wrapper for stgmedium.
//
//  history:    May-95   Ryszardk      Created.
//
//--------------------------------------------------------------------------

void __RPC_USER
ASYNC_STGMEDIUM_UserFree(
    unsigned long * pFlags,
    ASYNC_STGMEDIUM* pAsyncStgmed )
{
    // Needed to handle both GDI handles and Istream/IStorage correctly.

    *pFlags |= USER_CALL_IS_ASYNC;

    STGMEDIUM_UserFree( pFlags, pAsyncStgmed );
}


#if defined(_WIN64)
//
// NDR64 Support routines.
//

//+-------------------------------------------------------------------------
//
//  Function:   STGMEDIUM_UserSize64
//
//  Synopsis:   Sizes a stgmedium pbject for RPC marshalling.
//
//  history:    Dec-00   JohnDoty      Created based on 32b function.
//
//--------------------------------------------------------------------------

unsigned long  __RPC_USER
STGMEDIUM_UserSize64 (
    unsigned long * pFlags,
    unsigned long   Offset,
    STGMEDIUM     * pStgmed )
{
    if ( ! pStgmed )
        return Offset;

    LENGTH_ALIGN( Offset, 7 );

    // tymed is 4 bytes, plus 4 bytes padding, plus potentially the handle (8 bytes),
    // plus the pUnk.
    if ( pStgmed->tymed == TYMED_NULL )
        Offset += 4 + 4 + 8;                       // switch, pad, (empty arm), pUnk
    else
    {
        Offset += 4 + 4 + 8 + 8;                   // switch, pad, handle, pUnk

        // Pointee of the union arm.
        // Only if the handle/pointer field is non-null.
    
        if ( pStgmed->hGlobal )
        {
            SetContextFlagsForAsyncCall( pFlags, pStgmed );
            
            switch( pStgmed->tymed )
            {
            case TYMED_NULL:
                break;
            case TYMED_MFPICT:
                Offset = HMETAFILEPICT_UserSize64( pFlags,
                                                   Offset,
                                                   &pStgmed->hMetaFilePict );
                break;
            case TYMED_ENHMF:
                Offset = HENHMETAFILE_UserSize64( pFlags,
                                                  Offset,
                                                  &pStgmed->hEnhMetaFile );
                break;
            case TYMED_GDI:                
                // A GDI object is not necesarrily a BITMAP.  Therefore, we handle
                // those types we know about based on the object type, and reject
                // those which we do not support.

                // 4 bytes for disc, plus 4 bytes pad, plus 8 bytes pointer?
                Offset += 4 + 4 + 8; 

                switch( GetObjectType( (HGDIOBJ)pStgmed->hBitmap ) )
                {
                case OBJ_BITMAP:
                    Offset = HBITMAP_UserSize64( pFlags,
                                                 Offset,
                                                 &pStgmed->hBitmap );
                    break;

                case OBJ_PAL:
                    Offset = HPALETTE_UserSize64( pFlags,
                                                  Offset,
                                                  (HPALETTE *) & pStgmed->hBitmap );
                    break;

                default:
                    RAISE_RPC_EXCEPTION(DV_E_TYMED);
                    break;
                }
                break;

            case TYMED_HGLOBAL:
                Offset = HGLOBAL_UserSize64( pFlags,
                                             Offset,
                                             &pStgmed->hGlobal );
                break;

            case TYMED_FILE:
                {           
                    ulong ulDataSize = 0;
                    if (pStgmed->lpszFileName)
                        ulDataSize = lstrlenW(pStgmed->lpszFileName) + 1;
                    Offset += 3 * 8; // max size, offset, conformance
                    Offset += ulDataSize * sizeof(wchar_t);
                }
                break;

            case TYMED_ISTREAM:
                Offset = WdtpInterfacePointer_UserSize64((USER_MARSHAL_CB *)pFlags,
                                                         *pFlags,
                                                         Offset,
                                                         pStgmed->pstm,
                                                         IID_IStream);
                break;

            case TYMED_ISTORAGE:
                Offset = WdtpInterfacePointer_UserSize64((USER_MARSHAL_CB *)pFlags,
                                                         *pFlags,
                                                         Offset,
                                                         pStgmed->pstg,
                                                         IID_IStorage);
                break;

            default:
                RAISE_RPC_EXCEPTION(DV_E_TYMED);
                break;
            }
        }
    }

    // pUnkForRelease, if not null.

    if ( pStgmed->pUnkForRelease )
        Offset = WdtpInterfacePointer_UserSize64( (USER_MARSHAL_CB *)pFlags,
                                                  *pFlags,
                                                  Offset,
                                                  pStgmed->pUnkForRelease,
                                                  IID_IUnknown );
    
    return( Offset );
}

//+-------------------------------------------------------------------------
//
//  Function:   STGMEDIUM_UserMarshal64
//
//  Synopsis:   Marshals a stgmedium pbject for RPC.
//
//  history:    Dec-00   JohnDoty      Created based on 32b function.
//
//--------------------------------------------------------------------------

unsigned char __RPC_FAR * __RPC_USER
STGMEDIUM_UserMarshal64 (
    unsigned long * pFlags,
    unsigned char * pBufferStart,
    STGMEDIUM     * pStgmed )
{
    unsigned char * pBuffer;
    unsigned char * pUnionArmMark;
    DWORD tymed;

    if ( ! pStgmed )
        return pBufferStart;
    
    UserNdrDebugOut((
        UNDR_FORCE,
        "--STGMEDIUM_UserMarshal64: %s\n",
        WdtpGetStgmedName(pStgmed)));

    pBuffer = pBufferStart;
    ALIGN( pBuffer, 7 );

    // userSTGMEDIUM: switch, union arm, pUnk ptr.
    tymed = pStgmed->tymed;
    *( PULONG_LV_CAST pBuffer)++ = tymed;
    ALIGN( pBuffer, 7 );
    
    pUnionArmMark = pBuffer;
    if ( tymed != TYMED_NULL )
    {
        // hGlobal stands for any of these handles.        
      *(PHYPER_LV_CAST pBuffer)++ = (hyper)( pStgmed->hGlobal );
    }
    
    *(PHYPER_LV_CAST pBuffer)++ = (hyper)( pStgmed->pUnkForRelease );

    // Now the pointee of the union arm.
    // We need to marshal only if the handle/pointer field is non null.
    // Otherwise it is already in the buffer.
    if ( pStgmed->hGlobal )
    {
        SetContextFlagsForAsyncCall( pFlags, pStgmed );
        
        switch( pStgmed->tymed )
        {
        case TYMED_NULL:
            break;
            
        case TYMED_MFPICT:
            pBuffer = HMETAFILEPICT_UserMarshal64( pFlags,
                                                   pBuffer,
                                                   &pStgmed->hMetaFilePict );
            break;

        case TYMED_ENHMF:
            pBuffer = HENHMETAFILE_UserMarshal64( pFlags,
                                                  pBuffer,
                                                  &pStgmed->hEnhMetaFile );
            break;
            
        case TYMED_GDI:
            {
                // A GDI object is not necesarrily a BITMAP.  Therefore, we handle
                // those types we know about based on the object type, and reject
                // those which we do not support.

                ulong GdiObjectType = GetObjectType( (HGDIOBJ)pStgmed->hBitmap );

                // GDI_OBJECT
                *(PULONG_LV_CAST pBuffer)++ = GdiObjectType;
                ALIGN( pBuffer, 7 );
                *(PHYPER_LV_CAST pBuffer)++ = (hyper)(pStgmed->hBitmap);

                switch( GdiObjectType )
                {
                case OBJ_BITMAP:
                    pBuffer = HBITMAP_UserMarshal64( pFlags,
                                                     pBuffer,
                                                     &pStgmed->hBitmap );
                    break;

                case OBJ_PAL:
                    pBuffer = HPALETTE_UserMarshal64( pFlags,
                                                      pBuffer,
                                                      (HPALETTE *) & pStgmed->hBitmap );
                    break;
                    
                default:
                    RpcRaiseException(DV_E_TYMED);
                }
            }
            break;

        case TYMED_HGLOBAL:
            pBuffer = HGLOBAL_UserMarshal64( pFlags,
                                             pBuffer,
                                             &pStgmed->hGlobal );
            break;
        case TYMED_FILE:
            {
                // We marshal it as a [string].
                ulong Count = 0;
                if (pStgmed->lpszFileName)
                    Count = lstrlenW(pStgmed->lpszFileName) + 1;

                *( PHYPER_LV_CAST pBuffer)++ = Count;
                *( PHYPER_LV_CAST pBuffer)++ = 0;
                *( PHYPER_LV_CAST pBuffer)++ = Count;
                memcpy( pBuffer, pStgmed->lpszFileName, Count * sizeof(wchar_t) );
                pBuffer += Count * sizeof(wchar_t);
            }
            break;

        case TYMED_ISTREAM:
            pBuffer = WdtpInterfacePointer_UserMarshal64(((USER_MARSHAL_CB *)pFlags),
                                                         *pFlags,
                                                         pBuffer,
                                                         pStgmed->pstm,
                                                         IID_IStream);
            break;
            
        case TYMED_ISTORAGE:
            pBuffer = WdtpInterfacePointer_UserMarshal64(((USER_MARSHAL_CB *)pFlags),
                                                         *pFlags,
                                                         pBuffer,
                                                         pStgmed->pstg,
                                                         IID_IStorage);
            break;

        default:
            RpcRaiseException(DV_E_TYMED);
            break;
        }
    }

    // Marker for this pointer is already in the buffer.
    if ( pStgmed->pUnkForRelease )
        pBuffer = WdtpInterfacePointer_UserMarshal64( ((USER_MARSHAL_CB *)pFlags),
                                                      *pFlags,
                                                      pBuffer,
                                                      pStgmed->pUnkForRelease,
                                                      IID_IUnknown );
    
    return( pBuffer );
}

//+-------------------------------------------------------------------------
//
//  Function:   STGMEDIUM_UserUnmarshalWorker64
//
//  Synopsis:   Unmarshals a stgmedium object for RPC.
//
//  history:    Dec-00   JohnDoty      Created based on 32b function.
//
//--------------------------------------------------------------------------

unsigned char __RPC_FAR * __RPC_USER
STGMEDIUM_UserUnmarshalWorker64 (
    unsigned long * pFlags,
    unsigned char * pBuffer,
    STGMEDIUM     * pStgmed,
    ULONG_PTR       BufferSize )
{
    CarefulBufferReader stream(pBuffer, BufferSize);
    LONG_PTR        fUnkForRelease;
    LONG_PTR        Handle = 0;
    unsigned char  *mark = NULL;

    // Align the buffer and save the fixup size.
    stream.Align(8);

    // switch, union arm, pUnk.
    pStgmed->tymed = stream.ReadULONGNA();
    
    UserNdrDebugOut((UNDR_FORCE, "--STGMEDIUM_UserUnmarshal64: %s\n",
                     WdtpGetStgmedName(pStgmed) ));

    // (Force the align here so we only align once)
    stream.Align(8);
    if ( pStgmed->tymed != TYMED_NULL )
    {
        // This value is just a marker for the handle - a long.
        Handle = stream.ReadHYPERNA();
    }
    
    // pUnkForRelease pointer marker.
    fUnkForRelease = stream.ReadHYPERNA();

    // First pointee

    // Union arm pointee.
    // We need to unmarshal only if the handle/pointer field was not NULL.
    if ( Handle )
    {
        SetContextFlagsForAsyncCall( pFlags, pStgmed );

        hyper* pBuf = (hyper*)stream.GetBuffer();

        switch( pStgmed->tymed )
        {
        case TYMED_NULL:
            break;

        case TYMED_MFPICT:            
            // validate the handle...
            stream.CheckSize( 4 + 4 + 8 ); // enc. union: 4b switch + 4b pad + 8b handle            
            
            if ( Handle != pBuf[1] )
                RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );

            mark = HMETAFILEPICT_UserUnmarshalWorker64 (pFlags,
                                                        stream.GetBuffer(),
                                                        &pStgmed->hMetaFilePict,
                                                        stream.BytesRemaining() );
            stream.AdvanceTo(mark);
            break;

        case TYMED_ENHMF:
            // validate the handle...
            stream.CheckSize( 4 + 4 + 8 ); // enc. union: 4b switch + 4b pad + 8b handle            
            
            if ( Handle != pBuf[1] )
                RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );            

            mark = HENHMETAFILE_UserUnmarshalWorker64 ( pFlags,
                                                        stream.GetBuffer(),
                                                        &pStgmed->hEnhMetaFile,
                                                        stream.BytesRemaining() );
            stream.AdvanceTo(mark);
            break;

        case TYMED_GDI:
            {
                // A GDI object is not necesarrily a BITMAP.  Therefore, we
                // handle those types we know about based on the object type,
                // and reject those which we do not support.
                
                DWORD GdiObjectType = stream.ReadULONGNA();
                Handle = stream.ReadHYPER();

                switch( GdiObjectType )
                {
                case OBJ_BITMAP:
                    // Lookahead validation of the handle. 
                    stream.CheckSize( 4 + 4 + 8 );
                    pBuf = (hyper*)stream.GetBuffer();
                    if ( Handle != pBuf[1] )
                        RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );

                    mark = HBITMAP_UserUnmarshalWorker64( pFlags,
                                                          stream.GetBuffer(),
                                                          &pStgmed->hBitmap,
                                                          stream.BytesRemaining() );
                    stream.AdvanceTo(mark);
                    break;

                case OBJ_PAL:
                    // Lookahead validaton of the handle.
                    stream.CheckSize( 4 + 4 + 8 );
                    pBuf = (hyper*)stream.GetBuffer();
                    if ( Handle != pBuf[1] )
                        RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );

                    mark = HPALETTE_UserUnmarshalWorker64( pFlags,
                                                           stream.GetBuffer(),
                                                           (HPALETTE *) & pStgmed->hBitmap,
                                                           stream.BytesRemaining() );
                    stream.AdvanceTo(mark);
                    break;
                    
                default:
                    RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );
                }
            }
            break;

        case TYMED_HGLOBAL:
            // reallocation is forbidden for [in-out] hglobal in STGMEDIUM.
            // validate the handle.
            stream.CheckSize( 4 + 4 + 8 ); // enc. union: 4b switch + 4b pad + 8b handle            
            
            if ( Handle != pBuf[1] )
                RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );
            
            mark = WdtpGlobalUnmarshal64( pFlags,
                                          stream.GetBuffer(),
                                          & pStgmed->hGlobal,
                                          FALSE,
                                          stream.BytesRemaining() );
            stream.AdvanceTo(mark);
            break;
        
        case TYMED_FILE:
            {
                // Must be room in buffer for header.
                ulong Count = (ulong)stream.ReadHYPERNA();

                // We marshal it as a [string].
                if ( stream.ReadHYPERNA() != 0 )
                    RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );
                if ( stream.ReadHYPERNA() != Count )
                    RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );

                if ( ! pStgmed->lpszFileName )
                    pStgmed->lpszFileName = (LPOLESTR) WdtpAllocate( pFlags, Count * sizeof(wchar_t) );

                // Must be room in the buffer for the string.
                stream.CheckSize( Count * sizeof(WCHAR) );
                memcpy(pStgmed->lpszFileName, 
                       stream.GetBuffer(), 
                       Count * sizeof(wchar_t));
                stream.Advance( Count * sizeof(WCHAR) );
            }
            break;

        case TYMED_ISTREAM:
            mark = WdtpInterfacePointer_UserUnmarshalWorker((USER_MARSHAL_CB *)pFlags,
                                                            stream.GetBuffer(),
                                                            (IUnknown **) &pStgmed->pstm,
                                                            IID_IStream,
                                                            stream.BytesRemaining(),
                                                            TRUE);
            stream.AdvanceTo(mark);
            break;

        case TYMED_ISTORAGE:
            mark = WdtpInterfacePointer_UserUnmarshalWorker((USER_MARSHAL_CB *)pFlags,
                                                            stream.GetBuffer(),
                                                            (IUnknown **) &pStgmed->pstg,
                                                            IID_IStorage,
                                                            stream.BytesRemaining(),
                                                            TRUE);
            stream.AdvanceTo(mark);
            break;

        default:
            RAISE_RPC_EXCEPTION( RPC_X_BAD_STUB_DATA );
            break;
        }
    }
    else
    {
        // New handle/pointer field is null, so release the previous one
        // if it wasn't null.

        if ( pStgmed->hGlobal )
        {
            // This should never happen for GetDataHere.
            
            // Note, that we release the handle field, not the stgmedium itself.
            // Accordingly, we don't follow punkForRelease.            
            UserNdrDebugOut((
                  UNDR_FORCE,
                  "--STGMEDIUM_UserUnmarshal64: %s: NULL in, freeing old one\n",
                  WdtpGetStgmedName(pStgmed)));

            STGMEDIUM  TmpStg = *pStgmed;
            TmpStg.pUnkForRelease = NULL;
            
            if ( pStgmed->tymed == TYMED_HGLOBAL )
            {
                // Cannot reallocate.
                RAISE_RPC_EXCEPTION(DV_E_TYMED);
            }
            else
            {
                ReleaseStgMedium( &TmpStg );
            }
        }
        
        pStgmed->hGlobal = 0;
    }

    if ( fUnkForRelease )
    {
        // There is an interface pointer on the wire.
        mark = WdtpInterfacePointer_UserUnmarshalWorker((USER_MARSHAL_CB *)pFlags,
                                                        stream.GetBuffer(),
                                                        &pStgmed->pUnkForRelease,
                                                        IID_IUnknown,
                                                        stream.BytesRemaining(),
                                                        TRUE);
        stream.AdvanceTo(mark);
    }

    if ( pStgmed->pUnkForRelease )
    {
        // Replace the app's punkForRelease with our custom release
        // handler for special situations.

        // The special situation is when a handle is remoted with data
        // and so we have to clean up a side effect of having a data copy
        // around. UserFree does it properly but we need that for the callee.
        // When the callee releases a stgmed, it would invoke
        // ReleaseStgMedium and this API doesn't do anything for handles
        // when the punkForRelease is not NULL.
        ULONG fHandleWithData = 0;
        ULONG fTopLevelOnly = 0;

        switch ( pStgmed->tymed )
        {
        case TYMED_HGLOBAL:
            fHandleWithData = HGLOBAL_DATA_PASSING( *pFlags );
            break;
            
        case TYMED_ENHMF:
        case TYMED_GDI:
            fHandleWithData = GDI_DATA_PASSING( *pFlags );
            break;
            
        case TYMED_MFPICT:
            fHandleWithData = HGLOBAL_DATA_PASSING( *pFlags );
            fTopLevelOnly   = fHandleWithData  && !GDI_DATA_PASSING( *pFlags );
            break;
            
        default:
            break;
        }

        if ( fHandleWithData )
        {
            IUnknown *
                punkTmp = (IUnknown *) new CPunkForRelease( pStgmed,
                                                            fTopLevelOnly );
            if (!punkTmp)
            {
                RAISE_RPC_EXCEPTION(E_OUTOFMEMORY);
            }

            pStgmed->pUnkForRelease = punkTmp;
        }
    }

    return( stream.GetBuffer() );
}

//+-------------------------------------------------------------------------
//
//  Function:   STGMEDIUM_UserUnmarshal64
//
//  Synopsis:   Unmarshals a stgmedium object for RPC.
//
//  history:    Dec-00   JohnDoty      Created based on 32b function.
//
//--------------------------------------------------------------------------

unsigned char __RPC_FAR * __RPC_USER
STGMEDIUM_UserUnmarshal64 (
    unsigned long * pFlags,
    unsigned char * pBuffer,
    STGMEDIUM     * pStgmed )
{
    // Init buffer size and ptr to buffer.
    CUserMarshalInfo MarshalInfo( pFlags, pBuffer );
    ULONG_PTR BufferSize   = MarshalInfo.GetBufferSize();
    UCHAR*    pBufferStart = MarshalInfo.GetBuffer();

    pBuffer = STGMEDIUM_UserUnmarshalWorker64( pFlags,
                                               pBufferStart,
                                               pStgmed,
                                               BufferSize );
    return( pBuffer );
}


//+-------------------------------------------------------------------------
//
//  Function:   STGMEDIUM_UserFree64
//
//  Synopsis:   Frees a stgmedium object for RPC.
//
//  history:    Dec-00   JohnDoty      Created based on 32b function.
//
//  Note:       This routine is called from the freeing walk at server
//              or from the SetData *proxy*, when ownership has been passed.
//
//--------------------------------------------------------------------------

void __RPC_USER
STGMEDIUM_UserFree64 (
    unsigned long * pFlags,
    STGMEDIUM * pStgmed )
{
    UserNdrDebugOut((UNDR_FORCE, "--STGMEDIUM_UserFree64: %s\n", WdtpGetStgmedName(pStgmed)));

    if( pStgmed )
    {
        SetContextFlagsForAsyncCall( pFlags, pStgmed );
        
        switch ( pStgmed->tymed )
        {
        case TYMED_FILE:
            WdtpFree( pFlags, pStgmed->lpszFileName);
            NukeHandleAndReleasePunk( pStgmed );
            break;
            
        case TYMED_NULL:
        case TYMED_ISTREAM:
        case TYMED_ISTORAGE:
            ReleaseStgMedium( pStgmed );
            break;
            
        case TYMED_GDI:
        case TYMED_ENHMF:
            
            if ( GDI_HANDLE_PASSING(*pFlags) )
            {
                NukeHandleAndReleasePunk( pStgmed );
            }
            else
            {
                // Handle w/data: there is a side effect to clean up.
                // For punk !=0, this will go to our CPunk object.
                
                ReleaseStgMedium( pStgmed );
            }
            break;
            
        case TYMED_HGLOBAL:
            
            if ( HGLOBAL_HANDLE_PASSING(*pFlags) )
            {
                NukeHandleAndReleasePunk( pStgmed );
            }
            else
            {
                // Handle w/data: there is a side effect to clean up.
                // For punk ==0, this will just release the data.
                // For punk !=0, this will go to our CPunk object,
                // release the data, and then call the original punk.
                
                ReleaseStgMedium( pStgmed );
            }
            break;
            
        case TYMED_MFPICT:
            
            if ( HGLOBAL_HANDLE_PASSING(*pFlags) )
            {
                NukeHandleAndReleasePunk( pStgmed );
            }
            else if ( GDI_HANDLE_PASSING(*pFlags) )
            {
                if ( pStgmed->pUnkForRelease )
                {
                    pStgmed->pUnkForRelease->Release();
                }
                else
                {
                    if ( pStgmed->hGlobal )
                        GlobalFree( pStgmed->hGlobal );
                    pStgmed->hGlobal = NULL;
                    pStgmed->tymed = TYMED_NULL;
                }
            }
            else
            {
                // Handle w/data: there is a side effect to clean up.
                // For punk !=0, this will go to our CPunk object.
                
                ReleaseStgMedium( pStgmed );
            }
            break;
            
        default:
            RAISE_RPC_EXCEPTION( E_INVALIDARG );
            break;
        }
    }
}

//+-------------------------------------------------------------------------
//
//  Function:   FLAG_STGMEDIUM_UserSize64
//
//  Synopsis:   Sizes a wrapper for stgmedium.
//
//  history:    Dec-00   JohnDoty      Created based on 32b function.
//
//--------------------------------------------------------------------------

unsigned long  __RPC_USER
FLAG_STGMEDIUM_UserSize64 (
    unsigned long * pFlags,
    unsigned long   Offset,
    FLAG_STGMEDIUM* pFlagStgmed )
{
    if ( ! pFlagStgmed )
        return Offset;

    LENGTH_ALIGN( Offset, 7 );

    Offset += 2 * sizeof(long);
    Offset  = STGMEDIUM_UserSize64 ( pFlags, Offset, & pFlagStgmed->Stgmed );

    return( Offset );
}

//+-------------------------------------------------------------------------
//
//  Function:   FLAG_STGMEDIUM_UserMarshal64
//
//  Synopsis:   Marshals a wrapper for stgmedium. Used in SetData.
//
//  history:    Dec-00   JohnDoty      Created based on 32b function.
//
//--------------------------------------------------------------------------

unsigned char __RPC_FAR * __RPC_USER
FLAG_STGMEDIUM_UserMarshal64 (
    unsigned long * pFlags,
    unsigned char * pBuffer,
    FLAG_STGMEDIUM* pFlagStgmed )
{
    if ( ! pFlagStgmed )
        return pBuffer;

    ALIGN( pBuffer, 7 );

    // Flags: we need them when freeing in the client call_as routine

    pFlagStgmed->ContextFlags = *pFlags;

    *( PULONG_LV_CAST pBuffer)++ = *pFlags;
    *( PULONG_LV_CAST pBuffer)++ = pFlagStgmed->fPassOwnership;

    pBuffer = STGMEDIUM_UserMarshal64( pFlags,
                                       pBuffer,
                                       & pFlagStgmed->Stgmed );

    return( pBuffer );
}

//+-------------------------------------------------------------------------
//
//  Function:   FLAG_STGMEDIUM_UserUnmarshal64
//
//  Synopsis:   Unmarshals a wrapper for stgmedium.
//
//  history:    Dec-00   JohnDoty      Created based on 32b function.
//
//--------------------------------------------------------------------------

unsigned char __RPC_FAR * __RPC_USER
FLAG_STGMEDIUM_UserUnmarshal64 (
    unsigned long * pFlags,
    unsigned char * pBuffer,
    FLAG_STGMEDIUM* pFlagStgmed )
{
    // Init buffer size.
    CUserMarshalInfo MarshalInfo( pFlags, pBuffer );
    CarefulBufferReader stream(pBuffer, MarshalInfo.GetBufferSize());


    // Align the buffer.
    stream.Align(8);
    
    // Flags and buffer marker
    pFlagStgmed->ContextFlags = stream.ReadULONGNA();

    // Flags: we need them when freeing in the client call_as routine
    // We need that in the Proxy, when we call the user free routine.
    pFlagStgmed->fPassOwnership = stream.ReadULONGNA();
    pFlagStgmed->ContextFlags   = *pFlags;

    pBuffer = STGMEDIUM_UserUnmarshalWorker64( pFlags,
                                               stream.GetBuffer(),
                                               & pFlagStgmed->Stgmed,
                                               stream.BytesRemaining() );
    return( pBuffer );
}

//+-------------------------------------------------------------------------
//
//  Function:   FLAG_STGMEDIUM_UserFree64
//
//  Synopsis:   Freess a wrapper for stgmedium.
//
//  history:    Dec-00   JohnDoty      Created based on 32b function.
//
//--------------------------------------------------------------------------

void __RPC_USER
FLAG_STGMEDIUM_UserFree64 (
    unsigned long * pFlags,
    FLAG_STGMEDIUM* pFlagsStgmed )
{
    if ( ! pFlagsStgmed->fPassOwnership )
        STGMEDIUM_UserFree64 ( pFlags, & pFlagsStgmed->Stgmed );

    // else the callee is supposed to release the stg medium.
}

//+-------------------------------------------------------------------------
//
//  Function:   ASYNC_STGMEDIUM_UserSize64
//
//  Synopsis:   Sizes a wrapper for stgmedium.
//
//  history:    Dec-00   JohnDoty      Created based on 32b function.
//
//--------------------------------------------------------------------------

unsigned long  __RPC_USER
ASYNC_STGMEDIUM_UserSize64 (
    unsigned long * pFlags,
    unsigned long   Offset,
    ASYNC_STGMEDIUM* pAsyncStgmed )
{
    if ( ! pAsyncStgmed )
        return Offset;

    // Needed to handle both GDI handles and Istream/IStorage correctly.

    // BTW: This is needed only as a workaround because the [async] attr
    // has been temporarily removed from objidl.idl. (May 1997).
    // After we have the new [async] in place, this code is unnecessary
    // as the NDR engine is setting the same flag for every async call.

    *pFlags |= USER_CALL_IS_ASYNC;

    Offset  = STGMEDIUM_UserSize64 ( pFlags, Offset, pAsyncStgmed );

    return( Offset );
}

//+-------------------------------------------------------------------------
//
//  Function:   ASYNC_STGMEDIUM_UserMarshal64
//
//  Synopsis:   Marshals a wrapper for stgmedium. Used in SetData.
//
//  history:    Dec-00   JohnDoty      Created based on 32b function.
//
//--------------------------------------------------------------------------

unsigned char __RPC_FAR * __RPC_USER
ASYNC_STGMEDIUM_UserMarshal64 (
    unsigned long * pFlags,
    unsigned char * pBuffer,
    ASYNC_STGMEDIUM* pAsyncStgmed )
{
    if ( ! pAsyncStgmed )
        return pBuffer;

    // Needed to handle both GDI handles and Istream/IStorage correctly.

    *pFlags |= USER_CALL_IS_ASYNC;

    pBuffer = STGMEDIUM_UserMarshal64( pFlags,
                                       pBuffer,
                                       pAsyncStgmed );

    return( pBuffer );
}

//+-------------------------------------------------------------------------
//
//  Function:   ASYNC_STGMEDIUM_UserUnmarshal64
//
//  Synopsis:   Unmarshals a wrapper for stgmedium.
//
//  history:    Dec-00   JohnDoty      Created based on 32b function
//
//--------------------------------------------------------------------------

unsigned char __RPC_FAR * __RPC_USER
ASYNC_STGMEDIUM_UserUnmarshal64(
    unsigned long * pFlags,
    unsigned char * pBuffer,
    ASYNC_STGMEDIUM* pAsyncStgmed )
{
    // Init buffer size.
    CUserMarshalInfo MarshalInfo( pFlags, pBuffer );
    ULONG_PTR BufferSize   = MarshalInfo.GetBufferSize();
    UCHAR*    pBufferStart = MarshalInfo.GetBuffer();

    // Needed to handle both GDI handles and Istream/IStorage correctly.

    *pFlags |= USER_CALL_IS_ASYNC;

    pBuffer =  STGMEDIUM_UserUnmarshalWorker64( pFlags,
                                                pBufferStart,
                                                pAsyncStgmed,
                                                BufferSize );

    return( pBuffer );
}

//+-------------------------------------------------------------------------
//
//  Function:   ASYNC_STGMEDIUM_UserFree64
//
//  Synopsis:   Freess a wrapper for stgmedium.
//
//  history:    Dec-00   JohnDoty      Created based on 32b function.
//
//--------------------------------------------------------------------------

void __RPC_USER
ASYNC_STGMEDIUM_UserFree64(
    unsigned long * pFlags,
    ASYNC_STGMEDIUM* pAsyncStgmed )
{
    // Needed to handle both GDI handles and Istream/IStorage correctly.

    *pFlags |= USER_CALL_IS_ASYNC;

    STGMEDIUM_UserFree64( pFlags, pAsyncStgmed );
}

#endif