/*++

Microsoft Windows
Copyright (c) 1997 - 1999 Microsoft Corporation.  All rights reserved.

File:
    udt.cxx

Abstract:
    Marshalling support for user defined data types.

Author:
    ShannonC    21-Apr-1997

Environment:
    Windows NT and Windows 95.  We do not support DOS and Win16.

Revision History:

--*/
#include <ndrp.h>
#include <oaidl.h>
#include <typegen.h>
#include <interp2.h>

extern USER_MARSHAL_ROUTINE_QUADRUPLE UserMarshalRoutines[3];


class CTypeFactory : public ITypeFactory, public IClassFactory
{
public:
    HRESULT STDMETHODCALLTYPE QueryInterface(
        IN  REFIID riid,
        OUT void **ppv);

    ULONG   STDMETHODCALLTYPE AddRef();

    ULONG   STDMETHODCALLTYPE Release();

    HRESULT STDMETHODCALLTYPE CreateFromTypeInfo(
        IN  ITypeInfo *pTypeInfo,
        IN  REFIID riid,
        OUT IUnknown **ppv);

    HRESULT STDMETHODCALLTYPE CreateInstance(
        IN  IUnknown * pUnkOuter,
        IN  REFIID     riid,
        OUT void    ** ppv);

    HRESULT STDMETHODCALLTYPE LockServer(
	IN  BOOL fLock);

    CTypeFactory();

private:
    long _cRefs;
};

CLSID CLSID_TypeFactory = { /* b5866878-bd99-11d0-b04b-00c04fd91550 */
    0xb5866878,
    0xbd99,
    0x11d0,
    {0xb0, 0x4b, 0x00, 0xc0, 0x4f, 0xd9, 0x15, 0x50}
  };

class CTypeMarshal : public ITypeMarshal, public IMarshal
{
public:
    // IUnknown methods
    HRESULT STDMETHODCALLTYPE QueryInterface(
        IN  REFIID riid,
        OUT void **ppvObject);

    ULONG   STDMETHODCALLTYPE AddRef();

    ULONG   STDMETHODCALLTYPE Release();

    // ITypeMarshal methods
    HRESULT STDMETHODCALLTYPE Size(
        IN  void *  pType,
        IN  DWORD   dwDestContext,
        IN  void  * pvDestContext,
        OUT ULONG * pSize);

    HRESULT STDMETHODCALLTYPE Marshal(
        IN  void *  pType,
        IN  DWORD   dwDestContext,
        IN  void  * pvDestContext,
        IN  ULONG   cbBufferLength,
        OUT BYTE  * pBuffer,
        OUT ULONG * pcbWritten);

    HRESULT STDMETHODCALLTYPE Unmarshal(
        IN  void   * pType,
        IN  DWORD    dwFlags,
        IN  ULONG    cbBufferLength,
        IN  BYTE   * pBuffer,
        OUT ULONG  * pcbRead);

    HRESULT STDMETHODCALLTYPE Free(
        IN void * pType);

    // IMarshal methods.
    HRESULT STDMETHODCALLTYPE GetUnmarshalClass
    (
        IN  REFIID riid,
        IN  void *pv,
        IN  DWORD dwDestContext,
        IN  void *pvDestContext,
        IN  DWORD mshlflags,
        OUT CLSID *pCid
    );

    HRESULT STDMETHODCALLTYPE GetMarshalSizeMax
    (
        IN  REFIID riid,
        IN  void *pv,
        IN  DWORD dwDestContext,
        IN  void *pvDestContext,
        IN  DWORD mshlflags,
        OUT DWORD *pSize
    );

    HRESULT STDMETHODCALLTYPE MarshalInterface
    (
        IN  IStream *pStm,
        IN  REFIID riid,
        IN  void *pv,
        IN  DWORD dwDestContext,
        IN  void *pvDestContext,
        IN  DWORD mshlflags
    );

    HRESULT STDMETHODCALLTYPE UnmarshalInterface
    (
        IN  IStream *pStm,
        IN  REFIID riid,
        OUT void **ppv
    );

    HRESULT STDMETHODCALLTYPE ReleaseMarshalData
    (
        IN  IStream *pStm
    );

    HRESULT STDMETHODCALLTYPE DisconnectObject
    (
        IN  DWORD dwReserved
    );

    CTypeMarshal(
        IN PFORMAT_STRING pFormatString,
        IN ULONG          length,
        IN ULONG          offset);

private:
    ~CTypeMarshal();

    long           _cRefs;
    ULONG          _offset;
    ULONG          _length;
    PFORMAT_STRING _pFormatString;
    MIDL_STUB_DESC _StubDesc;
};


//+---------------------------------------------------------------------------
//
//  Function:   DllGetClassObject
//
//  Synopsis:   Gets an interface pointer to the specified class object.
//
//  Arguments:  rclsid - CLSID for the class object.
//              riid   - IID for the requested interface
//		[ppv]  - Returns the interface pointer to the class object.
//
//  Returns:   	S_OK
//              CLASS_E_CLASSNOTAVAILABLE
//              E_INVALIDARG
//              E_NOINTERFACE
//              E_OUTOFMEMORY
//
//----------------------------------------------------------------------------
STDAPI DllGetClassObject(
    REFCLSID rclsid,
    REFIID riid,
    void ** ppv)
{
    HRESULT hr;
    RPC_STATUS rc;

    __try
    {
        *ppv = NULL;

        //Initialize the RPC heap.
        rc = NdrpPerformRpcInitialization();

        if (rc == RPC_S_OK)
        {
            if(rclsid == CLSID_TypeFactory)
            {
                CTypeFactory * pTypeFactory;
    
                pTypeFactory = new CTypeFactory;
                if(pTypeFactory != NULL)
                {
                    hr = pTypeFactory->QueryInterface(riid, ppv);
                    pTypeFactory->Release();
                }
                else
                {
                    hr = E_OUTOFMEMORY;
                }
            }
            else
            {
                hr = CLASS_E_CLASSNOTAVAILABLE;
            }
        }
        else
            hr = HRESULT_FROM_WIN32(rc);
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        hr = E_INVALIDARG;
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTypeFactory::CTypeFactory
//
//  Synopsis:   Constructor for CTypeFactory
//
//----------------------------------------------------------------------------
CTypeFactory::CTypeFactory()
: _cRefs(1)
{
}

//+---------------------------------------------------------------------------
//
//  Method:     CTypeFactory::QueryInterface
//
//  Synopsis:   Gets a pointer to the specified interface.
//
//  Arguments:  riid - IID of the requested interface.
//		ppv  - Returns the interface pointer.
//
//  Returns:   	S_OK
//              E_INVALIDARG
//              E_NOINTERFACE
//
//----------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CTypeFactory::QueryInterface(
    REFIID riid,
    void **ppv)
{
    HRESULT hr;

    __try
    {
        *ppv = NULL;

        if(IsEqualIID(riid, IID_IUnknown) ||
           IsEqualIID(riid, IID_ITypeFactory))
        {
            AddRef();
            *ppv = (ITypeFactory *) this;
            hr = S_OK;
        }
        else if(IsEqualIID(riid, IID_IClassFactory))
        {
            AddRef();
            *ppv = (IClassFactory *) this;
            hr = S_OK;
        }
        else
        {
            hr = E_NOINTERFACE;
        }
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        hr = E_INVALIDARG;
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTypeFactory::AddRef
//
//  Synopsis:   Increment the reference count.
//
//  Arguments:  void
//
//  Returns:    ULONG -- the new reference count
//
//  Notes:      Use InterlockedIncrement to make it multi-thread safe.
//
//----------------------------------------------------------------------------
ULONG STDMETHODCALLTYPE
CTypeFactory::AddRef(void)
{
    InterlockedIncrement(&_cRefs);
    return _cRefs;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTypeFactory::Release
//
//  Synopsis:   Decrement the reference count.
//
//  Arguments:  void
//
//  Returns:    ULONG -- the remaining reference count
//
//  Notes:      Use InterlockedDecrement to make it multi-thread safe.
//              We use a local variable so that we don't access
//              a data member after decrementing the reference count.
//
//----------------------------------------------------------------------------
ULONG STDMETHODCALLTYPE
CTypeFactory::Release(void)
{
    ULONG count = _cRefs - 1;

    if(0 == InterlockedDecrement(&_cRefs))
    {
	    delete this;
	    count = 0;
    }

    return count;
}

//+-------------------------------------------------------------------------
//
//  Member:   	CTypeFactory::LockServer
//
//  Synopsis:   Lock the server. Does nothing.
//
//  Arguments:  fLock
//
//  Returns:    S_OK
//
//--------------------------------------------------------------------------
STDMETHODIMP CTypeFactory::LockServer(BOOL fLock)
{
    return S_OK;
}

//+-------------------------------------------------------------------------
//
//  Member:  	CTypeFactory::CreateInstance
//
//  Synopsis:   Creates a CTypeMarshal.
//
//  Arguments:  [pUnkOuter] - The controlling unknown (for aggregation)
//		[iid]       - The requested interface ID.
//		[ppv]       - Returns the pointer to the new object
//
//  Returns:    S_OK
//              CLASS_E_NOAGGREGATION
//              E_NOINTERFACE
//              E_OUTOFMEMORY
//              E_INVALIDARG
//
//--------------------------------------------------------------------------
STDMETHODIMP CTypeFactory::CreateInstance(
    IUnknown * pUnkOuter,
    REFIID     riid, 
    void **    ppv)
{
    HRESULT hr;
    IID     iid;

    __try
    {
        //Parameter validation.
        *ppv = NULL;
        iid = riid;

        if(NULL == pUnkOuter)
        {
            CTypeMarshal *pTypeMarshal = new CTypeMarshal(NULL, 0, 0);
            if(pTypeMarshal != NULL)
            {
                hr = pTypeMarshal->QueryInterface(iid, ppv);
                pTypeMarshal->Release();
            }
            else
            {
                hr = E_OUTOFMEMORY;
            }
        }
        else
        {
            //CTypeMarshal does not support aggregation.
            hr = CLASS_E_NOAGGREGATION;            
        }
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        hr = E_INVALIDARG;
    }
    
    return hr;
}


//+---------------------------------------------------------------------------
//
//  Method:     CTypeFactory::CreateFromTypeInfo
//
//  Synopsis:   Create a type marshaller from the typeinfo.
//
//  Arguments:  void
//
//  Returns:    S_OK
//              DISP_E_BADVARTYPE
//              E_INVALIDARG
//              E_NOINTERFACE
//              E_OUTOFMEMORY
//
//----------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE 
CTypeFactory::CreateFromTypeInfo(
    IN  ITypeInfo * pTypeInfo,
    IN  REFIID      riid,
    OUT IUnknown ** ppv)
{
    HRESULT        hr;
    CTypeMarshal * pTypeMarshal;
    PFORMAT_STRING pFormatString;
    USHORT         length;
    USHORT         offset;
    PARAMINFO      paramInfo;
    CTypeGen       *ptypeGen = new CTypeGen;
    DWORD          structInfo = 0;

    if (NULL == ptypeGen)
        return E_OUTOFMEMORY;

    __try
    {
        *ppv = NULL;

        //Build a type format string from the typeinfo.

        paramInfo.vt = VT_USERDEFINED;
        paramInfo.pTypeInfo = pTypeInfo;
        pTypeInfo->AddRef();
   
        hr = ptypeGen->RegisterType(&paramInfo,
                                  &offset,
                                  &structInfo);

        if (SUCCEEDED(hr) && (0 == offset))
        {
            ASSERT( !(paramInfo.vt & VT_BYREF));
            switch (paramInfo.vt)
            {
            case VT_I1:
            case VT_UI1:
                    offset = 734;
                break;

            case VT_I2:
            case VT_UI2:
            case VT_BOOL:
                    offset = 738;
                break;

            case VT_I4:
            case VT_UI4:
            case VT_INT:
            case VT_UINT:
            case VT_ERROR:
            case VT_HRESULT:
                    offset = 742;
                break;

            case VT_I8:
            case VT_UI8:
            case VT_CY:
                    offset = 310;
                break;

            case VT_R4:
                    offset = 746;
                break;

            case VT_R8:
            case VT_DATE:
                    offset = 750;
                break;  
            }
        }
        
        if(SUCCEEDED(hr))
        {
            hr = ptypeGen->GetTypeFormatString(&pFormatString, &length);
            if(SUCCEEDED(hr))
            {
                pTypeMarshal = new CTypeMarshal(pFormatString, length, offset);
                if(pTypeMarshal != NULL)
                {
                    hr = pTypeMarshal->QueryInterface(riid, (void **) ppv);
                    pTypeMarshal->Release();
                }
                else
                {
                    hr = E_OUTOFMEMORY;
                    ReleaseTypeFormatString(pFormatString);
                }
            }
        }
        delete ptypeGen;    // fix compile warning
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        delete ptypeGen;    // fix compile warning
        hr = E_INVALIDARG;
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTypeMarshal::CTypeMarshal
//
//  Synopsis:   Constructor for CTypeMarshal
//
//----------------------------------------------------------------------------
CTypeMarshal::CTypeMarshal(
    IN PFORMAT_STRING pFormatString,
    IN ULONG          length,
    IN ULONG          offset)
: _pFormatString(pFormatString), _length(length), _offset(offset), _cRefs(1)
{
    //Initialize the MIDL_STUB_DESC.
    MIDL_memset(&_StubDesc, 0, sizeof(_StubDesc));
    _StubDesc.pfnAllocate = NdrOleAllocate;
    _StubDesc.pfnFree = NdrOleFree;
    _StubDesc.pFormatTypes = pFormatString;
#if !defined(__RPC_WIN64__)
    _StubDesc.Version = 0x20000; /* Ndr library version */
    _StubDesc.MIDLVersion = MIDL_VERSION_3_0_44;
#else
    _StubDesc.Version = 0x50002; /* Ndr library version */
    _StubDesc.MIDLVersion = MIDL_VERSION_5_2_202;
#endif
    _StubDesc.aUserMarshalQuadruple = UserMarshalRoutines;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTypeMarshal::~CTypeMarshal
//
//  Synopsis:   Destructor for CTypeMarshal
//
//----------------------------------------------------------------------------
CTypeMarshal::~CTypeMarshal()
{
    ReleaseTypeFormatString(_pFormatString);
}


//+---------------------------------------------------------------------------
//
//  Method:     CTypeMarshal::QueryInterface
//
//  Synopsis:   Gets a pointer to the specified interface.
//
//  Arguments:  riid - IID of the requested interface.
//		ppv  - Returns the interface pointer.
//
//  Returns:   	S_OK
//              E_INVALIDARG
//              E_NOINTERFACE
//
//----------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CTypeMarshal::QueryInterface(
    REFIID riid,
    void **ppv)
{
    HRESULT hr;

    __try
    {
        *ppv = NULL;

        if(IsEqualIID(riid, IID_IUnknown) ||
           IsEqualIID(riid, IID_ITypeMarshal))
        {
            AddRef();
            *ppv = (ITypeMarshal *) this;
            hr = S_OK;
        }
        else if (IsEqualIID(riid, IID_IMarshal))
        {
            AddRef();
            *ppv = (IMarshal *) this;
            hr = S_OK;
        }
        else
        {
            hr = E_NOINTERFACE;
        }
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        hr = E_INVALIDARG;
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTypeMarshal::AddRef
//
//  Synopsis:   Increment the reference count.
//
//  Arguments:  void
//
//  Returns:    ULONG -- the new reference count
//
//  Notes:      Use InterlockedIncrement to make it multi-thread safe.
//
//----------------------------------------------------------------------------
ULONG STDMETHODCALLTYPE
CTypeMarshal::AddRef(void)
{
    InterlockedIncrement(&_cRefs);
    return _cRefs;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTypeMarshal::Release
//
//  Synopsis:   Decrement the reference count.
//
//  Arguments:  void
//
//  Returns:    ULONG -- the remaining reference count
//
//  Notes:      Use InterlockedDecrement to make it multi-thread safe.
//              We use a local variable so that we don't access
//              a data member after decrementing the reference count.
//
//----------------------------------------------------------------------------
ULONG STDMETHODCALLTYPE
CTypeMarshal::Release(void)
{
    ULONG count = _cRefs - 1;

    if(0 == InterlockedDecrement(&_cRefs))
    {
	    delete this;
	    count = 0;
    }

    return count;
}


//+---------------------------------------------------------------------------
//
//  Method:     CTypeMarshal::Size
//
//  Synopsis:   Computes the size of the marshalled data type.
//
//  Returns:    S_OK
//              E_INVALIDARG
//
//----------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CTypeMarshal::Size(
    IN  void *  pType,
    IN  DWORD   dwDestContext,
    IN  void  * pvDestContext,
    OUT ULONG * pSize)
{
    HRESULT           hr = S_OK;
    MIDL_STUB_MESSAGE StubMsg;
    PFORMAT_STRING    pTypeFormat;

    __try
    {
        if(!_pFormatString)
            return E_UNEXPECTED;

        pTypeFormat = _pFormatString + _offset;

        MIDL_memset(&StubMsg, 0, sizeof(StubMsg));
        StubMsg.StubDesc = &_StubDesc;
        StubMsg.pfnFree = NdrOleFree;
        StubMsg.pfnAllocate = NdrOleAllocate;
        StubMsg.IsClient = 1;
        StubMsg.BufferLength = 1; //Reserve space for an alignment gap.
        StubMsg.dwDestContext = dwDestContext;
        StubMsg.pvDestContext = pvDestContext;

        (*pfnSizeRoutines[ROUTINE_INDEX(*pTypeFormat)])(
            &StubMsg,
            (unsigned char *)pType,
            pTypeFormat);

        *pSize = StubMsg.BufferLength - 1;
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        hr = HRESULT_FROM_WIN32(GetExceptionCode());
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTypeMarshal::Marshal
//
//  Synopsis:   Marshals the user-defined type into a buffer.
//
//  Returns:    S_OK
//              E_INVALIDARG
//
//----------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CTypeMarshal::Marshal(
    IN  void *  pType,
    IN  DWORD   dwDestContext,
    IN  void  * pvDestContext,
    IN  ULONG   cbBufferLength,
    OUT BYTE  * pBuffer,
    OUT ULONG * pcbWritten)
{
    HRESULT           hr = S_OK;    
    MIDL_STUB_MESSAGE StubMsg;
    RPC_MESSAGE       RpcMsg;
    PFORMAT_STRING    pTypeFormat;

    __try
    {
        if(!_pFormatString)
            return E_UNEXPECTED;

        pTypeFormat = _pFormatString + _offset;

        MIDL_memset(&StubMsg, 0, sizeof(StubMsg));
        StubMsg.StubDesc = &_StubDesc;
        StubMsg.pfnFree = NdrOleFree;
        StubMsg.pfnAllocate = NdrOleAllocate;
        StubMsg.IsClient = 1;
        StubMsg.dwDestContext = dwDestContext;
        StubMsg.pvDestContext = pvDestContext;
        StubMsg.Buffer = pBuffer;

        MIDL_memset(&RpcMsg, 0, sizeof(RpcMsg));
        RpcMsg.DataRepresentation = NDR_LOCAL_DATA_REPRESENTATION;
        RpcMsg.Buffer = pBuffer;
        RpcMsg.BufferLength = cbBufferLength;
        StubMsg.RpcMsg = &RpcMsg;

        (*pfnMarshallRoutines[ROUTINE_INDEX(*pTypeFormat)])(
            &StubMsg,
            (unsigned char *)pType,
            pTypeFormat);

        *pcbWritten = (ULONG)(StubMsg.Buffer - pBuffer);
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        hr = HRESULT_FROM_WIN32(GetExceptionCode());
    }

    return hr;
}


//+---------------------------------------------------------------------------
//
//  Method:     CTypeMarshal::Unmarshal
//
//  Synopsis:   Unmarshals a user-defined type from a buffer.
//
//  Returns:    S_OK
//              E_INVALIDARG
//
//----------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CTypeMarshal::Unmarshal(
    OUT void   * pType,
    IN  DWORD    dwFlags,
    IN  ULONG    cbBufferLength,
    IN  BYTE   * pBuffer,
    OUT ULONG  * pcbRead)
{
    HRESULT           hr = S_OK;
    MIDL_STUB_MESSAGE StubMsg;
    RPC_MESSAGE       RpcMsg;
    PFORMAT_STRING    pTypeFormat;

    __try
    {
        if(pcbRead)
            *pcbRead = 0;

        if(!_pFormatString)
            return E_UNEXPECTED;

        pTypeFormat = _pFormatString + _offset;
        // HACK! OA might pass in 0xffffffff, which 
        // will break the attack checking in ndr engine.
        // what we do now is mask off the highest byte.
        // This will lead to a 64M limit buffer, but that
        // should be good enough for now.
        cbBufferLength &= 0xffffff;

        MIDL_memset(&StubMsg, 0, sizeof(StubMsg));
        StubMsg.StubDesc = &_StubDesc;
        StubMsg.pfnFree = NdrOleFree;
        StubMsg.pfnAllocate = NdrOleAllocate;
        StubMsg.IsClient = 1;
        StubMsg.dwDestContext = dwFlags & 0x0000FFFF;
        StubMsg.Buffer = pBuffer;
        StubMsg.BufferStart = pBuffer;
        StubMsg.BufferLength = cbBufferLength;
        StubMsg.BufferEnd = pBuffer + cbBufferLength;

        MIDL_memset(&RpcMsg, 0, sizeof(RpcMsg));
        RpcMsg.DataRepresentation = dwFlags >> 16;
        RpcMsg.Buffer = pBuffer;
        RpcMsg.BufferLength = cbBufferLength;
        StubMsg.RpcMsg = &RpcMsg;

        NdrClientZeroOut(&StubMsg,
                         pTypeFormat,
                         (uchar *) pType); 

        //Endianness
        if(RpcMsg.DataRepresentation != NDR_LOCAL_DATA_REPRESENTATION)
        {
            (*pfnConvertRoutines[ROUTINE_INDEX(*pTypeFormat)])(&StubMsg,
                                                               pTypeFormat,
                                                               FALSE);
            StubMsg.Buffer = pBuffer;
        }
        
        //Unmarshal
        (*pfnUnmarshallRoutines[ROUTINE_INDEX(*pTypeFormat)])(
            &StubMsg,
            (unsigned char **)&pType,
            pTypeFormat,
            FALSE);

        if(pcbRead)
            *pcbRead = (ULONG)(StubMsg.Buffer - pBuffer);
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        hr = HRESULT_FROM_WIN32(GetExceptionCode());
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
//  Method:     CTypeMarshal::Free
//
//  Synopsis:   Frees a user-defined type.
//
//  Returns:    S_OK
//              E_INVALIDARG
//
//----------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CTypeMarshal::Free(
    void * pType)
{
    HRESULT           hr = S_OK;
    MIDL_STUB_MESSAGE StubMsg;
    PFORMAT_STRING    pTypeFormat;

    __try
    {
        if(pType != NULL)
        {
            if(!_pFormatString)
                return E_UNEXPECTED;

            pTypeFormat = _pFormatString + _offset;

            MIDL_memset(&StubMsg, 0, sizeof(StubMsg));
            StubMsg.StubDesc = &_StubDesc;
            StubMsg.pfnFree = NdrOleFree;
            StubMsg.pfnAllocate = NdrOleAllocate;
            StubMsg.IsClient = 1;

            (*pfnFreeRoutines[ROUTINE_INDEX(*pTypeFormat)])(
                 &StubMsg,
                 (unsigned char *)pType,
                 pTypeFormat);
        }
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        hr = HRESULT_FROM_WIN32(GetExceptionCode());
    }

    return hr;
}


//+---------------------------------------------------------------------------
//
//  Method:     CTypeMarshal::GetUnmarshalClass
//
//  Synopsis:   Get the class ID.
//
//----------------------------------------------------------------------------
STDMETHODIMP CTypeMarshal::GetUnmarshalClass(
    REFIID  riid,
    LPVOID  pv,
    DWORD   dwDestContext,
    LPVOID  pvDestContext,
    DWORD   mshlflags,
    CLSID * pClassID)
{
    HRESULT hr = S_OK;

    __try
    {
        *pClassID = CLSID_TypeFactory;
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        hr = E_INVALIDARG;
    }

    return hr;
}



//+---------------------------------------------------------------------------
//
//  Method:     CTypeMarshal::GetMarshalSizeMax
//
//  Synopsis:   Get maximum size of marshalled moniker.
//
//----------------------------------------------------------------------------
STDMETHODIMP CTypeMarshal::GetMarshalSizeMax(
    REFIID riid,
    LPVOID pv,
    DWORD  dwDestContext,
    LPVOID pvDestContext,
    DWORD  mshlflags,
    DWORD *pSize)
{
    HRESULT hr;

    __try
    {
        *pSize =  sizeof(_offset) + sizeof(_length) + _length;
        hr = S_OK;
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        hr = E_INVALIDARG;
    }

    return hr;
}


//+---------------------------------------------------------------------------
//
//  Method:     CTypeMarshal::MarshalInterface
//
//  Synopsis:   Marshal moniker into a stream.
//
//----------------------------------------------------------------------------
STDMETHODIMP CTypeMarshal::MarshalInterface(
    IStream * pStream,
    REFIID    riid,
    void    * pv,
    DWORD     dwDestContext,
    LPVOID    pvDestContext,
    DWORD     mshlflags)
{
    HRESULT hr;
    ULONG cb;

    hr = pStream->Write(&_offset, sizeof(_offset), &cb);
    if(SUCCEEDED(hr))
    {
        hr = pStream->Write(&_length, sizeof(_length), &cb);
        if(SUCCEEDED(hr))
        {
            hr = pStream->Write(_pFormatString, _length, &cb);
        }
    }
   
    return hr;
}



//+---------------------------------------------------------------------------
//
//  Method:     CTypeMarshal::UnmarshalInterface
//
//  Synopsis:   Unmarshal moniker from a stream.
//
//----------------------------------------------------------------------------
STDMETHODIMP CTypeMarshal::UnmarshalInterface(
    IStream * pStream,
    REFIID    riid,
    void   ** ppv)
{
    HRESULT hr;
    ULONG   cb;

    __try
    {
        //Validate parameters.
        *ppv = NULL;

        hr = pStream->Read(&_offset, sizeof(_offset), &cb);
        if(SUCCEEDED(hr) && 
           (sizeof(_offset) == cb))
        {
            hr = pStream->Read(&_length, sizeof(_length), &cb);
            if(SUCCEEDED(hr) && 
               (sizeof(_length) == cb))
            {
                _pFormatString = (PFORMAT_STRING) I_RpcAllocate(_length);
                if(_pFormatString != NULL)
                {
                    hr = pStream->Read((void *) _pFormatString, _length, &_length);
                    if(SUCCEEDED(hr))
                    {
                        hr = QueryInterface(riid, ppv);
                    }
                }
                else
                {
                    hr = E_OUTOFMEMORY;
                }
            }
        }
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        hr = E_INVALIDARG;
    }

    return hr;
}


//+---------------------------------------------------------------------------
//
//  Method:     CTypeMarshal::ReleaseMarshalData
//
//  Synopsis:   Release a marshalled class moniker.
//              Just seek to the end of the marshalled class moniker.
//
//----------------------------------------------------------------------------
STDMETHODIMP CTypeMarshal::ReleaseMarshalData(
    IStream * pStream)
{
    HRESULT hr;
    ULONG   cb;    

    hr = pStream->Read(&_offset, sizeof(_offset), &cb);
    if(SUCCEEDED(hr) && 
       (sizeof(_offset) == cb))
    {
        hr = pStream->Read(&_length, sizeof(_length), &cb);
        if(SUCCEEDED(hr) && 
           (sizeof(_length) == cb))
        {
            LARGE_INTEGER liSize;
 
            liSize.LowPart = _length;
            liSize.HighPart = 0;
            hr = pStream->Seek(liSize, STREAM_SEEK_CUR, NULL);
        }
    }

    return hr;
}


//+---------------------------------------------------------------------------
//
//  Method:     CTypeMarshal::DisconnectObject
//
//  Synopsis:   Disconnect the object.
//
//----------------------------------------------------------------------------
STDMETHODIMP CTypeMarshal::DisconnectObject(
    DWORD dwReserved)
{
    return S_OK;
}