//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1994.
//
//  File:       thoputil.cxx
//
//  Contents:   Utility routines for thunking
//
//  History:    01-Mar-94       DrewB   Created
//
//----------------------------------------------------------------------------

#include "headers.cxx"
#pragma hdrstop

#include <stdio.h>
#include <limits.h>

#include <vdmdbg.h>
#include <valid.h>

//
//  Chicago doesn't support the NT ExpLdr API, use the new Chicago
//  WOWGetDescriptor that copies the LDT info to a provided buffer.
//
#if defined(_CHICAGO_)
extern "C" WOWGetDescriptor(VPVOID, VDMLDT_ENTRY *);
#else
extern "C" DECLSPEC_IMPORT VDMLDT_ENTRY *ExpLdt;
#endif

#include "struct16.hxx"

#define CF_INVALID ((CLIPFORMAT)0)

#define OBJDESC_CF(cf) \
    ((cf) == g_cfObjectDescriptor || (cf) == g_cfLinkSourceDescriptor)

// Alias manager for THOP_ALIAS32
CAliases gAliases32;

//+---------------------------------------------------------------------------
//
//  Function:   IidToIidIdx, public
//
//  Synopsis:   Looks up an interface index by IID
//              If it's not found, it returns the IID pointer
//
//  Arguments:  [riid] - IID
//
//  Returns:    Index or IID
//
//  History:    23-Feb-94       DrewB   Created
//
//----------------------------------------------------------------------------

IIDIDX IidToIidIdx(REFIID riid)
{
    int idx;

    for (idx = 0; idx < THI_COUNT; idx++)
    {
        if (IsEqualIID(riid, *aittIidToThi[idx].piid))
        {
            return INDEX_IIDIDX(aittIidToThi[idx].iThi);
        }
    }

    return IID_IIDIDX(&riid);
}

//+---------------------------------------------------------------------------
//
//  Function:   TaskMalloc32, public
//
//  Synopsis:   Task allocation for 32-bits
//
//  History:    01-Mar-94       DrewB   Created
//
//  Notes:      Temporary until CoTaskMemAlloc is hooked up
//
//----------------------------------------------------------------------------

#ifndef COTASK_DEFINED
LPVOID TaskMalloc32(SIZE_T cb)
{
    IMalloc *pm;
    LPVOID pv;

    if (FAILED(GetScode(CoGetMalloc(MEMCTX_TASK, &pm))))
    {
        return NULL;
    }
    else
    {
        pv = pm->Alloc(cb);
        pm->Release();
    }
    return pv;
}

//+---------------------------------------------------------------------------
//
//  Function:   TaskFree32, public
//
//  Synopsis:   Task free for 32-bits
//
//  History:    01-Mar-94       DrewB   Created
//
//  Notes:      Temporary until CoTaskMemAlloc is hooked up
//
//----------------------------------------------------------------------------

void TaskFree32(LPVOID pv)
{
    IMalloc *pm;

    if (FAILED(GetScode(CoGetMalloc(MEMCTX_TASK, &pm))))
    {
        thkAssert(!"CoGetMalloc failed");
    }
    else
    {
        pm->Free(pv);
        pm->Release();
    }
}
#endif

//+---------------------------------------------------------------------------
//
//  Function:   TaskMalloc16, public
//
//  Synopsis:   Allocates 16-bit task memory
//
//  Arguments:  [uiSize] - Amount of memory to allocate
//
//  Returns:    VPVOID for memory allocated
//
//  History:    01-Mar-94       DrewB   Created
//
//----------------------------------------------------------------------------

DWORD TaskMalloc16( UINT uiSize )
{
    return CallbackTo16(gdata16Data.fnTaskAlloc, uiSize);
}

//+---------------------------------------------------------------------------
//
//  Function:   TaskFree16, public
//
//  Synopsis:   Frees 16-bit task memory
//
//  Arguments:  [vpvoid] - VPVOID of allocated memory
//
//  History:    01-Mar-94       DrewB   Created
//
//----------------------------------------------------------------------------

void TaskFree16( DWORD vpvoid )
{
    CallbackTo16(gdata16Data.fnTaskFree, vpvoid);
}

// List of 16/32 HRESULT mappings for mapping functions
struct SHrMapping
{
    HRESULT hr16;
    HRESULT hr32;
};

// Since we're including 32-bit headers in this code we can use
// the defines for the 32-bit values but we must specify the
// 16-bit values explicitly
static SHrMapping hmMappings[] =
{
    0x80000001, E_NOTIMPL,
    0x80000002, E_OUTOFMEMORY,
    0x80000003, E_INVALIDARG,
    0x80000004, E_NOINTERFACE,
    0x80000005, E_POINTER,
    0x80000006, E_HANDLE,
    0x80000007, E_ABORT,
    0x80000008, E_FAIL,
    0x80000009, E_ACCESSDENIED
};
#define NMAPPINGS (sizeof(hmMappings)/sizeof(hmMappings[0]))

#define HR16_ERROR 0x80000000
#define HR16_MAP_FIRST 1
#define HR16_MAP_LAST 9

//+---------------------------------------------------------------------------
//
//  Function:   TransformHRESULT_1632, public
//
//  Synopsis:   Translates a 16-bit hresult into a 32-bit hresult
//
//  Arguments:  [hresult] - 16-bit hresult to transform
//
//  History:    15-Mar-94   BobDay  Created
//
//----------------------------------------------------------------------------

STDAPI_(DWORD) TransformHRESULT_1632( DWORD hresult )
{
    ULONG ulIndex;

    // We only map error codes
    if (hresult & HR16_ERROR)
    {
        // The 16-bit HRESULTs to be mapped are known quantities
        // whose values are sequential, so we can map directly from
        // the value to an array index

        ulIndex = hresult & ~HR16_ERROR;
        if (ulIndex >= HR16_MAP_FIRST && ulIndex <= HR16_MAP_LAST)
        {
            // Known value, index array to find 32-bit HRESULT
            return hmMappings[ulIndex-HR16_MAP_FIRST].hr32;
        }
    }

    // No mapping found, so return the original
    return hresult;
}

//+---------------------------------------------------------------------------
//
//  Function:   TransformHRESULT_3216, public
//
//  Synopsis:   Translates a 32-bit hresult into a 16-bit hresult
//
//  Arguments:  [hresult] - 32-bit hresult to transform
//
//  History:    15-Mar-94   BobDay  Created
//
//----------------------------------------------------------------------------

STDAPI_(DWORD) TransformHRESULT_3216( DWORD hresult )
{
    int i;
    SHrMapping *phm;

    // We don't know the true values of 32-bit HRESULTs since we're
    // using the defines and they may change, so we have to look up
    // the hard way

    phm = hmMappings;
    for (i = 0; i < NMAPPINGS; i++)
    {
        if (phm->hr32 == (HRESULT)hresult)
        {
            return phm->hr16;
        }

        phm++;
    }

    // No mapping found, so return the original
    return hresult;
}

//+---------------------------------------------------------------------------
//
//  Function:   RecordStackState, public debug
//
//  Synopsis:   Records the current state of the stack
//
//  Arguments:  [psr] - Storage space for information
//
//  Modifies:   [psr]
//
//  History:    28-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

#if DBG == 1
void RecordStackState16(SStackRecord *psr)
{
    CStackAllocator *psa;

    psa = TlsThkGetStack16();
    psa->RecordState(psr);
}

void RecordStackState32(SStackRecord *psr)
{
    CStackAllocator *psa;

    psa = TlsThkGetStack32();
    psa->RecordState(psr);
}
#endif

//+---------------------------------------------------------------------------
//
//  Function:   CheckStackState, public debug
//
//  Synopsis:   Checks recorded information about the stack against its
//              current state
//
//  Arguments:  [psr] - Recorded information
//
//  History:    28-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

#if DBG == 1
void CheckStackState16(SStackRecord *psr)
{
    CStackAllocator *psa;

    psa = TlsThkGetStack16();
    psa->CheckState(psr);
}

void CheckStackState32(SStackRecord *psr)
{
    CStackAllocator *psa;

    psa = TlsThkGetStack32();
    psa->CheckState(psr);
}
#endif

//+---------------------------------------------------------------------------
//
//  Function:   Convert_VPSTR_to_LPOLESTR
//
//  Synopsis:   Converts 16-bit VPSTR to 32-bit LPOLESTR pointer
//
//  Arguments:  [vpstr] - VPSTR
//              [lpOleStr] - OLESTR
//              [uiSizeInPlace] - Amount of data available in [lpOleStr]
//                      for in-place conversion (in characters, not bytes)
//                      including nul
//
//  Returns:    Pointer to LPOLESTR with data
//
//  History:    24-Feb-94       BobDay  Created
//
//----------------------------------------------------------------------------

LPOLESTR Convert_VPSTR_to_LPOLESTR(
    THUNKINFO   *pti,
    VPSTR       vpstr,
    LPOLESTR    lpOleStr,
    UINT        uiSizeInPlace
)
{
    LPSTR       lpstr;
    UINT        uiSize;
    LPOLESTR    lpOleStrResult;
    UINT        cChars;

    // We shouldn't be calling here for null strings
    thkAssert( vpstr != NULL );

    lpstr = GetStringPtr16(pti, vpstr, CCHMAXSTRING, &uiSize);
    if ( lpstr == NULL )
    {
        //
        // GetStringPtr will have filled in the pti->scResult
        //
        return( NULL );
    }

    // The string has to have at least one character in it
    // because it must be null-terminated to be valid
    thkAssert(uiSize > 0);

    lpOleStrResult = lpOleStr;

    if ( uiSize > uiSizeInPlace )
    {
        lpOleStrResult = (LPOLESTR)TaskMalloc32(uiSize*sizeof(OLECHAR));
        if (lpOleStrResult == NULL)
        {
            pti->scResult = E_OUTOFMEMORY;
            return NULL;
        }
    }

    cChars = MultiByteToWideChar( AreFileApisANSI() ? CP_ACP : CP_OEMCP,
                                  0,
                                  lpstr,
                                  uiSize,
                                  lpOleStrResult,
                                  uiSize );

    WOWRELVDMPTR(vpstr);

    if ( cChars == 0 )
    {
        if (lpOleStrResult != lpOleStr)
        {
            TaskFree32(lpOleStrResult);
        }

        pti->scResult = E_UNEXPECTED;
        return( NULL );
    }
    else
    {
        return( lpOleStrResult );
    }
}
//+---------------------------------------------------------------------------
//
//  Function:   Convert_LPOLESTR_to_VPSTR
//
//  Synopsis:   Converts 32-bit LPOLESTR to 16-bit VPSTR pointer
//
//  Arguments:  [lpOleStr] - OLESTR
//              [vpstr] - VPSTR
//              [uiSize32] - Length of OLESTR in characters (not bytes)
//                      including nul
//              [uiSize16] - Byte length of buffer referred to by VPSTR
//
//  Returns:    Appropriate status code
//
//  History:    24-Feb-94       BobDay  Created
//
//  Notes:      Always converts in place
//
//----------------------------------------------------------------------------

SCODE Convert_LPOLESTR_to_VPSTR(
    LPCOLESTR    lpOleStr,
    VPSTR       vpstr,
    UINT        uiSize32,
    UINT        uiSize16
)
{
    LPSTR       lpstr;
    UINT        cChars;
    SCODE       sc;

    sc = S_OK;

    lpstr = (LPSTR)WOWFIXVDMPTR(vpstr, uiSize16);

    cChars = WideCharToMultiByte( AreFileApisANSI() ? CP_ACP : CP_OEMCP,
                                  0,
                                  lpOleStr,
                                  uiSize32,
                                  lpstr,
                                  uiSize16,
                                  NULL,
                                  NULL );

    if ( cChars == 0 && uiSize32 != 0 )
    {
        sc = E_UNEXPECTED;
    }

    WOWRELVDMPTR(vpstr);

    return sc;
}
#ifdef _CHICAGO_
//+---------------------------------------------------------------------------
//
//  Function:   Convert_LPSTR_to_VPSTR
//
//  Synopsis:   Converts 32-bit LPSTR to 16-bit VPSTR pointer
//
//  Arguments:  [lpOleStr] - LPSTR
//              [vpstr] - VPSTR
//              [uiSize32] - Length of LPSTR in bytes including nul
//              [uiSize16] - Byte length of buffer referred to by VPSTR
//
//  Returns:    Appropriate status code
//
//  History:    10-21-95 KevinRo Created
//
//  Notes:
//
//----------------------------------------------------------------------------

SCODE Convert_LPSTR_to_VPSTR(
    LPCSTR	lpOleStr,
    VPSTR       vpstr,
    UINT        uiSize32,
    UINT        uiSize16
)
{
    LPSTR       lpstr;

    lpstr = (LPSTR)WOWFIXVDMPTR(vpstr, uiSize16);

    memcpy(lpstr,lpOleStr,uiSize32);

    WOWRELVDMPTR(vpstr);

    return S_OK;
}
#endif // _CHICAGO_

// Selector bit constants
#define SEL_TI          0x0004
#define SEL_RPL         0x0003
#define SEL_INDEX       0xfff8

#define IS_LDT_SELECTOR(sel) (((sel) & SEL_TI) == SEL_TI)

// LDT bit constants
#define LTYPE_APP       0x0010
#define LTYPE_CODE      0x0008
#define LTYPE_CREAD     0x0002
#define LTYPE_DDOWN     0x0004
#define LTYPE_DWRITE    0x0002

// Pointer access types, or'able
// Defined to be the same as thop in/out so that no translation
// is necessary for checks on thop memory access
#define PACC_READ       THOP_IN
#define PACC_WRITE      THOP_OUT
#define PACC_CODE       1           // Special for CODE PTRs

// Information about a VDM pointer
typedef struct _VPTRDESC
{
    BYTE *pbFlat;
    DWORD dwLengthLeft;
} VPTRDESC;

// VDM memory is always zero-based on Win95
#ifndef _CHICAGO_
DWORD dwBaseVDMMemory = 0xFFFFFFFF;
#else
#define dwBaseVDMMemory 0
#endif

// Extended success returns from GetPtr16Description
#define S_GDTENTRY      MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL, 1)
#define S_SYSLDTENTRY   MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL, 2)

//+---------------------------------------------------------------------------
//
//  Function:   GetPtr16Description, public
//
//  Synopsis:   Validates access for a VDM pointer and returns
//              information about it
//              Also forces not-present segments into memory by
//              touching them
//
//  Arguments:  [vp] - VDM pointer
//              [grfAccess] - Desired access
//              [dwSize] - Desired size of access, must be >= 1
//              [pvpd] - VPTRDESC out
//
//  Returns:    Appropriate status code
//
//  Modifies:   [pvpd]
//
//  History:    26-Apr-94       DrewB   Created
//
//  Notes:      Returns fixed memory
//
//----------------------------------------------------------------------------

SCODE GetPtr16Description(VPVOID vp,
                          WORD grfAccess,
                          DWORD dwSize,
                          VPTRDESC *pvpd)
{
    VDMLDT_ENTRY *vle;
#if defined(_CHICAGO_)
    VDMLDT_ENTRY LdtEntry;
#endif
    WORD wSel;
    WORD wOff;
    DWORD dwLength;

    thkAssert(vp != 0);
    thkAssert(dwSize > 0);
    thkAssert(grfAccess != 0);

    wSel = (WORD)(vp >> 16);
    wOff = (WORD)(vp & 0xffff);

    pvpd->dwLengthLeft = 0xffff-wOff+1;     // Default length remaining

    if (!IS_LDT_SELECTOR(wSel))
    {
        // According to the WOW developers, the only GDT selector
        // is for the BIOS data area so we should never see one

        thkDebugOut((DEB_ERROR, "GDT selector: 0x%04X\n", wSel));

        // Handle it just in case
        pvpd->pbFlat = (BYTE *)WOWFIXVDMPTR(vp, dwSize);

        return S_GDTENTRY;
    }

#if defined(_CHICAGO_)
    vle = &LdtEntry;
    if (!WOWGetDescriptor(vp, vle))
    {
        return E_INVALIDARG;
    }
#else
    vle = (VDMLDT_ENTRY *)((BYTE *)(ExpLdt)+(wSel & SEL_INDEX));
#endif

    if ((vle->HighWord.Bits.Type & LTYPE_APP) == 0)
    {
        // According to the WOW developers, they don't use
        // system segments so we should never see one

        thkDebugOut((DEB_ERROR, "System descriptor: 0x%04X\n", wSel));

        // Handle it just in case
        pvpd->pbFlat = (BYTE *)WOWFIXVDMPTR(vp, dwSize);

        return S_SYSLDTENTRY;
    }

    // Do as much up-front validation as possible
    // Since the segment may not be present, we are restricted to
    // only checking the access permissions

    if (vle->HighWord.Bits.Type & LTYPE_CODE)
    {
        // Validate access for code segments
        // Code segments are never writable

        if (((grfAccess & PACC_READ) &&
             (vle->HighWord.Bits.Type & LTYPE_CREAD) == 0) ||
            (grfAccess & PACC_WRITE))
        {
            return E_INVALIDARG;
        }
    }
    else
    {
        // Validate access for data segments
        // Data segments are always readable never executable

        if (((grfAccess & PACC_WRITE) &&
             (vle->HighWord.Bits.Type & LTYPE_DWRITE) == 0) ||
             (grfAccess & PACC_CODE))
        {
            return E_INVALIDARG;
        }
    }

    // Bring in segment if it's not present
    if (!vle->HighWord.Bits.Pres)
    {
        // We've validated access permissions and segments must
        // always be at least one byte long so it's safe to
        // touch the first byte to bring it in

        // On Win95, this will call GlobalFix on the pointer
        // to ensure that it stays in memory
        WOWCallback16(gdata16Data.fnTouchPointer16, vp);

#if defined(_CHICAGO_)
        // Since we only copy the descriptor, recopy it now.
        WOWGetDescriptor(vp, vle);
#endif

        thkAssert(vle->HighWord.Bits.Pres);
    }
#ifdef _CHICAGO_
    else
    {
        // Lock the LDT entry (as best as we can) by fixing it
        // This prevents global blocks from being relocated during
        // heap compaction
        WOWGetVDMPointerFix(vp, dwSize, TRUE);
    }
#endif


    dwLength = ((DWORD)vle->LimitLow |
                ((DWORD)vle->HighWord.Bits.LimitHi << 16))+1;
    if (vle->HighWord.Bits.Granularity)
    {
        // 4K granularity
        dwLength <<= 12;
    }

    if ((vle->HighWord.Bits.Type & LTYPE_CODE) ||
        (vle->HighWord.Bits.Type & LTYPE_DDOWN) == 0)
    {
        // Validate length for code and normal data segments

        if (wOff+dwSize > dwLength)
        {
            WOWRELVDMPTR(vp);
            return E_INVALIDARG;
        }

        pvpd->dwLengthLeft = dwLength-wOff;
    }
    else
    {
        // Expand-down segment

        if (wOff < dwLength)
        {
            WOWRELVDMPTR(vp);
            return E_INVALIDARG;
        }

        // Check for wraparound

        if (vle->HighWord.Bits.Granularity)
        {
            // Compiler - This should be +1, but
            // the compiler generates a warning about an overflow
            // in constant arithmetic
            pvpd->dwLengthLeft = 0xffffffff-wOff;
        }

        if (dwSize > pvpd->dwLengthLeft)
        {
            WOWRELVDMPTR(vp);
            return E_INVALIDARG;
        }
    }

    // VDM memory is always zero-based on Win95
#ifndef _CHICAGO_
    if ( dwBaseVDMMemory == 0xFFFFFFFF )
    {
        dwBaseVDMMemory = (DWORD)WOWGetVDMPointer(0, 0, FALSE);
    }
#endif

    // Translate the pointer even on Win95 because forcing the segment
    // present may have changed its address
    pvpd->pbFlat = (BYTE *)(dwBaseVDMMemory +
                            wOff +
                            (  (DWORD)vle->BaseLow |
                             ( (DWORD)vle->HighWord.Bytes.BaseMid << 16) |
                             ( (DWORD)vle->HighWord.Bytes.BaseHi  << 24) ) );

#if DBG == 1
    if (pvpd->pbFlat != WOWGetVDMPointer(vp, dwSize, TRUE))
    {
        thkDebugOut((DEB_ERROR, "GetPtr16Description: "
                     "%p computed, %p system\n",
                     pvpd->pbFlat, WOWGetVDMPointer(vp, dwSize, TRUE)));
    }
#endif

    return S_OK;
}

//+---------------------------------------------------------------------------
//
//  Function:   GetReadPtr16
//
//  Synopsis:   Validates a 16-bit pointer for reading and converts it into
//              a flat 32 pointer.
//
//  Arguments:  [pti] - THUNKINFO * for updating error code
//              [vp] - 16-bit pointer to validate/convert
//              [dwSize] - Length to validate
//
//  Returns:    Appropriate status code
//
//  History:    22-Mar-94   BobDay  Created
//
//----------------------------------------------------------------------------

VOID  *
GetReadPtr16(
    THUNKINFO   *pti,
    VPVOID      vp,
    DWORD       dwSize )
{
    VPTRDESC vpd;
    SCODE sc;

    sc = GetPtr16Description(vp, PACC_READ, dwSize, &vpd);
    if (FAILED(sc))
    {
        pti->scResult = sc;
        return NULL;
    }
    else
    {
        return vpd.pbFlat;
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   GetWritePtr16
//
//  Synopsis:   Validates a 16-bit pointer for writing and converts it into
//              a flat 32 pointer.
//
//  Arguments:  [pti] - THUNKINFO * for updating error code
//              [vp] - 16-bit pointer to validate/convert
//              [dwSize] - Length to validate
//
//  Returns:    Appropriate status code
//
//  History:    22-Mar-94   BobDay  Created
//
//----------------------------------------------------------------------------

VOID  *
GetWritePtr16(
    THUNKINFO   *pti,
    VPVOID      vp,
    DWORD       dwSize )
{
    VPTRDESC vpd;
    SCODE sc;

    sc = GetPtr16Description(vp, PACC_WRITE, dwSize, &vpd);
    if (FAILED(sc))
    {
        pti->scResult = sc;
        return NULL;
    }
    else
    {
        return vpd.pbFlat;
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   GetCodePtr16
//
//  Synopsis:   Validates a 16-bit pointer for execution and converts it
//              into a flat 32 pointer.
//
//  Arguments:  [pti] - THUNKINFO * for updating error code
//              [vp] - 16-bit pointer to validate/convert
//              [dwSize] - Length to validate
//
//  Returns:    Appropriate status code
//
//  History:    22-Jul-94   BobDay  Created
//
//----------------------------------------------------------------------------

VOID  *
GetCodePtr16(
    THUNKINFO   *pti,
    VPVOID      vp,
    DWORD       dwSize )
{
    VPTRDESC vpd;
    SCODE sc;

    sc = GetPtr16Description(vp, PACC_CODE, dwSize, &vpd);
    if (FAILED(sc))
    {
        pti->scResult = sc;
        return NULL;
    }
    else
    {
        return vpd.pbFlat;
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   GetReadWritePtr16
//
//  Synopsis:   Validates a 16-bit pointer for reading and writing and
//              converts it into a flat 32 pointer.
//
//  Arguments:  [pti] - THUNKINFO * for updating error code
//              [vp] - 16-bit pointer to validate/convert
//              [dwSize] - Length to validate
//
//  Returns:    Appropriate status code
//
//  History:    22-Mar-94   BobDay  Created
//
//----------------------------------------------------------------------------

VOID  *
GetReadWritePtr16(
    THUNKINFO   *pti,
    VPVOID      vp,
    DWORD       dwSize )
{
    VPTRDESC vpd;
    SCODE sc;

    sc = GetPtr16Description(vp, PACC_READ | PACC_WRITE, dwSize, &vpd);
    if (FAILED(sc))
    {
        pti->scResult = sc;
        return NULL;
    }
    else
    {
        return vpd.pbFlat;
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   GetStringPtr16
//
//  Synopsis:   Validates a 16-bit pointer to a string for reading and
//              converts it (the pointer) into a flat 32 pointer. It also
//              returns the length, since it has to compute it anyway.
//
//  Arguments:  [pti] - THUNKINFO * for updating error code
//              [vp] - 16-bit pointer to validate/convert
//              [cchMax] - Maximum legal length
//              [lpSize] - Place to return length
//
//  Returns:    Appropriate status code
//
//  History:    22-Mar-94   BobDay  Created
//
//----------------------------------------------------------------------------

CHAR  *
GetStringPtr16(
    THUNKINFO   *pti,
    VPSTR       vp,
    UINT        cchMax,
    PUINT       lpSize )
{
    VPTRDESC vpd;
    SCODE sc;

    // Check the first byte to ensure read access to the segment
    sc = GetPtr16Description(vp, PACC_READ, 1, &vpd);
    if (FAILED(sc))
    {
        pti->scResult = sc;
        return NULL;
    }
    else
    {
        UINT cchLen;
        BYTE *pb;
        BOOL fMbLead;

        pb = vpd.pbFlat;

        if (pb == NULL)
        {
            goto Exit;
        }


        // Restrict zero-termination search to cchMax characters
        // or valid remaining memory
        // Since we specified one in GetPtr16Description, dwLengthLeft
        // is one off here
        cchMax = min(cchMax, vpd.dwLengthLeft+1);

        cchLen = 0;
        fMbLead = FALSE;
        while (cchMax > 0)
        {
            cchLen++;

            if (*pb == 0 && !fMbLead)
            {
                break;
            }
            else
            {
                fMbLead = (BOOL)g_abLeadTable[*pb++];
                cchMax--;
            }
        }

        if (cchMax > 0)
        {
            *lpSize = cchLen;
            return (LPSTR)vpd.pbFlat;
        }

Exit:
        {
            pti->scResult = E_INVALIDARG;
            WOWRELVDMPTR(vp);
            return NULL;
        }
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   ValidatePtr16, public
//
//  Synopsis:   Calls an appropriate validation routine for 16-bit
//              memory based on in/out status
//
//  Arguments:  [pti] - Thunk info, can be NULL for no validation
//              [vp16] - 16-bit pointer
//              [dwSize] - Size
//              [thopInOut] - In/out type
//
//  Returns:    Pointer or NULL
//
//  Modifies:   [pti]->scResult for errors
//
//  History:    24-Apr-94       DrewB   Created
//
//  Notes:      0 - No validation
//              THOP_IN - Read validation
//              THOP_OUT - Write validation
//              THOP_INOUT - Read/write validation
//
//----------------------------------------------------------------------------

VOID  *
ValidatePtr16(THUNKINFO *pti,
                     VPVOID vp16,
                     DWORD dwSize,
                     THOP thopInOut)
{
    VPTRDESC vpd;
    SCODE sc;

    thopInOut &= THOP_INOUT;
    if (thopInOut != 0)
    {
        sc = GetPtr16Description(vp16, thopInOut, dwSize, &vpd);
        if (FAILED(sc))
        {
            pti->scResult = sc;
            return NULL;
        }
        else
        {
            return vpd.pbFlat;
        }
    }
    else
    {
        return WOWFIXVDMPTR(vp16, dwSize);
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   IsValidInterface16, public
//
//  Synopsis:   Validates that a provided 16-bit interface is really valid
//              (uses the same validation technique as 16-bit OLE 2.01)
//
//  Arguments:  [pti] - Thunk info, can be NULL for no validation
//              [vp16] - 16-bit pointer
//
//  Returns:    BOOL - true for valid, false for invalid
//
//  Modifies:   [pti]->scResult for errors
//
//  History:    22-Jul-92       BobDay      Created
//
//----------------------------------------------------------------------------

BOOL IsValidInterface16( THUNKINFO *pti, VPVOID vp )
{
    VPVOID UNALIGNED *pvpv;
    VPVOID  vpvtbl;
    VPVOID  vpfn;
    LPVOID  lpfn;

    //
    // Make sure we can read the vtbl pointer from the object.
    //
    pvpv = (VPVOID FAR *)GetReadPtr16(pti, vp, sizeof(VPVOID));
    if ( pvpv == NULL )
    {
        thkDebugOut((DEB_WARN, "IsValidInterface16: "
                     "Interface ptr invalid %p\n", vp));
        return FALSE;
    }

    vpvtbl = *pvpv;     // Read the vtbl ptr

    WOWRELVDMPTR(vp);

    // Make sure we can read the first entry from the vtbl (QI)

    pvpv = (VPVOID FAR *)GetReadPtr16(pti, vpvtbl, sizeof(VPVOID));
    if ( pvpv == NULL )
    {
        thkDebugOut((DEB_WARN, "Vtbl ptr invalid %p:%p\n", vp, vpvtbl));
        return FALSE;
    }

    vpfn = *pvpv;           // Get the QI Function

    WOWRELVDMPTR(vpvtbl);

    if ( vpfn == 0 )
    {
        thkDebugOut((DEB_WARN, "QI function NULL %p:%p\n", vp, vpvtbl));
        pti->scResult = E_INVALIDARG;
        return FALSE;
    }

    // Why it has to be 9 bytes long, I have no idea.
    // This check was taken from valid.cpp in
    // \src\ole2\dll\src\debug the 16-bit ole2.01
    // sources...
    lpfn = (LPVOID)GetCodePtr16(pti, vpfn, 9);

    WOWRELVDMPTR(vpfn);

    if ( lpfn == NULL )
    {
        thkDebugOut((DEB_WARN, "QI function ptr invalid %p:%p:%p\n",
                     vp,vpvtbl,vpfn));
        return FALSE;
    }

    return TRUE;
}

//+---------------------------------------------------------------------------
//
//  Function:   GuidString, debug public
//
//  Synopsis:   Converts a guid to a string
//
//  Arguments:  [pguid] - GUID
//
//  Returns:    Pointer to string
//
//  History:    08-Mar-94       DrewB   Created
//
//  Notes:      Uses a static buffer
//
//----------------------------------------------------------------------------

#if DBG == 1

#pragma pack(1)
struct SplitGuid
{
    DWORD dw1;
    WORD w1;
    WORD w2;
    BYTE b[8];
};
#pragma pack()

char *GuidString(GUID const *pguid)
{
    static char ach[39];
    SplitGuid *psg = (SplitGuid *)pguid;

    wsprintfA(ach, "{%08lX-%04hX-%04hX-%02X%02X-%02X%02X%02X%02X%02X%02X}",
            psg->dw1, psg->w1, psg->w2, psg->b[0], psg->b[1], psg->b[2],
            psg->b[3], psg->b[4], psg->b[5], psg->b[6], psg->b[7]);
    return ach;
}

#endif

#if DBG == 1
 char *apszThopNames[] =
{
    "THOP_END",
    "THOP_SHORTLONG",
    "THOP_WORDDWORD",
    "THOP_COPY",
    "THOP_LPSTR",
    "THOP_LPLPSTR",
    "THOP_BUFFER",
    "THOP_HUSER",
    "THOP_HGDI",
    "THOP_SIZE",
    "THOP_RECT",
    "THOP_MSG",
    "THOP_HRESULT",
    "THOP_STATSTG",
    "THOP_DVTARGETDEVICE",
    "THOP_STGMEDIUM",
    "THOP_FORMATETC",
    "THOP_HACCEL",
    "THOP_OIFI",
    "THOP_BINDOPTS",
    "THOP_LOGPALETTE",
    "THOP_SNB",
    "THOP_CRGIID",
    "THOP_OLESTREAM",
    "THOP_HTASK",
    "THOP_INTERFACEINFO",
    "THOP_IFACE",
    "THOP_IFACEOWNER",
    "THOP_IFACENOADDREF",
    "THOP_IFACECLEAN",
    "THOP_IFACEGEN",
    "THOP_IFACEGENOWNER",
    "THOP_UNKOUTER",
    "THOP_UNKINNER",
    "THOP_ROUTINEINDEX",
    "THOP_RETURNTYPE",
    "THOP_NULL",
    "THOP_ERROR",
    "THOP_ENUM",
    "THOP_CALLBACK",
    "THOP_RPCOLEMESSAGE",
    "THOP_ALIAS32",
    "THOP_CLSCONTEXT",
    "THOP_FILENAME",
    "THOP_SIZEDSTRING"
};
#endif

//+---------------------------------------------------------------------------
//
//  Function:   ThopName, debug public
//
//  Synopsis:   Returns the string name of a thop
//
//  Arguments:  [thop] - Thop
//
//  Returns:    Pointer to string
//
//  History:    11-Mar-94       DrewB   Created
//
//  Notes:      Uses a static buffer
//
//----------------------------------------------------------------------------

#if DBG == 1
char *ThopName(THOP thop)
{
    static char achString[80];
    char *psz;

    thkAssert((thop & THOP_OPMASK) < THOP_LASTOP);
    thkAssert(THOP_LASTOP ==
              (sizeof(apszThopNames)/sizeof(apszThopNames[0])));

    strcpy(achString, apszThopNames[thop & THOP_OPMASK]);

    psz = achString+strlen(achString);
    if (thop & THOP_IN)
    {
        strcpy(psz, " | THOP_IN");
        psz += strlen(psz);
    }
    if (thop & THOP_OUT)
    {
        strcpy(psz, " | THOP_OUT");
    }

    return achString;
}
#endif

#if DBG == 1
 char *apszEnumThopNames[] =
{
    "STRING",
    "UNKNOWN",
    "STATSTG",
    "FORMATETC",
    "STATDATA",
    "MONIKER",
    "OLEVERB"
};
#endif

//+---------------------------------------------------------------------------
//
//  Function:   EnumThopName, debug public
//
//  Synopsis:   Returns the string name of an enum thop
//
//  Arguments:  [thopEnum] - Thop
//
//  Returns:    Pointer to string
//
//  History:    11-Mar-94       DrewB   Created
//
//----------------------------------------------------------------------------

#if DBG == 1
char *EnumThopName(THOP thopEnum)
{
    thkAssert(thopEnum <
              (sizeof(apszEnumThopNames)/sizeof(apszEnumThopNames[0])));
    return apszEnumThopNames[thopEnum];
}
#endif

#if DBG == 1
// Maintain current thunking invocation nesting level
int _iThunkNestingLevel = 1;
#endif

//+---------------------------------------------------------------------------
//
//  Function:   NestingSpaces, debug public
//
//  Synopsis:   Spaces for each nesting level
//
//  History:    22-Mar-94       DrewB   Created
//
//----------------------------------------------------------------------------

#if DBG == 1

#define NESTING_SPACES 32
#define SPACES_PER_LEVEL 2

static char achSpaces[NESTING_SPACES+1] = "                                ";

void NestingSpaces(char *psz)
{
    int iSpaces, i;

    iSpaces = _iThunkNestingLevel*SPACES_PER_LEVEL;

    while (iSpaces > 0)
    {
        i = min(iSpaces, NESTING_SPACES);
        memcpy(psz, achSpaces, i);
        psz += i;
        *psz = 0;
        iSpaces -= i;
    }
}
#endif

//+---------------------------------------------------------------------------
//
//  Function:   NestingLevelString, debug public
//
//  Synopsis:   Provides a string describing the nesting level
//
//  History:    22-Mar-94       DrewB   Created
//
//  Notes:      Uses a static buffer
//
//----------------------------------------------------------------------------

#if DBG == 1
char *NestingLevelString(void)
{
    static char ach[256];
    char *psz;

    if ((thkInfoLevel & DEB_NESTING) == 0)
    {
        return "";
    }

    wsprintfA(ach, "%2d:", _iThunkNestingLevel);
    psz = ach+strlen(ach);

    if (sizeof(ach)/SPACES_PER_LEVEL <= _iThunkNestingLevel)
    {
        strcpy(psz, "...");
    }
    else
    {
        NestingSpaces(psz);
    }
    return ach;
}
#endif

//+---------------------------------------------------------------------------
//
//  Function:   IidOrInterfaceString, debug public
//
//  Synopsis:   Returns the interface name for known interfaces or
//              the IID string itself
//
//  Arguments:  [piid] - IID
//
//  Returns:    char *
//
//  History:    18-Jun-94       DrewB   Created
//
//----------------------------------------------------------------------------

#if DBG == 1
char *IidOrInterfaceString(IID const *piid)
{
    return IidIdxString(IidToIidIdx(*piid));
}
#endif

//+---------------------------------------------------------------------------
//
//  Function:   IidIdxString, debug public
//
//  Synopsis:   Returns the interface name for known interfaces or
//              the IID string itself
//
//  Arguments:  [iidx] - IID or index
//
//  Returns:    char *
//
//  History:    07-Jul-94       DrewB   Created
//
//----------------------------------------------------------------------------

#if DBG == 1
char *IidIdxString(IIDIDX iidx)
{
    if (IIDIDX_IS_IID(iidx))
    {
        return GuidString(IIDIDX_IID(iidx));
    }
    else if (IIDIDX_INDEX(iidx) == THI_COUNT)
    {
        // Special case here because of IMalloc's unusual unthunked-
        // but-indexed existence
        return "IMalloc";
    }
    else
    {
        return inInterfaceNames[IIDIDX_INDEX(iidx)].pszInterface;
    }
}
#endif

//+---------------------------------------------------------------------------
//
//  Function:   Handler routines, public
//
//  Synopsis:   Generic conversion routines for the generic thop handler
//
//  Arguments:  [pbFrom] - Data to convert from
//              [pbTo] - Buffer to convert into
//              [cbFrom] - Size of source data
//              [cbTo] - Size of destination data
//
//  History:    05-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

//+---------------------------------------------------------------------------
//
//  Function:   FhCopyMemory, public
//
//  Synopsis:   Handler routine for memory copies
//
//  History:    05-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

void FhCopyMemory(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    thkAssert(cbFrom == cbTo);

    memcpy(pbTo, pbFrom, cbFrom);

#if DBG == 1
    if (cbFrom == sizeof(DWORD))
    {
        thkDebugOut((DEB_ARGS, "Arg     DWORD: 0x%08lX\n",
                *(DWORD UNALIGNED *)pbFrom));
    }
    else if (cbFrom == sizeof(LARGE_INTEGER))
    {
        thkDebugOut((DEB_ARGS, "Arg     8 byte: 0x%08lX:%08lX\n",
                     *(DWORD UNALIGNED *)(pbFrom+1*sizeof(DWORD)),
                     *(DWORD UNALIGNED *)(pbFrom+0*sizeof(DWORD))));
    }
    else if (cbFrom == sizeof(GUID))
    {
        thkDebugOut((DEB_ARGS, "Arg     16 byte: 0x%08lX:%08lX:%08lX:%08lX\n",
                     *(DWORD UNALIGNED *)(pbFrom+3*sizeof(DWORD)),
                     *(DWORD UNALIGNED *)(pbFrom+2*sizeof(DWORD)),
                     *(DWORD UNALIGNED *)(pbFrom+1*sizeof(DWORD)),
                     *(DWORD UNALIGNED *)(pbFrom+0*sizeof(DWORD))));
    }
    else
    {
        thkDebugOut((DEB_ARGS, "Arg     %d byte copy\n", cbFrom));
    }
#endif
}

//+---------------------------------------------------------------------------
//
//  Function:   FhShortToLong, FhLongToShort, public
//
//  Synopsis:   Signed int conversion
//
//  History:    05-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

void FhShortToLong(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    thkAssert(cbFrom == sizeof(SHORT));
    thkAssert(cbTo == sizeof(LONG));

    *(LONG UNALIGNED *)pbTo = (LONG)*(SHORT UNALIGNED *)pbFrom;

    thkDebugOut((DEB_ARGS, "ShToLo  %d -> %d\n",
                 *(SHORT UNALIGNED *)pbFrom, *(LONG UNALIGNED *)pbTo));
}

void FhLongToShort(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    thkAssert(cbFrom == sizeof(LONG));
    thkAssert(cbTo == sizeof(SHORT));

    // Not used in situations where clamping is meaningful
    *(SHORT UNALIGNED *)pbTo = (SHORT)*(LONG UNALIGNED *)pbFrom;

    thkDebugOut((DEB_ARGS, "LoToSh  %d -> %d\n",
                 *(LONG UNALIGNED *)pbFrom, *(SHORT UNALIGNED *)pbTo));
}

//+---------------------------------------------------------------------------
//
//  Function:   FhWordToDword, FhDwordToWord, public
//
//  Synopsis:   Handler routine for memory copies
//
//  History:    05-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

void FhWordToDword(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    thkAssert(cbFrom == sizeof(WORD));
    thkAssert(cbTo == sizeof(DWORD));

    *(DWORD UNALIGNED *)pbTo = (DWORD)*(WORD UNALIGNED *)pbFrom;

    thkDebugOut((DEB_ARGS, "WoToDw  0x%04lX -> 0x%08lX\n",
                 *(WORD UNALIGNED *)pbFrom, *(DWORD UNALIGNED *)pbTo));
}

void FhDwordToWord(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    thkAssert(cbFrom == sizeof(DWORD));
    thkAssert(cbTo == sizeof(WORD));

    // Not used in situations where clamping is meaningful
    *(WORD UNALIGNED *)pbTo = (WORD)*(DWORD UNALIGNED *)pbFrom;

    thkDebugOut((DEB_ARGS, "DwToWo  0x%08lX -> 0x%04lX\n",
                 *(DWORD UNALIGNED *)pbFrom, *(WORD UNALIGNED *)pbTo));
}

//+---------------------------------------------------------------------------
//
//  Function:   Handle routines, public
//
//  Synopsis:   Handler routine for Windows handles
//
//  History:    05-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

void FhGdiHandle1632(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    thkAssert(cbFrom == sizeof(HAND16));
    thkAssert(cbTo == sizeof(HANDLE));

    *(HBITMAP *)pbTo = HBITMAP_32(*(HBITMAP16 UNALIGNED *)pbFrom);

    thkDebugOut((DEB_ARGS, "1632    HGdi: 0x%04lX -> 0x%p\n",
                 *(HAND16 UNALIGNED *)pbFrom, *(HANDLE *)pbTo));
}

void FhGdiHandle3216(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    thkAssert(cbFrom == sizeof(HANDLE));
    thkAssert(cbTo == sizeof(HAND16));

    *(HAND16 UNALIGNED *)pbTo = HBITMAP_16(*(HANDLE *)pbFrom);

    thkDebugOut((DEB_ARGS, "3216    HGdi: 0x%p -> 0x%04lX\n",
                 *(HANDLE *)pbFrom, *(HAND16 UNALIGNED *)pbTo));
}

void FhUserHandle1632(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    thkAssert(cbFrom == sizeof(HAND16));
    thkAssert(cbTo == sizeof(HANDLE));

    // Even though the constant is WOW_TYPE_FULLHWND, it
    // works for any user handle

    *(HANDLE *)pbTo = WOWHandle32(*(HAND16 UNALIGNED *)pbFrom,
                                  WOW_TYPE_FULLHWND);

    thkDebugOut((DEB_ARGS, "1632    HUser: 0x%04lX -> 0x%p\n",
                 *(HAND16 UNALIGNED *)pbFrom, *(HANDLE *)pbTo));
}

void FhUserHandle3216(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    thkAssert(cbFrom == sizeof(HANDLE));
    thkAssert(cbTo == sizeof(HAND16));

    *(HAND16 UNALIGNED *)pbTo = HWND_16(*(HANDLE *)pbFrom);

    thkDebugOut((DEB_ARGS, "3216    HUser: 0x%p -> 0x%04lX\n",
                 *(HANDLE *)pbFrom, *(HAND16 UNALIGNED *)pbTo));
}

void FhHaccel1632(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    thkAssert(cbFrom == sizeof(HAND16));
    thkAssert(cbTo == sizeof(HANDLE));

    *(HANDLE *)pbTo = HACCEL_32(*(HAND16 UNALIGNED *)pbFrom);

    thkDebugOut((DEB_ARGS, "1632    HACCEL: 0x%04lX -> 0x%p\n",
                 *(HAND16 UNALIGNED *)pbFrom, *(HANDLE *)pbTo));
}

void FhHaccel3216(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    thkAssert(cbFrom == sizeof(HANDLE));
    thkAssert(cbTo == sizeof(HAND16));

    *(HAND16 UNALIGNED *)pbTo = HACCEL_16(*(HANDLE *)pbFrom);

    thkDebugOut((DEB_ARGS, "3216    HACCEL: 0x%p -> 0x%04lX\n",
                 *(HANDLE *)pbFrom, *(HAND16 UNALIGNED *)pbTo));
}

void FhHtask1632(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    HAND16  h16;
    DWORD   h32;
    thkAssert(cbFrom == sizeof(HAND16));
    thkAssert(cbTo == sizeof(HANDLE));

    h16 = *(HAND16 UNALIGNED *)pbFrom;
    if ( h16 == 0 )
    {
        h32 = 0;
    }
    else
    {
        h32 = HTASK_32(h16);
    }
    *(DWORD *)pbTo = h32;

    thkDebugOut((DEB_ARGS, "1632    HTASK: 0x%04lX -> 0x%p\n", h16, h32));
}

void FhHtask3216(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    HAND16  h16;
    HANDLE  h32;

    thkAssert(cbFrom == sizeof(HANDLE));
    thkAssert(cbTo == sizeof(HAND16));

    h32 = *(HANDLE *)pbFrom;
    if ( h32 == NULL )
    {
        h16 = 0;
    }
    else
    {
        h16 = HTASK_16(h32);
    }
    *(HAND16 UNALIGNED *)pbTo = h16;

    thkDebugOut((DEB_ARGS, "3216    HTASK: 0x%p -> 0x%04lX\n",h32, h16));
}

//+---------------------------------------------------------------------------
//
//  Function:   HRESULT routines, public
//
//  Synopsis:   Handler routine for HRESULTs
//
//  History:    05-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

void FhHresult1632(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    thkAssert(cbFrom == sizeof(HRESULT));
    thkAssert(cbTo == sizeof(HRESULT));

    *(HRESULT *)pbTo = TransformHRESULT_1632(*(HRESULT UNALIGNED *)pbFrom);

    thkDebugOut((DEB_ARGS, "1632    HRESULT: 0x%08lX -> 0x%08lX\n",
                 *(HRESULT UNALIGNED *)pbFrom, *(HRESULT *)pbTo));
}

void FhHresult3216(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    thkAssert(cbFrom == sizeof(HRESULT));
    thkAssert(cbTo == sizeof(HRESULT));

    *(HRESULT UNALIGNED *)pbTo = TransformHRESULT_3216(*(HRESULT *)pbFrom);

    thkDebugOut((DEB_ARGS, "3216    HRESULT: 0x%08lX -> 0x%08lX\n",
                 *(HRESULT *)pbFrom, *(HRESULT UNALIGNED *)pbTo));
}

//+---------------------------------------------------------------------------
//
//  Function:   NULL routines, public
//
//  Synopsis:   Handler routine for NULL
//
//  History:    05-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

void FhNull(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    thkAssert(cbFrom == sizeof(void *));
    thkAssert(cbTo == sizeof(void *));

    thkDebugOut((DEB_WARN, "FhNull: %p NULL value not NULL\n", pbFrom));
    *(void UNALIGNED **)pbTo = NULL;

    thkDebugOut((DEB_ARGS, "Arg     NULL\n"));
}

//+---------------------------------------------------------------------------
//
//  Function:   Rect routines, public
//
//  Synopsis:   Handler routines for RECT
//
//  History:    05-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

void FhRect1632(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    RECT *pr32;
    RECT16 UNALIGNED * pr16;

    thkAssert(cbFrom == sizeof(RECT16));
    thkAssert(cbTo == sizeof(RECT));

    pr16 = (RECT16 UNALIGNED *)pbFrom;
    pr32 = (RECT *)pbTo;

    pr32->left   = (LONG)pr16->left;   // Sign extend
    pr32->top    = (LONG)pr16->top;    // Sign extend
    pr32->right  = (LONG)pr16->right;  // Sign extend
    pr32->bottom = (LONG)pr16->bottom; // Sign extend

    thkDebugOut((DEB_ARGS, "1632    RECT: {%d, %d, %d, %d}\n",
                 pr32->left, pr32->top, pr32->right, pr32->bottom));
}

void FhRect3216(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    RECT *pr32;
    RECT16 UNALIGNED *pr16;

    thkAssert(cbFrom == sizeof(RECT));
    thkAssert(cbTo == sizeof(RECT16));

    pr32 = (RECT *)pbFrom;
    pr16 = (RECT16 UNALIGNED *)pbTo;

    pr16->left   = ClampLongToShort(pr32->left);
    pr16->top    = ClampLongToShort(pr32->top);
    pr16->right  = ClampLongToShort(pr32->right);
    pr16->bottom = ClampLongToShort(pr32->bottom);

    thkDebugOut((DEB_ARGS, "3216    RECT: {%d, %d, %d, %d}\n",
                 pr32->left, pr32->top, pr32->right, pr32->bottom));
}

//+---------------------------------------------------------------------------
//
//  Function:   Size routines, public
//
//  Synopsis:   Handler routines for SIZE
//
//  History:    05-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

void FhSize1632(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    SIZE16 UNALIGNED *psize16;
    SIZE *psize32;

    thkAssert(cbFrom == sizeof(SIZE16));
    thkAssert(cbTo == sizeof(SIZE));

    psize16 = (SIZE16 UNALIGNED *)pbFrom;
    psize32 = (SIZE *)pbTo;

    psize32->cx = (LONG)psize16->cx;
    psize32->cy = (LONG)psize16->cy;

    thkDebugOut((DEB_ARGS, "1632    SIZE: {%d, %d}\n",
                 psize32->cx, psize32->cy));
}

void FhSize3216(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    SIZE16 UNALIGNED *psize16;
    SIZE *psize32;

    thkAssert(cbFrom == sizeof(SIZE));
    thkAssert(cbTo == sizeof(SIZE16));

    psize32 = (SIZE *)pbFrom;
    psize16 = (SIZE16 UNALIGNED *)pbTo;

    psize16->cx = ClampLongToShort(psize32->cx);
    psize16->cy = ClampLongToShort(psize32->cy);

    thkDebugOut((DEB_ARGS, "3216    SIZE: {%d, %d}\n",
                 psize32->cx, psize32->cy));
}

//+---------------------------------------------------------------------------
//
//  Function:   Message routines, public
//
//  Synopsis:   Handler routines for MSG
//
//  History:    05-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

void FhMsg1632(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    MSG16 UNALIGNED *pmsg16;
    MSG *pmsg32;

    thkAssert(cbFrom == sizeof(MSG16));
    thkAssert(cbTo == sizeof(MSG));

    pmsg16 = (MSG16 UNALIGNED *)pbFrom;
    pmsg32 = (MSG *)pbTo;

    pmsg32->hwnd    = HWND_32(pmsg16->hwnd);
    pmsg32->message = (UINT)pmsg16->message;
    pmsg32->wParam  = (WPARAM)pmsg16->wParam; // Should we sign extend?
    pmsg32->lParam  = (LPARAM)pmsg16->lParam;
    pmsg32->time    = pmsg16->time;
    pmsg32->pt.x    = (LONG)(SHORT)LOWORD(pmsg16->pt);   // Sign extend
    pmsg32->pt.y    = (LONG)(SHORT)HIWORD(pmsg16->pt);   // Sign extend

    thkDebugOut((DEB_ARGS, "1632    MSG: {0x%p, %d, 0x%08lX, 0x%08lX, "
                 "0x%08lX, {%d, %d}}\n",
                 pmsg32->hwnd, pmsg32->message, pmsg32->wParam,
                 pmsg32->lParam, pmsg32->time, pmsg32->pt.x,
                 pmsg32->pt.y));
}

void FhMsg3216(BYTE *pbFrom, BYTE *pbTo, UINT cbFrom, UINT cbTo)
{
    MSG16 UNALIGNED *pmsg16;
    MSG *pmsg32;

    thkAssert(cbFrom == sizeof(MSG));
    thkAssert(cbTo == sizeof(MSG16));

    pmsg32 = (MSG *)pbFrom;
    pmsg16 = (MSG16 UNALIGNED *)pbTo;

    pmsg16->hwnd    = HWND_16(pmsg32->hwnd);
    pmsg16->message = (WORD)pmsg32->message;
    pmsg16->wParam  = (WORD)pmsg32->wParam;           // Sign truncate
    pmsg16->lParam  = (LONG)pmsg32->lParam;
    pmsg16->time    = pmsg32->time;
    pmsg16->pt      = MAKELONG(ClampLongToShort(pmsg32->pt.x),
                               ClampLongToShort(pmsg32->pt.y));

    thkDebugOut((DEB_ARGS, "3216    MSG: {0x%p, %d, 0x%08lX, 0x%08lX, "
                 "0x%08lX, {%d, %d}}\n",
                 pmsg32->hwnd, pmsg32->message, pmsg32->wParam,
                 pmsg32->lParam, pmsg32->time, pmsg32->pt.x,
                 pmsg32->pt.y));
}

//+---------------------------------------------------------------------------
//
//  Function:   ALLOCROUTINE, public
//
//  Synopsis:   A routine which allocates memory
//
//  Arguments:  [cb] - Amount to allocate
//
//  Returns:    Pointer to memory
//
//  History:    19-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

//+---------------------------------------------------------------------------
//
//  Function:   FREEROUTINE, public
//
//  Synopsis:   A routine which frees memory
//
//  Arguments:  [pv] - Memory to free
//              [cb] - Size of memory to free
//
//  History:    19-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

void *ArTask16(UINT cb)
{
    return (void *)TaskMalloc16(cb);
}

void FrTask16(void *pv, UINT cb)
{
    TaskFree16((VPVOID)pv);
}

void *ArTask32(UINT cb)
{
    return TaskMalloc32(cb);
}

void FrTask32(void *pv, UINT cb)
{
    TaskFree32(pv);
}

void *ArStack16(UINT cb)
{
    return (void *)STACKALLOC16(cb);
}

void FrStack16(void *pv, UINT cb)
{
    STACKFREE16((VPVOID)pv, cb);
}

void *ArStack32(UINT cb)
{
    // Can't use STACKALLOC32 on NT since it may be _alloca which wouldn't
    // live beyond this routine
#ifdef _CHICAGO_
    return STACKALLOC32(cb);
#else
    return (void *)LocalAlloc(LMEM_FIXED, cb);
#endif
}

void FrStack32(void *pv, UINT cb)
{
#ifdef _CHICAGO_
    STACKFREE32(pv, cb);
#else
    LocalFree(pv);
#endif
}

//+---------------------------------------------------------------------------
//
//  Function:   ConvertDvtd1632, private
//
//  Synopsis:   Converts a DVTARGETDEVICE from 16 to 32-bits
//
//  Arguments:  [pti] - Thunking state information
//              [vpdvtd16] - Source
//              [pfnAlloc] - ALLOCROUTINE
//              [pfnFree] - FREEROUTINE
//              [ppdvtd32] - Destination
//              [pcbSize] - Size return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [ppdvtd32]
//              [pcbSize]
//
//  History:    18-Apr-94       DrewB   Created
//
//  Notes:      [pfnAlloc/Free] must deal with 32-bit memory
//
//----------------------------------------------------------------------------

SCODE ConvertDvtd1632(THUNKINFO *pti,
                      VPVOID vpdvtd16,
                      ALLOCROUTINE pfnAlloc,
                      FREEROUTINE pfnFree,
                      DVTARGETDEVICE **ppdvtd32,
                      UINT *pcbSize)
{
    DVTARGETDEVICE UNALIGNED *pdvtd16;
    DVTARGETDEVICE *pdvtd32;
    DVTDINFO dvtdi;

    pdvtd16 = (DVTARGETDEVICE UNALIGNED *)GetReadPtr16(pti, vpdvtd16,
                                                       sizeof(DVTARGETDEVICE));
    if (pdvtd16 == NULL)
    {
        return pti->scResult;
    }

    pdvtd16 = (DVTARGETDEVICE UNALIGNED *)GetReadPtr16(pti, vpdvtd16,
                                                       pdvtd16->tdSize);

    WOWRELVDMPTR(vpdvtd16);

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

    pti->scResult = UtGetDvtd16Info( pdvtd16, &dvtdi );

    if ( FAILED(pti->scResult) )
    {
        WOWRELVDMPTR(vpdvtd16);
        return pti->scResult;
    }

    pdvtd32 = (DVTARGETDEVICE *)pfnAlloc(dvtdi.cbConvertSize);
    if (pdvtd32 == NULL)
    {
        WOWRELVDMPTR(vpdvtd16);
        return E_OUTOFMEMORY;
    }

    pti->scResult = UtConvertDvtd16toDvtd32( pdvtd16, &dvtdi, pdvtd32 );

    WOWRELVDMPTR(vpdvtd16);

    if ( FAILED(pti->scResult) )
    {
        pfnFree(pdvtd32, dvtdi.cbConvertSize);
        return pti->scResult;
    }

    *ppdvtd32 = pdvtd32;
    *pcbSize = dvtdi.cbConvertSize;

    return S_OK;
}

//+---------------------------------------------------------------------------
//
//  Function:   ConvertDvtd3216, private
//
//  Synopsis:   Converts a DVTARGETDEVICE from 32 to 16-bits
//
//  Arguments:  [pti] - Thunking state information
//              [pdvtd32] - Source
//              [pfnAlloc] - Allocator
//              [pfnFree] - Freer
//              [ppvdvtd16] - Destination
//              [pcbSize] - Size return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [ppvdvtd16]
//              [pcbSize]
//
//  History:    18-Apr-94       DrewB   Created
//
//  Notes:      [pfnAlloc/Free] must deal with 16-bit memory
//
//----------------------------------------------------------------------------

SCODE ConvertDvtd3216(THUNKINFO *pti,
                      DVTARGETDEVICE *pdvtd32,
                      ALLOCROUTINE pfnAlloc,
                      FREEROUTINE pfnFree,
                      VPVOID *ppvdvtd16,
                      UINT *pcbSize)
{
    DVTARGETDEVICE UNALIGNED *pdvtd16;
    VPVOID vpdvtd16;
    DVTDINFO dvtdi;

    if (IsBadReadPtr(pdvtd32, sizeof(DVTARGETDEVICE)) ||
        IsBadReadPtr(pdvtd32, pdvtd32->tdSize))
    {
        return E_INVALIDARG;
    }

    pti->scResult = UtGetDvtd32Info( pdvtd32, &dvtdi );

    if ( FAILED(pti->scResult) )
    {
        return pti->scResult;
    }

    vpdvtd16 = (VPVOID)pfnAlloc(dvtdi.cbConvertSize);
    if (vpdvtd16 == 0)
    {
        return E_OUTOFMEMORY;
    }

    pdvtd16 = (DVTARGETDEVICE UNALIGNED *)WOWFIXVDMPTR(vpdvtd16,
                                                       dvtdi.cbConvertSize);

    pti->scResult = UtConvertDvtd32toDvtd16( pdvtd32, &dvtdi, pdvtd16 );

    WOWRELVDMPTR(vpdvtd16);

    if ( FAILED(pti->scResult) )
    {
        pfnFree((void *)vpdvtd16, dvtdi.cbConvertSize);
        return pti->scResult;
    }

    *ppvdvtd16 = vpdvtd16;
    *pcbSize = dvtdi.cbConvertSize;

    return S_OK;
}

#if !defined(_CHICAGO_)

SCODE ConvertHDrop1632(HMEM16 hg16, HGLOBAL* phg32)
{
    SCODE sc = S_OK;

    *phg32 = CopyDropFilesFrom16(hg16);
    if (!*phg32)
        sc = E_INVALIDARG;

    return sc;
}


SCODE ConvertHDrop3216(HGLOBAL hg32, HMEM16* phg16)
{
    SCODE sc = S_OK;

    *phg16 = CopyDropFilesFrom32(hg32);
    if (!*phg16)
        sc = E_INVALIDARG;

    return sc;
}

#endif



//+---------------------------------------------------------------------------
//
//  Function:   ConvertHGlobal1632, public
//
//  Synopsis:   Creates a 32-bit HGLOBAL for a 16-bit HGLOBAL
//
//  Arguments:  [pti] - Thunk info, can be NULL for no validation
//              [hg16] - 16-bit HGLOBAL
//              [thopInOut] - Validation type
//              [phg32] - 32-bit HGLOBAL in/out
//              [pdwSize] - Size in/out
//
//  Returns:    Appropriate status code
//
//  Modifies:   [phg32]
//              [pdwSize]
//
//  History:    24-Apr-94       DrewB   Created
//
//  Notes:      If [phg32] is non-NULL on entry, [pdwSize] must be set
//              appropriately also
//
//----------------------------------------------------------------------------

SCODE ConvertHGlobal1632(THUNKINFO *pti,
                         HMEM16 hg16,
                         THOP thopInOut,
                         HGLOBAL *phg32,
                         DWORD *pdwSize)
{
    SCODE sc;
    VPVOID vpdata16;
    LPVOID lpdata16;
    LPVOID lpdata32;
    HGLOBAL hg32;
    DWORD dwSize;
    BOOL fOwn;

    sc = S_OK;

    vpdata16 = WOWGlobalLockSize16( hg16, &dwSize );
    if ( vpdata16 == 0 )
    {
        sc = E_INVALIDARG;
    }
    else
    {
        if (*phg32 != 0 && *pdwSize == dwSize)
        {
            hg32 = *phg32;
            fOwn = FALSE;
        }
        else
        {
            hg32 = GlobalAlloc( GMEM_MOVEABLE, dwSize );
            fOwn = TRUE;
        }

        if ( hg32 == 0 )
        {
            sc = E_OUTOFMEMORY;
        }
        else
        {
            lpdata32 = GlobalLock( hg32 );

            lpdata16 = (LPVOID)ValidatePtr16(pti, vpdata16, dwSize, thopInOut);
            if ( lpdata16 != NULL )
            {
                memcpy( lpdata32, lpdata16, dwSize );
                WOWRELVDMPTR(vpdata16);
            }
            else
            {
                sc = pti->scResult;
            }

            GlobalUnlock(hg32);

            if (FAILED(sc) && fOwn)
            {
                GlobalFree(hg32);
            }
        }

        WOWGlobalUnlock16( hg16 );
    }

    if (SUCCEEDED(sc))
    {
        if (*phg32 != 0 && hg32 != *phg32)
        {
            GlobalFree(*phg32);
        }

        *phg32 = hg32;
        *pdwSize = dwSize;

        thkDebugOut((DEB_ARGS, "1632    HGLOBAL: 0x%04X -> 0x%p, %u\n",
                     hg16, hg32, dwSize));
    }

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   ConvertHGlobal3216, public
//
//  Synopsis:   Creates a 16-bit HGLOBAL for a 32-bit HGLOBAL
//
//  Arguments:  [pti] - Thunk info, can be NULL for no validation
//              [hg32] - 32-bit HGLOBAL
//              [thopInOut] - Validation type
//              [phg16] - 16-bit HGLOBAL in/out
//              [pdwSize] - Size in/out
//
//  Returns:    Appropriate status code
//
//  Modifies:   [phg16]
//              [pdwSize]
//
//  History:    24-Apr-94       DrewB   Created
//
//  Notes:      If [phg16] is non-NULL on entry, [pdwSize] must be set
//              appropriately also
//
//----------------------------------------------------------------------------

SCODE ConvertHGlobal3216(THUNKINFO *pti,
                         HGLOBAL hg32,
                         THOP thopInOut,
                         HMEM16 *phg16,
                         DWORD *pdwSize)
{
    SCODE sc;
    VPVOID vpdata16;
    LPVOID lpdata16;
    LPVOID lpdata32;
    HMEM16 hg16;
    DWORD dwSize;
    BOOL fOwn;

    sc = S_OK;

    dwSize = (DWORD) GlobalSize(hg32);
    if (dwSize == 0)
    {
        sc = E_INVALIDARG;
    }
    else
    {
        lpdata32 = GlobalLock(hg32);

        if (*phg16 != 0 && *pdwSize == dwSize)
        {
            hg16 = *phg16;
            vpdata16 = WOWGlobalLock16(hg16);
            fOwn = FALSE;
        }
        else
        {
            vpdata16 = WOWGlobalAllocLock16(GMEM_MOVEABLE | GMEM_DDESHARE,
                                            dwSize, &hg16);
            fOwn = TRUE;
        }

        if (vpdata16 == 0)
        {
            sc = E_OUTOFMEMORY;
        }
        else
        {
            lpdata16 = (LPVOID)WOWFIXVDMPTR( vpdata16, dwSize );
            if ( lpdata16 == NULL )
            {
                sc = E_UNEXPECTED;
            }
            else
            {
                memcpy( lpdata16, lpdata32, dwSize );

                WOWRELVDMPTR(vpdata16);
            }

            WOWGlobalUnlock16( hg16 );

            if (FAILED(sc) && fOwn)
            {
                WOWGlobalFree16(hg16);
            }
        }

        GlobalUnlock(hg32);
    }

    if (SUCCEEDED(sc))
    {
        if (*phg16 != 0 && hg16 != *phg16)
        {
            WOWGlobalFree16(*phg16);
        }

        *phg16 = hg16;
        *pdwSize = dwSize;

        thkDebugOut((DEB_ARGS, "3216    HGLOBAL: 0x%p -> 0x%04X, %u\n",
                     hg32, hg16, dwSize));
    }

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   ConvertMfPict1632, public
//
//  Synopsis:   Converts a 16-bit METAFILEPICT to 32-bit
//
//  Arguments:  [pti] - Thunk info
//              [hg16] - 16-bit HGLOBAL containing METAFILEPICT
//              [phg32] - 32-bit HGLOBAL return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [phg32]
//
//  History:    24-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

 SCODE ConvertMfPict1632(THUNKINFO *pti,
                               HMEM16 hg16,
                               HGLOBAL *phg32)
{
    SCODE sc;
    VPVOID vpmfp16;
    METAFILEPICT16 UNALIGNED *pmfp16;
    METAFILEPICT *pmfp32;
    HGLOBAL hg32;
    DWORD dwSize;
#if DBG == 1
    BOOL fSaveToFile = FALSE;
#endif

    thkDebugOut((DEB_ITRACE, "In  ConvertMfPict1632(%p, 0x%04X, %p)\n",
                 pti, hg16, phg32));

    *phg32 = 0;
    sc = S_OK;

    vpmfp16 = WOWGlobalLockSize16( hg16, &dwSize );
    if ( vpmfp16 == 0 || dwSize < sizeof(METAFILEPICT16))
    {
        sc = E_INVALIDARG;
    }
    else
    {
        hg32 = GlobalAlloc( GMEM_MOVEABLE, sizeof(METAFILEPICT) );
        if ( hg32 == 0 )
        {
            sc = E_OUTOFMEMORY;
        }
        else
        {
            pmfp32 = (METAFILEPICT *)GlobalLock( hg32 );

            pmfp16 = (METAFILEPICT16 UNALIGNED *)GetReadPtr16(pti, vpmfp16,
                                                              dwSize);
            if ( pmfp16 != NULL )
            {
                pmfp32->mm = (LONG)pmfp16->mm;
                pmfp32->xExt = (LONG)pmfp16->xExt;
                pmfp32->yExt = (LONG)pmfp16->yExt;

                pmfp32->hMF = HMETAFILE_32(pmfp16->hMF);

                thkDebugOut((DEB_ARGS, "1632    METAFILEPICT: "
                         "{%d, %d, %d, 0x%p} -> {%d, %d, %d, 0x%4x}\n",
                         pmfp16->mm, pmfp16->xExt, pmfp16->yExt, pmfp16->hMF,
                         pmfp32->mm, pmfp32->xExt, pmfp32->yExt, pmfp32->hMF));

                WOWRELVDMPTR(vpmfp16);

#if DBG == 1
                if (fSaveToFile)
                {
                    HMETAFILE hmf;

                    hmf = CopyMetaFile(pmfp32->hMF, __TEXT("thkmf.wmf"));
                    if (hmf != NULL)
                    {
                        DeleteMetaFile(hmf);
                    }
                }
#endif
            }
            else
            {
                sc = pti->scResult;
            }

            GlobalUnlock(hg32);

            if (FAILED(sc))
            {
                GlobalFree(hg32);
            }
        }

        WOWGlobalUnlock16( hg16 );
    }

    if (SUCCEEDED(sc))
    {
        *phg32 = hg32;
    }

    thkDebugOut((DEB_ITRACE, "Out ConvertMfPict1632 => 0x%08lX, 0x%p\n",
                 sc, *phg32));

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   ConvertMfPict3216, public
//
//  Synopsis:   Converts a 32-bit METAFILEPICT to 16-bit
//
//  Arguments:  [pti] - Thunk info
//              [hg32] - 32-bit HGLOBAL containing METAFILEPICT
//              [phg16] - 16-bit HGLOBAL return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [phg16]
//
//  History:    24-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

 SCODE ConvertMfPict3216(THUNKINFO *pti,
                               HGLOBAL hg32,
                               HMEM16 *phg16)
{
    SCODE sc;
    VPVOID vpmfp16;
    METAFILEPICT16 UNALIGNED *pmfp16;
    METAFILEPICT *pmfp32;
    DWORD dwSize;
    HMEM16 hg16;
#if DBG == 1
    BOOL fSaveToFile = FALSE;
#endif

    thkDebugOut((DEB_ITRACE, "In  ConvertMfPict3216(%p, 0x%p, %p)\n",
                 pti, hg32, phg16));

    *phg16 = 0;
    sc = S_OK;

    dwSize = (DWORD) GlobalSize(hg32);
    pmfp32 = (METAFILEPICT *)GlobalLock(hg32);
    if (dwSize == 0 || dwSize < sizeof(METAFILEPICT) || pmfp32 == NULL)
    {
        sc = E_INVALIDARG;
    }
    else
    {
        vpmfp16 = WOWGlobalAllocLock16(GMEM_MOVEABLE | GMEM_DDESHARE,
                                       sizeof(METAFILEPICT16), &hg16);
        if (vpmfp16 == 0)
        {
            sc = E_OUTOFMEMORY;
        }
        else
        {
            pmfp16 = FIXVDMPTR(vpmfp16, METAFILEPICT16);
            if ( pmfp16 != NULL )
            {
                pmfp16->mm = (SHORT)pmfp32->mm;
                pmfp16->xExt = ClampLongToShort(pmfp32->xExt);
                pmfp16->yExt = ClampLongToShort(pmfp32->yExt);
                pmfp16->hMF = HMETAFILE_16(pmfp32->hMF);

                thkDebugOut((DEB_ARGS, "3216    METAFILEPICT: "
                         "{%d, %d, %d, 0x%p} -> {%d, %d, %d, 0x%4x}\n",
                         pmfp32->mm, pmfp32->xExt, pmfp32->yExt, pmfp32->hMF,
                         pmfp16->mm, pmfp16->xExt, pmfp16->yExt, pmfp16->hMF));

                RELVDMPTR(vpmfp16);

#if DBG == 1
                if (fSaveToFile)
                {
                    HMETAFILE hmf;

                    hmf = CopyMetaFile(pmfp32->hMF, __TEXT("thkmf.wmf"));
                    if (hmf != NULL)
                    {
                        DeleteMetaFile(hmf);
                    }
                }
#endif
            }
            else
            {
                sc = E_UNEXPECTED;
            }

            WOWGlobalUnlock16(hg16);

            if (FAILED(sc))
            {
                WOWGlobalFree16(hg16);
            }
        }

        GlobalUnlock(hg32);
    }

    if (SUCCEEDED(sc))
    {
        *phg16 = hg16;
    }

    thkDebugOut((DEB_ITRACE, "Out ConvertMfPict3216 => 0x%08lX, 0x%04X\n",
                 sc, *phg16));

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   ConvertObjDesc1632, public
//
//  Synopsis:   Converts an OBJECTDESCRIPTOR structure
//
//  Arguments:  [pti] - THUNKINFO
//              [hg16] - HGLOBAL containing structure
//              [phg32] - Output HGLOBAL
//
//  Returns:    Appropriate status code
//
//  Modifies:   [phg32]
//
//  History:    04-May-94       DrewB   Created
//
//----------------------------------------------------------------------------

SCODE ConvertObjDesc1632(THUNKINFO *pti,
                         HMEM16 hg16,
                         HGLOBAL *phg32)
{
    SCODE sc;
    VPVOID vp16;
    HGLOBAL hg32;
    DWORD dwSize;
    OBJECTDESCRIPTOR UNALIGNED *pod16;
    OBJECTDESCRIPTOR *pod32;
    char *pszFutn, *pszSoc;
    UINT cchFutn, cchSoc;
    UINT cbOffset;

    sc = S_OK;

	*phg32 = NULL;

    vp16 = WOWGlobalLock16(hg16);
    if ( vp16 == 0 )
    {
        return E_INVALIDARG;
    }

    pszFutn = NULL;
    pszSoc = NULL;

    pod16 = (OBJECTDESCRIPTOR UNALIGNED *)
        GetReadPtr16(pti, vp16, sizeof(OBJECTDESCRIPTOR));
    if (pod16 == NULL)
    {
        sc = pti->scResult;
        goto EH_Unlock;
    }

    dwSize = sizeof(OBJECTDESCRIPTOR);

    if (pod16->dwFullUserTypeName > 0)
    {
        pszFutn = (char *)GetStringPtr16(pti, vp16+pod16->dwFullUserTypeName,
                                         CCHMAXSTRING, &cchFutn);
        if (pszFutn == NULL)
        {
            sc = pti->scResult;
            goto EH_Unlock;
        }

        dwSize += cchFutn*sizeof(WCHAR);
    }

    if (pod16->dwSrcOfCopy > 0)
    {
        pszSoc = (char *)GetStringPtr16(pti, vp16+pod16->dwSrcOfCopy,
                                        CCHMAXSTRING, &cchSoc);
        if (pszSoc == NULL)
        {
            sc = pti->scResult;
            goto EH_Unlock;
        }

        dwSize += cchSoc*sizeof(WCHAR);
    }

    hg32 = GlobalAlloc(GMEM_MOVEABLE, dwSize);
    if ( hg32 == 0 )
    {
        sc = E_OUTOFMEMORY;
        goto EH_Unlock;
    }

    pod32 = (OBJECTDESCRIPTOR *)GlobalLock(hg32);
    memcpy(pod32, pod16, sizeof(OBJECTDESCRIPTOR));
    pod32->cbSize = dwSize;

    cbOffset = sizeof(OBJECTDESCRIPTOR);

    if (pod16->dwFullUserTypeName > 0)
    {
        if (MultiByteToWideChar(AreFileApisANSI() ? CP_ACP : CP_OEMCP,
                                0, pszFutn, cchFutn,
                                (WCHAR *)((BYTE *)pod32+cbOffset),
                                cchFutn) == 0)
        {
            sc = E_UNEXPECTED;
            goto EH_Free;
        }

        pod32->dwFullUserTypeName = cbOffset;
        cbOffset += cchFutn*sizeof(WCHAR);
    }

    if (pod16->dwSrcOfCopy > 0)
    {
        if (MultiByteToWideChar(AreFileApisANSI() ? CP_ACP : CP_OEMCP,
                                0, pszSoc, cchSoc,
                                (WCHAR *)((BYTE *)pod32+cbOffset),
                                cchSoc)  == 0)
        {
            sc = E_UNEXPECTED;
            goto EH_Free;
        }

        pod32->dwSrcOfCopy = cbOffset;
        cbOffset += cchFutn*sizeof(WCHAR);
    }

#if DBG == 1
    WCHAR *pwcsFutn, *pwcsSoc;
    if (pod32->dwFullUserTypeName > 0)
    {
        pwcsFutn = (WCHAR *)((BYTE *)pod32+pod32->dwFullUserTypeName);
    }
    else
    {
        pwcsFutn = NULL;
    }
    if (pod32->dwSrcOfCopy > 0)
    {
        pwcsSoc = (WCHAR *)((BYTE *)pod32+pod32->dwSrcOfCopy);
    }
    else
    {
        pwcsSoc = NULL;
    }
    thkDebugOut((DEB_ARGS, "1632    OBJECTDESCRIPTOR: "
                 "{%d, ..., \"%ws\" (%s), \"%ws\" (%s)} %p -> %p\n",
                 pod32->cbSize, pwcsFutn, pszFutn, pwcsSoc, pszSoc,
                 vp16, pod32));
#endif

    GlobalUnlock(hg32);

    *phg32 = hg32;

 EH_Unlock:
    if (pszFutn != NULL)
    {
        WOWRELVDMPTR(vp16+pod16->dwFullUserTypeName);
    }
    if (pszSoc != NULL)
    {
        WOWRELVDMPTR(vp16+pod16->dwSrcOfCopy);
    }
    if (pod16 != NULL)
    {
        WOWRELVDMPTR(vp16);
    }

    WOWGlobalUnlock16(hg16);

    return sc;

 EH_Free:
    GlobalUnlock(hg32);
    GlobalFree(hg32);
    goto EH_Unlock;
}

//+---------------------------------------------------------------------------
//
//  Function:   ConvertObjDesc3216, public
//
//  Synopsis:   Converts an OBJECTDESCRIPTOR structure
//
//  Arguments:  [pti] - THUNKINFO
//              [hg32] - HGLOBAL containing structure
//              [phg16] - Output HGLOBAL
//
//  Returns:    Appropriate status code
//
//  Modifies:   [phg16]
//
//  History:    04-May-94       DrewB   Created
//
//----------------------------------------------------------------------------

SCODE ConvertObjDesc3216(THUNKINFO *pti,
                         HGLOBAL hg32,
                         HMEM16 *phg16)
{
    SCODE sc;
    VPVOID vp16;
    HMEM16 hg16;
    DWORD dwSize;
    OBJECTDESCRIPTOR UNALIGNED *pod16;
    OBJECTDESCRIPTOR *pod32;
    WCHAR *pwcsFutn, *pwcsSoc;
    UINT cchFutn, cchSoc;
    UINT cbOffset;

    sc = S_OK;

    pod32 = (OBJECTDESCRIPTOR *)GlobalLock(hg32);
    if ( pod32 == 0 )
    {
        return E_INVALIDARG;
    }

    if (IsBadReadPtr(pod32, sizeof(OBJECTDESCRIPTOR)))
    {
        sc = E_INVALIDARG;
        goto EH_Unlock;
    }

    dwSize = sizeof(OBJECTDESCRIPTOR);

    pwcsFutn = NULL;
    if (pod32->dwFullUserTypeName > 0)
    {
        pwcsFutn = (WCHAR *)((BYTE *)pod32+pod32->dwFullUserTypeName);
        if (IsBadStringPtrW(pwcsFutn, CCHMAXSTRING))
        {
            sc = E_INVALIDARG;
            goto EH_Unlock;
        }

        cchFutn = lstrlenW(pwcsFutn)+1;
        dwSize += cchFutn*2;
    }

    pwcsSoc = NULL;
    if (pod32->dwSrcOfCopy > 0)
    {
        pwcsSoc = (WCHAR *)((BYTE *)pod32+pod32->dwSrcOfCopy);
        if (IsBadStringPtrW(pwcsSoc, CCHMAXSTRING))
        {
            sc = E_INVALIDARG;
            goto EH_Unlock;
        }

        cchSoc = lstrlenW(pwcsSoc)+1;
        dwSize += cchSoc*2;
    }

    vp16 = WOWGlobalAllocLock16(GMEM_MOVEABLE, dwSize, &hg16);
    if ( vp16 == 0 )
    {
        sc = E_OUTOFMEMORY;
        goto EH_Unlock;
    }

    pod16 = FIXVDMPTR(vp16, OBJECTDESCRIPTOR);
    memcpy(pod16, pod32, sizeof(OBJECTDESCRIPTOR));
    pod16->cbSize = dwSize;

    cbOffset = sizeof(OBJECTDESCRIPTOR);

    if (pod32->dwFullUserTypeName > 0)
    {
        if (WideCharToMultiByte(AreFileApisANSI() ? CP_ACP : CP_OEMCP,
                                0, pwcsFutn, cchFutn,
                                (char *)pod16+cbOffset, 2 * cchFutn,
                                NULL, NULL) == 0)
        {
            sc = E_UNEXPECTED;
            goto EH_Free;
        }

        pod16->dwFullUserTypeName = cbOffset;
        cbOffset += cchFutn*2;
    }

    if (pod32->dwSrcOfCopy > 0)
    {
        if (WideCharToMultiByte(AreFileApisANSI() ? CP_ACP : CP_OEMCP,
                                0, pwcsSoc, cchSoc,
                                (char *)pod16+cbOffset, 2 * cchSoc,
                                NULL, NULL) == 0)
        {
            sc = E_UNEXPECTED;
            goto EH_Free;
        }

        pod16->dwSrcOfCopy = cbOffset;
        cbOffset += cchFutn*2;
    }

#if DBG == 1
    char *pszFutn, *pszSoc;
    if (pod16->dwFullUserTypeName > 0)
    {
        pszFutn = (char *)((BYTE *)pod16+pod16->dwFullUserTypeName);
    }
    else
    {
        pszFutn = NULL;
    }
    if (pod16->dwSrcOfCopy > 0)
    {
        pszSoc = (char *)((BYTE *)pod16+pod16->dwSrcOfCopy);
    }
    else
    {
        pszSoc = NULL;
    }
    thkDebugOut((DEB_ARGS, "3216    OBJECTDESCRIPTOR: "
                 "{%d, ..., \"%s\" (%ws), \"%s\" (%ws)} %p -> %p\n",
                 pod16->cbSize, pszFutn, pwcsFutn, pszSoc, pwcsSoc,
                 pod32, vp16));
#endif

    RELVDMPTR(vp16);

    WOWGlobalUnlock16(hg16);

    *phg16 = hg16;

 EH_Unlock:
    GlobalUnlock(hg32);

    return sc;

 EH_Free:
    WOWGlobalUnlockFree16(vp16);
    goto EH_Unlock;
}

//+---------------------------------------------------------------------------
//
//  Class:      CSm32ReleaseHandler (srh)
//
//  Purpose:    Provides punkForRelease for 16->32 STGMEDIUM conversion
//
//  Interface:  IUnknown
//
//  History:    24-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

class CSm32ReleaseHandler : public IUnknown
{
public:
    CSm32ReleaseHandler(void)
    {
        _vpvForRelease = NULL;
    }
    ~CSm32ReleaseHandler(void)
    {
    }

    void Init(CThkMgr *pThkMgr,
              STGMEDIUM UNALIGNED *psm16,
              STGMEDIUM *psm32,
              VPVOID vpvForRelease,
              CLIPFORMAT cfFormat)
    {
        // Unfortunately, the MIPS compiler is not smart enough
        // to do the right thing if we just declare psm16 as UNALIGNED -- it
        // doesn't recognize that each member of the structure is also
        // unaligned when it does the structure copy.  So...to make
        // sure we don't generate an alignment fault, we just copy each
        // member of the structure directly.

        _sm16.tymed          = psm16->tymed;
        switch(_sm16.tymed) {
        case TYMED_HGLOBAL:
            _sm16.hGlobal = psm16->hGlobal;
        case TYMED_FILE:
            _sm16.lpszFileName = psm16->lpszFileName;
        case TYMED_ISTREAM:
            _sm16.pstm = psm16->pstm;
        case TYMED_ISTORAGE:
            _sm16.pstg = psm16->pstg;
        }
        _sm16.pUnkForRelease = psm16->pUnkForRelease;
        _sm32 = *psm32;
        _vpvForRelease = vpvForRelease;
        _cReferences = 1;
        _cfFormat = cfFormat;
        _pThkMgr = pThkMgr;
    }

    STDMETHOD(QueryInterface)(REFIID riid, void **ppv)
    {
        if ( IsEqualIID(riid,IID_IUnknown) )
        {
            *ppv = this;
            AddRef();
            return NOERROR;
        }
        else
        {
            thkDebugOut((DEB_WARN, "Not a QI for IUnknown\n"));
            *ppv = NULL;
            return E_NOINTERFACE;
        }
    }
    STDMETHOD_(ULONG, AddRef)(void)
    {
        return InterlockedIncrement(&_cReferences);
    }
    STDMETHOD_(ULONG, Release)(void);

    void CallFailed() {
        _vpvForRelease = NULL;
    }

private:
    STGMEDIUM _sm16;
    STGMEDIUM _sm32;
    VPVOID _vpvForRelease;
    CLIPFORMAT _cfFormat;
    CThkMgr *_pThkMgr;
    LONG _cReferences;
};

//+---------------------------------------------------------------------------
//
//  Member:     CSm32ReleaseHandler::Release, public
//
//  Synopsis:   Frees resources for the 32-bit copy and then
//              passes the ReleaseStgMedium on to 16-bits
//
//  Returns:    Ref count
//
//  History:    24-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

STDMETHODIMP_(ULONG) CSm32ReleaseHandler::Release(void)
{
    STGMEDIUM UNALIGNED *psm16;
    VPVOID vpvunk;
    STGMEDIUM *psm32;
    LONG lRet;
    SCODE sc;
    BOOL fCopy;
    DWORD dwSize;

    lRet = InterlockedDecrement(&_cReferences);
    if (lRet != 0) {
        return lRet;
    }
    
    if(_vpvForRelease) {
        // This is the last release on ReleaseHandler
        // Cleanup 32-bit STGMEDIUM after copying it to 
        // 16-bit STGMEDIUM.
        fCopy = TRUE;
    }
    else {
        // The current call failed.
        // As the fields in the 16-bit STGMEDIUM are not longer valid,
        // cleanup the 32-bit STGMEDIUM without copying it to
        // 16-bit STGMEDIUM
        fCopy = FALSE;
    }

    psm16 = &_sm16;
    psm32 = &_sm32;

    switch(psm32->tymed)
    {
    case TYMED_HGLOBAL:
        // Assumption that OBJECTDESCRIPTOR does not need copyback
        if (fCopy && !OBJDESC_CF(_cfFormat))
        {
            // Do we ever need to do this?
            // Is it valid to rely on the contents of the HGLOBAL
            // at release time?

            // Is this the right time to copy back?

            Assert(NULL != psm32->hGlobal);

            WOWGlobalLockSize16((HMEM16)psm16->hGlobal, &dwSize);
            WOWGlobalUnlock16((HMEM16)psm16->hGlobal);

            sc = ConvertHGlobal3216(NULL, psm32->hGlobal, 0,
                                    (HMEM16 *)&psm16->hGlobal, &dwSize);
            // What happens on errors?
            thkAssert(SUCCEEDED(sc));
        }

        GlobalFree(psm32->hGlobal);
        psm32->hGlobal = NULL;
        break;

    case TYMED_MFPICT:
        //  Chicago uses the same GDI handles for both 32bit and 16bit worlds.
        //  Don't delete the handle after a copy since Chicago doesn't actually
        //  copy the handle.
#if !defined(_CHICAGO_)
        METAFILEPICT *pmfp32;

        pmfp32 = (METAFILEPICT *)GlobalLock(psm32->hGlobal);
        DeleteMetaFile(pmfp32->hMF);
        GlobalUnlock(psm32->hGlobal);
#endif
        GlobalFree(psm32->hGlobal);
        break;

    case TYMED_FILE:
        // 32-bit handled by ReleaseStgMedium
        // Clean up 16-bit ourselves
#ifdef SM_FREE_16BIT_FILENAME
        if(fCopy) {
            // 16-bit OLE did not free the filename, so we can't
            // either.  This may lead to memory leaks, but there's not
            // really anything we can do about it
            TaskFree16((VPVOID)psm16->lpszFileName);
        }
#endif
        break;

    case TYMED_ISTREAM:
        if(fCopy) {
            // The proxy to the 16-bit stream interface was released by
            // ReleaseStgMedium
            // Release the reference kept on the 16-bit stream interface
            ReleaseOnObj16((VPVOID) psm16->pstm);
        }
        break;

    case TYMED_ISTORAGE:
        if(fCopy) {
            // The proxy to the 16-bit storage interface was released by
            // ReleaseStgMedium
            // Release the reference kept on the 16-bit storage interface
            ReleaseOnObj16((VPVOID) psm16->pstg);
        }
        break;

    case TYMED_GDI:
    case TYMED_NULL:
        // Nothing to release
        break;

    default:
        thkAssert(!"Unknown tymed in CSm32ReleaseHandler::Release");
        break;
    }

    // Call Release on the 16-bit vpvForRelease
    if(_vpvForRelease) {
        ReleaseOnObj16(_vpvForRelease);
        _vpvForRelease = NULL;
    }

    // Clean up this
    delete this;

    return 0;
}

//+---------------------------------------------------------------------------
//
//  Member:     CSm16ReleaseHandler::Init, public
//
//  Synopsis:   Initialize class
//
//  Arguments:  [psm32] - 32-bit STGMEDIUM
//              [psm16] - 16-bit STGMEDIUM
//              [vpvUnkForRelease] - Object for punkForRelease
//              [cfFormat] - Clipboard format associated with STGMEDIUM
//
//  History:    24-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

void CSm16ReleaseHandler::Init(IUnknown *pThkMgr,
                               STGMEDIUM *psm32,
                               STGMEDIUM UNALIGNED *psm16,
                               IUnknown *punkForRelease,
                               CLIPFORMAT cfFormat)
{
    _avpfnVtbl = gdata16Data.avpfnSm16ReleaseHandlerVtbl;
    _sm32 = *psm32;

    // Unfortunately, the MIPS compiler is not smart enough
    // to do the right thing if we just (ony) declare psm16 as UNALIGNED,
    // it doesn't recognize that each member of the structure is also
    // unaligned when it does the structure copy.  So...to make
    // sure we don't generate an alignment fault, we just copy each
    // member of the structure directly.

    _sm16.tymed          = psm16->tymed;
    _sm16.hGlobal        = psm16->hGlobal;
    _sm16.pstm           = psm16->pstm;
    _sm16.pstg           = psm16->pstg;
    _sm16.pUnkForRelease = psm16->pUnkForRelease;

    _punkForRelease = punkForRelease;
    _cReferences = 1;
    _cfFormat = cfFormat;
    _pUnkThkMgr = pThkMgr;
}

//+---------------------------------------------------------------------------
//
//  Function:   CSm16ReleaseHandler_Release32, public
//
//  Synopsis:   Handles 32-bit portion of cleaning up STGMEDIUMs for
//              punkForRelease
//
//  Arguments:  [psrh] - this
//              [dw1]
//              [dw2]
//
//  Returns:    punkForRelease->Release()
//
//  History:    24-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

STDAPI_(DWORD) CSm16ReleaseHandler_Release32(CSm16ReleaseHandler *psrh,
                                             DWORD dw1,
                                             DWORD dw2)
{
    STGMEDIUM UNALIGNED *psm16;
    STGMEDIUM *psm32;
    DWORD dwSize;
    SCODE sc;
    BOOL fCopy;

    if(psrh->_punkForRelease) {
        // This is the last release on ReleaseHandler
        // Cleanup 16-bit STGMEDIUM after copying it to 
        // 32-bit STGMEDIUM.
        fCopy = TRUE;
    }
    else {
        // The current call failed.
        // As the fields in the 32-bit STGMEDIUM are not longer valid,
        // cleanup the 16-bit STGMEDIUM without copying it to
        // 32-bit STGMEDIUM
        fCopy = FALSE;
    }

    psm16 = &psrh->_sm16;
    psm32 = &psrh->_sm32;
    switch(psm32->tymed)
    {
    case TYMED_FILE:
        // 16-bit code cleaned up the 16-bit name,
        // now clean up the 32-bit name
        if (fCopy) {
            TaskFree32(psm32->lpszFileName);
        }
        break;

    case TYMED_HGLOBAL:
        //  Assumption that OBJECTDESCRIPTOR does not need copyback
        if(fCopy && !OBJDESC_CF(psrh->_cfFormat))
        {
            // Do we ever need to do this?
            // Copy data back and free global memory

            dwSize = (DWORD) GlobalSize(psm32->hGlobal);

            sc = ConvertHGlobal1632(NULL, (HMEM16)psm16->hGlobal, 0,
                                    &psm32->hGlobal, &dwSize);
            // What happens on errors?
            thkAssert(SUCCEEDED(sc));
        }

        WOWGlobalFree16((HMEM16)psm16->hGlobal);
        break;

    case TYMED_MFPICT:
        // Untouched in this case
        break;

    case TYMED_ISTREAM:
        if(fCopy) {
            // The proxy to the 32-bit stream interface was released by
            // ReleaseStgMedium
            // Release the reference kept on the 32-bit stream interface
            psm32->pstm->Release();
        }
        break;

    case TYMED_ISTORAGE:
        if(fCopy) {
            // The proxy to the 32-bit stream interface was released by
            // ReleaseStgMedium
            // Release the reference kept on the 32-bit stream interface
            psm32->pstg->Release();
        }
        break;

    case TYMED_GDI:
    case TYMED_NULL:
        // Nothing to release
        break;

    default:
        thkAssert(!"Unknown tymed in ReleaseStgMedium32");
        break;
    }

    // Call Release on the 32-bit punkForRelease
    if(fCopy) {
        psrh->_punkForRelease->Release();
        psrh->_punkForRelease = NULL;
    }

    return 0;
}

//+---------------------------------------------------------------------------
//
//  Function:   ConvertStgMed1632, public
//
//  Synopsis:   Converts a 16-bit STGMEDIUM to 32-bits
//
//  Arguments:  [pti] - Thunk info
//              [vpsm16] - VDM pointer to 16-bit STGMEDIUM
//              [psm32] - 32-bit STGMEDIUM to fill in
//              [pfe] - FORMATETC paired with STGMEDIUM or NULL
//              [pdwSize] - Size return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [pdwSize]
//
//  History:    24-Apr-94       DrewB   Created
//
//  Notes:      [pdwSize] is only set for TYMED_HGLOBAL
//
//----------------------------------------------------------------------------

SCODE ConvertStgMed1632(THUNKINFO *pti,
                        VPVOID vpsm16,
                        STGMEDIUM *psm32,
                        FORMATETC *pfe,
                        BOOL fPassingOwnershipIn,
                        DWORD *pdwSize)
{
    SCODE sc;
    STGMEDIUM UNALIGNED *psm16;
    CSm32ReleaseHandler *psrh;
    IUnknown *punkForRelease;
    VPVOID vpvUnk, vpvForRelease;
    HMEM16 hmem16;
    HGDIOBJ hGDI = NULL;
    THKSTATE thkstateSaved;

    psm16 = (STGMEDIUM UNALIGNED *)
        GetReadPtr16(pti, vpsm16, sizeof(STGMEDIUM));
    if (psm16 == NULL)
    {
        return pti->scResult;
    }

    sc = S_OK;

    psm32->tymed = psm16->tymed;

    vpvForRelease = (VPVOID)psm16->pUnkForRelease;
    WOWRELVDMPTR(vpsm16);

    if(vpvForRelease) {
        // Create the 32-bit punkForRelease
        thkDebugOut((DEB_WARN, "pUnkForRelease present in StgMedium1632\n"));
        psrh = new CSm32ReleaseHandler;
        if(psrh == NULL)
            return E_OUTOFMEMORY;
    }
    else {
        psrh = NULL;
    }
    psm32->pUnkForRelease = psrh;

    psm16 = FIXVDMPTR(vpsm16, STGMEDIUM);

    // Word 6 insists on treating BITMAPs as HGLOBALS, which is bogus.
    // If this is the case, just patch the tymed to the correct value

    if (pfe != NULL)
    {
        if( (pfe->cfFormat == CF_BITMAP || pfe->cfFormat == CF_PALETTE ) &&
            psm16->tymed == TYMED_HGLOBAL )
        {
            DWORD dw = TlsThkGetAppCompatFlags();

            // if we are in Word 6, then hack the tymed so we thunk the
            // bitmaps as GDI objects

            if( (dw & OACF_USEGDI ) )
            {
                DWORD dwType;

                hGDI = HBITMAP_32((HBITMAP16)psm16->hBitmap);

                // make sure HGDI is either a bitmap or palette

                dwType = GetObjectType(hGDI);
                if( (pfe->cfFormat == CF_BITMAP && dwType == OBJ_BITMAP) ||
                    (pfe->cfFormat == CF_PALETTE && dwType == OBJ_PAL) )
                {
                    psm16->tymed = TYMED_GDI;
                }
                else
                {
                    thkDebugOut((DEB_WARN,
                        "WARNING! invalid bitmap or palette!\n"));
                    hGDI = NULL;
                }
            }
            else
            {
                thkDebugOut((DEB_WARN, "WARNING!  App trying to transfer a "
                    "bitmap or palette on an HGLOBAL\n"));
            }
        }
    }

    switch( psm16->tymed )
    {
    case TYMED_HGLOBAL:
        hmem16 = (HMEM16)psm16->hGlobal;
        RELVDMPTR(vpsm16);

        if (pfe && OBJDESC_CF(pfe->cfFormat))
        {
            sc = ConvertObjDesc1632(pti, hmem16, &psm32->hGlobal);
        }
#if !defined(_CHICAGO_)

        else if (pfe && pfe->cfFormat == CF_HDROP)
        {
            // fix for mapi forms
            // thunk CF_HDROP passed as HGLOBAL format
            sc = ConvertHDrop1632(hmem16, &psm32->hGlobal);
        }

#endif
        else
        {
            psm32->hGlobal = 0;
            sc = ConvertHGlobal1632(pti, hmem16, THOP_INOUT,
                                    &psm32->hGlobal, pdwSize);
        }
        break;

    case TYMED_MFPICT:
        hmem16 = (HMEM16)psm16->hGlobal;
        RELVDMPTR(vpsm16);

        sc = ConvertMfPict1632(pti, hmem16, &psm32->hGlobal);
        break;

    case TYMED_FILE:
        psm32->lpszFileName =
            Convert_VPSTR_to_LPOLESTR( pti,
                                       (VPVOID)psm16->lpszFileName,
                                       NULL, 0 );
        if (psm32->lpszFileName == NULL)
        {
            sc = pti->scResult;
        }
        else
        {
#if DBG == 1
            thkDebugOut((DEB_ARGS, "1632    TYMED_FILE: '%ws' (%s)\n",
                         psm32->lpszFileName,
                         WOWFIXVDMPTR((VPVOID)psm16->lpszFileName, 0)));
            WOWRELVDMPTR((VPVOID)psm16->lpszFileName);
#endif
        }
        RELVDMPTR(vpsm16);
        break;

    case TYMED_ISTREAM:
        vpvUnk = (VPVOID)psm16->pstm;
        RELVDMPTR(vpsm16);

        psm32->pstm =
            (LPSTREAM)pti->pThkMgr->FindProxy3216(NULL, vpvUnk, NULL,
                                                  INDEX_IIDIDX(THI_IStream), 
                                                  FALSE, NULL);
        if(psm32->pstm) {
            thkDebugOut((DEB_ARGS, "1632    TYMED_ISTREAM: %p -> %p\n",
                         vpvUnk, psm32->pstm));
        }
        else {
            sc = E_OUTOFMEMORY;
        }

        break;

    case TYMED_ISTORAGE:
        vpvUnk = (VPVOID)psm16->pstg;
        RELVDMPTR(vpsm16);

        psm32->pstg =
            (LPSTORAGE)pti->pThkMgr->FindProxy3216(NULL, vpvUnk, NULL,
                                                   INDEX_IIDIDX(THI_IStorage),
                                                   FALSE, NULL);
        if(psm32->pstg) {
            thkDebugOut((DEB_ARGS, "1632    TYMED_ISTORAGE: %p -> %p\n",
                         vpvUnk, psm32->pstg));
        }
        else {
            sc = E_OUTOFMEMORY;
        }

        break;

    case TYMED_GDI:
        // if we're in Word6, then we may have already converted the bitmap
        // or palette handle
        if( hGDI == NULL )
        {
            psm32->hBitmap = HBITMAP_32((HBITMAP16)psm16->hBitmap);
        }
        else
        {
            psm32->hBitmap = (HBITMAP)hGDI;
        }

        thkDebugOut((DEB_ARGS, "1632    TYMED_GDI: 0x%04X -> 0x%p\n",
                     psm16->hBitmap, psm32->hBitmap));
        RELVDMPTR(vpsm16);
        break;

    case TYMED_NULL:
        RELVDMPTR(vpsm16);
        break;

    default:
        RELVDMPTR(vpsm16);
        sc = E_INVALIDARG;
        break;
    }

    if (FAILED(sc))
    {
        delete psrh;
    }
    else
    {
        if (psrh)
        {
            CLIPFORMAT cf;

            if (pfe)
            {
                cf = pfe->cfFormat;
            }
            else
            {
                cf = CF_INVALID;
            }
            thkAssert(vpvForRelease);
            psrh->Init(pti->pThkMgr, FIXVDMPTR(vpsm16, STGMEDIUM), psm32,
                       vpvForRelease, cf);
            RELVDMPTR(vpsm16);
        }

#if DBG == 1
        if (pfe)
        {
            thkDebugOut((DEB_ARGS, "1632    STGMEDIUM FORMATETC %p {%d}\n",
                         pfe, pfe->cfFormat));
        }
        thkDebugOut((DEB_ARGS, "1632    STGMEDIUM: %p {%d, %p, ...} -> "
                     "%p {%d, %p, ...}\n", vpsm16, psm16->tymed,
                     psm16->pUnkForRelease, psm32, psm32->tymed,
                     psm32->pUnkForRelease));
#endif
    }

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   CleanStgMed32, public
//
//  Synopsis:   Cleans up a 32-bit STGMEDIUM
//
//  Arguments:  [pti] - Thunk info
//              [psm32] - STGMEDIUM to clean
//              [vpsm16] - Source STGMEDIUM if thunk
//              [dwSize] - Source size if thunk
//              [fIsThunk] - STGMEDIUM was generated by thunking
//              [pfe] - FORMATETC or NULL
//
//  Returns:    Appropriate status code
//
//  History:    24-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

SCODE CleanStgMed32(THUNKINFO *pti,
                    STGMEDIUM *psm32,
                    VPVOID vpsm16,
                    DWORD dwSize,
                    BOOL fIsThunk,
                    FORMATETC *pfe)
{
    SCODE sc;
    STGMEDIUM UNALIGNED *psm16;
    HMEM16 hmem16;
    VPVOID vpvUnk;
    BOOL fCleanup = TRUE;

    thkDebugOut((DEB_ITRACE, "In  CleanStgMed32(%p, %p, %p, %u, %d, %p)\n",
                 pti, psm32, vpsm16, dwSize, fIsThunk, pfe));

    sc = S_OK;

    if(fIsThunk && psm32->pUnkForRelease) {
        // This means that the current call failed
        // Inform the 32-bit punkForRelease created during thunking
        // so that it would cleanup 32-bit STGMEDIUM
        ((CSm32ReleaseHandler *) (psm32->pUnkForRelease))->CallFailed();
        fCleanup = FALSE;
    }

    switch( psm32->tymed )
    {
    case TYMED_HGLOBAL:
        if (fIsThunk &&
            (pfe == NULL || !OBJDESC_CF(pfe->cfFormat)))
        {
            psm16 = FIXVDMPTR(vpsm16, STGMEDIUM);
            hmem16 = (HMEM16)psm16->hGlobal;
            RELVDMPTR(vpsm16);

            Assert(NULL != psm32->hGlobal);

            sc = ConvertHGlobal3216(pti, psm32->hGlobal, 0,
                                    &hmem16, &dwSize);
            psm16 = FIXVDMPTR(vpsm16, STGMEDIUM);
            psm16->hGlobal = (HGLOBAL)hmem16;
            RELVDMPTR(vpsm16);
        }

        if(fCleanup) {
            GlobalFree( psm32->hGlobal );
            psm32->hGlobal = NULL;
        }
        break;

    case TYMED_MFPICT:
        //  Chicago uses the same GDI handles for both 32bit and 16bit worlds.
        //  Don't delete the handle after a copy since Chicago doesn't actually
        //  copy the handle.
        if(fCleanup) {
#if !defined(_CHICAGO_)
            // Can't modify an MFPICT
            METAFILEPICT *pmfp32;

            pmfp32 = (METAFILEPICT *)GlobalLock(psm32->hGlobal);
            DeleteMetaFile(pmfp32->hMF);
            GlobalUnlock(psm32->hGlobal);
#endif
            GlobalFree(psm32->hGlobal);
        }
        break;

    case TYMED_FILE:
        Convert_VPSTR_to_LPOLESTR_free( NULL, psm32->lpszFileName );
#ifdef SM_FREE_16BIT_FILENAME
        if(fCleanup) {
            // 16-bit OLE did not free the filename, so we can't
            // either.  This may lead to memory leaks, but there's not
            // really anything we can do about it
            TaskFree16((VPVOID)psm16->lpszFileName);
        }
#endif
        break;

    case TYMED_ISTREAM:
        if(fIsThunk) {
            // Release the 32-bit stream interface
            psm32->pstm->Release();
        }
        break;

    case TYMED_ISTORAGE:
        if(fIsThunk) {
            // Release the 32-bit storage interface
            psm32->pstg->Release();
        }
        break;

    case TYMED_GDI:
        //
        // No unthunking needed
        //
        break;

    case TYMED_NULL:
        break;

    default:
        // Ignore, this case is handled on input
        thkAssert(!"STGMEDIUM with invalid tymed");
        break;
    }

    if(fIsThunk && psm32->pUnkForRelease) {
        // Release the 32-bit STGMEDIUM pUnkForRelease.
        // 32-bit STGMEDIUM would be cleaned up after the last release
        psm32->pUnkForRelease->Release();
    }

    thkDebugOut((DEB_ITRACE, "Out CleanStgMed32 => 0x%08lX\n", sc));

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   ConvertStgMed3216, public
//
//  Synopsis:   Converts a 32-bit STGMEDIUM to 16-bits
//
//  Arguments:  [pti] - Thunk info
//              [psm32] - 32-bit STGMEDIUM
//              [vpsm16] - VDM pointer to 16-bit STGMEDIUM
//              [pfe] - FORMATETC paired with STGMEDIUM or NULL
//              [pdwSize] - Size return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [pdwSize]
//
//  History:    24-Apr-94       DrewB   Created
//
//  Notes:      [pdwSize] is only set for TYMED_HGLOBAL
//
//----------------------------------------------------------------------------

SCODE ConvertStgMed3216(THUNKINFO *pti,
                        STGMEDIUM *psm32,
                        VPVOID vpsm16,
                        FORMATETC *pfe,
                        BOOL fPassingOwnershipIn,
                        DWORD *pdwSize)
{
    SCODE sc;
    STGMEDIUM UNALIGNED *psm16;
    VPVOID vpsrh;
    VPSTR vpstr;
    UINT uiSize;
    VPVOID vpvUnkForRelease;
    VPVOID vpvUnk;
    HMEM16 hmem16;
    THKSTATE thkstateSaved;

    sc = S_OK;

    if(psm32->pUnkForRelease) {
        thkDebugOut((DEB_WARN, "pUnkForRelease present in StgMedium3216\n"));
        vpsrh = WOWGlobalAllocLock16(GMEM_MOVEABLE, sizeof(CSm16ReleaseHandler),
                                     NULL);
        if(vpsrh == 0)
            return E_OUTOFMEMORY;
    }
    else {
        vpsrh = 0;
    }

    psm16 = FIXVDMPTR(vpsm16, STGMEDIUM);
    psm16->tymed = psm32->tymed;
    psm16->pUnkForRelease = (IUnknown *)vpsrh;
    RELVDMPTR(vpsm16);

    switch( psm32->tymed )
    {
    case TYMED_HGLOBAL:
        if (pfe && OBJDESC_CF(pfe->cfFormat))
        {
            sc = ConvertObjDesc3216(pti, psm32->hGlobal, &hmem16);
        }
#if !defined(_CHICAGO_)

        else if (pfe && pfe->cfFormat == CF_HDROP)
        {
            // fix for mapi forms
            sc = ConvertHDrop3216(psm32->hGlobal, &hmem16);
        }

#endif
        else
        {
            hmem16 = 0;
            sc = ConvertHGlobal3216(pti, psm32->hGlobal, THOP_INOUT,
                                    &hmem16, pdwSize);
        }

        psm16 = FIXVDMPTR(vpsm16, STGMEDIUM);
        psm16->hGlobal = (HGLOBAL)hmem16;
        RELVDMPTR(vpsm16);
        break;

    case TYMED_MFPICT:
        sc = ConvertMfPict3216(pti, psm32->hGlobal, &hmem16);

        psm16 = FIXVDMPTR(vpsm16, STGMEDIUM);
        psm16->hGlobal = (HGLOBAL)hmem16;
        RELVDMPTR(vpsm16);
        break;

    case TYMED_FILE:
        uiSize = lstrlenW(psm32->lpszFileName) + 1;
        vpstr = TaskMalloc16( uiSize*2 );
        if ( vpstr == NULL )
        {
            sc = E_OUTOFMEMORY;
        }
        else
        {
            sc = Convert_LPOLESTR_to_VPSTR( psm32->lpszFileName,
                                            vpstr, uiSize, uiSize*2 );
            if (FAILED(sc))
            {
                TaskFree16(vpstr);
            }
            else
            {
                psm16 = FIXVDMPTR(vpsm16, STGMEDIUM);
                psm16->lpszFileName = (LPOLESTR)vpstr;
                RELVDMPTR(vpsm16);

#if DBG == 1
                thkDebugOut((DEB_ARGS, "3216    TYMED_FILE: '%s' (%ws)\n",
                             WOWFIXVDMPTR(vpstr, 0),
                             psm32->lpszFileName));
                WOWRELVDMPTR(vpstr);
#endif
            }
        }
        break;

    case TYMED_ISTREAM:
        vpvUnk = pti->pThkMgr->FindProxy1632(NULL, psm32->pstm, NULL,
                                             INDEX_IIDIDX(THI_IStream),
                                             NULL);
        if (vpvUnk == 0)
        {
            sc = E_OUTOFMEMORY;
        }
        else
        {
            thkDebugOut((DEB_ARGS, "3216    TYMED_ISTREAM: %p -> %p\n",
                         psm32->pstm, vpvUnk));

            psm16 = FIXVDMPTR(vpsm16, STGMEDIUM);
            psm16->pstm = (IStream *)vpvUnk;
            RELVDMPTR(vpsm16);
        }
        break;

    case TYMED_ISTORAGE:
        vpvUnk = pti->pThkMgr->FindProxy1632(NULL, psm32->pstg, NULL,
                                             INDEX_IIDIDX(THI_IStorage),
                                             NULL);
        if (vpvUnk == 0)
        {
            sc = E_OUTOFMEMORY;
        }
        else
        {
            thkDebugOut((DEB_ARGS, "3216    TYMED_ISTORAGE: %p -> %p\n",
                         psm32->pstg, vpvUnk));

            psm16 = FIXVDMPTR(vpsm16, STGMEDIUM);
            psm16->pstg = (IStorage *)vpvUnk;
            RELVDMPTR(vpsm16);
        }
        break;

    case TYMED_GDI:
        psm16 = FIXVDMPTR(vpsm16, STGMEDIUM);
        psm16->hBitmap = (HBITMAP)HBITMAP_16(psm32->hBitmap);
        thkDebugOut((DEB_ARGS, "3216    TYMED_GDI: 0x%p -> 0x%04X\n",
                     psm32->hBitmap, psm16->hBitmap));
        RELVDMPTR(vpsm16);
        break;

    case TYMED_NULL:
        break;

    default:
        sc = E_INVALIDARG;
        break;
    }

    if (FAILED(sc))
    {
        if (vpsrh != 0)
        {
            WOWGlobalUnlockFree16(vpsrh);
        }
    }
    else
    {
        if (vpsrh != 0)
        {
            CSm16ReleaseHandler UNALIGNED *psrh;
            CLIPFORMAT cf;

            if (pfe)
            {
                cf = pfe->cfFormat;
            }
            else
            {
                cf = CF_INVALID;
            }
            thkAssert(psm32->pUnkForRelease);
            psrh = FIXVDMPTR(vpsrh, CSm16ReleaseHandler);
            psrh->Init(pti->pThkMgr, psm32, FIXVDMPTR(vpsm16, STGMEDIUM),
                       psm32->pUnkForRelease, cf);
            RELVDMPTR(vpsrh);
            RELVDMPTR(vpsm16);
        }

#if DBG == 1
        if (pfe)
        {
            thkDebugOut((DEB_ARGS, "3216    STGMEDIUM FORMATETC %p {%d}\n",
                         pfe, pfe->cfFormat));
        }
        thkDebugOut((DEB_ARGS, "3216    STGMEDIUM: %p {%d, %p, ...} -> "
                     "%p {%d, %p, ...}\n", psm32, psm32->tymed,
                     psm32->pUnkForRelease, vpsm16, psm16->tymed,
                     psm16->pUnkForRelease));
#endif
    }

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   CleanStgMed16, public
//
//  Synopsis:   Frees up resources in a 16-bit STGMEDIUM
//
//  Arguments:  [pti] - Thunk info
//              [vpsm16] - STGMEDIUM to clean
//              [psm32] - Source STGMEDIUM if thunk
//              [dwSize] - Source size for thunked HGLOBAL
//              [fIsThunk] - If the STGMEDIUM is a result of thunking
//              [pfe] - FORMATETC or NULL
//
//  Returns:    Appropriate status code
//
//  History:    24-Apr-94       DrewB   Created
//
//----------------------------------------------------------------------------

SCODE CleanStgMed16(THUNKINFO *pti,
                    VPVOID vpsm16,
                    STGMEDIUM *psm32,
                    DWORD dwSize,
                    BOOL fIsThunk,
                    FORMATETC *pfe)
{
    SCODE sc;
    STGMEDIUM UNALIGNED *psm16;
    VPVOID vpvUnk, vpv;
    HMEM16 hmem16;
    BOOL fCleanup = TRUE;

    thkDebugOut((DEB_ITRACE, "In  CleanStgMed16(%p, %p, %p, %u, %d, %p)\n",
                 pti, vpsm16, psm32, dwSize, fIsThunk, pfe));

    sc = S_OK;

    psm16 = FIXVDMPTR(vpsm16, STGMEDIUM);
    vpvUnk = (VPVOID)psm16->pUnkForRelease;
    RELVDMPTR(vpsm16);

    if (fIsThunk && vpvUnk)
    {
        // This means that the current call failed
        // Inform the 32-bit punkForRelease created during thunking
        // so that it would cleanup 32-bit STGMEDIUM
        CSm16ReleaseHandler UNALIGNED *psrh;

        psrh = FIXVDMPTR(vpvUnk, CSm16ReleaseHandler);
        psrh->CallFailed();
        RELVDMPTR(vpvUnk);
        fCleanup = FALSE;
    }

    psm16 = FIXVDMPTR(vpsm16, STGMEDIUM);
    switch( psm16->tymed )
    {
    case TYMED_HGLOBAL:
        hmem16 = (HMEM16)psm16->hGlobal;
        RELVDMPTR(vpsm16);

        if (fIsThunk &&
            (pfe == NULL || !OBJDESC_CF(pfe->cfFormat)))
        {
            sc = ConvertHGlobal1632(pti, hmem16, 0,
                                    &psm32->hGlobal, &dwSize);
        }

        if(fCleanup)
            WOWGlobalFree16(hmem16);

        break;

    case TYMED_MFPICT:
        hmem16 = (HMEM16)psm16->hGlobal;
        RELVDMPTR(vpsm16);

        //  Chicago uses the same GDI handles for both 32bit and 16bit worlds.
        //  Don't delete the handle after a copy since Chicago doesn't actually
        //  copy the handle.
        if(fCleanup) {
#if !defined(_CHICAGO_)
            VPVOID vpvmfp16;
            METAFILEPICT16 *pmfp16;
            HMEM16 hmf16;

            vpvmfp16 = WOWGlobalLock16(hmem16);
            pmfp16 = FIXVDMPTR(vpvmfp16, METAFILEPICT16);
            hmf16 = pmfp16->hMF;
            RELVDMPTR(vpvmfp16);

            // Relies on the fact that a 16-bit metafile is an HGLOBAL
            WOWGlobalFree16(hmf16);

            WOWGlobalUnlockFree16(vpvmfp16);
#else
            WOWGlobalFree16(hmem16);
#endif
        }
        break;

    case TYMED_FILE:
        vpv = (VPVOID)psm16->lpszFileName;
        RELVDMPTR(vpsm16);

        if(fCleanup)
            TaskFree16(vpv);
        break;

    case TYMED_ISTREAM:
        vpv = (VPVOID) psm16->pstm;
        RELVDMPTR(vpsm16);

        if(fIsThunk) {
            // Release the 16-bit stream interface
            ReleaseOnObj16(vpv);
        }
        break;

    case TYMED_ISTORAGE:
        vpv = (VPVOID) psm16->pstg;
        RELVDMPTR(vpsm16);

        if(fIsThunk) {
            // Release the 16-bit storage interface
            ReleaseOnObj16(vpv);
        }
        break;

    case TYMED_GDI:
        RELVDMPTR(vpsm16);

        //
        // No unthunking needed
        //
        break;

    case TYMED_NULL:
        RELVDMPTR(vpsm16);

        break;

    default:
        // Ignore, this case is handled on input
        thkAssert(!"CleanStgMed16 with invalid tymed");
        break;
    }

    if(fIsThunk && vpvUnk) {
        // Release the 16-bit STGMEDIUM vpvForRelease.
        // 16-bit STGMEDIUM would be cleaned up after the last release
        ReleaseOnObj16(vpvUnk);
    }

    thkDebugOut((DEB_ITRACE, "Out CleanStgMed16 => 0x%08lX\n", sc));

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   ConvertFetc1632, public
//
//  Synopsis:   Converts a FORMATETC
//
//  Arguments:  [pti] - Thunk info
//              [vpfe16] - FORMATETC
//              [pfe32] - FORMATETC
//              [fFree] - Free resources as converting
//
//  Returns:    Appropriate status code
//
//  Modifies:   [pfe32]
//
//  History:    14-May-94       DrewB   Created
//				11-Dec-00		DickD	initialize pdv32 (prefix bug 22397)
//
//----------------------------------------------------------------------------

SCODE ConvertFetc1632(THUNKINFO *pti,
                      VPVOID vpfe16,
                      FORMATETC *pfe32,
                      BOOL fFree)
{
    FORMATETC16 UNALIGNED *pfe16;
    VPVOID vpdv16;
    DVTARGETDEVICE *pdv32 = NULL;
    UINT cbSize;
    SCODE sc;

    pfe16 = FIXVDMPTR(vpfe16, FORMATETC16);
    vpdv16 = (VPVOID)pfe16->ptd;
    RELVDMPTR(vpfe16);

    if ( vpdv16 == 0 )
    {
        pdv32 = NULL;
    }
    else
    {
        sc = ConvertDvtd1632(pti, vpdv16, ArTask32, FrTask32,
                             &pdv32, &cbSize);

        if (fFree)
        {
            TaskFree16(vpdv16);
        }

        if (FAILED(sc))
        {
            return sc;
        }
    }

    pfe16 = FIXVDMPTR(vpfe16, FORMATETC16);
    pfe32->cfFormat = pfe16->cfFormat;
    pfe32->ptd      = pdv32;
    pfe32->dwAspect = pfe16->dwAspect;
    pfe32->lindex   = pfe16->lindex;
    pfe32->tymed    = pfe16->tymed;

    thkDebugOut((DEB_ARGS, "1632    FORMATETC: "
                 "%p -> %p {%d, %p (%p), %u, %u, 0x%X}\n",
                 vpfe16, pfe32,
                 pfe32->cfFormat,
                 pfe32->ptd, vpdv16,
                 pfe32->dwAspect,
                 pfe32->lindex,
                 pfe32->tymed));

    RELVDMPTR(vpfe16);

    return S_OK;
}

//+---------------------------------------------------------------------------
//
//  Function:   ConvertFetc3216, public
//
//  Synopsis:   Converts a FORMATETC
//
//  Arguments:  [pti] - Thunk info
//              [pfe32] - FORMATETC
//              [vpfe16] - FORMATETC
//              [fFree] - Free resources as converting
//
//  Returns:    Appropriate status code
//
//  Modifies:   [vpfe16]
//
//  History:    14-May-94       DrewB   Created
//
//----------------------------------------------------------------------------

SCODE ConvertFetc3216(THUNKINFO *pti,
                      FORMATETC *pfe32,
                      VPVOID vpfe16,
                      BOOL fFree)
{
    FORMATETC16 UNALIGNED *pfe16;
    DVTARGETDEVICE *pdv32;
    SCODE sc;
    VPVOID vpdv16;
    UINT cbSize;

    pdv32 = pfe32->ptd;
    if (pdv32 != NULL)
    {
        sc = ConvertDvtd3216(pti, pdv32, ArTask16, FrTask16,
                             &vpdv16, &cbSize);

        if (fFree)
        {
            TaskFree32(pdv32);
        }

        if (FAILED(sc))
        {
            return sc;
        }
    }
    else
    {
        vpdv16 = 0;
    }

    pfe16 = FIXVDMPTR(vpfe16, FORMATETC16);
    pfe16->cfFormat = pfe32->cfFormat;
    pfe16->ptd      = vpdv16;
    pfe16->dwAspect = pfe32->dwAspect;
    pfe16->lindex   = pfe32->lindex;
    pfe16->tymed    = pfe32->tymed;

    thkDebugOut((DEB_ARGS, "3216    FORMATETC: "
                 "%p -> %p {%d, %p (%p), %u, %u, 0x%X}\n",
                 pfe32, vpfe16,
                 pfe16->cfFormat,
                 vpdv16, pdv32,
                 pfe16->dwAspect,
                 pfe16->lindex,
                 pfe16->tymed));

    RELVDMPTR(vpfe16);

    return S_OK;
}

//+---------------------------------------------------------------------------
//
//  Function:   DebugValidateProxy1632, debug public
//
//  Synopsis:   Validates a 16->32 proxy pointer and its memory
//
//  Arguments:  [vpvProxy] - Proxy
//
//  History:    07-Jul-94       DrewB   Created
//
//----------------------------------------------------------------------------

#if DBG == 1
void DebugValidateProxy1632(VPVOID vpvProxy)
{
    THUNK1632OBJ UNALIGNED *pto;
    THUNKINFO ti;

    thkAssert(vpvProxy != 0 && "Invalid proxy pointer");

    pto = (THUNK1632OBJ UNALIGNED *)
        GetReadWritePtr16(&ti, vpvProxy, sizeof(THUNK1632OBJ));
    thkAssert(pto != NULL && "Invalid proxy pointer");

    thkAssert(pto->dwSignature == PSIG1632 && "Dead or invalid proxy!");

    thkAssert(pto->cRefLocal>=0 && "Invalid proxy refcount");
    thkAssert(pto->cRefLocal>=pto->cRef && "Invalid proxy refcount");
    thkAssert((pto->cRefLocal>0 && pto->cRef>0) || (pto->cRef==0 && pto->cRefLocal==0));
    thkAssert(pto->pphHolder && "Proxy without a holder");

    if (!IsValidInterface(pto->punkThis32))
    {
        thkDebugOut((DEB_ERROR, "1632 %p: Invalid proxied object %p\n",
                     vpvProxy, pto->punkThis32));
    }

    WOWRELVDMPTR(vpvProxy);
}
#endif

//+---------------------------------------------------------------------------
//
//  Function:   DebugValidateProxy3216, debug public
//
//  Synopsis:   Validates a 32->16 proxy pointer and its memory
//
//  Arguments:  [pto] - Proxy
//
//  History:    07-Jul-94       DrewB   Created
//
//----------------------------------------------------------------------------

#if DBG == 1
void DebugValidateProxy3216(THUNK3216OBJ *pto)
{
    THUNKINFO ti;

    thkAssert(pto != 0 && "Invalid proxy pointer");

    thkAssert(!IsBadReadPtr(pto, sizeof(THUNK3216OBJ)) &&
              !IsBadWritePtr(pto, sizeof(THUNK3216OBJ)) &&
              "Invalid proxy pointer");

    thkAssert(pto->dwSignature == PSIG3216 && "Dead or invalid proxy!");

    thkAssert(pto->cRefLocal>=0 && "Invalid proxy refcount");
    thkAssert(pto->cRefLocal>=pto->cRef && "Invalid proxy refcount");
    thkAssert((pto->cRefLocal>0 && pto->cRef>0) || (pto->cRef==0 && pto->cRefLocal==0));
    thkAssert(pto->pphHolder && "Proxy without a holder");

    if (!IsValidInterface16(&ti, pto->vpvThis16))
    {
        thkDebugOut((DEB_ERROR, "3216 %p: Invalid proxied object %p\n",
                     pto, pto->vpvThis16));
    }
}
#endif

//+---------------------------------------------------------------------------
//
//  Function:   ClampLongToShort, public
//
//  Synopsis:   Restricts a long value to a short value by clamping
//
//  Arguments:  [l] - Long
//
//  Returns:    Short
//
//  History:    16-Aug-94       DrewB   Created
//
//----------------------------------------------------------------------------

SHORT ClampLongToShort(LONG l)
{
    SHORT s;

    if (l < SHRT_MIN)
    {
        s = SHRT_MIN;
        thkDebugOut((DEB_WARN, "ClampLongToShort: %ld -> %d\n", l, s));
    }
    else if (l > SHRT_MAX)
    {
        s = SHRT_MAX;
        thkDebugOut((DEB_WARN, "ClampLongToShort: %ld -> %d\n", l, s));
    }
    else
    {
        s = (SHORT)l;
    }

    return s;
}

//+---------------------------------------------------------------------------
//
//  Function:   ClampULongToUShort, public
//
//  Synopsis:   Restricts an unsigned long value to an unsigned short value
//              by clamping
//
//  Arguments:  [ul] - Long
//
//  Returns:    UShort
//
//  History:    16-Aug-94       DrewB   Created
//
//----------------------------------------------------------------------------

USHORT ClampULongToUShort(ULONG ul)
{
    USHORT us;

    if (ul > USHRT_MAX)
    {
        us = USHRT_MAX;
        thkDebugOut((DEB_WARN, "ClampULongToUShort: %ld -> %d\n", ul, us));
    }
    else
    {
        us = (USHORT)ul;
    }

    return us;
}

//+---------------------------------------------------------------------------
//
//  Function:   ConvertObjDescriptor
//
//  Synopsis:   Exported API called by WOW to convert ObjectDescriptors to
//              the indicated format.
//
//
//  Arguments:  [hMem] --  Handle to the ObjectDescriptor to convert.
//              [flag] --  Flag indicating which direction the convertion
//              should take place.  Valid values are:
//                          CFOLE_UNICODE_TO_ANSI.
//                          CFOLE_ANSI_TO_UNICODE.
//
//  Returns:    HGLOBAL to the converted ObjectDescriptor,
//              or NULL on failure.
//
//  History:    8-16-94   terryru   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDAPI_(HGLOBAL) ConvertObjDescriptor( HANDLE hMem, UINT flag )
{

   const UINT CFOLE_UNICODE_TO_ANSI = 0;
   const UINT CFOLE_ANSI_TO_UNICODE = 1;

   THUNKINFO ti;
   HGLOBAL hMem32;
   HMEM16  hMem16;

    switch ( flag )
    {
    case CFOLE_UNICODE_TO_ANSI:
        if( FAILED( ConvertObjDesc3216( &ti, (HGLOBAL) hMem, &hMem16 )))
        {
            return (HGLOBAL) NULL;
        }
        else
        {
            return (HGLOBAL)  hMem16;
        }
        break;

    case CFOLE_ANSI_TO_UNICODE:
        if( FAILED( ConvertObjDesc1632( &ti, (HMEM16) hMem, &hMem32 )))
        {
            return (HGLOBAL) NULL;
        }
        else
        {
            return (HGLOBAL) hMem32;
        }
        break;

    default:
        thkAssert(!"ConvertObjDescriptor, Invalid flag");
        break;
    }
    return (HGLOBAL) NULL;
}

#if defined(_CHICAGO_)

//
//  A hack so everyone can build Chicago OLE until I write the thunking
//  library later this week.
//
//  (15-Feb-2000:  I find the fact that this comment is still here amazingly
//                 entertaining - JohnDoty)
//

#define ERR ((char*) -1)

#if DBG==1
int UnicodeToAnsi(LPSTR sz, LPCWSTR pwsz, LONG cb)
{
    int ret;

    ret = WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, pwsz, -1, sz, cb, NULL, NULL);

    thkAssert(ret != 0 && "Lost characters in thk Unicode->Ansi conversion");
    if (ret == 0)
    {
        DebugBreak();
    }

    return ret;
}
#else
#define UnicodeToAnsi(sz,pwsz,cb) WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, pwsz, -1, sz, cb, NULL, NULL)
#endif


#if DBG==1
int AnsiToUnicode(LPWSTR pwsz, LPCSTR sz, LONG cb)
{
    int ret;

    ret = MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, sz, -1, pwsz, cb);

    thkAssert(ret != 0 && "Lost characters in thk Ansi->Unicode conversion");
    if (ret == 0)
    {
        DebugBreak();
    }

    return ret;
}
#else
#define AnsiToUnicode(pwsz,sz,cb) MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, sz, -1, pwsz, cb)
#endif



extern "C"
DWORD
WINAPI
GetShortPathNameX(
    LPCWSTR lpszFullPath,
    LPWSTR  lpszShortPath,
    DWORD   cchBuffer
    )
{
    #ifdef DEBUG_OUTPUT
    OutputDebugString("GetShortPathName\n");
    #endif

    CHAR  szFullPath[MAX_PATH];
    CHAR  szShortBuffer[MAX_PATH];
    DWORD ret;


    UnicodeToAnsi(szFullPath, lpszFullPath, sizeof(szFullPath));

    if (lpszShortPath == NULL)
    {
        ret = GetShortPathNameA(szFullPath, NULL, cchBuffer);
    }
    else
    {
        ret = GetShortPathNameA(szFullPath, szShortBuffer,
                                sizeof(szShortBuffer));

        thkAssert(ret != cchBuffer &&
                  "GetShortPathName - Output buffer too short");
        //
        // Don't convert the buffer if the
        // call to GetShortPathNameA() failed.
        //
        if(0 != ret)
        {
            //
            //  Only convert the actual data, not the whole buffer.
            //
            if (cchBuffer > ret + 1)
                cchBuffer = ret + 1;

            AnsiToUnicode(lpszShortPath, szShortBuffer, cchBuffer);
        }
    }

    return ret;
}

#endif  // _CHICAGO_