//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1994.
//
//  File:       thop32.cxx
//
//  Contents:   Thop implementations for 32->16
//
//  History:    22-Feb-94       DrewB   Created
//
//----------------------------------------------------------------------------

#include "headers.cxx"
#pragma hdrstop

#include <ole2.h>
#include <string.h>
#include <valid.h>
#include "olethk32.hxx"
#include "struct16.hxx"

//+---------------------------------------------------------------------------
//
//  Function:   EXECUTE_THOP3216, public
//
//  Synopsis:   Debugging version of thop dispatch routine
//
//  Arguments:  [pti] - Thunking info
//
//  Returns:    Appropriate status
//
//  History:    24-Feb-94       DrewB   Created
//
//----------------------------------------------------------------------------

#if DBG == 1
DWORD EXECUTE_THOP3216(THUNKINFO *pti)
{
    thkDebugOut((DEB_THOPS, "ExThop3216: %s (0x%02X), s16 %p, s32 %p\n",
                 ThopName(*pti->pThop), *pti->pThop, pti->s16.pbCurrent,
                 pti->s32.pbCurrent));
    thkAssert((*pti->pThop & THOP_OPMASK) < THOP_LASTOP);
    return (*aThopFunctions3216[*((pti)->pThop) & THOP_OPMASK])(pti);
}
#endif

#if DBG == 1
DWORD EXECUTE_ENUMTHOP3216(THUNKINFO *pti)
{
    thkDebugOut((DEB_THOPS, "ExEnumThop3216: %s (0x%02X), s16 %p, s32 %p\n",
                 EnumThopName(*pti->pThop), *pti->pThop, pti->s16.pbCurrent,
                 pti->s32.pbCurrent));
    return (*aThopEnumFunctions3216[*(pti)->pThop])(pti);
}
#endif

//+---------------------------------------------------------------------------
//
//  Function:   FixedThopHandler, public
//
//  Synopsis:   Generic function which handles the high-level details
//              of thop execution for thops that operate on known-size
//              data
//
//  Arguments:  [pti] - Thunking state information
//              [thop] - Thop being executed
//              [cb16] - 16-bit size
//              [pfn1632] - 16->32 conversion routine
//              [cb32] - 32-bit size
//              [pfn3216] - 32->16 conversion routine
//
//  Returns:    Appropriate status code
//
//  History:    05-Apr-94       DrewB   Created
//
//  Notes:      Automatically increments pThop
//
//----------------------------------------------------------------------------

DWORD FixedThopHandler3216(THUNKINFO *pti,
                           THOP thop,
                           UINT cb16,
                           FIXEDHANDLERROUTINE pfn1632,
                           UINT cb32,
                           FIXEDHANDLERROUTINE pfn3216)
{
    DWORD   dwResult;
    BOOL    fThopInput;
    BOOL    fThopOutput;
    VPVOID  vp16;
    BYTE    *pb16;
    BYTE    *pb32;

    fThopInput  = (BOOL)(thop & THOP_IN);
    fThopOutput = (BOOL)(thop & THOP_OUT);

    if ( fThopInput || fThopOutput )
    {
        vp16 = 0;

        GET_STACK32(pti, pb32, BYTE *);
        if ( pb32 != 0 )
        {
            if (fThopInput)
            {
                if (IsBadReadPtr(pb32, cb32))
                {
                    return (DWORD)E_INVALIDARG;
                }
            }
            if (fThopOutput)
            {
                if (IsBadWritePtr(pb32, cb32))
                {
                    return (DWORD)E_INVALIDARG;
                }
            }

            vp16 = STACKALLOC16(cb16);
            if (vp16 == 0)
            {
                return (DWORD)E_OUTOFMEMORY;
            }
            else if (fThopInput)
            {
                pb16 = (BYTE *)WOWGetVDMPointer(vp16, cb16, TRUE);
                (pfn3216)(pb32, pb16, cb32, cb16);
            }
        }

        TO_STACK16(pti, vp16, VPVOID);

        pti->pThop++;
        dwResult = EXECUTE_THOP3216(pti);

        if ( fThopOutput && pb32 != NULL )
        {
            if (SUCCEEDED(dwResult))
            {
                pb16 = (BYTE *)WOWGetVDMPointer(vp16, cb16, TRUE);
                (pfn1632)(pb16, pb32, cb16, cb32);
            }
            else if (!fThopInput)
            {
                // Zero out-only parameters on failure
                memset(pb32, 0, cb32);
            }
        }

        if (vp16 != 0)
        {
            STACKFREE16(vp16, cb16);
        }
    }
    else
    {
        (pfn3216)(PTR_STACK32(&pti->s32), PTR_STACK16(&pti->s16, cb16),
                  cb32, cb16);

        SKIP_STACK16(&pti->s16, cb16);
        SKIP_STACK32(&pti->s32, cb32);

        pti->pThop++;
        dwResult = EXECUTE_THOP3216(pti);
    }

    return dwResult;
}

//-----------------------------------------------------------------------------
//
// Handler-based thunks
//
// These thunks use the fixed-size generic thop handler to do their work
//
//-----------------------------------------------------------------------------

// Handle straight copy
DWORD Thop_Copy_3216(THUNKINFO *pti)
{
    THOP thopSize;

    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_COPY);

    thopSize = *++pti->pThop;
    return FixedThopHandler3216(pti,
                                *(pti->pThop-1),
                                thopSize, FhCopyMemory,
                                thopSize, FhCopyMemory);
}

DWORD Thop_ShortToLong_3216(THUNKINFO *pti)
{
    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_SHORTLONG);

    return FixedThopHandler3216(pti,
                                *pti->pThop,
                                sizeof(SHORT), FhShortToLong,
                                sizeof(LONG), FhLongToShort);
}

DWORD Thop_WordToDword_3216(THUNKINFO *pti)
{
    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_WORDDWORD);

    return FixedThopHandler3216(pti,
                                *pti->pThop,
                                sizeof(WORD), FhWordToDword,
                                sizeof(DWORD), FhDwordToWord);
}

DWORD Thop_GdiHandle_3216(THUNKINFO *pti)
{
    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_HGDI);

    return FixedThopHandler3216(pti,
                                *pti->pThop,
                                sizeof(HAND16), FhGdiHandle1632,
                                sizeof(HANDLE), FhGdiHandle3216);
}

DWORD Thop_UserHandle_3216(THUNKINFO *pti)
{
    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_HUSER);

    return FixedThopHandler3216(pti,
                                *pti->pThop,
                                sizeof(HAND16), FhUserHandle1632,
                                sizeof(HANDLE), FhUserHandle3216);
}

DWORD Thop_HACCEL_3216(THUNKINFO *pti)
{
    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_HACCEL);

    return FixedThopHandler3216(pti,
                                *pti->pThop,
                                sizeof(HAND16), FhHaccel1632,
                                sizeof(HANDLE), FhHaccel3216);
}

DWORD Thop_HTASK_3216(THUNKINFO *pti)
{
    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_HTASK);

    return FixedThopHandler3216(pti,
                                *pti->pThop,
                                sizeof(HAND16), FhHtask1632,
                                sizeof(HANDLE), FhHtask3216);
}

DWORD Thop_HRESULT_3216( THUNKINFO *pti )
{
    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_HRESULT);

    return FixedThopHandler3216(pti,
                                *pti->pThop,
                                sizeof(HRESULT), FhHresult1632,
                                sizeof(HRESULT), FhHresult3216);
}

DWORD Thop_NULL_3216(THUNKINFO *pti)
{
    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_NULL);

    return FixedThopHandler3216(pti,
                                *pti->pThop,
                                sizeof(void *), FhNull,
                                sizeof(void *), FhNull);
}

DWORD Thop_RECT_3216( THUNKINFO *pti )
{
    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_RECT);

    return FixedThopHandler3216(pti,
                                *pti->pThop,
                                sizeof(RECT16), FhRect1632,
                                sizeof(RECT), FhRect3216);
}

DWORD Thop_BINDOPTS_3216( THUNKINFO *pti )
{
    LPBIND_OPTS pbo;
    UINT cb;

    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_BINDOPTS);

    PEEK_STACK32(pti, pbo, LPBIND_OPTS);
    if (!IsBadReadPtr(pbo, sizeof(LPBIND_OPTS)))
    {
        cb = pbo->cbStruct;
    }
    else
    {
        return (DWORD)E_INVALIDARG;
    }

    return FixedThopHandler3216(pti,
                                *pti->pThop,
                                cb, FhCopyMemory,
                                cb, FhCopyMemory);
}

DWORD Thop_SIZE_3216( THUNKINFO *pti )
{
    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_SIZE);

    return FixedThopHandler3216(pti,
                                *pti->pThop,
                                sizeof(SIZE16), FhSize1632,
                                sizeof(SIZE), FhSize3216);
}

DWORD Thop_MSG_3216( THUNKINFO *pti )
{
    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_MSG);

    return FixedThopHandler3216(pti,
                                *pti->pThop,
                                sizeof(MSG16), FhMsg1632,
                                sizeof(MSG), FhMsg3216);
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_ERROR_3216, public
//
//  Synopsis:   Any Thop type which should just fail with an error
//              should go be directed here.
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    23-Feb-94       BobDay  Created
//
//----------------------------------------------------------------------------
DWORD Thop_ERROR_3216 ( THUNKINFO *pti )
{
    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_ERROR);

    thkAssert( FALSE && "Hey we hit an ERROR Thop in 32->16" );

    return (DWORD)E_UNEXPECTED;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_LPSTR_3216, public
//
//  Synopsis:   Converts 32-bit LPOLESTR to 16-bit LPSTR pointer
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    23-Feb-94       BobDay  Created
//
//----------------------------------------------------------------------------

DWORD Thop_LPSTR_3216( THUNKINFO *pti )
{
    DWORD       dwResult;
    BOOL        fThopInput;
    BOOL        fThopOutput;
    LPOLESTR    lpstr32;
    VPSTR       vpstr16;
    UINT        uiSize;

    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_LPSTR);

    fThopInput  = IS_THOP_IN(pti);
    fThopOutput = IS_THOP_OUT(pti);

    //
    // We have only input LPSTRs
    //
    thkAssert( fThopInput && !fThopOutput && "LPSTR must be input only!" );

    dwResult = (DWORD)S_OK;

    GET_STACK32(pti, lpstr32, LPOLESTR);

    vpstr16 = 0;
    if (lpstr32 != NULL)
    {
        if (IsBadStringPtr(lpstr32, CCHMAXSTRING))
        {
            return (DWORD)E_INVALIDARG;
        }

        uiSize = wcslen( lpstr32 ) + 1;

        vpstr16 = STACKALLOC16(uiSize*2);
        if (vpstr16 == 0)
        {
            dwResult = (DWORD)E_OUTOFMEMORY;
        }
        else
        {
            dwResult = Convert_LPOLESTR_to_VPSTR(lpstr32, vpstr16, uiSize,
                                                 uiSize*2);
        }
    }

    if (SUCCEEDED((SCODE)dwResult))
    {
        thkDebugOut((DEB_ARGS, "In3216  LPSTR %p -> %p '%ws'\n",
                     lpstr32, vpstr16, lpstr32));

        TO_STACK16(pti, vpstr16, VPSTR);

        pti->pThop++;
        dwResult = EXECUTE_THOP3216(pti);
    }

    if (vpstr16 != 0)
    {
        STACKFREE16(vpstr16, uiSize*2);
    }

    return( dwResult );
}

//+---------------------------------------------------------------------------
//
//  Function:   ConvertTaskString1632, public
//
//  Synopsis:   Converts a task-memory string
//
//  Arguments:  [pti] - Thunk info
//              [vpstr16] - String
//              [posPreAlloc] - Preallocated string or NULL
//              [cchPreAlloc] - Preallocated size or zero
//              [ppos32] - String
//
//  Returns:    Appropriate status code
//
//  Modifies:   [pti]
//              [ppos32]
//
//  History:    14-May-94       DrewB   Created
//
//  Notes:      Frees preallocation if successful and:
//                  Name is too large or
//                  Name is NULL
//
//              Always frees source string if non-zero
//
//----------------------------------------------------------------------------

SCODE ConvertTaskString1632(THUNKINFO *pti,
                            VPSTR vpstr16,
                            LPOLESTR posPreAlloc,
                            UINT cchPreAlloc,
                            LPOLESTR *ppos32)
{
    LPOLESTR pos32;

    if (vpstr16 == 0)
    {
        pos32 = NULL;
    }
    else
    {
        pos32 = Convert_VPSTR_to_LPOLESTR(pti, vpstr16, posPreAlloc,
                                          cchPreAlloc);

        TaskFree16(vpstr16);

        if (pos32 == NULL)
        {
            return pti->scResult;
        }
    }

    // If there was a preallocated string we didn't use,
    // free it
    if (posPreAlloc != NULL && posPreAlloc != pos32)
    {
        TaskFree32(posPreAlloc);
    }

    *ppos32 = pos32;

    return S_OK;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_LPLPSTR_3216, public
//
//  Synopsis:   Converts 16-bit LPSTR to 32-bit LPSTR pointer
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    26-Feb-94       BobDay  Created
//
//----------------------------------------------------------------------------
DWORD Thop_LPLPSTR_3216( THUNKINFO *pti )
{
    DWORD           dwResult;
    BOOL            fThopInput;
    BOOL            fThopOutput;
    LPOLESTR        *lplpstr32;
    VPVOID          vpvpstr16;
    VPSTR UNALIGNED *lpvpstr16;
    LPOLESTR        lpstr32;

    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_LPLPSTR);

    fThopInput  = IS_THOP_IN(pti);
    fThopOutput = IS_THOP_OUT(pti);

    //
    // We don't have anything but unmodified LPLPSTRs
    //
    thkAssert( !fThopInput && !fThopOutput &&
               "LPLPSTR must be unmodified only!" );

    GET_STACK32(pti, lplpstr32, LPOLESTR FAR *);

    if ( lplpstr32 == NULL )
    {
        vpvpstr16 = 0;
    }
    else
    {
        if (IsBadWritePtr(lplpstr32, sizeof(LPOLESTR)))
        {
            return (DWORD)E_INVALIDARG;
        }

        vpvpstr16 = STACKALLOC16(sizeof(VPSTR));
        if (vpvpstr16 == 0)
        {
            return (DWORD)E_OUTOFMEMORY;
        }

        lpstr32 = (LPOLESTR)TaskMalloc32(CBSTRINGPREALLOC);
        if (lpstr32 == NULL)
        {
            STACKFREE16(vpvpstr16, sizeof(VPSTR));
            return (DWORD)E_OUTOFMEMORY;
        }
    }

    TO_STACK16(pti, vpvpstr16, VPVOID);

    pti->pThop++;
    dwResult = EXECUTE_THOP3216(pti);

    if (lplpstr32 != NULL)
    {
        if (SUCCEEDED(dwResult))
        {
            lpvpstr16 = GETVDMPTR(vpvpstr16, VPSTR);
            if (lpvpstr16 == NULL)
            {
                dwResult = (DWORD)E_INVALIDARG;
            }
            else
            {
                SCODE sc;

                sc = ConvertTaskString1632(pti, *lpvpstr16, lpstr32,
                                           CWCSTRINGPREALLOC, &lpstr32);
                if (FAILED(sc))
                {
                    dwResult = sc;
                }
            }
        }

        if (FAILED(dwResult))
        {
            TaskFree32(lpstr32);

            *lplpstr32 = NULL;
        }
        else
        {
            *lplpstr32 = lpstr32;
        }

        thkDebugOut((DEB_ARGS, "Out3216 LPLPSTR: %p -> %p, '%ws'\n",
                     *lpvpstr16, lpstr32, lpstr32));
    }

    if (vpvpstr16 != 0)
    {
        STACKFREE16(vpvpstr16, sizeof(VPSTR));
    }

    return( dwResult );
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_BUFFER_3216, public
//
//  Synopsis:   Converts 32-bit block of memory to 16-bit block of memory
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    25-Feb-94       BobDay  Created
//
//----------------------------------------------------------------------------

#define WATCH_VALUE 0xfef1f0

DWORD Thop_BUFFER_3216( THUNKINFO *pti )
{
    DWORD       dwResult;
    BOOL        fThopInput;
    BOOL        fThopOutput;
    LPVOID      lp32;
    VPVOID      vp16;
    LPVOID      lp16;
    DWORD       dwCount;

    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_BUFFER);

    fThopInput  = IS_THOP_IN(pti);
    fThopOutput = IS_THOP_OUT(pti);

    //
    // Buffers can only be in or out
    //
    thkAssert( (fThopInput || fThopOutput) &&
               (fThopInput != fThopOutput) &&
               "BUFFER must be in or out only!" );

    GET_STACK32(pti, lp32, LPVOID);
    GET_STACK32(pti, dwCount, DWORD);

    if ( lp32 == NULL )
    {
        vp16 = 0;
    }
    else if (dwCount == 0)
    {
        // If the count is zero then we can pass any valid 16-bit
        // pointer

#if DBG == 1
        // In debug, make sure that no data is written back to the
        // memory we pass on
        vp16 = STACKALLOC16(sizeof(DWORD));
        if ( vp16 == 0 )
        {
            return (DWORD)E_OUTOFMEMORY;
        }
        *GETVDMPTR(vp16, DWORD) = WATCH_VALUE;
#else
        vp16 = gdata16Data.atfnProxy1632Vtbl;
#endif
    }
    else
    {
        if ((fThopInput && IsBadReadPtr(lp32, dwCount)) ||
            (fThopOutput && IsBadWritePtr(lp32, dwCount)))
        {
            return (DWORD)E_INVALIDARG;
        }

        vp16 = (VPVOID)WOWGlobalAllocLock16( GMEM_MOVEABLE, dwCount, NULL );
        if ( vp16 == 0 )
        {
            return (DWORD)E_OUTOFMEMORY;
        }

        if ( fThopInput )
        {
            lp16 = (LPVOID)WOWGetVDMPointer(vp16, dwCount, TRUE);
            memcpy( lp16, lp32, dwCount );
        }
    }

    thkDebugOut((DEB_ARGS, "3216    BUFFER: %p -> %p, %u\n",
                 lp32, vp16, dwCount));

    TO_STACK16(pti, vp16, VPVOID );
    TO_STACK16(pti, dwCount, DWORD );

    pti->pThop++;
    dwResult = EXECUTE_THOP3216(pti);

    if ( SUCCEEDED(dwResult) && fThopOutput )
    {
        if (dwCount > 0)
        {
            lp16 = (LPVOID)WOWGetVDMPointer( vp16, dwCount, TRUE );
            memcpy( lp32, lp16, dwCount );
        }
    }

#if DBG == 1
    if (vp16 != 0 && dwCount == 0)
    {
        thkAssert(*GETVDMPTR(vp16, DWORD) == WATCH_VALUE);
        STACKFREE16(vp16, sizeof(DWORD));
    }
#endif

    //
    // Now free the buffer
    //
    if ( vp16 != 0 && dwCount > 0 )
    {
        WOWGlobalUnlockFree16( vp16 );
    }

    return( dwResult );
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_SNB_3216, public
//
//  Synopsis:   Converts 32-bit SNB to 16-bit SNB pointer
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    23-Feb-94       BobDay  Created
//
//----------------------------------------------------------------------------
DWORD Thop_SNB_3216( THUNKINFO *pti )
{
    DWORD           dwResult;
    BOOL            fThopInput;
    BOOL            fThopOutput;
    SNB             snb32;
    VPVOID          snb16;
    LPOLESTR FAR    *lplpstr;
    LPOLESTR FAR    *lplpstrTemp;
    VPSTR UNALIGNED FAR *lpvpstr16;
    LPOLESTR        lpstr32;
    UINT            cStr;
    UINT            cStrTemp;
    UINT            cLength;
    UINT            cbStrings;
    char UNALIGNED  *pszStr;
    UINT            cChars;
    UINT            cbAlloc;
    VPVOID          vpsnb16;

    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_SNB);

    fThopInput  = IS_THOP_IN(pti);
    fThopOutput = IS_THOP_OUT(pti);

    //
    // We don't have anything but unmodified SNBs
    //
    thkAssert( !fThopInput && !fThopOutput && "SNB must be unmodified only!" );

    GET_STACK32(pti, snb32, LPOLESTR FAR *);

    if ( snb32 == NULL )
    {
        snb16 = 0;
    }
    else
    {
        //
        // Count the strings in the 32-bit snb
        //
        lplpstr = snb32;
        lplpstrTemp = lplpstr;

        cStr = 0;
        cbStrings = 0;
        do
        {
            cStr++;
            if (IsBadReadPtr(lplpstrTemp, sizeof(LPOLESTR)))
            {
                return (DWORD)E_INVALIDARG;
            }

            lpstr32 = *lplpstrTemp++;

            if ( lpstr32 == NULL )
            {
                break;
            }

            if (IsBadStringPtr(lpstr32, CCHMAXSTRING))
            {
                return (DWORD)E_INVALIDARG;
            }

            cbStrings += wcslen(lpstr32)+1;
        }
        while ( TRUE );

        // Double to account for multi-byte translation growth
        cbStrings *= 2;

        //
        // Allocate a table for the 16-bit snb
        //
        cbAlloc = cStr*sizeof(VPSTR)+cbStrings;
        snb16 = (VPVOID)STACKALLOC16(cbAlloc);
        if (snb16 == 0)
        {
            return (DWORD)E_OUTOFMEMORY;
        }

        //
        // Now convert the strings
        //
        lpvpstr16 = (VPSTR UNALIGNED FAR *)WOWGetVDMPointer( snb16, cbAlloc,
                                                             TRUE );
        pszStr = (char UNALIGNED *)
            ((BYTE UNALIGNED *)lpvpstr16+cStr*sizeof(VPSTR));

        vpsnb16 = snb16;

        lplpstrTemp = lplpstr;
        cStrTemp = cStr - 1;
        while ( cStrTemp > 0 )
        {
            --cStrTemp;

            lpstr32 = *lplpstrTemp++;

            thkAssert( lpstr32 != NULL && "Loop is processing end of snb\n" );

            cLength = wcslen( lpstr32 ) + 1;

            cChars = WideCharToMultiByte( CP_ACP, 0, lpstr32, cLength,
                                          pszStr, cbStrings, NULL, NULL );
            if ( cChars == 0 && cLength != 0 )
            {
                STACKFREE16(snb16, cbAlloc);
                return (DWORD)E_UNEXPECTED;
            }

            *lpvpstr16++ = vpsnb16;

            vpsnb16 = (VPVOID)((DWORD)vpsnb16 + cChars);
        }

        // Terminate SNB
        *lpvpstr16 = NULL;

        thkAssert( *lplpstrTemp == NULL &&
                   "Loop is out of sync with count\n" );
    }

    thkDebugOut((DEB_ARGS, "In3216  SNB: %p -> %p\n", snb32, snb16));

    TO_STACK16(pti, snb16, VPVOID );

    pti->pThop++;
    dwResult = EXECUTE_THOP3216(pti);

    //
    // Free SNB data if necessary
    //
    if ( snb16 != 0 )
    {
        STACKFREE16( snb16, cbAlloc );
    }

    return( dwResult );
}

//+---------------------------------------------------------------------------
//
//  Function:   ThunkInterface3216, private
//
//  Synopsis:   Handles 32->16 interface thunking for THOP_IFACE and
//              THOP_IFACEGEN
//
//  Arguments:  [pti] - Thunking state information
//              [iidx] - Interface IID or index
//              [thop] - Thop being executed
//              [vpvOuter] - Controlling unknown or NULL
//
//  Returns:    Appropriate status code
//
//  History:    01-Mar-94       DrewB   Created
//
//  Notes:      Assumes pti->pThop is adjusted by caller
//
//----------------------------------------------------------------------------

DWORD ThunkInterface3216(THUNKINFO *pti,
                         IIDIDX iidx,
                         THOP thop,
                         VPVOID vpvOuter)
{
    DWORD dwResult;
    BOOL fThopIn, fThopOut;
    void *pv;
    VPVOID vpvOutParam;
    VPVOID vpvThis16In, vpvThis16Out;
    IUnknown *punkThis32;
    IUnknown *punkIn;
    THUNK3216OBJ *ptoPreAlloc = NULL;

    dwResult = (DWORD)S_OK;

    vpvOutParam = 0;
    vpvThis16In = 0;

    fThopIn = thop & THOP_IN;
    fThopOut = thop & THOP_OUT;

    // Retrieve in or out interface pointer
    GET_STACK32(pti, pv, void *);

    // Out takes precedence over in for determining indirection depth

    if (fThopOut && pv != NULL)
    {
        if (IsBadReadPtr(pv, sizeof(void *)) ||
            IsBadWritePtr(pv, sizeof(void *)))
        {
            thkDebugOut((DEB_WARN, "WARNING: failing - bad pointer %p\n", pv));
            return (DWORD)E_INVALIDARG;
        }
        else
        {
            punkIn = *(IUnknown **)pv;
        }
    }
    else
    {
        punkIn = (IUnknown *)pv;
    }

    if (fThopIn)
    {
        if (punkIn == NULL)
        {
            vpvThis16In = 0;
        }
        else
        {
            if (!IsValidInterface(punkIn))
            {
                dwResult = (DWORD)E_INVALIDARG;
                thkDebugOut((DEB_WARN, "WARNING: failing - "
                                       "invalid interface %p\n", punkIn));
            }
            else
            {
                thkAssert(IIDIDX_IS_IID(iidx) ||
                          (IIDIDX_INDEX(iidx) >= 0 &&
                           IIDIDX_INDEX(iidx) < THI_COUNT));

                vpvThis16In = pti->pThkMgr->FindProxy1632(NULL, punkIn, iidx,
                                                          NULL);
                if (vpvThis16In == 0)
                {
                    dwResult = (DWORD)E_OUTOFMEMORY;
                    thkDebugOut((DEB_WARN, "WARNING: failing - "
                                 "Can't create proxy for %p\n", punkIn));
                }
            }
        }

        thkDebugOut((DEB_ARGS, "In3216  %s %p -> %p\n",
                     IidIdxString(iidx), punkIn, vpvThis16In));
    }

    if (fThopOut && SUCCEEDED(dwResult) && pv != NULL)
    {
        thkAssert(IIDIDX_IS_IID(iidx) ||
                  (IIDIDX_INDEX(iidx) >= 0 &&
                   IIDIDX_INDEX(iidx) < THI_COUNT));

        // Preallocate a proxy for the out parameter
        if ((ptoPreAlloc = pti->pThkMgr->CanGetNewProxy3216(iidx)) == NULL)
        {
            dwResult = (DWORD)E_OUTOFMEMORY;
            thkDebugOut((DEB_WARN, "WARNING: failing - "
                         "Cannot allocate proxy\n"));
        }
        else
        {
            vpvOutParam = STACKALLOC16(sizeof(VPVOID));
            if (vpvOutParam == 0)
            {
                dwResult = (DWORD)E_OUTOFMEMORY;
            }
            else
            {
                *GETVDMPTR(vpvOutParam, VPVOID) = vpvThis16In;
                TO_STACK16(pti, vpvOutParam, VPVOID);
            }
        }
    }
    else
    {
        TO_STACK16(pti, vpvThis16In, VPVOID);
    }

    if (SUCCEEDED((SCODE)dwResult))
    {
        // Assumes pThop is already adjusted
        dwResult = EXECUTE_THOP3216(pti);
    }

    if (vpvThis16In != 0)
    {
        // Note that if the routine called keeps the proxy around
        // it should AddRef it so this won't really remove the object
        pti->pThkMgr->FreeProxy1632(punkIn);

        if ((thop & THOP_OPMASK) == THOP_IFACECLEAN)
        {
            // IRpcStubBuffer::DebugServerQueryInterface returns an
            // interface non-addref'ed
            // IRpcStubBuffer::DebugServerRelease cleans up a returned
            // pointer from QI, so for it we have to clean up the proxy
            // from QI

            // We're assuming that punkIn is actually a proxy
            pti->pThkMgr->ReleaseUnreferencedProxy3216((THUNK3216OBJ *)punkIn);
        }
    }

    if (fThopOut && pv != NULL)
    {
        punkThis32 = NULL;

        if (SUCCEEDED((SCODE)dwResult))
        {
            vpvThis16Out = *GETVDMPTR(vpvOutParam, VPVOID);

            // BUGBUG - No easy way to check interface validity
            // Requires a 16-bit transition

            if (vpvThis16Out != 0)
            {
                // Get a 32-bit proxy object for the 16-bit object
                if (vpvOuter != 0)
                {
                    punkThis32 =
                        pti->pThkMgr->FindAggregate3216(ptoPreAlloc, vpvOuter,
                                                        vpvThis16Out, iidx);
                }
                else
                {
                    punkThis32 =
                        pti->pThkMgr->FindProxy3216(ptoPreAlloc,
                                                    vpvThis16Out, iidx,
                                                    NULL);
                }
                if (punkThis32 == NULL)
                {
                    dwResult = (DWORD)E_UNEXPECTED;
                }
            }
        }
        else if (ptoPreAlloc != NULL)
        {
            // Return our preallocated proxy because we don't need it
            pti->pThkMgr->FreeNewProxy3216(ptoPreAlloc, iidx);
        }

        // Set the out param
        *(void **)pv = (void *)punkThis32;

        thkDebugOut((DEB_ARGS, "Out3216 %s %p -> %p\n",
                     IidIdxString(iidx), vpvThis16Out, punkThis32));
    }

    if (vpvOutParam != 0)
    {
        STACKFREE16(vpvOutParam, sizeof(VPVOID));
    }

    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_IFACEGEN_3216, public
//
//  Synopsis:   Thunks riid,ppv pairs from 16->32
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    23-Feb-94       DrewB   Created
//
//----------------------------------------------------------------------------

DWORD Thop_IFACEGEN_3216(THUNKINFO *pti)
{
    IIDIDX iidx;
    THOP thop, thopOp, thopWeakOffset;
    VPVOID vpvOuter;
    IID const *piid;

    thop = *pti->pThop++;
    thopOp = thop & THOP_OPMASK;

    thkAssert(thopOp == THOP_IFACEGEN ||
              thopOp == THOP_IFACEGENOWNER);

    // The current thop byte indicates how many bytes to look
    // back in the stack to find the IID which identifies the
    // interface being returned
    INDEX_STACK32(pti, piid, IID const *, *pti->pThop);

    if (!IsValidIid(*piid))
    {
        return (DWORD)E_INVALIDARG;
    }

    pti->pThop++;

    iidx = IidToIidIdx(*piid);
    vpvOuter = 0;
    if (thopOp == THOP_IFACEGENOWNER)
    {
        thopWeakOffset = *pti->pThop++;
        INDEX_STACK16(pti, vpvOuter, VPVOID, thopWeakOffset, sizeof(DWORD));
    }

    return ThunkInterface3216(pti, iidx, thop, vpvOuter);
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_OIFI_3216, public
//
//  Synopsis:   Convert OLEINPLACEFRAMEINFO
//
//  Arguments:  [pti] - Thunking state information
//
//  Returns:    Appropriate status code
//
//  History:    26-May-94       DrewB   Created
//
//----------------------------------------------------------------------------

DWORD Thop_OIFI_3216( THUNKINFO *pti )
{
    DWORD dwResult;
    VPVOID vpoifi16;
    OIFI16 UNALIGNED *poifi16;
    OLEINPLACEFRAMEINFO *poifi32;

    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_OIFI);
    thkAssert((*pti->pThop & THOP_IOMASK) == THOP_OUT);

    // OIFIs are out-only parameters for their contents
    // However, cb is in/out, so we need to copy cb on the way in
    // Furthermore, cb may not be set to a valid value, in which
    // case the documentation mentions that it should be assumed
    // that this is an OLE 2.0 OIFI
    // This thop simply ignores cb on the way in and always sets
    // it to the OLE 2.0 size
    // Since we're out-only, this always works since the number of
    // fields we thunk is the size of the structure that we give out
    // If OLEINPLACEFRAMEINFO is extended, this thop will break

    // Assert that OLEINPLACEFRAMEINFO is what we expect it to be
    thkAssert(sizeof(OLEINPLACEFRAMEINFO) == 20);

    GET_STACK32(pti, poifi32, OLEINPLACEFRAMEINFO *);

    vpoifi16 = 0;
    if (poifi32 != NULL)
    {
        if (IsBadWritePtr(poifi32, sizeof(OLEINPLACEFRAMEINFO)))
        {
            return (DWORD)E_INVALIDARG;
        }

        vpoifi16 = STACKALLOC16(sizeof(OIFI16));
        if (vpoifi16 == 0)
        {
            return (DWORD)E_OUTOFMEMORY;
        }

        (GETVDMPTR(vpoifi16, OIFI16))->cb = sizeof(OIFI16);
    }

    TO_STACK16(pti, vpoifi16, VPVOID);

    pti->pThop++;
    dwResult = EXECUTE_THOP3216(pti);

    if (vpoifi16 != NULL)
    {
        poifi16 = GETVDMPTR(vpoifi16, OIFI16);

        if (SUCCEEDED(dwResult))
        {
            poifi32->cb            = sizeof(OLEINPLACEFRAMEINFO);
            poifi32->fMDIApp       = (BOOL)poifi16->fMDIApp;
            poifi32->hwndFrame     = (HWND)HWND32(poifi16->hwndFrame);
            poifi32->cAccelEntries = (UINT)poifi16->cAccelEntries;

            if (poifi16->haccel == NULL)
            {
                poifi32->haccel = NULL;
            }
            else
            {
                // WOW will clean up any dangling accelerator tables when
                // tasks die
                poifi32->haccel = (HACCEL)HACCEL32(poifi16->haccel);
                if (poifi32->haccel == NULL)
                {
                    dwResult = (DWORD)E_UNEXPECTED;
                }
            }

#if DBG == 1
            if (SUCCEEDED(dwResult))
            {
                thkDebugOut((DEB_ARGS, "Out3216 OIFI: "
                             "%p {%d, %d, 0x%04X, 0x%04X, %d} -> "
                             "%p {%d, %d, 0x%p, 0x%p, %d}\n",
                             vpoifi16, poifi16->cb, (BOOL)poifi16->fMDIApp,
                             (DWORD)poifi16->hwndFrame, (DWORD)poifi16->haccel,
                             poifi16->cAccelEntries,
                             poifi32, poifi32->cb, poifi32->fMDIApp,
                             poifi32->hwndFrame, poifi32->haccel,
                             poifi32->cAccelEntries));
            }
#endif
        }

        if (FAILED(dwResult))
        {
            memset(poifi32, 0, sizeof(OLEINPLACEFRAMEINFO));
        }

        STACKFREE16(vpoifi16, sizeof(OIFI16));
    }

    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_STGMEDIUM_3216, public
//
//  Synopsis:   Converts 32-bit STGMEDIUM to 16-bit STGMEDIUM returned
//              structure
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    23-Feb-94       BobDay  Created
//
//----------------------------------------------------------------------------

DWORD Thop_STGMEDIUM_3216(THUNKINFO *pti)
{
    DWORD       dwResult;
    BOOL        fThopInput;
    BOOL        fThopOutput;
    VPVOID      vpstgmedium16;
    STGMEDIUM   *lpstgmedium32;
    DWORD       dwSize;
    SCODE       sc;
    BOOL        fReleaseParam;
    BOOL        fTransferOwnership;
    FORMATETC   *pfe;
    THOP        thopFeOffset;
    DWORD	vpIStream = 0;
    STGMEDIUM UNALIGNED *psm16;

    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_STGMEDIUM);

    fThopInput  = IS_THOP_IN(pti);
    fThopOutput = IS_THOP_OUT(pti);

    //
    // We currently don't have any unmodified or inout thops for STGMEDIUMs
    //
    thkAssert( (fThopInput || fThopOutput) &&
               (fThopInput != fThopOutput) &&
               "STGMEDIUM must be input or output only" );

    // +2 thop byte indicates whether there's a FORMATETC to look at
    // or not
    // We need to reference this now before the stack is modified
    // by argument recovery
    thopFeOffset = *(pti->pThop+2);
    if (thopFeOffset > 0)
    {
        INDEX_STACK32(pti, pfe, FORMATETC *, thopFeOffset);
    }
    else
    {
        pfe = NULL;
    }

    GET_STACK32(pti, lpstgmedium32, STGMEDIUM FAR *);

    // Next thop byte indicates whether there's an ownership transfer
    // argument or not
    pti->pThop++;
    fReleaseParam = (BOOL)*pti->pThop++;

    if (fReleaseParam)
    {
        GET_STACK32(pti, fTransferOwnership, BOOL);
    }
    else
    {
        fTransferOwnership = FALSE;
    }

    // Skip FORMATETC offset thop
    pti->pThop++;

    vpstgmedium16 = 0;

    if ( lpstgmedium32 != NULL )
    {
        if ((fThopInput && IsBadReadPtr(lpstgmedium32, sizeof(STGMEDIUM))) ||
            (fThopOutput && IsBadWritePtr(lpstgmedium32, sizeof(STGMEDIUM))))
        {
            return (DWORD)E_INVALIDARG;
        }

        vpstgmedium16 = STACKALLOC16(sizeof(STGMEDIUM));
        if (vpstgmedium16 == 0)
        {
            return (DWORD)E_OUTOFMEMORY;
        }

        if ( fThopInput )
        {
            sc = ConvertStgMed3216(pti, lpstgmedium32, vpstgmedium16,
                                   pfe, &dwSize);
            if (SUCCEEDED(sc))
            {
                // Apparently if you pass TYMED_NULL into GetDataHere
                // it's supposed to work like GetData, so switch input-only
                // TYMED_NULLs to output
                if (lpstgmedium32->tymed == TYMED_NULL &&
                    !fTransferOwnership)
                {
                    fThopInput = FALSE;
                    fThopOutput = TRUE;
                }
		else if (lpstgmedium32->tymed == TYMED_ISTREAM)
		{
		    //
		    // Excel has a bug in its Clipboard data object that when
		    // GetDataHere is done providing a IStream interface, it
		    // will create its own stream and pounce on the pointer
		    // being passed in. So, if the thing is input, and the
		    // TYMED is ISTREAM we need to stash away the original
		    // 16-bit stream pointer for use later.
		    //
		    psm16 = GETVDMPTR(vpstgmedium16,STGMEDIUM);
		    vpIStream = (DWORD)psm16->pstm;
		}
            }
            else
            {
                STACKFREE16(vpstgmedium16, sizeof(STGMEDIUM));
                return (DWORD)sc;
            }

        }
        else
        {
            // Even though this is an out parameter, some apps
            // (Graph 5 is one) check its values and depend on it
            // being zeroed out
            memset(GETVDMPTR(vpstgmedium16, STGMEDIUM), 0, sizeof(STGMEDIUM));
        }
    }

    TO_STACK16(pti, vpstgmedium16, VPVOID);

    if (fReleaseParam)
    {
        TO_STACK16(pti, (SHORT)fTransferOwnership, SHORT);
    }

    dwResult = EXECUTE_THOP3216(pti);

    if (lpstgmedium32 != NULL)
    {
        if (fThopInput)
        {

	    if (SUCCEEDED(dwResult) &&
	        (lpstgmedium32->tymed == TYMED_ISTREAM) &&
	        (vpIStream != 0))
	    {
		//
		// To continue our Excel Clipboard GetDataHere hack, if the
		// TYMED was ISTREAM, and the medium was input (as it is now)
		// then we need to detect the case where the IStream pointer
		// changed. If it did change, then we have a special function
		// in the 16-bit world that will copy the contents of the
		// 'new' stream into 'our' stream, and release the 'new'
		// stream. This should make the clipboard work properly.
		//
		psm16 = GETVDMPTR(vpstgmedium16,STGMEDIUM);
		if( (psm16->tymed == TYMED_ISTREAM) &&
		    (vpIStream != (DWORD)psm16->pstm))
		{
		    DWORD b32Args[WCB16_MAX_CBARGS];
		    b32Args[0] = vpIStream;
		    b32Args[1] = (DWORD)psm16->pstm;


		    if( !WOWCallback16Ex( (DWORD)gdata16Data.fnStgMediumStreamHandler16,
					  WCB16_PASCAL,
					  sizeof(b32Args),
					  b32Args,
					  &dwResult) )
		    {
			dwResult = (DWORD)E_UNEXPECTED;
		    }
		
		}
		else
		{
		    //
		    // Two possibilites
		    // The stream pointers are the same. Good news
		    // The tymed was changed. Bad news. There isn't anything
		    // we can safely do with the different tymed, so ignore
		    // the whole thing.
		    //
		}
	    }


            if (!fTransferOwnership || FAILED(dwResult))
            {
                sc = CleanStgMed16(pti, vpstgmedium16, lpstgmedium32,
                                   dwSize, TRUE, pfe);
                if (FAILED(sc))
                {
                    dwResult = (DWORD)sc;
                }
            }
            else if (SUCCEEDED(dwResult))
            {

                if (lpstgmedium32->pUnkForRelease == NULL)
                {
                    sc = CleanStgMed32(pti, lpstgmedium32, vpstgmedium16,
                                       0, FALSE, pfe);
                    thkAssert(SUCCEEDED(sc));
                }
            }
        }
        else
        {
            thkAssert(fThopOutput);

            if (SUCCEEDED(dwResult))
            {
                sc = ConvertStgMed1632(pti, vpstgmedium16, lpstgmedium32,
                                       pfe, &dwSize);
                if (FAILED(sc))
                {
                    dwResult = (DWORD)sc;
                    WOWCallback16(gdata16Data.fnReleaseStgMedium16,
                                  vpstgmedium16);
                }
                else if (lpstgmedium32->pUnkForRelease == NULL)
                {
                    sc = CleanStgMed16(pti, vpstgmedium16, lpstgmedium32,
                                       dwSize, FALSE, pfe);
                    thkAssert(SUCCEEDED(sc));
                }
            }

            if (FAILED(dwResult))
            {
                memset(lpstgmedium32, 0, sizeof(STGMEDIUM));
            }
        }
    }

    if (vpstgmedium16 != 0)
    {
        STACKFREE16(vpstgmedium16, sizeof(STGMEDIUM));
    }

    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   ConvertStatStg1632, public
//
//  Synopsis:   Converts a STATSTG
//
//  Arguments:  [pti] - Thunk info
//              [vpss16] - STATSTG
//              [pss32] - STATSTG
//              [posPreAlloc] - Preallocated string memory or NULL
//              [cchPreAlloc] - Amount preallocated
//
//  Returns:    Appropriate status code
//
//  Modifies:   [pss32]
//
//  History:    14-May-94       DrewB   Created
//
//  Notes:      Assumes input STATSTG memory is valid
//              Assumes task memory for the string
//
//----------------------------------------------------------------------------

SCODE ConvertStatStg1632(THUNKINFO *pti,
                         VPVOID vpss16,
                         STATSTG *pss32,
                         LPOLESTR posPreAlloc,
                         UINT cchPreAlloc)
{
    STATSTG UNALIGNED *pss16;
    SCODE sc;
    LPOLESTR pos32;

    pss16 = GETVDMPTR(vpss16, STATSTG);

    sc = ConvertTaskString1632(pti, (VPSTR)pss16->pwcsName,
                               posPreAlloc, cchPreAlloc,
                               &pos32);
    if (SUCCEEDED(sc))
    {
        memcpy(pss32, pss16, sizeof(STATSTG));
        pss32->pwcsName = pos32;
    }

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_STATSTG_3216, public
//
//  Synopsis:   Converts 32-bit STATSTG to 16-bit STATSTG returned structure
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    23-Feb-94       BobDay  Created
//
//----------------------------------------------------------------------------
DWORD Thop_STATSTG_3216( THUNKINFO *pti )
{
    DWORD       dwResult;
    BOOL        fThopInput;
    BOOL        fThopOutput;
    STATSTG     *lpstatstg32;
    VPVOID      vpstatstg16;
    LPOLESTR    lpstr32;

    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_STATSTG);

    fThopInput  = IS_THOP_IN(pti);
    fThopOutput = IS_THOP_OUT(pti);

    //
    // We currently don't have any input thops for STATSTGs
    //
    thkAssert( fThopOutput && !fThopInput && "STATSTG must be output only" );

    GET_STACK32(pti, lpstatstg32, STATSTG FAR *);

    if (IsBadWritePtr(lpstatstg32, sizeof(STATSTG)))
    {
        return (DWORD)E_INVALIDARG;
    }

    vpstatstg16 = STACKALLOC16(sizeof(STATSTG));
    if (vpstatstg16 == 0)
    {
        return (DWORD)E_OUTOFMEMORY;
    }

    lpstr32 = (LPOLESTR)TaskMalloc32(CBSTRINGPREALLOC);
    if (lpstr32 == NULL)
    {
        STACKFREE16(vpstatstg16, sizeof(STATSTG));
        return (DWORD)E_OUTOFMEMORY;
    }

    TO_STACK16(pti, vpstatstg16, VPVOID);

    pti->pThop++;
    dwResult = EXECUTE_THOP3216(pti);

    if (SUCCEEDED(dwResult))
    {
        SCODE sc;

        sc = ConvertStatStg1632(pti, vpstatstg16, lpstatstg32,
                                lpstr32, CWCSTRINGPREALLOC);
        if (FAILED(sc))
        {
            dwResult = sc;
        }
    }

    if (FAILED(dwResult))
    {
        TaskFree32(lpstr32);

        memset(lpstatstg32, 0, sizeof(STATSTG));
    }

    STACKFREE16(vpstatstg16, sizeof(STATSTG));

    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_DVTARGETDEVICE_3216, public
//
//  Synopsis:   Converts 16-bit DVTARGETDEVICE to 32-bit DVTARGETDEVICE
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    23-Feb-94       BobDay  Created
//
//----------------------------------------------------------------------------
DWORD Thop_DVTARGETDEVICE_3216( THUNKINFO *pti )
{
    DWORD               dwResult;
    BOOL                fThopInput;
    BOOL                fThopOutput;
    UINT                uiSize;
    DVTARGETDEVICE FAR  *lpdv32;
    VPVOID              vpdv16;
    SCODE               sc;

    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_DVTARGETDEVICE);

    fThopInput  = IS_THOP_IN(pti);
    fThopOutput = IS_THOP_OUT(pti);

    //
    // We currently don't have any output thops for DVTARGETDEVICEs
    //
    thkAssert( fThopInput && !fThopOutput &&
               "DVTARGETDEVICE must be input only" );

    //
    // Processing for a DVTARGETDEVICE FAR * as input
    //
    GET_STACK32(pti, lpdv32, DVTARGETDEVICE FAR *);

    vpdv16 = 0;

    if ( lpdv32 != NULL )
    {
        sc = ConvertDvtd3216(pti, lpdv32, ArStack16, FrStack16, &vpdv16,
                             &uiSize);
        if (FAILED(sc))
        {
            return (DWORD)sc;
        }
    }

    TO_STACK16(pti, vpdv16, VPVOID);
    pti->pThop++;
    dwResult = EXECUTE_THOP3216(pti);

    if ( lpdv32 != NULL )
    {
        FrStack16((void *)vpdv16, uiSize);
    }

    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_FORMATETC_3216, public
//
//  Synopsis:   Converts 16-bit FORMATETC to 32-bit FORMATETC and back
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    24-Feb-94       BobDay  Created
//
//----------------------------------------------------------------------------
DWORD Thop_FORMATETC_3216( THUNKINFO *pti )
{
    DWORD               dwResult;
    BOOL                fThopInput;
    BOOL                fThopOutput;
    VPVOID              vpformatetc16;
    FORMATETC16 UNALIGNED *lpformatetc16;
    LPFORMATETC         lpformatetc32;
    VPVOID              vpdv16;
    SCODE               sc;

    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_FORMATETC);

    fThopInput  = IS_THOP_IN(pti);
    fThopOutput = IS_THOP_OUT(pti);

    vpdv16 = 0;

    //
    // We have only input or output thops
    //
    thkAssert( (fThopInput || fThopOutput) &&
               (fThopInput != fThopOutput) &&
               "formatetc must be input or output only" );

    GET_STACK32(pti, lpformatetc32, LPFORMATETC);

    if ( lpformatetc32 == NULL )
    {
        vpformatetc16 = 0;
    }
    else
    {
        if ((fThopInput && IsBadReadPtr(lpformatetc32, sizeof(LPFORMATETC))) ||
            (fThopOutput && IsBadWritePtr(lpformatetc32, sizeof(LPFORMATETC))))
        {
            return (DWORD)E_INVALIDARG;
        }

        vpformatetc16 = STACKALLOC16(sizeof(FORMATETC16));
        if (vpformatetc16 == 0)
        {
            return (DWORD)E_OUTOFMEMORY;
        }

        if ( fThopInput )
        {
            sc = ConvertFetc3216(pti, lpformatetc32, vpformatetc16, FALSE);
            if (FAILED(sc))
            {
                STACKFREE16(vpformatetc16, sizeof(FORMATETC16));
                return (DWORD)sc;
            }
        } else {
            thkAssert( fThopOutput );

            //
            // The below memset is needed at least for the DATA_S_SAMEFORMATETC
            // case.  This allows it to be cleaned up because all its pointers
            // will be null.
            //
            lpformatetc16 = GETVDMPTR(vpformatetc16, FORMATETC16);
            memset(lpformatetc16, 0, sizeof(FORMATETC16) );
        }
    }

    TO_STACK16(pti, vpformatetc16, VPVOID);
    pti->pThop++;
    dwResult = EXECUTE_THOP3216(pti);

    if (vpformatetc16 != 0)
    {
        lpformatetc16 = GETVDMPTR(vpformatetc16, FORMATETC16);
        if (lpformatetc16->ptd != NULL)
        {
            TaskFree16((VPVOID)lpformatetc16->ptd);
        }

        STACKFREE16(vpformatetc16, sizeof(FORMATETC16));
    }

    if ( fThopOutput && lpformatetc32 != NULL)
    {
        if (SUCCEEDED(dwResult))
        {
            sc = ConvertFetc1632(pti, vpformatetc16, lpformatetc32, TRUE);
            if (FAILED(sc))
            {
                dwResult = sc;
            }
        }

        if (FAILED(dwResult))
        {
            memset(lpformatetc32, 0, sizeof(FORMATETC));
        }
    }

    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_LOGPALETTE_3216, public
//
//  Synopsis:   Converts 16-bit LOGPALLETE to 32-bit LOGPALETTE
//              and converts 32-bit LOGPALETTE returned to 16-bit structure
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    23-Feb-94       BobDay  Created
//
//----------------------------------------------------------------------------

DWORD Thop_LOGPALETTE_3216 ( THUNKINFO *pti )
{
    DWORD             dwResult;
    BOOL              fThopInput;
    BOOL              fThopOutput;
    UINT              uiSize;
    LPLOGPALETTE      lplogpal32;
    VPVOID            vplogpal16;
    LOGPALETTE UNALIGNED *lplogpal16;
    LPLOGPALETTE      *lplplogpal32;
    VPVOID            vpvplogpal16;
    VPVOID UNALIGNED *lpvplogpal16;

    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_LOGPALETTE);

    fThopInput  = IS_THOP_IN(pti);
    fThopOutput = IS_THOP_OUT(pti);

    //
    // It must be either an input or output LOGPALETTE
    //
    thkAssert( ((fThopOutput && !fThopInput) || (fThopInput && !fThopOutput))
                && "Hey, LOGPALETTE can't be input and output!" );

    if ( fThopInput )
    {
        //
        // Processing for a LPLOGPALETTE as input
        //
        GET_STACK32(pti, lplogpal32, LPLOGPALETTE);

        if ( lplogpal32 == NULL )
        {
            vplogpal16 = 0;
        }
        else
        {
            if (IsBadReadPtr(lplogpal32, sizeof(LOGPALETTE)))
            {
                return (DWORD)E_INVALIDARG;
            }

            uiSize = CBPALETTE(lplogpal32->palNumEntries);

            if (IsBadReadPtr(lplogpal32, uiSize))
            {
                return (DWORD)E_INVALIDARG;
            }

            vplogpal16 = STACKALLOC16(uiSize);
            if (vplogpal16 == 0)
            {
                return (DWORD)E_OUTOFMEMORY;
            }

            lplogpal16 = (LOGPALETTE UNALIGNED *)
                WOWGetVDMPointer( vplogpal16, uiSize, TRUE );

            memcpy( lplogpal16, lplogpal32, uiSize );
        }

        TO_STACK16(pti, vplogpal16, VPVOID);
        pti->pThop++;
        dwResult = EXECUTE_THOP3216(pti);

        if ( vplogpal16 != 0 )
        {
            STACKFREE16(vplogpal16, uiSize);
        }
    }
    else
    {
        //
        // Processing for LPLPLOGPALETTE as output
        //
        thkAssert(fThopOutput);

        GET_STACK32(pti, lplplogpal32, LPLOGPALETTE FAR *);
        if (IsBadWritePtr(lplplogpal32, sizeof(LPLOGPALETTE)))
        {
            return (DWORD)E_INVALIDARG;
        }

        vpvplogpal16 = (VPVOID)STACKALLOC16(sizeof(LPLOGPALETTE));
        if (vpvplogpal16 == 0)
        {
            return (DWORD)E_OUTOFMEMORY;
        }

        lplogpal32 = (LPLOGPALETTE)TaskMalloc32(CBPALETTE(NPALETTEPREALLOC));
        if (lplogpal32 == NULL)
        {
            STACKFREE16(vpvplogpal16, sizeof(LPLOGPALETTE));
            return (DWORD)E_OUTOFMEMORY;
        }

        //
        // We DO need to zero out the pointer on the way in.
        //
        *GETVDMPTR(vpvplogpal16, LPLOGPALETTE) = 0;

        TO_STACK16(pti, vpvplogpal16, VPVOID);

        pti->pThop++;
        dwResult = EXECUTE_THOP3216(pti);

        if (SUCCEEDED(dwResult))
        {
            lpvplogpal16 = GETVDMPTR( vpvplogpal16, VPVOID);

            vplogpal16 = *lpvplogpal16;

            if ( vplogpal16 == 0 )
            {
                TaskFree32(lplogpal32);
                lplogpal32 = NULL;
            }
            else
            {
                lplogpal16 = GETVDMPTR( vplogpal16, LOGPALETTE );

                //
                // Copy the returned LOGPALETTE into 16-bit memory
                //
                uiSize = CBPALETTE(lplogpal16->palNumEntries);
                if (uiSize > CBPALETTE(NPALETTEPREALLOC))
                {
                    TaskFree32(lplogpal32);

                    lplogpal32 = (LPLOGPALETTE)TaskMalloc32(uiSize);
                    if ( lplogpal32 == NULL )
                    {
                        dwResult = (DWORD)E_OUTOFMEMORY;
                    }
                }

                if (lplogpal32 != NULL)
                {
                    memcpy( lplogpal32, lplogpal16, uiSize );
                }

                TaskFree16( vplogpal16 );
            }
        }
        else
        {
            TaskFree32(lplogpal32);
            lplogpal32 = NULL;
        }

        //
        // Update the value pointed to by the parameter on the 16-bit stack
        //
        *lplplogpal32 = lplogpal32;

        if (vpvplogpal16 != 0)
        {
            STACKFREE16(vpvplogpal16, sizeof(LPLOGPALETTE));
        }
    }
    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_CRGIID_3216, public
//
//  Synopsis:   Converts 32-bit CRGIID to 16-bit CRGIID structure
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    23-Feb-94       BobDay  Created
//
//----------------------------------------------------------------------------
DWORD Thop_CRGIID_3216( THUNKINFO *pti )
{
    DWORD       dwResult;
    BOOL        fThopInput;
    BOOL        fThopOutput;
    DWORD       dwCount;
    VPVOID      vpiid16;
    IID UNALIGNED *lpiid16;
    IID         *lpiid32;

    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_CRGIID);

    fThopInput  = IS_THOP_IN(pti);
    fThopOutput = IS_THOP_OUT(pti);

    //
    // We currently don't have any output thops for CRGIIDs
    //
    thkAssert( !fThopInput && !fThopOutput &&
               "CRGIID must be unmodified only" );

    GET_STACK32(pti, dwCount, DWORD);
    GET_STACK32(pti, lpiid32, IID FAR *);

    if ( lpiid32 == NULL )
    {
        vpiid16 = 0;
    }
    else
    {
        if (IsBadReadPtr(lpiid32, dwCount*sizeof(IID)))
        {
            return (DWORD)E_INVALIDARG;
        }

        vpiid16 = STACKALLOC16( dwCount * sizeof(IID) );
        if (vpiid16 == 0)
        {
            return (DWORD)E_OUTOFMEMORY;
        }

        lpiid16 = (IID UNALIGNED *)
            WOWGetVDMPointer( vpiid16, dwCount*sizeof(IID), TRUE );

        memcpy( lpiid16, lpiid32, dwCount*sizeof(IID) );
    }

    TO_STACK16(pti, dwCount, DWORD);
    TO_STACK16(pti, vpiid16, VPVOID);

    pti->pThop++;
    dwResult = EXECUTE_THOP3216(pti);

    if ( vpiid16 != 0 )
    {
        STACKFREE16( vpiid16, dwCount * sizeof(IID) );
    }
    return( dwResult );
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_INTERFACEINFO_3216, public
//
//  Synopsis:   Converts an INTERFACEINFO
//
//  Arguments:  [pti] - Thunking state information
//
//  Returns:    Appropriate status code
//
//  History:    19-May-94       DrewB   Created
//
//----------------------------------------------------------------------------

DWORD Thop_INTERFACEINFO_3216(THUNKINFO *pti)
{
    INTERFACEINFO *pii32;
    INTERFACEINFO16 UNALIGNED *pii16;
    VPVOID vpii16;
    DWORD dwResult;
    VPVOID vpunk16;

    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_INTERFACEINFO);
    thkAssert((*pti->pThop & THOP_INOUT) == THOP_IN);

    vpunk16 = 0;

    GET_STACK32(pti, pii32, INTERFACEINFO *);
    if (pii32 == NULL)
    {
        vpii16 = 0;
    }
    else
    {
        if (IsBadReadPtr(pii32, sizeof(INTERFACEINFO)))
        {
            return (DWORD)E_INVALIDARG;
        }

        vpii16 = STACKALLOC16(sizeof(INTERFACEINFO16));
        if (vpii16 == 0)
        {
            return (DWORD)E_OUTOFMEMORY;
        }
        pii16 = GETVDMPTR(vpii16, INTERFACEINFO16);

        if (pii32->pUnk != NULL)
        {
            vpunk16 = pti->pThkMgr->FindProxy1632(NULL, pii32->pUnk,
                                                  INDEX_IIDIDX(THI_IUnknown),
                                                  NULL);
            if (vpunk16 == 0)
            {
                STACKFREE16(vpii16, sizeof(INTERFACEINFO16));
                return (DWORD)E_OUTOFMEMORY;
            }
        }

        pii16->pUnk = vpunk16;
        pii16->iid = pii32->iid;
        pii16->wMethod = pii32->wMethod;

        thkDebugOut((DEB_ARGS,
                     "In3216  INTERFACEINFO: %p -> %p {%p (%p), %s, %u}\n",
                     pii32, vpii16, pii16->pUnk, pii32->pUnk,
                     IidOrInterfaceString(&pii16->iid), pii16->wMethod));
    }

    TO_STACK16(pti, vpii16, VPVOID);

    pti->pThop++;
    dwResult = EXECUTE_THOP3216(pti);

    if (vpunk16 != 0)
    {
        pti->pThkMgr->FreeProxy1632(pii32->pUnk);
    }

    if (vpii16 != 0)
    {
        STACKFREE16(vpii16, sizeof(INTERFACEINFO16));
    }

    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_RETURNTYPE_3216, public
//
//  Synopsis:   Thunks the return value of a call
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    24-Feb-94       DrewB   Created
//
//  Notes:      This thunk assumes that the return value will always fit
//              in 32 bits and that the thops for it are only one thop
//              long.  This fits the existing APIs and methods
//
//----------------------------------------------------------------------------

DWORD Thop_RETURNTYPE_3216(THUNKINFO *pti)
{
    THOP thops[2];
    DWORD dwResult;
    THUNK3216OBJ *ptoPreAlloc = NULL;
    IIDIDX iidx;

    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_RETURNTYPE);
    thkAssert((*pti->pThop & THOP_IOMASK) == 0);

    pti->fResultThunked = TRUE;

    pti->pThop++;

    // Remember return type thop
    thops[0] = *pti->pThop++;
    if ((thops[0] & THOP_OPMASK) == THOP_COPY ||
        (thops[0] & THOP_OPMASK) == THOP_IFACE ||
        (thops[0] & THOP_OPMASK) == THOP_ALIAS32)
    {
        thops[1] = *pti->pThop++;
    }

    // Preallocate any necessary resources
    switch(thops[0])
    {
    case THOP_IFACE | THOP_IN:
        iidx = INDEX_IIDIDX(thops[1]);
        if ((ptoPreAlloc = pti->pThkMgr->CanGetNewProxy3216(iidx)) == NULL)
        {
            return (DWORD)E_OUTOFMEMORY;
        }
        break;
    }

    dwResult = EXECUTE_THOP3216(pti);

    // Now that we have the return value thunk it from 16->32

    switch(thops[0])
    {
    case THOP_COPY:
        // Only handle DWORD copies
        thkAssert(thops[1] == sizeof(DWORD));
        break;

    case THOP_SHORTLONG:
        // For boolean results, not necessary to clamp
        dwResult = (DWORD)(LONG)*(SHORT *)&dwResult;
        break;

    case THOP_IFACE | THOP_IN:
        if (dwResult != 0)
        {
            // BUGBUG - What if another thop failed and returned an HRESULT?
            // This will break
            dwResult =
                (DWORD)pti->pThkMgr->FindProxy3216(ptoPreAlloc, dwResult,
                                                   iidx, NULL);
            thkAssert(dwResult != 0);

            thkDebugOut((DEB_ARGS, "Ret3216 %s %p\n",
                         inInterfaceNames[thops[1]].pszInterface,
                         dwResult));
        }
        else
        {
            pti->pThkMgr->FreeNewProxy3216(ptoPreAlloc, iidx);
        }
        break;

    default:
        thkAssert(!"Unhandled 3216 return type");
        break;
    }

    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_IFACE_3216, public
//
//  Synopsis:   Thunks a known interface pointer
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    24-Feb-94       DrewB   Created
//
//----------------------------------------------------------------------------

DWORD Thop_IFACE_3216(THUNKINFO *pti)
{
    IIDIDX iidx;
    THOP thop, thopOp, thopWeakOffset;
    VPVOID vpvOuter;

    thop = *pti->pThop++;
    thopOp = thop & THOP_OPMASK;

    thkAssert(thopOp == THOP_IFACE ||
              thopOp == THOP_IFACEOWNER ||
              thopOp == THOP_IFACECLEAN);

    iidx = INDEX_IIDIDX(*pti->pThop++);
    // There's a bit of a special case here in that IMalloc is
    // not thunked so it doesn't have a real index but it's used
    // in thop strings so it has a fake index to function as a placeholder
    // The fake index is THI_COUNT so allow that in the assert
    thkAssert(IIDIDX_INDEX(iidx) >= 0 && IIDIDX_INDEX(iidx) <= THI_COUNT);

    vpvOuter = 0;
    if (thopOp == THOP_IFACEOWNER)
    {
        // For cases where the controlling unknown is known,
        // index back on the stack and find the 32-bit unknown
        // pointer to use
        thopWeakOffset = *pti->pThop++;
        INDEX_STACK16(pti, vpvOuter, VPVOID, thopWeakOffset, sizeof(DWORD));
    }

    return ThunkInterface3216(pti, iidx, thop, vpvOuter);
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_ALIAS32_3216, public
//
//  Synopsis:   Handles 16-bit aliases to 32-bit quantities
//
//  Arguments:  [pti] - Thunking state information
//
//  Returns:    Appropriate status code
//
//  History:    27-May-94       DrewB   Created
//
//----------------------------------------------------------------------------

DWORD Thop_ALIAS32_3216(THUNKINFO *pti)
{
    ALIAS alias;
    DWORD dwValue;
    THOP thopAction;
    BOOL fTemporary = FALSE;
    DWORD dwResult;

    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_ALIAS32);
    thkAssert((*pti->pThop & THOP_IOMASK) == 0);

    pti->pThop++;

    GET_STACK32(pti, dwValue, DWORD);

    // Second byte indicates how the alias should be handled
    thopAction = *pti->pThop++;

    if (dwValue != 0)
    {
        switch(thopAction)
        {
        case ALIAS_RESOLVE:
            alias = gAliases32.ValueAlias(dwValue);

            // There may be cases where there is no existing alias
            // for a value (for example, remoted SetMenu calls where
            // the HOLEMENU is a temporary RPC object)
            // so create a temporary one
            if (alias == INVALID_ALIAS)
            {
                alias = gAliases32.AddValue(dwValue);
                if (alias == INVALID_ALIAS)
                {
                    return (DWORD)E_OUTOFMEMORY;
                }

                fTemporary = TRUE;
            }
            break;

        default:
            thkAssert(!"Default hit in Thop_ALIAS32_3216");
            break;
        }
    }
    else
    {
        alias = 0;
    }

    thkDebugOut((DEB_ARGS, "In3216  ALIAS32: 0x%08lX -> 0x%04X\n",
                 dwValue, alias));

    TO_STACK16(pti, alias, ALIAS);

    dwResult = EXECUTE_THOP3216(pti);

    if (fTemporary)
    {
        gAliases32.RemoveAlias(alias);
    }

    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_RPCOLEMESSAGE_3216, public
//
//  Synopsis:   Converts 32-bit RPCOLEMESSAGE to 16-bit RPCOLEMESSAGE
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    23-Feb-94       JohannP  Created
//
//----------------------------------------------------------------------------
DWORD Thop_RPCOLEMESSAGE_3216( THUNKINFO *pti )
{
    DWORD           dwResult;
    BOOL            fThopInput;
    BOOL            fThopOutput;
    PRPCOLEMESSAGE  prom32;
    VPVOID          vprom16;
    RPCOLEMESSAGE UNALIGNED *prom16;
    VPVOID          vpvBuffer16;
    LPVOID          lp16;

    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_RPCOLEMESSAGE);

    fThopInput  = IS_THOP_IN(pti);
    fThopOutput = IS_THOP_OUT(pti);

    //
    // We currently have only IN/OUT RPCOLEMESSAGE
    //
    thkAssert( fThopInput && fThopOutput &&
               "RPCOLEMESSAGE must be input/output only" );

    vprom16 = 0;
    vpvBuffer16 = 0;

    //
    // Processing for a RPCOLEMESSAGE FAR * as input/output
    //
    GET_STACK32(pti, prom32, RPCOLEMESSAGE *);
    if ( prom32 != 0 )
    {
        // Copy over the input RPCOLEMESSAGE structure

        vprom16 = STACKALLOC16(sizeof(RPCOLEMESSAGE));
        if (vprom16 == NULL)
        {
            return (DWORD)E_OUTOFMEMORY;
        }

        prom16 = GETVDMPTR(vprom16, RPCOLEMESSAGE);
        *prom16 = *prom32;
        ROM_THUNK_FIELD(prom16) = (void *)prom32;

        // If there's a buffer, copy it
        if (prom32->cbBuffer != 0)
        {
            vpvBuffer16 = TaskMalloc16(prom32->cbBuffer);
            if (vpvBuffer16 == NULL)
            {
                STACKFREE16(vprom16, sizeof(RPCOLEMESSAGE));
                return (DWORD)E_OUTOFMEMORY;
            }

            prom16->Buffer = (LPVOID) vpvBuffer16;
            lp16 = (LPVOID)WOWGetVDMPointer(vpvBuffer16, prom32->cbBuffer,
                                            TRUE );
            memcpy( lp16, prom32->Buffer, prom32->cbBuffer );
        }
    }

    TO_STACK16(pti, vprom16, VPVOID);
    pti->pThop++;
    dwResult = EXECUTE_THOP3216(pti);

    prom16 = (PRPCOLEMESSAGE)GETVDMPTR(vprom16, RPCOLEMESSAGE);
    if (prom16 == NULL)
    {
        dwResult = (DWORD)E_UNEXPECTED;
    }
    else
    {
        if (SUCCEEDED(dwResult))
        {
            if ( prom32->Buffer != NULL )
            {
                lp16 = (LPVOID)WOWGetVDMPointer(
                                      (VPVOID)prom16->Buffer,
                                      prom16->cbBuffer,
                                      TRUE );
                if (lp16 == NULL)
                {
                    dwResult = (DWORD)E_UNEXPECTED;
                }
                else
                {
                    memcpy( prom32->Buffer, lp16, prom16->cbBuffer );
                }
            }
        }
    }

    if ( vprom16 != 0 )
    {
        STACKFREE16(vprom16, sizeof(RPCOLEMESSAGE));
    }

    if (vpvBuffer16)
    {
        TaskFree16(vpvBuffer16);
    }

    return dwResult;
}


//+---------------------------------------------------------------------------
//
//  Function:   Thop_ENUM_3216, public
//
//  Synopsis:   Thunks Enum::Next parameters
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    1-Mar-94        BobDay  Created
//
//  Notes:      This thunk is the start of a 2-byte thop.  The next thop
//              byte references a function in the enumerator table, rather
//              than the standard thop table.
//
//----------------------------------------------------------------------------

DWORD Thop_ENUM_3216(THUNKINFO *pti)
{
    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_ENUM);
    thkAssert((*pti->pThop & THOP_IOMASK) == 0);

    //
    // Get then next thop byte and execute it as a Enum thop
    //
    pti->pThop++;
    return EXECUTE_ENUMTHOP3216(pti);
}

//+---------------------------------------------------------------------------
//
//  Function:   CallbackProcessing_3216, public
//
//  Synopsis:   Thunks IOleObject::Draw pfnContinue & DWORD parameters
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    3-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------
typedef struct tagCallbackControl
{
    DWORD   dwContinue;
    LPVOID  lpfn32;
} CALLBACKCONTROL;


STDAPI_(BOOL) CallbackProcessing_3216( DWORD dwContinue, DWORD dw1, DWORD dw2 )
{
    BOOL            fResult;
    CALLBACKCONTROL *lpcbc;
    BOOL            (*lpfn32)(DWORD);

    lpcbc = (CALLBACKCONTROL *)dwContinue;

    lpfn32 = (BOOL (*)(DWORD))lpcbc->lpfn32;

    fResult = (*lpfn32)(lpcbc->dwContinue);

    if ( fResult )      // This maps DWORD sized BOOLs into WORD sized BOOLs
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_CALLBACK_3216, public
//
//  Synopsis:   Thunks IOleObject::Draw pfnContinue & DWORD parameters
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    3-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

DWORD Thop_CALLBACK_3216(THUNKINFO *pti)
{
    LPVOID              lpfn32;
    DWORD               dwContinue;
    CALLBACKCONTROL     cbc;

    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_CALLBACK);
    thkAssert((*pti->pThop & THOP_IOMASK) == 0);

    GET_STACK32(pti, lpfn32, LPVOID);
    GET_STACK32(pti, dwContinue, DWORD);

    if ( lpfn32 == 0 )
    {
        TO_STACK16(pti, NULL, VPVOID);
        TO_STACK16(pti, dwContinue, DWORD);
    }
    else
    {
        cbc.lpfn32     = lpfn32;
        cbc.dwContinue = dwContinue;

        TO_STACK16(pti, gdata16Data.fnCallbackHandler, DWORD);
        TO_STACK16(pti, (DWORD)&cbc, DWORD);
    }

    pti->pThop++;
    return EXECUTE_THOP3216(pti);
}

//+---------------------------------------------------------------------------
//
//  Function:	Thop_CLSCONTEXT_3216, public
//
//  Synopsis:	Converts a class context flags DWORD
//
//  Arguments:	[pti] - Thunk state information
//
//  Returns:	Appropriate status code
//
//  History:	29-Jun-94	DrewB	Created
//
//----------------------------------------------------------------------------

DWORD Thop_CLSCONTEXT_3216(THUNKINFO *pti)
{
    DWORD dwClsContext;

    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_CLSCONTEXT);
    thkAssert((*pti->pThop & THOP_IOMASK) == 0);

    // When passing a 32-bit class context to 16-bits nothing
    // nothing special needs to be done

    GET_STACK32(pti, dwClsContext, DWORD);
    TO_STACK16(pti, dwClsContext, DWORD);

    pti->pThop++;
    return EXECUTE_THOP3216(pti);
}

#define THOP_FN(x)  Thop_ ## x ## _3216

DWORD (*aThopFunctions3216[])(THUNKINFO *) =
{

                                // x = Implemented
                                // ? = Mysteriously not needed
                                //   = Left to do
                                //
                                // ^
                                // |
                                // +===+
                                //     |
                                //     v
                                //
    ThunkCall3216,                  // x Terminating THOP
    Thop_ShortToLong_3216,          // x SHORTLONG
    Thop_WordToDword_3216,          // x WORDDWORD
    Thop_Copy_3216,                 // x COPY
    THOP_FN(LPSTR),                 // x LPSTR
    THOP_FN(LPLPSTR),               // x LPLPSTR
    THOP_FN(BUFFER),                // x BUFFER
    Thop_UserHandle_3216,           // x HUSER
    Thop_GdiHandle_3216,            // x HGDI
    THOP_FN(SIZE),                  // x SIZE
    THOP_FN(RECT),                  // x RECT
    THOP_FN(MSG),                   // x MSG
    THOP_FN(HRESULT),               // x HRESULT
    THOP_FN(STATSTG),               // x STATSTG
    THOP_FN(DVTARGETDEVICE),        // x DVTARGETDEVICE
    THOP_FN(STGMEDIUM),             // x STGMEDIUM
    THOP_FN(FORMATETC),             // x FORMATETC
    THOP_FN(HACCEL),                // x HACCEL
    THOP_FN(OIFI),                  // x OLEINPLACEFRAMEINFO
    THOP_FN(BINDOPTS),              // x BIND_OPTS
    THOP_FN(LOGPALETTE),            // x LOGPALETTE
    THOP_FN(SNB),                   // x SNB
    THOP_FN(CRGIID),                // x CRGIID
    Thop_ERROR_3216,                // x OLESTREAM  (only 16-bit)
    THOP_FN(HTASK),                 // x HTASK
    THOP_FN(INTERFACEINFO),         // x INTERFACEINFO
    THOP_FN(IFACE),                 // x IFACE
    THOP_FN(IFACE),                 // x IFACEOWNER
    THOP_FN(IFACE),                 // x IFACECLEAN
    THOP_FN(IFACEGEN),              // x IFACEGEN
    THOP_FN(IFACEGEN),              // x IFACEGENOWNER
    Thop_ERROR_3216,                // x ROUTINE_INDEX
    THOP_FN(RETURNTYPE),            // x RETURN_TYPE
    THOP_FN(NULL),                  // x NULL
    Thop_ERROR_3216,                // x ERROR
    THOP_FN(ENUM),                  // x ENUM
    THOP_FN(CALLBACK),              // x CALLBACK
    THOP_FN(RPCOLEMESSAGE),         // x RPCOLEMESSAGE
    THOP_FN(ALIAS32),               // x ALIAS32
    THOP_FN(CLSCONTEXT),            // x CLSCONTEXT
};

//+---------------------------------------------------------------------------
//
//  Function:   General_Enum_3216, private
//
//  Synopsis:   Thunking for standard OLE enumerator interface ::Next member
//              function.
//
//  Arguments:  [pti] - Thunk state information
//              [uiSize32] - 32-bit information size
//              [uiSize16] - 16-bit information size
//              [pfnCallback] - Data thunking callback
//              [pfnCleanup] - Thunking cleanup
//
//  Returns:    Appropriate status code
//
//  History:    1-Mar-94        BobDay  Created
//
//  Notes:      This handler is called from many IXXXEnum::Next handlers thop
//              thunks to do the standard sorts of "buffer of structures"
//              processing.
//
//----------------------------------------------------------------------------
#define MAX_ALLOCA_STRUCT   5       // 16-bit stacks are precious

DWORD General_Enum_3216(
    THUNKINFO   *pti,
    UINT        uiSize32,
    UINT        uiSize16,
    SCODE       (*pfnCallback)( THUNKINFO *, LPVOID, VPVOID),
    void        (*pfnCleanup)( THUNKINFO *, LPVOID, VPVOID)   )
{
    DWORD       dwResult;
    ULONG       ulCount;
    VPVOID      vpstruct16;
    VPVOID      vpfetched16;
    LPVOID      lpstruct32;
    LPVOID      lpstruct32Iterate;
    VPVOID      vpstruct16Iterate;
    ULONG       *lpfetched32;
    ULONG UNALIGNED *lpfetched16;
    ULONG       ulFetched16;
    ULONG       ulIterate;
    BOOL        fError;
    SCODE       sc;
    LPVOID      pvArg32;

    dwResult = (DWORD)S_OK;

    GET_STACK32(pti, ulCount, ULONG );
    GET_STACK32(pti, lpstruct32, LPVOID );
    GET_STACK32(pti, lpfetched32, ULONG FAR *);

    vpfetched16 = STACKALLOC16(sizeof(ULONG));
    if (vpfetched16 == 0)
    {
        dwResult = (DWORD)E_OUTOFMEMORY;
    }
    else
    {
        // Zero this out so that we don't have a random value sitting underneath
        // when bad apps like 16-bit MsWorks don't return the number of items
        // in the returned enumeration.

        lpfetched16  = GETVDMPTR(vpfetched16,ULONG);
        *lpfetched16 = 0;
    }

    pvArg32 = NULL;
    vpstruct16 = 0;

    if ( lpstruct32 != NULL )
    {
        if ( ulCount == 0 )
        {
            dwResult = (DWORD)E_INVALIDARG;
        }
        else
        {
            if (IsBadWritePtr(lpstruct32, ulCount*uiSize32))
            {
                dwResult = (DWORD)E_INVALIDARG;
            }
            else
            {
                pvArg32 = lpstruct32;

                if ( ulCount > MAX_ALLOCA_STRUCT )
                {
                    vpstruct16 = WOWGlobalAllocLock16( GMEM_MOVEABLE,
                                                       ulCount * uiSize16,
                                                       NULL );
                }
                else
                {
                    vpstruct16 = STACKALLOC16( ulCount * uiSize16 );
                }

                if (vpstruct16 == 0)
                {
                    dwResult = (DWORD)E_OUTOFMEMORY;
                }
            }
        }
    }

    if (SUCCEEDED(dwResult))
    {
        TO_STACK16(pti, ulCount, ULONG);
        TO_STACK16(pti, vpstruct16, VPVOID);
        TO_STACK16(pti, vpfetched16, VPVOID);

        pti->pThop++;
        dwResult = EXECUTE_THOP3216(pti);
    }

    if ( SUCCEEDED(dwResult) )
    {
        lpfetched16 = GETVDMPTR( vpfetched16, ULONG);
        ulFetched16 = *lpfetched16;

        if ( lpstruct32 != NULL )
        {
            // Some apps (MsWorks3 is one) return S_FALSE and do not return
            // the number of elements retrieved.  The only thing we can
            // do is ignore the enumeration since we don't know how many
            // were actually set.  Of course, we can't ignore all enumerations
            // when the return is S_FALSE so we only handle the case
            // where S_FALSE was returned on a enumeration of one element,
            // in which we can be sure there isn't any valid data
            if (dwResult == (DWORD)S_FALSE && ulCount == 1)
            {
                ulFetched16 = 0;
            }

            //
            // Iterate through all of the structures, converting them
            // into 16-bit
            //
            fError = FALSE;
            ulIterate = 0;
            vpstruct16Iterate = vpstruct16;
            lpstruct32Iterate = lpstruct32;

            while ( ulIterate < ulFetched16 )
            {
                //
                // Callback to the callback function to do any specific
                // processing
                //
                sc = (*pfnCallback)( pti, lpstruct32Iterate,
                                     vpstruct16Iterate );

                if ( FAILED(sc) )
                {
                    fError = TRUE;
                    dwResult = sc;
                }

                vpstruct16Iterate = (VPVOID)((DWORD)vpstruct16Iterate +
                                             uiSize16);
                lpstruct32Iterate = (LPVOID)((DWORD)lpstruct32Iterate +
                                             uiSize32);

                ulIterate++;
            }

            if ( fError )
            {
                //
                // Cleanup all these guys
                //
                ulIterate = 0;
                vpstruct16Iterate = vpstruct16;
                lpstruct32Iterate = lpstruct32;

                while ( ulIterate <= ulFetched16 )
                {
                    (*pfnCleanup)( pti, lpstruct32Iterate, vpstruct16Iterate );
                    vpstruct16Iterate = (VPVOID)((DWORD)vpstruct16Iterate +
                                                 uiSize16);
                    lpstruct32Iterate = (LPVOID)((DWORD)lpstruct32Iterate +
                                                 uiSize32);

                    ulIterate++;
                }
            }
        }
    }

    if (FAILED(dwResult) && pvArg32 != NULL)
    {
        memset(pvArg32, 0, ulCount*uiSize32);
    }

    if ( lpfetched32 != NULL )
    {
        *lpfetched32 = ulFetched16;
    }

    //
    // Free up any space we've allocated
    //
    if (vpstruct16 != 0)
    {
        if ( ulCount > MAX_ALLOCA_STRUCT )
        {
            WOWGlobalUnlockFree16( vpstruct16 );
        }
        else
        {
            STACKFREE16( vpstruct16, ulCount * uiSize16 );
        }
    }

    if (vpfetched16 != 0)
    {
        STACKFREE16(vpfetched16, sizeof(ULONG));
    }

    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   Callback_STRING_3216, public
//
//  Synopsis:   Prepares the LPOLESTR for the copy back into 16-bit address
//              space.
//
//  Arguments:  [pti] - Thunking state information
//              [lp32] - Pointer to 32-bit output structure
//              [lp16] - Pointer to 16-bit returned structure
//
//  Returns:    SCODE indicating success/failure
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

SCODE Callback_STRING_3216( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    *(LPOLESTR *)lp32 = NULL;
    return ConvertTaskString1632(pti, *GETVDMPTR(vp16, VPSTR), NULL, 0,
                                 (LPOLESTR *)lp32);
}

//+---------------------------------------------------------------------------
//
//  Function:   Cleanup_STRING_3216, public
//
//  Synopsis:   Cleans up the any STRINGs returned (either to 16-bit or 32-bit)
//
//  Arguments:  [pti] - Thunking state information
//              [lp32] - Pointer to 32-bit output structure
//              [lp16] - Pointer to 16-bit returned structure
//
//  Returns:    nothing, should NEVER fail
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

void Cleanup_STRING_3216( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    LPOLESTR lpstr32;

    lpstr32 = *(LPOLESTR *)lp32;
    if ( lpstr32 != NULL )
    {
        TaskFree32( lpstr32 );
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_Enum_STRING_3216, public
//
//  Synopsis:   Thunks IEnumSTRING::Next parameters
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    1-Mar-94        BobDay  Created
//
//  Notes:      This thunk is 2nd part of a 2-byte thop.
//
//----------------------------------------------------------------------------

DWORD Thop_Enum_STRING_3216(THUNKINFO *pti)
{
    return General_Enum_3216(pti,
                             sizeof(LPOLESTR),
                             sizeof(VPSTR),
                             Callback_STRING_3216,
                             Cleanup_STRING_3216 );
}

//+---------------------------------------------------------------------------
//
//  Function:   Callback_UNKNOWN_3216, public
//
//  Synopsis:   Prepares the UNKNOWN structure for the copy back into 16-bit
//              address space.
//
//  Arguments:  [pti] - Thunking state information
//              [lp32] - Pointer to 32-bit output structure
//              [lp16] - Pointer to 16-bit returned structure
//
//  Returns:    SCODE indicating success/failure
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

SCODE Callback_UNKNOWN_3216( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    SCODE           sc = S_OK;
    VPVOID          vpunknown16;
    IUnknown        *punkThis32;

    vpunknown16 = *GETVDMPTR( vp16, VPVOID );

    punkThis32 = pti->pThkMgr->FindProxy3216(NULL, vpunknown16,
                                             INDEX_IIDIDX(THI_IUnknown),
                                             NULL);
    if (punkThis32 == NULL)
    {
        sc = E_OUTOFMEMORY;
    }

    *(LPUNKNOWN *)lp32 = (LPUNKNOWN)punkThis32;

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   Cleanup_UNKNOWN_3216, public
//
//  Synopsis:   Cleans up the any UNKNOWNs returned (either to 16-bit
//              or 32-bit)
//
//  Arguments:  [pti] - Thunking state information
//              [lp32] - Pointer to 32-bit output structure
//              [lp16] - Pointer to 16-bit returned structure
//
//  Returns:    nothing, should NEVER fail
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

void Cleanup_UNKNOWN_3216( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    LPUNKNOWN lpunknown32;

    lpunknown32 = *(LPUNKNOWN *)lp32;

    // BUGBUG - What is the proper cleanup for IEnum<Interface>?
    // Should the objects be released or only the proxies we
    // created?
    if (lpunknown32 != NULL)
    {
        pti->pThkMgr->FreeProxy3216(*GETVDMPTR(vp16, VPVOID));
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_Enum_UNKNOWN_3216, public
//
//  Synopsis:   Thunks IEnumUNKNOWN::Next parameters
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    1-Mar-94        BobDay  Created
//
//  Notes:      This thunk is 2nd part of a 2-byte thop.
//
//----------------------------------------------------------------------------

DWORD Thop_Enum_UNKNOWN_3216(THUNKINFO *pti)
{
    return General_Enum_3216(pti,
                             sizeof(LPUNKNOWN),
                             sizeof(LPUNKNOWN),
                             Callback_UNKNOWN_3216,
                             Cleanup_UNKNOWN_3216 );
}

//+---------------------------------------------------------------------------
//
//  Function:   Callback_STATSTG_3216, public
//
//  Synopsis:   Prepares the STATSTG structure for the copy back into 16-bit
//              address space.
//
//  Arguments:  [pti] - Thunking state information
//              [lp32] - Pointer to 32-bit output structure
//              [lp16] - Pointer to 16-bit returned structure
//
//  Returns:    SCODE indicating success/failure
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

SCODE Callback_STATSTG_3216( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    ((STATSTG *)lp32)->pwcsName = NULL;
    return ConvertStatStg1632(pti, vp16, (STATSTG *)lp32,
                              NULL, 0);
}

//+---------------------------------------------------------------------------
//
//  Function:   Cleanup_STATSTG_3216, public
//
//  Synopsis:   Cleans up the any STATSTGs returned (either to 16-bit
//              or 32-bit)
//
//  Arguments:  [pti] - Thunking state information
//              [lp32] - Pointer to 32-bit output structure
//              [lp16] - Pointer to 16-bit returned structure
//
//  Returns:    nothing, should NEVER fail
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

void Cleanup_STATSTG_3216( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    STATSTG FAR     *lpstatstg32;

    lpstatstg32 = (STATSTG FAR *)lp32;

    if ( lpstatstg32->pwcsName != NULL )
    {
        TaskFree32( lpstatstg32->pwcsName );
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_Enum_STATSTG_3216, public
//
//  Synopsis:   Thunks IEnumSTATSTG::Next parameters
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    1-Mar-94        BobDay  Created
//
//  Notes:      This thunk is 2nd part of a 2-byte thop.
//
//----------------------------------------------------------------------------

DWORD Thop_Enum_STATSTG_3216(THUNKINFO *pti)
{
    return General_Enum_3216(pti,
                             sizeof(STATSTG),
                             sizeof(STATSTG),
                             Callback_STATSTG_3216,
                             Cleanup_STATSTG_3216 );
}

//+---------------------------------------------------------------------------
//
//  Function:   Callback_FORMATETC_3216, public
//
//  Synopsis:   Prepares the FORMATETC structure for the copy back into 16-bit
//              address space.
//
//  Arguments:  [pti] - Thunking state information
//              [lp32] - Pointer to 32-bit output structure
//              [lp16] - Pointer to 16-bit returned structure
//
//  Returns:    SCODE indicating success/failure
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

SCODE Callback_FORMATETC_3216( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    ((FORMATETC *)lp32)->ptd = NULL;
    return ConvertFetc1632(pti, vp16, (FORMATETC *)lp32, TRUE);
}

//+---------------------------------------------------------------------------
//
//  Function:   Cleanup_FORMATETC_3216, public
//
//  Synopsis:   Cleans up the any FORMATETCs returned (either to 16-bit
//              or 32-bit)
//
//  Arguments:  [pti] - Thunking state information
//              [lp32] - Pointer to 32-bit output structure
//              [lp16] - Pointer to 16-bit returned structure
//
//  Returns:    nothing, should NEVER fail
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

void Cleanup_FORMATETC_3216( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    FORMATETC FAR   *lpformatetc32;

    lpformatetc32 = (FORMATETC FAR *)lp32;

    if ( lpformatetc32->ptd != NULL )
    {
        TaskFree32( lpformatetc32->ptd );
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_Enum_FORMATETC_3216, public
//
//  Synopsis:   Thunks IEnumFORMATETC::Next parameters
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    1-Mar-94        BobDay  Created
//
//  Notes:      This thunk is 2nd part of a 2-byte thop.
//
//----------------------------------------------------------------------------

DWORD Thop_Enum_FORMATETC_3216(THUNKINFO *pti)
{
    return General_Enum_3216(pti,
                             sizeof(FORMATETC),
                             sizeof(FORMATETC16),
                             Callback_FORMATETC_3216,
                             Cleanup_FORMATETC_3216 );
}

//+---------------------------------------------------------------------------
//
//  Function:   Callback_STATDATA_3216, public
//
//  Synopsis:   Prepares the STATDATA structure for the copy back into 16-bit
//              address space.
//
//  Arguments:  [pti] - Thunking state information
//              [lp32] - Pointer to 32-bit output structure
//              [lp16] - Pointer to 16-bit returned structure
//
//  Returns:    SCODE indicating success/failure
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------
SCODE Callback_STATDATA_3216( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    SCODE               sc;
    LPSTATDATA          lpstatdata32;
    STATDATA16 UNALIGNED *lpstatdata16;
    LPADVISESINK        lpadv32;
    VPVOID              vpadv16;
    IUnknown            *punkThis32;

    sc = S_OK;

    lpstatdata32 = (LPSTATDATA)lp32;
    lpstatdata16 = GETVDMPTR( vp16, STATDATA16 );

    vpadv16 = lpstatdata16->pAdvSink;
    if ( vpadv16 != 0)
    {
        // We don't know whether it's an AdviseSink or
        // an AdviseSink2, so pass AdviseSink2 since it's
        // a superset of AdviseSink and will work for both

        punkThis32 =
            pti->pThkMgr->FindProxy3216(NULL, vpadv16,
                                        INDEX_IIDIDX(THI_IAdviseSink2),
                                        NULL);
        if (punkThis32 == NULL)
        {
            sc = E_OUTOFMEMORY;
        }

        lpadv32 = (LPADVISESINK)punkThis32;
    }
    else
    {
        lpadv32 = NULL;
    }

    lpstatdata32->formatetc.ptd = NULL;
    if (SUCCEEDED(sc))
    {
        // If this fails the AdviseSink proxy will be cleaned up in
        // the cleanup function later

        sc = ConvertFetc1632(pti,
                             vp16+FIELD_OFFSET(STATDATA16, formatetc),
                             &lpstatdata32->formatetc, TRUE);
    }

    if (SUCCEEDED(sc))
    {
        lpstatdata32->advf = lpstatdata16->advf;
        lpstatdata32->pAdvSink = lpadv32;
        lpstatdata32->dwConnection = lpstatdata16->dwConnection;
    }

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   Cleanup_STATDATA_3216, public
//
//  Synopsis:   Cleans up the any STATDATAs returned (either to 16-bit
//              or 32-bit)
//
//  Arguments:  [pti] - Thunking state information
//              [lp32] - Pointer to 32-bit output structure
//              [lp16] - Pointer to 16-bit returned structure
//
//  Returns:    nothing, should NEVER fail
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

void Cleanup_STATDATA_3216( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    STATDATA FAR    *lpstatdata32;
    STATDATA16 UNALIGNED *lpstatdata16;

    lpstatdata32 = (STATDATA FAR *)lp32;

    lpstatdata16 = GETVDMPTR( vp16, STATDATA16 );

    if ( lpstatdata32->formatetc.ptd != NULL )
    {
        TaskFree32( lpstatdata32->formatetc.ptd );
    }

    if ( lpstatdata32->pAdvSink != NULL )
    {
        // BUGBUG - What is the proper cleanup for interfaces?
        // Should the objects be released or only the proxies we
        // created?
        pti->pThkMgr->FreeProxy3216((VPVOID)lpstatdata16->pAdvSink);
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_Enum_STATDATA_3216, public
//
//  Synopsis:   Thunks IEnumSTATDATA::Next parameters
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    1-Mar-94        BobDay  Created
//
//  Notes:      This thunk is 2nd part of a 2-byte thop.
//
//----------------------------------------------------------------------------

DWORD Thop_Enum_STATDATA_3216(THUNKINFO *pti)
{
    return General_Enum_3216(pti,
                             sizeof(STATDATA),
                             sizeof(STATDATA16),
                             Callback_STATDATA_3216,
                             Cleanup_STATDATA_3216 );
}

//+---------------------------------------------------------------------------
//
//  Function:   Callback_MONIKER_3216, public
//
//  Synopsis:   Prepares the MONIKER structure for the copy back into 16-bit
//              address space.
//
//  Arguments:  [pti] - Thunking state information
//              [lp32] - Pointer to 32-bit output structure
//              [lp16] - Pointer to 16-bit returned structure
//
//  Returns:    SCODE indicating success/failure
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

SCODE Callback_MONIKER_3216( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    VPVOID          vpmoniker16;
    IUnknown        *punkThis32;
    SCODE           sc = S_OK;

    vpmoniker16 = *GETVDMPTR( vp16, VPVOID );

    punkThis32 = pti->pThkMgr->FindProxy3216(NULL, vpmoniker16,
                                             INDEX_IIDIDX(THI_IMoniker),
                                             NULL);
    if (punkThis32 == NULL)
    {
        sc = E_OUTOFMEMORY;
    }

    *(LPMONIKER *)lp32 = (LPMONIKER)punkThis32;

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   Cleanup_MONIKER_3216, public
//
//  Synopsis:   Cleans up the any MONIKERs returned (either to 16-bit
//              or 32-bit)
//
//  Arguments:  [pti] - Thunking state information
//              [lp32] - Pointer to 32-bit output structure
//              [lp16] - Pointer to 16-bit returned structure
//
//  Returns:    nothing, should NEVER fail
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

void Cleanup_MONIKER_3216( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    LPMONIKER       lpmoniker32;

    lpmoniker32 = *(LPMONIKER *)lp32;

    // BUGBUG - What is the proper cleanup for IEnum<Interface>?
    // Should the objects be released or only the proxies we
    // created?
    if (lpmoniker32 != NULL)
    {
        pti->pThkMgr->FreeProxy3216(*GETVDMPTR(vp16, VPVOID));
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_Enum_MONIKER_3216, public
//
//  Synopsis:   Thunks IEnumMONIKER::Next parameters
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    1-Mar-94        BobDay  Created
//
//  Notes:      This thunk is 2nd part of a 2-byte thop.
//
//----------------------------------------------------------------------------

DWORD Thop_Enum_MONIKER_3216(THUNKINFO *pti)
{
    return General_Enum_3216(pti,
                             sizeof(LPMONIKER),
                             sizeof(LPMONIKER),
                             Callback_MONIKER_3216,
                             Cleanup_MONIKER_3216 );
}

//+---------------------------------------------------------------------------
//
//  Function:   Callback_OLEVERB_3216, public
//
//  Synopsis:   Prepares the OLEVERB structure for the copy back into 16-bit
//              address space.
//
//  Arguments:  [pti] - Thunking state information
//              [lp32] - Pointer to 32-bit output structure
//              [lp16] - Pointer to 16-bit returned structure
//
//  Returns:    SCODE indicating success/failure
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

SCODE Callback_OLEVERB_3216( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    SCODE       sc;
    OLEVERB *lpoleverb32;
    OLEVERB UNALIGNED *lpoleverb16;

    lpoleverb32 = (LPOLEVERB)lp32;
    lpoleverb16 = GETVDMPTR(vp16, OLEVERB);

    lpoleverb32->lpszVerbName = NULL;
    sc = ConvertTaskString1632(pti, (VPSTR)lpoleverb16->lpszVerbName,
                               NULL, 0, &lpoleverb32->lpszVerbName);
    if (SUCCEEDED(sc))
    {
        lpoleverb32->lVerb        = lpoleverb16->lVerb;
        lpoleverb32->fuFlags      = lpoleverb16->fuFlags;
        lpoleverb32->grfAttribs   = lpoleverb16->grfAttribs;
    }

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   Cleanup_OLEVERB_3216, public
//
//  Synopsis:   Cleans up the any OLEVERBs returned (either to 16-bit
//              or 32-bit)
//
//  Arguments:  [pti] - Thunking state information
//              [lp32] - Pointer to 32-bit output structure
//              [lp16] - Pointer to 16-bit returned structure
//
//  Returns:    nothing, should NEVER fail
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

void Cleanup_OLEVERB_3216( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    OLEVERB FAR     *lpoleverb32;

    lpoleverb32 = (LPOLEVERB)lp32;

    if ( lpoleverb32->lpszVerbName != NULL )
    {
        TaskFree32( lpoleverb32->lpszVerbName );
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_Enum_OLEVERB_3216, public
//
//  Synopsis:   Thunks IEnumOLEVERB::Next parameters
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    1-Mar-94        BobDay  Created
//
//  Notes:      This thunk is 2nd part of a 2-byte thop.
//
//----------------------------------------------------------------------------

DWORD Thop_Enum_OLEVERB_3216(THUNKINFO *pti)
{
    return General_Enum_3216(pti,
                             sizeof(OLEVERB),
                             sizeof(OLEVERB),
                             Callback_OLEVERB_3216,
                             Cleanup_OLEVERB_3216 );
}

#define THOP_EFN(x)  Thop_Enum_ ## x ## _3216

DWORD (*aThopEnumFunctions3216[])(THUNKINFO *) =
{
    THOP_EFN(STRING),               // STRING
    THOP_EFN(UNKNOWN),              // UNKNOWN
    THOP_EFN(STATSTG),              // STATSTG
    THOP_EFN(FORMATETC),            // FORMATETC
    THOP_EFN(STATDATA),             // STATDATA
    THOP_EFN(MONIKER),              // MONIKER
    THOP_EFN(OLEVERB),              // OLEVERB
};
