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

#include "headers.cxx"
#pragma hdrstop

#include <ole2.h>
#include <valid.h>
#include <string.h>

#include "struct16.hxx"

//+---------------------------------------------------------------------------
//
//  Function:   EXECUTE_THOP1632, 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_THOP1632(THUNKINFO *pti)
{
    thkDebugOut((DEB_THOPS, "ExThop1632: %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 (*aThopFunctions1632[*((pti)->pThop) & THOP_OPMASK])(pti);
}
#endif

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

// Generated by the tool, in tc1632.cxx
DWORD ThunkCall1632( THUNKINFO * );

//+---------------------------------------------------------------------------
//
//  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 FixedThopHandler1632(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 )
    {
        pb32 = NULL;

        GET_STACK16(pti, vp16, VPVOID);
        if ( vp16 != 0 )
        {
            pb16 = (BYTE *)ValidatePtr16(pti, vp16, cb16, thop);
            if (pb16 != NULL)
            {
                pb32 = (BYTE *)_alloca(cb32);
                if (pb32 == NULL)
                {
                    pti->scResult = E_OUTOFMEMORY;
                }
                else if (fThopInput)
                {
                    (pfn1632)(pb16, pb32, cb16, cb32);
                }
            }
        }

        TO_STACK32(pti, pb32, BYTE *);

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

        if ( fThopOutput && vp16 != 0 )
        {
            pb16 = (BYTE *)WOWGetVDMPointer(vp16, cb16, TRUE);

            if (SUCCEEDED(dwResult))
            {
                (pfn3216)(pb32, pb16, cb32, cb16);
            }
            else if (!fThopInput)
            {
                // Zero out-only parameters on failure
                memset(pb16, 0, cb16);
            }
        }
    }
    else
    {
        (pfn1632)(PTR_STACK16(&pti->s16, cb16), PTR_STACK32(&pti->s32),
                  cb16, cb32);
        SKIP_STACK16(&pti->s16, cb16);
        SKIP_STACK32(&pti->s32, cb32);

        pti->pThop++;
        dwResult = EXECUTE_THOP1632(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_1632(THUNKINFO *pti)
{
    THOP thopSize;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

DWORD Thop_BINDOPTS_1632( THUNKINFO *pti )
{
    VPVOID vpbo16;
    BIND_OPTS UNALIGNED *pbo;
    UINT cb;

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

    PEEK_STACK16(pti, vpbo16, VPVOID);
    pbo = (BIND_OPTS UNALIGNED *)GetReadPtr16(pti, vpbo16, sizeof(BIND_OPTS));
    if (pbo != NULL)
    {
        cb = pbo->cbStruct;
    }
    else
    {
        // Doesn't really matter, since pti->scResult was set to error
        // by GetReadPtr16
        cb = sizeof(BIND_OPTS);
    }

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

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

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

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

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

//+---------------------------------------------------------------------------
//
//  Function:   Thop_ERROR_1632, 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_1632 ( THUNKINFO *pti )
{
    thkAssert((*pti->pThop & THOP_OPMASK) == THOP_ERROR);

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

    return (DWORD)E_UNEXPECTED;
}

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

DWORD Thop_LPSTR_1632( THUNKINFO *pti )
{
    DWORD   dwResult;
    BOOL    fThopInput;
    BOOL    fThopOutput;
    VPSTR   vpstr;
    LPTSTR  lpstrDest;
    TCHAR   atcStackText[CWCSTRINGPREALLOC];

    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_STACK16(pti, vpstr, VPSTR);

    if (vpstr == 0)
    {
        lpstrDest = NULL;
    }
    else
    {
        lpstrDest = Convert_VPSTR_to_LPOLESTR( pti, vpstr, atcStackText,
                                               CWCSTRINGPREALLOC );
    }

    thkDebugOut((DEB_ARGS, "In1632  LPSTR: %p -> %p '%ws'\n",
                 vpstr, lpstrDest, lpstrDest));

    TO_STACK32(pti, lpstrDest, LPTSTR );

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

    if (lpstrDest != NULL)
    {
        Convert_VPSTR_to_LPOLESTR_free( atcStackText, lpstrDest );
    }

    return( dwResult );
}

//+---------------------------------------------------------------------------
//
//  Function:   ConvertTaskString3216, public
//
//  Synopsis:   Converts a task-memory string
//
//  Arguments:  [pti] - Thunk info
//              [pos32] - String
//              [vpstrPreAlloc] - Preallocated string or NULL
//              [cchPreAlloc] - Preallocated size or zero
//              [pvpstr16] - String
//
//  Returns:    Appropriate status code
//
//  Modifies:   [pti]
//              [pvpstr16]
//
//  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 and valid
//
//----------------------------------------------------------------------------

SCODE ConvertTaskString3216(THUNKINFO *pti,
                            LPOLESTR pos32,
                            VPSTR vpstrPreAlloc,
                            UINT cchPreAlloc,
                            VPSTR UNALIGNED *pvpstr16)
{
    VPVOID vpstr16;
    UINT cch;
    SCODE sc;

    sc = S_OK;

    if (pos32 == NULL)
    {
        vpstr16 = 0;
    }
    else
    {
        if (IsBadStringPtr(pos32, CCHMAXSTRING))
        {
            sc = E_INVALIDARG;
        }
        else
        {
            cch = wcslen(pos32)+1;

            if (cch > cchPreAlloc)
            {
                // Our prealloc buffer is too small, so try and get a
                // new one

                // Allow for each Unicode character to take two
                // bytes in case of multibyte expansion
                vpstr16 = (VPSTR)TaskMalloc16( cch*2 );
                if (vpstr16 == 0)
                {
                    sc = E_OUTOFMEMORY;
                }
            }
            else
            {
                vpstr16 = vpstrPreAlloc;
            }

            if (SUCCEEDED(sc))
            {
                sc = Convert_LPOLESTR_to_VPSTR(pos32, vpstr16, cch, cch*2);
                if (FAILED(sc) && vpstr16 != vpstrPreAlloc)
                {
                    TaskFree16(vpstr16);
                }
            }

            TaskFree32(pos32);
        }
    }

    if (SUCCEEDED(sc))
    {
        // If there was a preallocated string we didn't use,
        // free it
        if (vpstrPreAlloc != 0 && vpstrPreAlloc != vpstr16)
        {
            TaskFree16(vpstrPreAlloc);
        }

        *pvpstr16 = vpstr16;
    }

    return sc;
}

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

    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_STACK16(pti, vpvpstr16, VPSTR);

    lplpstr32 = NULL;
    if ( vpvpstr16 != 0 )
    {
        lpvpstr16 = (VPSTR UNALIGNED *)GetWritePtr16(pti, vpvpstr16,
                                                     sizeof(VPSTR));
        if ( lpvpstr16 != NULL )
        {
            lplpstr32 = &lpstr;

            vpstr16 = (VPSTR)TaskMalloc16(CBSTRINGPREALLOC);
            if (vpstr16 == 0)
            {
                pti->scResult = E_OUTOFMEMORY;
            }
        }
    }

    TO_STACK32(pti, lplpstr32, LPOLESTR FAR *);

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

    if ( lplpstr32 != NULL )
    {
        if ( SUCCEEDED(dwResult) )
        {
            SCODE sc;

            sc = ConvertTaskString3216(pti, lpstr, vpstr16,
                                       CWCSTRINGPREALLOC, &vpstr16);
            if (FAILED(sc))
            {
                dwResult = sc;
            }
            else
            {
                thkDebugOut((DEB_ARGS, "Out1632 LPLPSTR: %p -> %p '%s'\n",
                             lpstr, vpstr16,
                             (char *)WOWGetVDMPointer(vpstr16, 0, TRUE)));
            }
        }

        if (FAILED(dwResult))
        {
            TaskFree16(vpstr16);
            vpstr16 = 0;
        }

        lpvpstr16 = GETVDMPTR( vpvpstr16, VPSTR );
        *lpvpstr16 = vpstr16;
    }

    return( dwResult );
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_BUFFER_1632, public
//
//  Synopsis:   Converts 16-bit block of memory to 32-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_1632( THUNKINFO *pti )
{
    DWORD       dwResult;
    BOOL        fThopInput;
    BOOL        fThopOutput;
    VPVOID      vp16;
    LPVOID      lp16;
    LPVOID      lp32;
    DWORD       dwCount;
#if DBG == 1
    DWORD       dwWatch;
#endif

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

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

    //
    // We only have in and out BUFFER thops
    //
    thkAssert( fThopInput || fThopOutput && "BUFFER must be in or out only!" );

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

    lp32 = NULL;

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

#if DBG == 1
        // In debug, make sure that no data is written back to the
        // memory we pass on
        dwWatch = WATCH_VALUE;
        lp32 = &dwWatch;
#else
        lp32 = &dwResult;
#endif
    }
    else
    {
        if ( fThopInput )
        {
            lp16 = GetReadPtr16(pti, vp16, dwCount);
        }
        if ( fThopOutput )
        {
            lp16 = GetWritePtr16(pti, vp16, dwCount);
        }

        if ( lp16 != NULL )
        {
            lp32 = (LPVOID)malloc( dwCount );
            if ( lp32 == NULL )
            {
                pti->scResult = E_OUTOFMEMORY;
            }
            else
            {
                if ( fThopInput )
                {
                    memcpy( lp32, lp16, dwCount );
                }
            }
        }
    }

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

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

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

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

            memcpy( lp16, lp32, dwCount );
        }
    }

#if DBG == 1
    if (lp32 != NULL && dwCount == 0)
    {
        thkAssert(dwWatch == WATCH_VALUE);
    }
#endif

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

    return( dwResult );
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_SNB_1632, public
//
//  Synopsis:   Converts 16-bit SNB to 32-bit SNB pointer
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    23-Feb-94       BobDay  Created
//
//----------------------------------------------------------------------------
DWORD Thop_SNB_1632( THUNKINFO *pti )
{
    DWORD       dwResult;
    BOOL        fThopInput;
    BOOL        fThopOutput;
    VPVOID      snb16;
    VPSTR       *lpvpstr;
    VPSTR UNALIGNED *lpvpstrTemp;
    VPSTR       vpstr;
    SNB         snb32;
    UINT        cStr;
    UINT        cStrTemp;
    LPTSTR      *lplpstr;
    UINT        cbStrings;
    OLECHAR     *pocStr;
    LPSTR       lpstr16;

    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_STACK16(pti, snb16, VPVOID);

    snb32 = NULL;
    if ( snb16 != 0 )
    {
        //
        // Count the strings in the 16-bit snb
        //
        lpvpstr = (VPSTR FAR *)GetReadPtr16( pti, snb16, sizeof(VPSTR) );
        if ( lpvpstr != 0 )
        {
            lpvpstrTemp = lpvpstr;
            cStr = 0;
            cbStrings = 0;
            do
            {
                cStr++;
                vpstr = *lpvpstrTemp++;

                if ( vpstr == 0 )
                {
                    break;
                }

                lpstr16 = GetStringPtr16(pti, vpstr, CCHMAXSTRING,
                                         &cStrTemp);
                if ( lpstr16 == 0 )
                {
                    lpvpstr = NULL;
                    break;      // Leave with snb32 still NULL
                }
                cbStrings += cStrTemp;
            }
            while ( TRUE );
        }

        if ( lpvpstr != NULL )
        {
            thkDebugOut((DEB_ARGS, "In1632  SNB: %d strings\n", cStr));

            //
            // Allocate space for the 32-bit snb
            //
            snb32 = (LPOLESTR FAR *)malloc( cStr*sizeof(LPOLESTR) +
                                            cbStrings*sizeof(WCHAR));
            if (snb32 == NULL)
            {
                pti->scResult = E_OUTOFMEMORY;
            }
            else
            {
                pocStr = (OLECHAR *)((BYTE *)snb32+cStr*sizeof(LPOLESTR));

                //
                // Now convert the strings
                //
                lpvpstrTemp = lpvpstr;
                lplpstr = snb32;
                cStrTemp = cStr - 1;

                while ( cStrTemp > 0 )
                {
                    --cStrTemp;

                    vpstr = *lpvpstrTemp++;

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

                    // Guaranteed to use pocStr as storage since cbStrings is
                    // large enough to contain all the strings
                    *lplpstr = Convert_VPSTR_to_LPOLESTR( pti, vpstr,
                                                          pocStr, cbStrings );

                    if (*lplpstr == NULL)
                    {
                        free(snb32);
                        snb32 = NULL;
                        break;
                    }

                    thkDebugOut((DEB_ARGS, "In1632  SNB: %p '%s' -> "
                                 "%p '%ws'\n",
                                 vpstr, WOWGetVDMPointer(vpstr, 0, TRUE),
                                 *lplpstr, *lplpstr));

                    pocStr += wcslen(pocStr)+1;
                    lplpstr++;
                }

                // Terminate SNB
                *lplpstr = NULL;

                thkAssert(*lpvpstrTemp == 0);
            }
        }
    }

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

    TO_STACK32(pti, snb32, SNB );

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

    //
    // Free SNB memory if necessary
    //
    if ( snb32 != 0 )
    {
        free(snb32);
    }

    return( dwResult );
}

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

DWORD ThunkInterface1632(THUNKINFO *pti,
                         IIDIDX iidx,
                         THOP thop,
                         IUnknown *punkOuter)
{
    VPVOID vpv;
    DWORD dwResult;
    IUnknown *punkThis32In;
    IUnknown *punkThis32Out;
    VPVOID vpvThis16;
    BOOL fThopIn, fThopOut;
    VPVOID vpvIn;
    VPVOID *pvpvOut;
    VPVOID vpPreAlloc = NULL;

    punkThis32In = NULL;

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

    // Retrieve in or out interface pointer
    GET_STACK16(pti, vpv, VPVOID);

    // Out takes precedence over in for determining indirection depth

    if (fThopOut && vpv != 0)
    {
        pvpvOut = (VPVOID FAR *)GetWritePtr16(pti, vpv, sizeof(VPVOID));
        if (pvpvOut != NULL)
        {
            vpvIn = *(VPVOID UNALIGNED *)pvpvOut;
        }
        else
        {
            thkDebugOut((DEB_WARN, "WARNING: failing - bad "
                                   "pointer %p\n", vpv));
        }
    }
    else
    {
        vpvIn = vpv;
    }

    if (fThopIn && SUCCEEDED(pti->scResult))
    {
        if (vpvIn == 0)
        {
            punkThis32In = NULL;
        }
        else
        {
            if ( !IsValidInterface16(pti, vpvIn) )
            {
                thkDebugOut((DEB_WARN, "WARNING: failing - invalid "
                                       "interface %p\n", vpvIn));
            }
            else
            {
                thkAssert(IIDIDX_IS_IID(iidx) ||
                          (IIDIDX_INDEX(iidx) >= 0 &&
                           IIDIDX_INDEX(iidx) < THI_COUNT));

                punkThis32In = pti->pThkMgr->FindProxy3216(NULL, vpvIn, iidx,
                                                           NULL);
                if (punkThis32In == NULL)
                {
                    pti->scResult = E_OUTOFMEMORY;
                    thkDebugOut((DEB_WARN, "WARNING: failing - Can't "
                                           "create proxy for %p\n", vpvIn));
                }
            }
        }

        thkDebugOut((DEB_ARGS, "In1632  %s %p -> %p\n",
                     IidIdxString(iidx), vpvIn, punkThis32In));
    }

    if (fThopOut && vpv != 0)
    {
        thkAssert(IIDIDX_IS_IID(iidx) ||
                  (IIDIDX_INDEX(iidx) >= 0 &&
                   IIDIDX_INDEX(iidx) < THI_COUNT));
            
        // Preallocate a proxy for the out parameter
        if ((vpPreAlloc = pti->pThkMgr->CanGetNewProxy1632(iidx))== NULL)
        {
            pti->scResult = E_OUTOFMEMORY;
            thkDebugOut((DEB_WARN, "failing - Cannot allocate proxy\n"));
        }

        punkThis32Out = punkThis32In;
        TO_STACK32(pti, &punkThis32Out, IUnknown **);
    }
    else
    {
        TO_STACK32(pti, punkThis32In, IUnknown *);
    }

    // Assume that our caller has already adjusted pThop
    dwResult = EXECUTE_THOP1632(pti);

    if (punkThis32In != NULL)
    {
        // Note that if the routine called keeps the proxy around
        // it should AddRef it so this won't really remove the object
        pti->pThkMgr->FreeProxy3216(vpvIn);
    }

    if (fThopOut && vpv != 0)
    {
        vpvThis16 = 0;

        if (SUCCEEDED((SCODE)dwResult) && punkThis32Out != NULL)
        {
            // Get a 16-bit proxy object for the 32-bit object

            if (punkOuter != NULL)
            {
                vpvThis16 =
                    pti->pThkMgr->FindAggregate1632(vpPreAlloc, punkOuter,
                                                    punkThis32Out, iidx);
            }
            else
            {
                vpvThis16 = pti->pThkMgr->FindProxy1632(vpPreAlloc,
                                                        punkThis32Out, iidx,
                                                        NULL);
            }
        }
        else if (vpPreAlloc != NULL)
        {
            // Return our preallocated proxy because we don't need it
            pti->pThkMgr->FreeNewProxy1632(vpPreAlloc, iidx);
        }

        // Set the out param
        // Get the VDM pointer again since we may have done nested
        // transitions since we last got it
        pvpvOut = GETVDMPTR(vpv, VPVOID);
        *(VPVOID UNALIGNED *)pvpvOut = vpvThis16;

        thkDebugOut((DEB_ARGS, "Out1632 %s %p -> %p\n",
                     IidIdxString(iidx), punkThis32Out, vpvThis16));
    }

    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_IFACEGEN_1632, public
//
//  Synopsis:   Thunks interfaces out through ppvs from 16->32
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    23-Feb-94       DrewB   Created
//
//----------------------------------------------------------------------------

DWORD Thop_IFACEGEN_1632(THUNKINFO *pti)
{
    IIDIDX iidx;
    THOP thop, thopOp, thopWeakOffset;
    VPVOID vpvIid;
    IUnknown *punkOuter;
    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_STACK16(pti, vpvIid, VPVOID, *pti->pThop, sizeof(VPVOID));
    pti->pThop++;

    piid = (IID const *)GetReadPtr16(pti, vpvIid, sizeof(IID const));
    if (piid == NULL)
    {
        pti->scResult = E_INVALIDARG;
    }
    else
    {
        if (!IsValidIid(*piid))
        {
            pti->scResult = E_INVALIDARG;
        }
        else
        {
            iidx = IidToIidIdx(*piid);
        }
    }

    punkOuter = NULL;
    if (thopOp == THOP_IFACEGENOWNER)
    {
        // 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_STACK32(pti, punkOuter, IUnknown *, thopWeakOffset);
    }

    return ThunkInterface1632(pti, iidx, thop, punkOuter);
}

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

DWORD Thop_OIFI_1632( THUNKINFO *pti )
{
    DWORD dwResult;
    VPVOID vpoifi16;
    OIFI16 UNALIGNED *poifi16;
    OLEINPLACEFRAMEINFO oifi32;
    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_STACK16(pti, vpoifi16, VPVOID);

    poifi32 = NULL;
    if (vpoifi16 != 0)
    {
        if (GetWritePtr16(pti, vpoifi16, sizeof(OIFI16)) != NULL)
        {
            poifi32 = &oifi32;
            poifi32->cb = sizeof(OLEINPLACEFRAMEINFO);
        }
    }

    TO_STACK32(pti, poifi32, OLEINPLACEFRAMEINFO *);

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

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

        if (SUCCEEDED(dwResult))
        {
            poifi16->cb            = sizeof(OIFI16);
            poifi16->fMDIApp       = (WORD)poifi32->fMDIApp;
            poifi16->hwndFrame     = GETHWND16(poifi32->hwndFrame);
            poifi16->cAccelEntries =
                ClampULongToUShort(poifi32->cAccelEntries);

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

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

        if (FAILED(dwResult))
        {
            memset(poifi16, 0, sizeof(OIFI16));
        }
    }

    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_STGMEDIUM_1632, 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_1632( THUNKINFO *pti )
{
    DWORD       dwResult;
    BOOL        fThopInput;
    BOOL        fThopOutput;
    VPVOID      vpstgmedium16;
    STGMEDIUM   UNALIGNED *lpstgmedium16;
    STGMEDIUM   *lpstgmedium32;
    STGMEDIUM   stgmedium32;
    DWORD       dwSize;
    SCODE       sc;
    BOOL        fReleaseParam;
    SHORT       fTransferOwnership;
    FORMATETC   *pfe;
    THOP        thopFeOffset;

    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_STACK16(pti, vpstgmedium16, VPVOID);

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

    if (fReleaseParam)
    {
        GET_STACK16(pti, fTransferOwnership, SHORT);
    }
    else
    {
        fTransferOwnership = FALSE;
    }

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

    lpstgmedium32 = NULL;

    if ( vpstgmedium16 != 0 )
    {
        if ( fThopInput )
        {
            // Apparently if you pass TYMED_NULL into GetDataHere
            // it's supposed to work like GetData, so switch input-only
            // TYMED_NULLs to output

            lpstgmedium16 = (STGMEDIUM FAR *)GetReadPtr16(pti, vpstgmedium16,
                                                           sizeof(STGMEDIUM));

            if (lpstgmedium16->tymed == TYMED_NULL &&
                !fTransferOwnership)
            {
                memset(&stgmedium32,0,sizeof(stgmedium32));
                thkAssert( TYMED_NULL == 0 );   // Don't need to set tymed to 0

                fThopInput = FALSE;
                fThopOutput = TRUE;
            }
            else
            {
                sc = ConvertStgMed1632(pti, vpstgmedium16, &stgmedium32,
                                       pfe, &dwSize);
                if (SUCCEEDED(sc))
                {
                    lpstgmedium32 = &stgmedium32;
                }
            }
        }
        if ( fThopOutput )
        {
            thkAssert( fThopOutput );

            lpstgmedium16 = (STGMEDIUM FAR *)GetWritePtr16(pti, vpstgmedium16,
                                                           sizeof(STGMEDIUM));
            if ( lpstgmedium16 != NULL )
            {
                lpstgmedium32 = &stgmedium32;
            }

            // BUGBUG - 32->16 sets tymed to TYMED_NULL before passing
            // on the STGMEDIUM because some apps incorrectly check for this
            // I'm assuming this isn't necessary for 16->32 because 32-bit
            // apps can't rely on tymed being NULL since nothing in the 32-bit
            // code sets it that way for out parameters
            // DrewB 5/30
        }
    }

    TO_STACK32(pti, lpstgmedium32, STGMEDIUM FAR *);

    if (fReleaseParam)
    {
        TO_STACK32(pti, (BOOL)fTransferOwnership, BOOL);
    }

    dwResult = EXECUTE_THOP1632(pti);

    if ( lpstgmedium32 != NULL)
    {
        if ( fThopInput )
        {
            if (!fTransferOwnership || FAILED(dwResult))
            {
                sc = CleanStgMed32(pti, lpstgmedium32, vpstgmedium16,
                                   dwSize, TRUE, pfe);
                if (FAILED(sc))
                {
                    dwResult = (DWORD)sc;
                }
            }
            else if (SUCCEEDED(dwResult))
            {
                lpstgmedium16 = GETVDMPTR(vpstgmedium16, STGMEDIUM);
                if (lpstgmedium16->pUnkForRelease == NULL)
                {
                    sc = CleanStgMed16(pti, vpstgmedium16, lpstgmedium32,
                                       0, FALSE, pfe);
                    thkAssert(SUCCEEDED(sc));
                }
            }
        }
        else
        {
            thkAssert( fThopOutput );

            if (SUCCEEDED(dwResult))
            {
                sc = ConvertStgMed3216(pti, lpstgmedium32, vpstgmedium16,
                                       pfe, &dwSize);
                if (FAILED(sc))
                {
                    dwResult = (DWORD)sc;
                    ReleaseStgMedium(lpstgmedium32);
                }
                else if (lpstgmedium32->pUnkForRelease == NULL)
                {
                    sc = CleanStgMed32(pti, lpstgmedium32, vpstgmedium16,
                                       dwSize, FALSE, pfe);
                    thkAssert(SUCCEEDED(sc));
                }
            }

            if (FAILED(dwResult))
            {
                lpstgmedium16 = GETVDMPTR(vpstgmedium16, STGMEDIUM);
                memset(lpstgmedium16, 0, sizeof(STGMEDIUM));
            }
        }
    }

    return dwResult;
}

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

SCODE ConvertStatStg3216(THUNKINFO *pti,
                         STATSTG *pss32,
                         VPVOID vpss16,
                         VPSTR vpstrPreAlloc,
                         UINT cchPreAlloc)
{
    STATSTG UNALIGNED *pss16;
    SCODE sc;
    VPSTR vpstr16;

    pss16 = GETVDMPTR(vpss16, STATSTG);

    sc = ConvertTaskString3216(pti, pss32->pwcsName,
                               vpstrPreAlloc, cchPreAlloc,
                               &vpstr16);
    if (SUCCEEDED(sc))
    {
        memcpy(pss16, pss32, sizeof(STATSTG));
        pss16->pwcsName = (LPOLESTR)vpstr16;
    }

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_STATSTG_1632, 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_1632( THUNKINFO *pti )
{
    DWORD       dwResult;
    BOOL        fThopInput;
    BOOL        fThopOutput;
    VPVOID      vpstatstg16;
    STATSTG UNALIGNED *lpstatstg16;
    VPSTR       vpstr;
    STATSTG     statstg32;
    STATSTG     *lpstatstg32;

    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_STACK16(pti, vpstatstg16, VPVOID);

    lpstatstg32 = NULL;

    lpstatstg16 = (STATSTG FAR *)GetWritePtr16( pti, vpstatstg16,
                                                sizeof(STATSTG) );
    if ( lpstatstg16 != NULL )
    {
        statstg32.pwcsName = NULL;
        lpstatstg32 = &statstg32;

        vpstr = (VPSTR)TaskMalloc16(CBSTRINGPREALLOC);
        if (vpstr == 0)
        {
            pti->scResult = E_OUTOFMEMORY;
        }
    }

    TO_STACK32(pti, lpstatstg32, STATSTG FAR *);

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

    if ( lpstatstg32 != NULL )
    {
        if ( SUCCEEDED(dwResult) )
        {
            SCODE sc;

            sc = ConvertStatStg3216(pti, &statstg32, vpstatstg16,
                                    vpstr, CWCSTRINGPREALLOC);
            if (FAILED(sc))
            {
                dwResult = sc;
            }
        }

        if (FAILED(dwResult))
        {
            TaskFree16(vpstr);

            lpstatstg16 = GETVDMPTR(vpstatstg16, STATSTG);
            memset(lpstatstg16, 0, sizeof(STATSTG));
        }
    }

    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_DVTARGETDEVICE_1632, 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_1632( THUNKINFO *pti )
{
    DWORD               dwResult;
    BOOL                fThopInput;
    BOOL                fThopOutput;
    UINT                uiSize;
    DVTARGETDEVICE      *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_STACK16(pti, vpdv16, VPVOID);

    lpdv32 = NULL;

    if ( vpdv16 != 0 )
    {
        sc = ConvertDvtd1632(pti, vpdv16, ArStack32, FrStack32, &lpdv32,
                             &uiSize);
        if (FAILED(sc))
        {
            pti->scResult = sc;
        }
    }

    TO_STACK32(pti, lpdv32, DVTARGETDEVICE FAR *);
    pti->pThop++;
    dwResult = EXECUTE_THOP1632(pti);

    if (lpdv32 != NULL)
    {
        FrStack32(lpdv32, uiSize);
    }

    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_FORMATETC_1632, 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_1632( THUNKINFO *pti )
{
    DWORD               dwResult;
    BOOL                fThopInput;
    BOOL                fThopOutput;
    VPVOID              vpformatetc16;
    FORMATETC16 UNALIGNED *lpformatetc16;
    LPFORMATETC         lpformatetc32;
    FORMATETC           formatetc32;
    DVTARGETDEVICE      *lpdv32;
    SCODE               sc;

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

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

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

    lpdv32 = NULL;

    GET_STACK16(pti, vpformatetc16, VPVOID);

    if ( vpformatetc16 == 0 )
    {
        lpformatetc32 = NULL;
    }
    else
    {
        lpformatetc32 = &formatetc32;

        if ( fThopInput )
        {
            sc = ConvertFetc1632(pti, vpformatetc16, lpformatetc32, FALSE);
            if (FAILED(sc))
            {
                pti->scResult = 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.
            //
            memset( &formatetc32, 0, sizeof(formatetc32) );
            lpformatetc16 = (LPFORMATETC16)GetWritePtr16(pti, vpformatetc16,
                                                         sizeof(FORMATETC16));
        }
    }

    TO_STACK32(pti, lpformatetc32, LPFORMATETC);
    pti->pThop++;
    dwResult = EXECUTE_THOP1632(pti);

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

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

        if (FAILED(dwResult))
        {
            memset(lpformatetc16, 0, sizeof(FORMATETC16));
        }
    }

    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_LOGPALETTE_1632, 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_1632 ( THUNKINFO *pti )
{
    DWORD         dwResult;
    BOOL          fThopInput;
    BOOL          fThopOutput;
    UINT          uiSize;
    LPLOGPALETTE  lplogpal32;
    VPVOID        vplogpal16;
    LOGPALETTE UNALIGNED *lplogpal16;
    VPVOID        vp16;
    LPVOID UNALIGNED *lp16;

    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!" );

    lplogpal32 = NULL;

    if ( fThopInput )
    {
        //
        // Processing for a LPLOGPALETTE as input
        //
        GET_STACK16(pti, vplogpal16, VPVOID);

        if ( vplogpal16 != 0 )
        {
            // Copy over the input LOGPALETTE structure
            lplogpal16 = (LPLOGPALETTE)GetReadPtr16( pti, vplogpal16,
                                                     sizeof(LOGPALETTE) );

            if ( lplogpal16 != NULL )
            {
                uiSize = CBPALETTE(lplogpal16->palNumEntries);
                lplogpal16 = (LPLOGPALETTE)GetReadPtr16(pti, vplogpal16,
                                                        uiSize);
                if ( lplogpal16 != NULL )
                {
                    lplogpal32 = (LPLOGPALETTE)malloc(uiSize);
                    if ( lplogpal32 == NULL )
                    {
                        pti->scResult = E_OUTOFMEMORY;
                    }
                    else
                    {
                        memcpy( lplogpal32, lplogpal16, uiSize );
                    }
                }
            }
        }

        TO_STACK32(pti, lplogpal32, LPLOGPALETTE);
        pti->pThop++;
        dwResult = EXECUTE_THOP1632(pti);

        if ( lplogpal32 )
        {
            free( lplogpal32 );
        }
    }
    else
    {
        //
        // Processing for LPLPLOGPALETTE as output
        //

        thkAssert(fThopOutput);

        GET_STACK16(pti, vp16, VPVOID);

        vplogpal16 = (VPVOID)TaskMalloc16(CBPALETTE(NPALETTEPREALLOC));
        if (vplogpal16 == 0)
        {
            pti->scResult = E_OUTOFMEMORY;
        }

        TO_STACK32(pti, &lplogpal32, LPLOGPALETTE *);

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

        if ( SUCCEEDED(dwResult) && lplogpal32 != NULL )
        {
            //
            // Copy the returned LOGPALETTE into 16-bit memory
            //
            uiSize = CBPALETTE(lplogpal32->palNumEntries);
            if (uiSize > CBPALETTE(NPALETTEPREALLOC))
            {
                vplogpal16 = (VPVOID)TaskMalloc16(uiSize);
                if ( vplogpal16 == 0 )
                {
                    dwResult = (DWORD)E_OUTOFMEMORY;
                }
            }

            if (vplogpal16 != 0)
            {
                lplogpal16 = (LPLOGPALETTE)WOWGetVDMPointer(vplogpal16,
                                                            uiSize, TRUE);
                if ( lplogpal16 == NULL )
                {
                    dwResult = (DWORD)E_UNEXPECTED;
                    vplogpal16 = 0;
                }
                else
                {
                    memcpy( lplogpal16, lplogpal32, uiSize );
                }
            }

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

        //
        // Update the value pointed to by the parameter on the 16-bit stack
        //
        lp16 = GETVDMPTR(vp16, LPVOID);
        if ( lp16 == NULL )
        {
            dwResult = (DWORD)E_UNEXPECTED;
        }
        else
        {
            *lp16 = (LPVOID)vplogpal16;
        }
    }
    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_CRGIID_1632, public
//
//  Synopsis:   Converts 16-bit CRGIID to 32-bit CRGIID structure
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    23-Feb-94       BobDay  Created
//
//----------------------------------------------------------------------------
DWORD Thop_CRGIID_1632( 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_STACK16(pti, dwCount, DWORD);
    GET_STACK16(pti, vpiid16, VPVOID);

    lpiid32 = NULL;

    if ( vpiid16 != 0 )
    {
        lpiid16 = (IID UNALIGNED *)GetReadPtr16( pti, vpiid16,
                                                 dwCount*sizeof(IID) );
        if ( lpiid16 != NULL )
        {
            lpiid32 = (IID FAR *)malloc( dwCount * sizeof(IID) );
            if ( lpiid32 == NULL )
            {
                pti->scResult = E_OUTOFMEMORY;
            }
            else
            {
                memcpy( lpiid32, lpiid16, dwCount*sizeof(IID) );
            }
        }
    }

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

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

    if ( lpiid32 != NULL )
    {
        free( lpiid32 );
    }
    return( dwResult );
}

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

DWORD Thop_INTERFACEINFO_1632(THUNKINFO *pti)
{
    INTERFACEINFO *pii32;
    INTERFACEINFO ii32;
    INTERFACEINFO16 UNALIGNED *pii16;
    VPVOID vpii16;
    DWORD dwResult;
    IUnknown *punk32;

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

    pii32 = NULL;
    punk32 = NULL;

    GET_STACK16(pti, vpii16, VPVOID);
    if (vpii16 != 0)
    {
        pii16 = (INTERFACEINFO16 UNALIGNED *)
            GetReadPtr16(pti, vpii16, sizeof(INTERFACEINFO16));
        if (pii16 != NULL)
        {
            if (pii16->pUnk != NULL)
            {
                punk32 =
                    pti->pThkMgr->FindProxy3216(NULL, pii16->pUnk,
                                                INDEX_IIDIDX(THI_IUnknown),
                                                NULL);
                if (punk32 == NULL)
                {
                    pti->scResult = E_OUTOFMEMORY;
                }
            }

            pii32 = &ii32;
            pii32->pUnk = punk32;
            pii32->iid = pii16->iid;
            pii32->wMethod = pii16->wMethod;

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

    TO_STACK32(pti, pii32, INTERFACEINFO *);

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

    if (punk32 != NULL)
    {
        pii16 = GETVDMPTR(vpii16, INTERFACEINFO16);
        pti->pThkMgr->FreeProxy3216(pii16->pUnk);
    }

    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_RETURNTYPE_1632, 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_1632(THUNKINFO *pti)
{
    THOP thops[2];
    DWORD dwResult;
    ALIAS alias;
    VPVOID vpPreAlloc = 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 ((vpPreAlloc =
             pti->pThkMgr->CanGetNewProxy1632(iidx)) == NULL)
        {
            pti->scResult = E_OUTOFMEMORY;
        }
        break;

    case THOP_ALIAS32:
        thkAssert(thops[1] == ALIAS_CREATE);

        // The value used for preallocation doesn't really matter
        // as long as it's unique and not INVALID_VALUE
        // In our case we know that we won't have to deal with nested
        // preallocations so uniqueness is not as much of an issue
        // By using INVALID_HANDLE_VALUE, we can be sure that there
        // won't already be an entry with that value already in the
        // alias table since we're aliasing handles
        alias = gAliases32.AddValue((DWORD)INVALID_HANDLE_VALUE);
        if (alias == INVALID_ALIAS)
        {
            pti->scResult = E_OUTOFMEMORY;
        }
        break;
    }

    dwResult = EXECUTE_THOP1632(pti);

    // Now that we have the return value thunk it from 32->16
    // We must use pti->scResult to check for failure rather than
    // dwResult because dwResult may not be an SCODE

    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)(SHORT)*(LONG *)&dwResult;
        break;

    case THOP_IFACE | THOP_IN:
        if (dwResult != 0)
        {
            if (FAILED(pti->scResult))
            {
                dwResult = 0;
            }
            else
            {
                dwResult =
                    (DWORD)pti->pThkMgr->FindProxy1632(vpPreAlloc,
                                                       (IUnknown *)dwResult,
                                                       iidx, NULL);
                thkAssert(dwResult != 0);

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

    case THOP_ALIAS32:
        if (dwResult != 0)
        {
            if (FAILED(pti->scResult))
            {
                dwResult = 0;
            }
            else
            {
                gAliases32.SetValue(alias, dwResult);

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

                dwResult = (DWORD)alias;
            }
        }
        break;

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

    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_IFACE_1632, 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_1632(THUNKINFO *pti)
{
    IIDIDX iidx;
    THOP thop, thopOp, thopWeakOffset;
    IUnknown *punkOuter;

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

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

    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);

    punkOuter = NULL;
    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_STACK32(pti, punkOuter, IUnknown *, thopWeakOffset);
    }

    return ThunkInterface1632(pti, iidx, thop, punkOuter);
}

typedef struct tagOLESTREAMOBJECT
{
    OLESTREAM   os;
    VPVOID      vpolestream16;
} OLESTREAMOBJECT, FAR * LPOLESTREAMOBJECT;

#define OLESTREAM_GET 0
#define OLESTREAM_PUT 1


//+---------------------------------------------------------------------------
//
//  Function:   OLESTREAM_Callback, private
//
//  Synopsis:   Handles callbacks into 16-bit world for OLESTREAM methods
//
//  Arguments:  [dwMethod] - Index of method to invoke
//              [lposo]    - Pointer to 32 LPOLESTREAM
//              [lpv]      - Pointer to 32 bit buffer
//              [dwCount]  - Size of 32 bit buffer
//
//
//  Returns:    Appropriate status code
//
//  History:    11-Mar-94   BobDay  Created
//              29-May-94   TerryRu Converted to call WOWCallBackEx directly.
//
//----------------------------------------------------------------------------

DWORD OLESTREAM_Callback( DWORD dwMethod,
                          LPOLESTREAM lpos,
                          LPVOID lpv,
                          DWORD dwCount )
{


    const DWORD             cbStack     = sizeof( DWORD ) +
        sizeof( VPVOID ) + sizeof( VPVOID );

    BYTE                    b32Args[cbStack];
    DWORD                   dwResult;

    VPVOID                  vpvVtbl16;
    VTBLFN                  vpfn16;

    VPVOID                  vpolestream16;
    LPOLESTREAMOBJECT       lposo;

    VPVOID                  vp16;
    LPVOID                  lp16;


    lposo = (LPOLESTREAMOBJECT)lpos;
    vpolestream16 = lposo->vpolestream16;


    // Get pointer to 16 bit this pointer

    vpvVtbl16 =  (VPVOID)*GETVDMPTR( vpolestream16,  VPVOID );
    vpfn16    =  (VTBLFN)*GETVDMPTR( vpvVtbl16+dwMethod*sizeof(VPVOID),
                                     VPVOID );

    //
    // now thop the  IN 32 bit-block of memory to 16 bit block
    //

    vp16 = STACKALLOC16( dwCount );

    if ( vp16 == NULL )
    {
        return (DWORD) E_OUTOFMEMORY;
    }


    if( dwMethod == OLESTREAM_PUT )
    {
        lp16 = WOWGetVDMPointer( vp16, dwCount, TRUE );
        memcpy( lp16, lpv, dwCount );
    }


    // setup 32 bit arguements.
    *(DWORD *)(b32Args)             = dwCount;
    *(VPVOID *)(b32Args+4)          = vp16;
    *(VPVOID *)(b32Args+8)          = vpolestream16;



    if( !WOWCallback16Ex( (DWORD)vpfn16, WCB16_PASCAL, cbStack, b32Args,
                         &dwResult) )
    {
        dwResult = (DWORD)E_UNEXPECTED;
    }

    if( dwMethod == OLESTREAM_GET )
    {

        lp16 = WOWGetVDMPointer( vp16, dwCount, TRUE );
        memcpy( (LPVOID) lpv, lp16, dwCount );

    }

    STACKFREE16( vp16, dwCount );

    thkDebugOut((DEB_INVOKES, "OLESTREAM_Callback returns 0x%08lX\n",
                 dwResult));

    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   OLESTREAM_Get_Proxy, private
//
//  Synopsis:   Handles callbacks into 16-bit world for OLESTREAM::Get
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    11-Mar-94   BobDay  Created
//              29-May-94   TerryRu Now calls OLESTREAM::Get using Pascal
//                                  calling conventions.
//
//----------------------------------------------------------------------------


DWORD OLESTREAM_Get_Proxy(
    LPOLESTREAM lpos,
    void FAR *  lpv,
    DWORD       dwCount
)
{
    return OLESTREAM_Callback( OLESTREAM_GET, lpos, lpv, dwCount );
}

//+---------------------------------------------------------------------------
//
//  Function:   OLESTREAM_Put_Proxy, private
//
//  Synopsis:   Handles callbacks into 16-bit world for OLESTREAM::Put
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    11-Mar-94   BobDay  Created
//              29-May-94   TerryRu Now call OLESTREAM::Put using pascal calling conventions.
//
//----------------------------------------------------------------------------


DWORD OLESTREAM_Put_Proxy(
    LPOLESTREAM lpos,
    const void FAR* lpv,
    DWORD       dwCount
)
{

    return OLESTREAM_Callback( OLESTREAM_PUT, lpos,(LPVOID) lpv, dwCount );

}


//+---------------------------------------------------------------------------
//
//  Function:   Thop_OLESTREAM_1632, public
//
//  Synopsis:   Thunks an OLESTREAM parameter from 16-bit to 32-bit
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    14-Mar-94   BobDay  Created
//
//----------------------------------------------------------------------------

_OLESTREAMVTBL osVtbl =
{
    OLESTREAM_Get_Proxy,
    OLESTREAM_Put_Proxy
};

DWORD Thop_OLESTREAM_1632(THUNKINFO *pti)
{
    OLESTREAMOBJECT osObject;

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

    //
    // Ignore the THOP_INPUT/THOP_OUTPUT, it is always just an interface
    //

    osObject.os.lpstbl = &osVtbl;
    GET_STACK16(pti, osObject.vpolestream16, VPVOID);

    TO_STACK32(pti, &osObject, LPOLESTREAMOBJECT );

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

//+---------------------------------------------------------------------------
//
//  Function:   Thop_RPCOLEMESSAGE_1632, public
//
//  Synopsis:   Converts 16-bit RPCOLEMESSAGE to 32-bit RPCOLEMESSAGE
//
//  Arguments:  [pti] - Thunk state information
//
//  Returns:    Appropriate status code
//
//  History:    23-Feb-94       BobDay  Created
//		28-May-94	JohannP Rewritten
//		13-Aug-94	Rickhi	made it work for GetBuffer when the
//					buffer size grows, commented better
//
//  CODEWORK:	this routine is inefficient. since it cant tell why it was
//		called (GetBuffer/SendReceive/Invoke/FreeBuffer) it always
//		copies the data, when it only really needs to in Invoke and
//		in SendReceive.
//
//----------------------------------------------------------------------------
DWORD Thop_RPCOLEMESSAGE_1632( THUNKINFO *pti )
{
    DWORD           dwResult;
    BOOL            fThopInput;
    BOOL            fThopOutput;
    PRPCOLEMESSAGE  prom32;
    VPVOID          vprom16;
    RPCOLEMESSAGE UNALIGNED *prom16;
    LPVOID          lp16;
    RPCOLEMESSAGE   rom32;
    BOOL	    fAllocNew = FALSE;

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

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

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

    //
    // Processing for a RPCOLEMESSAGE FAR * as input/output
    //
    GET_STACK16(pti, vprom16, VPVOID);

    prom32 = NULL;
    if ( vprom16 != 0 )
    {
        // Copy over the input RPCOLEMESSAGE structure
        prom16 = (RPCOLEMESSAGE UNALIGNED *)
            GetReadWritePtr16( pti, vprom16, sizeof(RPCOLEMESSAGE) );
        if ( prom16 != NULL )
        {
            // Note: ROM_THUNK_FIELD(prom) holds the pointer to the 32 bit rom
	    //	     in case the buffer is not NULL

	    // Note: this assert is not valid when a fault happens on the
	    //	     server side. In that case, the return buffer is NULLed
	    //	     by the 16bit stub but the THUNK_FIELD is non-null.

	    //thkAssert((prom16->Buffer == NULL &&
	    //		ROM_THUNK_FIELD(prom16) == NULL) ||
	    //	       (prom16->Buffer != NULL &&
	    //		ROM_THUNK_FIELD(prom16) != NULL));

            if (prom16->Buffer != NULL)
            {
                prom32 = (RPCOLEMESSAGE *)ROM_THUNK_FIELD(prom16);

                if ( prom32->Buffer != NULL )
		{
		    // we use the min size of the two buffers because when
		    // the stub (server side) calls GetBuffer he is supplying
		    // the old pBuffer with the new (and potentially larger)
		    // cbBuffer

		    DWORD cbSizeMin = (prom16->cbBuffer <= prom32->cbBuffer) ?
				       prom16->cbBuffer :  prom32->cbBuffer;

		    lp16 = (LPVOID)GetReadPtr16(pti,
						(VPVOID)prom16->Buffer,
						cbSizeMin);
                    if (lp16 == NULL)
		    {
                        prom32 = NULL;
                    }
                    else
                    {
                        memcpy( prom32->Buffer, lp16, prom32->cbBuffer );
                    }
		}

		// the stub might be asking for a larger buffer for output
		// parameters than it was given for input parameters. We have
		// to figure that out here by comparing the 16bit size with
		// the 32bit size.

		fAllocNew = (prom32->cbBuffer < prom16->cbBuffer);
		prom32->cbBuffer = prom16->cbBuffer;
            }
            else
            {
                rom32 = *prom16;
                prom32 = &rom32;
            }
        }
    }

    TO_STACK32(pti, prom32, PRPCOLEMESSAGE);
    pti->pThop++;
    dwResult = EXECUTE_THOP1632(pti);

    if ( prom32 != NULL )
    {
        prom16 = (RPCOLEMESSAGE UNALIGNED *)
            GetReadWritePtr16( pti, vprom16, sizeof(RPCOLEMESSAGE) );
        if ( prom16 == NULL )
        {
            dwResult = (DWORD)E_UNEXPECTED;
        }
        else
        {
            if (SUCCEEDED(dwResult))
            {
                LPVOID pBuffer;
                
                if (prom32->Buffer == NULL)
                {
                    // Free up the 16 bit buffer
                    //
                    if (prom16->Buffer != 0)
                    {
                        TaskFree16((VPVOID)prom16->Buffer);
                    }

                    *prom16 = *prom32;
                    
		    // free the 32 bit rom, so long as it was really allocated
		    if (ROM_THUNK_FIELD(prom16) != NULL && prom32 != &rom32)
                    {
                        TaskFree32(prom32);
                        ROM_THUNK_FIELD(prom16) = NULL;
                    }
                }
                else
                {
                    RPCOLEMESSAGE *pr32;
                    
                    // Create a message to save the 32-bit message in
                    // Use the existing one in the thunk field if we can
                    if (ROM_THUNK_FIELD(prom16) == NULL)
                    {
                        pr32 = (RPCOLEMESSAGE *)
                            TaskMalloc32(sizeof(RPCOLEMESSAGE));
                    }
                    else
                    {
                        pr32 = (RPCOLEMESSAGE *)ROM_THUNK_FIELD(prom16);
                    }
                    *pr32 = *prom32;
                    
                    //
                    // Allocate an output buffer and copy the buffer back
                    //
                    if (   (prom16->Buffer == NULL)
			|| (prom16->cbBuffer < prom32->cbBuffer)
			|| fAllocNew)
                    {
                        if (prom16->Buffer != NULL)
                        {
                            TaskFree16((VPVOID) prom16->Buffer);
                        }
                        
                        pBuffer = (LPVOID)TaskMalloc16(prom32->cbBuffer );
                    }
                    else
                    {
                        pBuffer = prom16->Buffer;
                    }
                    
                    *prom16 = *prom32;
                    prom16->Buffer = pBuffer;
                    ROM_THUNK_FIELD(prom16) = pr32;

                    if (prom16->Buffer == NULL)
                    {
                        dwResult = (DWORD)E_OUTOFMEMORY;
                    }
                    else
                    {
                        lp16 = (LPVOID)GetReadPtr16(pti,
                                                    (VPVOID)prom16->Buffer,
                                                    prom16->cbBuffer);
                        if ( lp16 == NULL )
                        {
                            dwResult = (DWORD)E_UNEXPECTED;
                        }
                        else
                        {
                            memcpy( lp16, prom32->Buffer,
                                    prom32->cbBuffer );
                        }
                    }
                }
            }
        }
    }
    
    return dwResult;
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_ALIAS32_1632, 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_1632(THUNKINFO *pti)
{
    ALIAS alias;
    DWORD dwValue;
    THOP thopAction;

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

    pti->pThop++;

    GET_STACK16(pti, alias, ALIAS);

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

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

        case ALIAS_REMOVE:
            dwValue = gAliases32.AliasValue(alias);
            gAliases32.RemoveAlias(alias);
            break;

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

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

    TO_STACK32(pti, dwValue, DWORD);

    return EXECUTE_THOP1632(pti);
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_ENUM_1632, 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_1632(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_ENUMTHOP1632(pti);
}

//+---------------------------------------------------------------------------
//
//  Function:   CallbackProcessing_1632, 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;
    VPVOID  vpfn16;
} CALLBACKCONTROL;


BOOL CallbackProcessing_1632( DWORD dwContinue )
{
    DWORD           dwResult;
    CALLBACKCONTROL *lpcbc;

    lpcbc = (CALLBACKCONTROL *)dwContinue;

    // The callback function must be FAR PASCAL
    // It's declared CALLBACK in the methods so this is ensured
    dwResult = WOWCallback16( lpcbc->vpfn16, lpcbc->dwContinue );

    return (BOOL)((WORD)dwResult);  // Ignore HIWORD
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_CALLBACK_1632, 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_1632(THUNKINFO *pti)
{
    VPVOID          vpfn16;
    DWORD           dwContinue;
    CALLBACKCONTROL cbc;

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

    GET_STACK16(pti, vpfn16, VPVOID);
    GET_STACK16(pti, dwContinue, DWORD);

    if ( vpfn16 == 0 )
    {
        TO_STACK32(pti, NULL, LPVOID);
        TO_STACK32(pti, dwContinue, DWORD);
    }
    else
    {
        cbc.vpfn16     = vpfn16;
        cbc.dwContinue = dwContinue;

        TO_STACK32(pti, CallbackProcessing_1632, LPVOID);
        TO_STACK32(pti, (DWORD)&cbc, DWORD);
    }

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

//+---------------------------------------------------------------------------
//
//  Function:	Thop_CLSCONTEXT_1632, 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_1632(THUNKINFO *pti)
{
    DWORD dwClsContext;

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

    GET_STACK16(pti, dwClsContext, DWORD);

    // When passing a 16-bit class context on to 32-bits,
    // add on a flag to indicate that this is a 16-bit request
    // in the inproc server case

    if (dwClsContext & CLSCTX_INPROC_SERVER)
    {
        dwClsContext |= CLSCTX_INPROC_SERVER16;
    }

    TO_STACK32(pti, dwClsContext, DWORD);

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

#define THOP_FN(x)  Thop_ ## x ## _1632

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

                                // x = Implemented
                                // ? = Mysteriously not needed
                                //   = Left to do
                                //
                                // ^
                                // |
                                // +===+
                                //     |
                                //     v
                                //
    ThunkCall1632,                  // x Terminating THOP
    Thop_ShortToLong_1632,          // x SHORTLONG
    Thop_WordToDword_1632,          // x WORDDWORD
    Thop_Copy_1632,                 // x COPY
    THOP_FN(LPSTR),                 // x LPSTR
    THOP_FN(LPLPSTR),               // x LPLPSTR
    THOP_FN(BUFFER),                // x BUFFER
    Thop_UserHandle_1632,           // x HUSER
    Thop_GdiHandle_1632,            // 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_FN(OLESTREAM),             // x OLESTREAM
    THOP_FN(HTASK),                 // x HTASK
    THOP_FN(INTERFACEINFO),         // x INTERFACEINFO
    THOP_FN(IFACE),                 // x IFACE
    THOP_FN(IFACE),                 // x IFACEOWNER
    Thop_ERROR_1632,                // x IFACECLEAN
    THOP_FN(IFACEGEN),              // x IFACEGEN
    THOP_FN(IFACEGEN),              // x IFACEGENOWNER
    Thop_ERROR_1632,                // x ROUTINE_INDEX
    THOP_FN(RETURNTYPE),            // x RETURN_TYPE
    THOP_FN(NULL),                  // x NULL
    Thop_ERROR_1632,                // 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_1632, 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   100

DWORD General_Enum_1632(
    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       ulFetched32;
    ULONG       *lpfetched32;
    ULONG UNALIGNED *lpfetched16;
    ULONG       ulIterate;
    LPVOID      lp16;
    BOOL        fError;
    SCODE       sc;

    dwResult = (DWORD)S_OK;

    GET_STACK16(pti, ulCount, ULONG );
    GET_STACK16(pti, vpstruct16, VPVOID );
    GET_STACK16(pti, vpfetched16, VPVOID );

    //
    // THIS ROUTINE CAN DEAL WITH dwResult RATHER THAN pti->scResult BECAUSE
    // WE KNOW THIS IS THE ONLY THOP FOR THIS FUNCTION!  NO OTHER CLEANUP
    // WILL HAVE TO BE DONE!
    //
    ulFetched32 = 0;
    lpfetched32 = &ulFetched32;
    lp16 = NULL;

    if ( vpstruct16 == 0 )
    {
        lpstruct32 = NULL;
    }
    else
    {
        if ( ulCount == 0 )
        {
            dwResult = (DWORD)E_INVALIDARG;
        }
        else
        {
            //
            // Verify we have write access to the 16-bit memory.
            //
            lp16 = GetWritePtr16(pti, vpstruct16, uiSize16*ulCount);
            if ( lp16 == NULL )
            {
                dwResult = (DWORD)E_INVALIDARG;
            }
            else
            {
                if ( ulCount > MAX_ALLOCA_STRUCT )
                {
                    lpstruct32 = (LPVOID)malloc( ulCount * uiSize32 );
                    if (lpstruct32 == NULL)
                    {
                        dwResult = (DWORD)E_OUTOFMEMORY;
                    }
                }
                else
                {
                    lpstruct32 = (LPVOID)alloca( ulCount * uiSize32 );
                    if (lpstruct32 == NULL)
                    {
                        dwResult = (DWORD)E_OUTOFMEMORY;
                    }
                }
            }
        }
    }

    if (SUCCEEDED(dwResult))
    {
        TO_STACK32(pti, ulCount, ULONG);
        TO_STACK32(pti, lpstruct32, LPVOID);
        TO_STACK32(pti, lpfetched32, ULONG FAR *);

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

    if ( SUCCEEDED(dwResult) )
    {
        if ( vpstruct16 != 0 )
        {
            // 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)
            {
                ulFetched32 = 0;
            }

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

            while ( ulIterate < ulFetched32 )
            {
                //
                // 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 <= ulFetched32 )
                {
                    (*pfnCleanup)( pti, lpstruct32Iterate, vpstruct16Iterate );
                    vpstruct16Iterate = (VPVOID)((DWORD)vpstruct16Iterate +
                                                 uiSize16);
                    lpstruct32Iterate = (LPVOID)((DWORD)lpstruct32Iterate +
                                                 uiSize32);

                    ulIterate++;
                }
            }

            //
            // Free up any space we've allocated
            //
            if ( ulCount > MAX_ALLOCA_STRUCT )
            {
                free( lpstruct32 );
            }
        }
    }

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

    if ( vpfetched16 != 0 )
    {
        lpfetched16 = GETVDMPTR( vpfetched16, ULONG);
        *lpfetched16 = ulFetched32;
    }

    return dwResult;
}

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

SCODE Callback_STRING_1632( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    VPSTR UNALIGNED *pvpstr16;

    pvpstr16 = GETVDMPTR(vp16, VPSTR);
    *pvpstr16 = 0;
    return ConvertTaskString3216(pti, *(LPOLESTR *)lp32, NULL, 0,
                                 pvpstr16);
}

//+---------------------------------------------------------------------------
//
//  Function:   Cleanup_STRING_1632, 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 returned structure
//              [lp16] - Pointer to 16-bit output structure
//
//  Returns:    nothing, should NEVER fail
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

void Cleanup_STRING_1632( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    VPSTR UNALIGNED *lpvpstr16;
    VPSTR           vpstr16;

    lpvpstr16 = GETVDMPTR( vp16, VPSTR );

    vpstr16 = *lpvpstr16;

    if ( vpstr16 != 0 )
    {
        TaskFree16( vpstr16 );
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_Enum_STRING_1632, 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_1632(THUNKINFO *pti)
{
    return General_Enum_1632(pti,
                             sizeof(LPOLESTR),
                             sizeof(VPSTR),
                             Callback_STRING_1632,
                             Cleanup_STRING_1632 );
}

//+---------------------------------------------------------------------------
//
//  Function:   Callback_UNKNOWN_1632, 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 returned structure
//              [lp16] - Pointer to 16-bit output structure
//
//  Returns:    SCODE indicating success/failure
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

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

    vpunknown16 =
        pti->pThkMgr->FindProxy1632(NULL, *(LPUNKNOWN *)lp32,
                                    INDEX_IIDIDX(THI_IUnknown), NULL);
    if (vpunknown16 == 0)
    {
        sc = E_OUTOFMEMORY;
    }

    *GETVDMPTR( vp16, VPVOID ) = vpunknown16;

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   Cleanup_UNKNOWN_1632, 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 returned structure
//              [lp16] - Pointer to 16-bit output structure
//
//  Returns:    nothing, should NEVER fail
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

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

    vpunknown16 = *GETVDMPTR( vp16, VPVOID );
    lpunknown32 = *(LPUNKNOWN *)lp32;

    // BUGBUG - What is the proper cleanup for IEnum<Interface>?
    // Should the objects be released or only the proxies we
    // created?
    if (vpunknown16 != 0)
    {
        pti->pThkMgr->FreeProxy1632( lpunknown32 );
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_Enum_UNKNOWN_1632, 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_1632(THUNKINFO *pti)
{
    return General_Enum_1632(pti,
                             sizeof(LPUNKNOWN),
                             sizeof(LPUNKNOWN),
                             Callback_UNKNOWN_1632,
                             Cleanup_UNKNOWN_1632 );
}

//+---------------------------------------------------------------------------
//
//  Function:   Callback_STATSTG_1632, 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 returned structure
//              [lp16] - Pointer to 16-bit output structure
//
//  Returns:    SCODE indicating success/failure
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

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

//+---------------------------------------------------------------------------
//
//  Function:   Cleanup_STATSTG_1632, 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 returned structure
//              [lp16] - Pointer to 16-bit output structure
//
//  Returns:    nothing, should NEVER fail
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

void Cleanup_STATSTG_1632( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    STATSTG UNALIGNED *lpstatstg16;

    lpstatstg16 = GETVDMPTR( vp16, STATSTG );
    if ( lpstatstg16->pwcsName != NULL )
    {
        TaskFree16( (VPVOID)lpstatstg16->pwcsName );
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_Enum_STATSTG_1632, 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_1632(THUNKINFO *pti)
{
    return General_Enum_1632(pti,
                             sizeof(STATSTG),
                             sizeof(STATSTG),
                             Callback_STATSTG_1632,
                             Cleanup_STATSTG_1632 );
}

//+---------------------------------------------------------------------------
//
//  Function:   Callback_FORMATETC_1632, 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 returned structure
//              [lp16] - Pointer to 16-bit output structure
//
//  Returns:    SCODE indicating success/failure
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

SCODE Callback_FORMATETC_1632( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    (GETVDMPTR(vp16, FORMATETC16))->ptd = NULL;
    return ConvertFetc3216(pti, (FORMATETC *)lp32, vp16, TRUE);
}

//+---------------------------------------------------------------------------
//
//  Function:   Cleanup_FORMATETC_1632, 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 returned structure
//              [lp16] - Pointer to 16-bit output structure
//
//  Returns:    nothing, should NEVER fail
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

void Cleanup_FORMATETC_1632( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    FORMATETC16 UNALIGNED *lpformatetc16;

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

//+---------------------------------------------------------------------------
//
//  Function:   Thop_Enum_FORMATETC_1632, 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_1632(THUNKINFO *pti)
{
    return General_Enum_1632(pti,
                             sizeof(FORMATETC),
                             sizeof(FORMATETC16),
                             Callback_FORMATETC_1632,
                             Cleanup_FORMATETC_1632 );
}

//+---------------------------------------------------------------------------
//
//  Function:   Callback_STATDATA_1632, 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 returned structure
//              [lp16] - Pointer to 16-bit output structure
//
//  Returns:    SCODE indicating success/failure
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------
SCODE Callback_STATDATA_1632( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    SCODE               sc;
    LPSTATDATA          lpstatdata32;
    STATDATA16 UNALIGNED *lpstatdata16;
    VPVOID              vpadv16;

    sc = S_OK;

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

    if (lpstatdata32->pAdvSink != NULL)
    {
        // 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

        vpadv16 = pti->pThkMgr->FindProxy1632(NULL, lpstatdata32->pAdvSink,
                                              INDEX_IIDIDX(THI_IAdviseSink2),
                                              NULL);
        if (vpadv16 == 0)
        {
            sc = E_OUTOFMEMORY;
        }
    }
    else
    {
        vpadv16 = 0;
    }

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

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

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

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   Cleanup_STATDATA_1632, 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 returned structure
//              [lp16] - Pointer to 16-bit output structure
//
//  Returns:    nothing, should NEVER fail
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

void Cleanup_STATDATA_1632( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    STATDATA *lpstatdata32;
    STATDATA16 UNALIGNED *lpstatdata16;
    LPADVISESINK lpadv32;

    lpstatdata32 = (STATDATA FAR *)lp32;
    lpadv32 = lpstatdata32->pAdvSink;

    lpstatdata16 = GETVDMPTR( vp16, STATDATA16 );

    if ( lpstatdata16->formatetc.ptd != NULL )
    {
        TaskFree16( lpstatdata16->formatetc.ptd );
    }

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

//+---------------------------------------------------------------------------
//
//  Function:   Thop_Enum_STATDATA_1632, 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_1632(THUNKINFO *pti)
{
    return General_Enum_1632(pti,
                             sizeof(STATDATA),
                             sizeof(STATDATA16),
                             Callback_STATDATA_1632,
                             Cleanup_STATDATA_1632 );
}

//+---------------------------------------------------------------------------
//
//  Function:   Callback_MONIKER_1632, 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 returned structure
//              [lp16] - Pointer to 16-bit output structure
//
//  Returns:    SCODE indicating success/failure
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

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

    vpmoniker16 = pti->pThkMgr->FindProxy1632(NULL, *(LPMONIKER *)lp32,
                                              INDEX_IIDIDX(THI_IMoniker),
                                              NULL);
    if (vpmoniker16 == 0)
    {
        sc = E_OUTOFMEMORY;
    }

    *GETVDMPTR(vp16, VPVOID) = vpmoniker16;

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   Cleanup_MONIKER_1632, 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 returned structure
//              [lp16] - Pointer to 16-bit output structure
//
//  Returns:    nothing, should NEVER fail
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

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

    vpmoniker16 = *GETVDMPTR( vp16, VPVOID );
    lpmoniker32 = *(LPMONIKER *)lp32;

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

//+---------------------------------------------------------------------------
//
//  Function:   Thop_Enum_MONIKER_1632, 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_1632(THUNKINFO *pti)
{
    return General_Enum_1632(pti,
                             sizeof(LPMONIKER),
                             sizeof(LPMONIKER),
                             Callback_MONIKER_1632,
                             Cleanup_MONIKER_1632 );
}

//+---------------------------------------------------------------------------
//
//  Function:   Callback_OLEVERB_1632, 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 returned structure
//              [lp16] - Pointer to 16-bit output structure
//
//  Returns:    SCODE indicating success/failure
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

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

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

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

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   Cleanup_OLEVERB_1632, 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 returned structure
//              [lp16] - Pointer to 16-bit output structure
//
//  Returns:    nothing, should NEVER fail
//
//  History:    1-Mar-94        BobDay  Created
//
//----------------------------------------------------------------------------

void Cleanup_OLEVERB_1632( THUNKINFO *pti, LPVOID lp32, VPVOID vp16 )
{
    OLEVERB UNALIGNED *lpoleverb16;

    lpoleverb16 = GETVDMPTR( vp16, OLEVERB );
    if ( lpoleverb16->lpszVerbName != NULL )
    {
        TaskFree16( (VPVOID)lpoleverb16->lpszVerbName );
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   Thop_Enum_OLEVERB_1632, 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_1632(THUNKINFO *pti)
{
    return General_Enum_1632(pti,
                             sizeof(OLEVERB),
                             sizeof(OLEVERB),
                             Callback_OLEVERB_1632,
                             Cleanup_OLEVERB_1632 );
}

#define THOP_EFN(x)  Thop_Enum_ ## x ## _1632

DWORD (*aThopEnumFunctions1632[])(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
};
