// This is a part of the Active Template Library.
// Copyright (C) Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

#ifndef __ATLSOAP_H__
#define __ATLSOAP_H__

#pragma once

#if (defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_))
	#error require winsock2.h -- include <winsock2.h> before you include <windows.h>
#endif

#if !defined(_WIN32_WCE) && ((_WIN32_WINNT < 0x0400) && (_WIN32_WINDOWS <= 0x0400))
	#error require _WIN32_WINNT >= 0x0400 or _WIN32_WINDOWS > 0x0400
#endif

#ifndef ATLSOAP_TRACE
	#if defined(_ATLSOAP_TRACE_XML) && !defined(_WIN32_WCE)
		#define ATLSOAP_TRACE(__data, __len) AtlSoapTraceXML(__data, __len)
	#else
		#define ATLSOAP_TRACE(__data, __len) __noop
	#endif
#endif // ATLSOAP_TRACE

// override this macro to ATL_BASE64_FLAG_NOCRLF if you do 
// not want Base64-encoded binary data to contain CRLFs
#ifndef ATLSOAP_BASE64_FLAGS
	#define ATLSOAP_BASE64_FLAGS ATL_BASE64_FLAG_NONE
#endif // ATLSOAP_BASE64_FLAGS

#ifndef _WIN32_WCE
[ emitidl(restricted) ];
#endif // _WIN32_WCE
#include <winsock2.h>
#include <atlstr.h>
#include <atlcoll.h>
#include <atlbase.h>
#include <msxml2.h>
#include <atlenc.h>
#ifndef _WIN32_WCE
#include <fcntl.h>
#else
#include <altcecrt.h>
#endif // _WIN32_WCE
#include <float.h>
#include <math.h>
#include <limits>
#include <atlisapi.h>
#ifndef _WIN32_WCE
#include <atlstencil.h>
#endif // _WIN32_WCE
#include <atlhttp.h>
#include <atlhttp.inl>

#pragma warning(push)
#pragma warning(disable: 4625) // copy constructor could not be generated because a base class copy constructor is inaccessible
#pragma warning(disable: 4626) // assignment operator could not be generated because a base class assignment operator is inaccessible
#pragma warning(disable: 4061) // enumerate 'enum value' in switch of enum 'enum type' is not explicitly handled by a case label

#ifndef _CPPUNWIND
#pragma warning(disable: 4702) // unreachable code
#endif // _CPPUNWIND

#ifndef ATLSOAP_NOWININET
	#include <wininet.h>
	#ifndef ATLSOAPINET_CLIENT
		#define ATLSOAPINET_CLIENT _T("VCSoapClient")
	#endif
#endif

#ifndef _ATL_NO_DEFAULT_LIBS
#ifndef _WIN32_WCE
#pragma comment(lib, "msxml2.lib")
#endif // _WIN32_WCE
	#ifndef ATLSOAP_NOWININET
		#pragma comment(lib, "wininet.lib")
	#endif
#endif

#define _ATLSOAP_MAKEWIDESTR( str ) L ## str
#define ATLSOAP_MAKEWIDESTR( str ) _ATLSOAP_MAKEWIDESTR( str )


#pragma pack(push,_ATL_PACKING)
namespace ATL
{

#ifndef _WIN32_WCE
ATL_NOINLINE inline void AtlSoapTraceXML(LPBYTE pdwData, DWORD dwLen)
{
	HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
	if (hStdOut != INVALID_HANDLE_VALUE)
	{
		DWORD dwWritten;
		WriteFile(hStdOut, 
			"\n-----------------------------------------------------------------\n",
			sizeof("\n-----------------------------------------------------------------\n")-1, 
			&dwWritten, NULL);

		WriteFile(hStdOut, pdwData, dwLen, &dwWritten, NULL);

		WriteFile(hStdOut,
			"\n-----------------------------------------------------------------\n",
			sizeof("\n-----------------------------------------------------------------\n")-1, 
			&dwWritten, NULL);
	}
}
#endif // _WIN32_WCE

////////////////////////////////////////////////////////////////////////////////
//
// IStreamImpl - stub IStream implementation class
//
////////////////////////////////////////////////////////////////////////////////

class IStreamImpl : public IStream
{
public:

	HRESULT __stdcall Read(void * /*pDest*/, ULONG /*nMaxLen*/, ULONG * /*pnRead*/)
	{
		return E_NOTIMPL;
	}

	HRESULT __stdcall Write(const void * /*pv*/, ULONG /*cb*/, ULONG * /*pcbWritten*/)
	{
		return E_NOTIMPL;
	}

	HRESULT __stdcall Seek(LARGE_INTEGER /*dlibMove*/, DWORD /*dwOrigin*/, 
		ULARGE_INTEGER * /*pLibNewPosition*/)
	{
		return E_NOTIMPL;
	}

	HRESULT __stdcall SetSize(ULARGE_INTEGER /*libNewSize*/)
	{
		return E_NOTIMPL;
	}

	HRESULT __stdcall CopyTo(IStream * /*pStream*/, ULARGE_INTEGER /*cb*/, 
		ULARGE_INTEGER * /*pcbRead*/, ULARGE_INTEGER * /*pcbWritten*/)
	{
		return E_NOTIMPL;
	}

	HRESULT __stdcall Commit(DWORD /*grfCommitFlags*/)
	{
		return E_NOTIMPL;
	}

	HRESULT __stdcall Revert()
	{
		return E_NOTIMPL;
	}

	HRESULT __stdcall LockRegion(ULARGE_INTEGER /*libOffset*/, ULARGE_INTEGER /*cb*/, DWORD /*dwLockType*/)
	{
		return E_NOTIMPL;
	}

	HRESULT __stdcall UnlockRegion(ULARGE_INTEGER /*libOffset*/, ULARGE_INTEGER /*cb*/, DWORD /*dwLockType*/)
	{
		return E_NOTIMPL;
	}

	HRESULT __stdcall Stat(STATSTG * /*pstatstg*/, DWORD /*grfStatFlag*/)
	{
		return E_NOTIMPL;
	}

	HRESULT __stdcall Clone(IStream ** /*ppstm*/)
	{
		return E_NOTIMPL;
	}
}; // class IStreamImpl

////////////////////////////////////////////////////////////////////////////////
//
// CStreamOnServerContext
//
////////////////////////////////////////////////////////////////////////////////

class CStreamOnServerContext : public IStreamImpl
{
public:

	HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
	{
		if (ppv == NULL)
		{
			return E_POINTER;
		}

		*ppv = NULL;

		if (InlineIsEqualGUID(riid, IID_IUnknown) ||
			InlineIsEqualGUID(riid, IID_IStream) ||
			InlineIsEqualGUID(riid, IID_ISequentialStream))
		{
			*ppv = static_cast<IStream *>(this);
			return S_OK;
		}

		return E_NOINTERFACE;
	}

	ULONG __stdcall AddRef()
	{
		return 1;
	}

	ULONG __stdcall Release()
	{
		return 1;
	}

private:

	IHttpServerContext * m_pServerContext;
	DWORD m_dwBytesRead;

public:

	CStreamOnServerContext(IHttpServerContext *pServerContext = NULL)
		: m_pServerContext(pServerContext), m_dwBytesRead(0)
	{
	}

	void SetServerContext(IHttpServerContext *pServerContext)
	{
		ATLASSUME( m_pServerContext == NULL );

		m_pServerContext = pServerContext;
	}

	HRESULT __stdcall Read(void *pDest, ULONG nMaxLen, ULONG *pnRead)
	{
		ATLENSURE( pDest != NULL );
		ATLASSUME( m_pServerContext != NULL );

		DWORD dwToRead = __min(m_pServerContext->GetTotalBytes()-m_dwBytesRead, nMaxLen);
		if (ReadClientData(m_pServerContext, (LPSTR) pDest, &dwToRead, m_dwBytesRead) != FALSE)
		{
			m_dwBytesRead+= dwToRead;

			if (pnRead != NULL)
			{
				*pnRead = dwToRead;
			}

			return S_OK;
		}

		ATLTRACE( _T("ATLSOAP: CStreamOnServerContext::Read -- ReadClientData failed.\r\n") );

		return E_FAIL;
	}
}; // class CStreamOnServerContext

////////////////////////////////////////////////////////////////////////////////
//
// CReadStreamOnSocket
//
////////////////////////////////////////////////////////////////////////////////

template <typename TSocketClass>
class CReadStreamOnSocket : public IStreamImpl
{
public:

	HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
	{
		if (ppv == NULL)
		{
			return E_POINTER;
		}

		*ppv = NULL;

		if (InlineIsEqualGUID(riid, IID_IUnknown) ||
			InlineIsEqualGUID(riid, IID_IStream) ||
			InlineIsEqualGUID(riid, IID_ISequentialStream))
		{
			*ppv = static_cast<IStream *>(this);
			return S_OK;
		}

		return E_NOINTERFACE;
	}

	ULONG __stdcall AddRef()
	{
		return 1;
	}

	ULONG __stdcall Release()
	{
		return 1;
	}

private:

	CAtlHttpClientT<TSocketClass> * m_pSocket;
	LPCSTR m_szBuffer;
	LPCSTR m_szCurr;
	long m_nBodyLen;

public:

	CReadStreamOnSocket()
		: m_pSocket(NULL), m_szBuffer(NULL), m_szCurr(NULL), m_nBodyLen(0)
	{
	}

	BOOL Init(CAtlHttpClientT<TSocketClass> *pSocket)
	{
		ATLENSURE( pSocket != NULL );

		m_pSocket = pSocket;
		m_szBuffer = (LPCSTR) pSocket->GetBody();

		ATLSOAP_TRACE( (LPBYTE) pSocket->GetBody(), pSocket->GetBodyLength() );

		if (m_szBuffer != NULL)
		{
			m_szCurr = m_szBuffer;
			m_nBodyLen = pSocket->GetBodyLength();
			if (m_nBodyLen != 0)
			{
				return TRUE;
			}
		}

		ATLTRACE( _T("ATLSOAP: CReadStreamOnSocket::Init failed.\r\n") );

		return FALSE;
	}

	HRESULT __stdcall Read(void *pDest, ULONG nMaxLen, ULONG *pnRead)
	{
		ATLASSERT( pDest != NULL );
		ATLASSUME( m_pSocket != NULL );
		ATLASSUME( m_szBuffer != NULL );

		if (pnRead != NULL)
		{
			*pnRead = 0;
		}

		long nRead = (int) (m_szCurr-m_szBuffer);
		if (nRead < m_nBodyLen)
		{
			long nLength = __min((int)(m_nBodyLen-nRead), (LONG) nMaxLen);
			Checked::memcpy_s(pDest, nMaxLen, m_szCurr, nLength);
			m_szCurr+= nLength;

			if (pnRead != NULL)
			{
				*pnRead = (ULONG) nLength;
			}
		}

		return S_OK;
	}
}; // class CReadStreamOnSocket

////////////////////////////////////////////////////////////////////////////////
//
// CWriteStreamOnCString
//
////////////////////////////////////////////////////////////////////////////////

class CWriteStreamOnCString : public IWriteStream
{

public:
	CStringA m_str;

	virtual ~CWriteStreamOnCString()
	{
	}

	HRESULT WriteStream(LPCSTR szOut, int nLen, LPDWORD pdwWritten)
	{
		ATLENSURE_RETURN( szOut != NULL );

		if (nLen < 0)
		{
			nLen = (int) strlen(szOut);
		}

		_ATLTRY
		{
			m_str.Append(szOut, nLen);
		}
		_ATLCATCHALL()
		{
			return E_OUTOFMEMORY;
		}

		if (pdwWritten != NULL)
		{
			*pdwWritten = (DWORD) nLen;
		}

		return S_OK;
	}

	HRESULT FlushStream()
	{
		return S_OK;
	}

	void Cleanup()
	{
		m_str.Empty();
	}
}; // class CWriteStreamOnCString

////////////////////////////////////////////////////////////////////////////////
//
// Namespaces
//
////////////////////////////////////////////////////////////////////////////////

#define SOAPENV_NAMESPACEA "http://schemas.xmlsoap.org/soap/envelope/"
#define SOAPENV_NAMESPACEW ATLSOAP_MAKEWIDESTR( SOAPENV_NAMESPACEA )

#define SOAPENC_NAMESPACEA "http://schemas.xmlsoap.org/soap/encoding/"
#define SOAPENC_NAMESPACEW ATLSOAP_MAKEWIDESTR( SOAPENC_NAMESPACEA )

#define XSI_NAMESPACEA  "http://www.w3.org/2001/XMLSchema-instance"
#define XSI_NAMESPACEW  ATLSOAP_MAKEWIDESTR( XSI_NAMESPACEA )

#define XSD_NAMESPACEA  "http://www.w3.org/2001/XMLSchema"
#define XSD_NAMESPACEW  ATLSOAP_MAKEWIDESTR( XSD_NAMESPACEA )

#ifndef ATLSOAP_GENERIC_NAMESPACE
#define ATLSOAP_GENERIC_NAMESPACE L"http://www.tempuri.org"
#endif

////////////////////////////////////////////////////////////////////////////////
//
// Helpers
//
////////////////////////////////////////////////////////////////////////////////

inline HRESULT GetAttribute(
	__in ISAXAttributes *pAttributes, 
	__in_ecount(cchName) const wchar_t *wszAttrName, __in int cchName, 
	__out_ecount_part(*pcchValue, *pcchValue) const wchar_t **pwszValue, __inout int *pcchValue,
	__in_ecount_opt(cchNamespace) wchar_t *wszNamespace = NULL, __in int cchNamespace = 0)
{
	if (!pAttributes || !wszAttrName || !pwszValue || !pcchValue)
	{
		return E_INVALIDARG;
	}

	*pwszValue = NULL;
	*pcchValue = 0;
	if (!wszNamespace)
	{
		return (pAttributes->getValueFromQName(wszAttrName, cchName, pwszValue, pcchValue) == S_OK ? S_OK : E_FAIL);
	}
	return (pAttributes->getValueFromName(wszNamespace, cchNamespace, 
		wszAttrName, cchName, pwszValue, pcchValue) == S_OK ? S_OK : E_FAIL);
}

inline HRESULT GetAttribute(
	__in ISAXAttributes *pAttributes, 
	__in_ecount(cchName) const wchar_t *wszAttrName, __in int cchName, 
	__inout CStringW &strValue,
	__in_ecount_opt(cchNamespace) wchar_t *wszNamespace = NULL, __in int cchNamespace = 0)
{
	const wchar_t *wszValue = NULL;
	int cchValue = 0;

	if (!pAttributes || !wszAttrName)
	{
		return E_INVALIDARG;
	}

	HRESULT hr;
	if (!wszNamespace)
	{
		hr = (pAttributes->getValueFromQName(wszAttrName, cchName, &wszValue, &cchValue) == S_OK ? S_OK : E_FAIL);
	}
	else
	{
		hr = (pAttributes->getValueFromName(wszNamespace, cchNamespace, 
			wszAttrName, cchName, &wszValue, &cchValue) == S_OK ? S_OK : E_FAIL);
	}

	if (hr == S_OK)
	{
		_ATLTRY
		{
			strValue.SetString(wszValue, cchValue);
		}
		_ATLCATCHALL()
		{
			ATLTRACE( _T("ATLSOAP: GetAttribute -- out of memory.\r\n") );

			hr = E_OUTOFMEMORY;
		}
	}

	return hr;
}

inline const wchar_t *SkipWhitespace(const wchar_t *wsz)
{
	while (*wsz && iswspace(*wsz))
		++wsz;
	return wsz;
}

} // namespace ATL
#pragma pack(pop)

////////////////////////////////////////////////////////////////////////////////
//
// BLOB data type - use this struct when you want to send BLOB data
//   the attribute provider and proxy generator will only properly special
//   case blob data when using this struct.
//
////////////////////////////////////////////////////////////////////////////////

#ifndef _WIN32_WCE
[ export ]
#endif // _WIN32_WCE
typedef struct _tagATLSOAP_BLOB
{
	unsigned long size;
	unsigned char *data;
} ATLSOAP_BLOB;

#ifndef _ATL_SOAP_NO_PARAMETER_VALIDATIONS
#define _ATL_VALIDATE_PARAMETER_END(p)\
	do			\
	{			\
		if(*(p) !='\0') \
			return E_FAIL; \
	} while(0)
#else
#define _ATL_VALIDATE_PARAMETER_END(p) 
#endif

// All non-integral types have specializations which
// will be called. The following function will be called
// only for integral types

#pragma push_macro("max")
#pragma push_macro("min")
#undef max
#undef min
template <typename T>
inline HRESULT AtlGetSAXValue(T * pVal , const wchar_t * wsz , int cch )
{
	__int64 nVal = *pVal;
	if (FAILED(AtlGetSAXValue(&nVal, wsz, cch)))
		return E_FAIL;

#ifndef _ATL_SOAP_NO_PARAMETER_VALIDATIONS
	if(nVal < std::numeric_limits<T>::min() || nVal > std::numeric_limits<T>::max())
		return E_FAIL;
#endif

	*pVal = T(nVal);
	return S_OK;


}
#pragma pop_macro("max")
#pragma pop_macro("min")

////////////////////////////////////////////////////////////////////////////////
//
// AtlGetXMLValue (for IXMLDOMDocument) - get the real type from the XML data
//
///////////////////////////////////////////////////////////////////////////////

//
// generic IXMLDOMNode template function
// delegates to AtlGetSAXValue
//
template <typename T>
inline HRESULT AtlGetXMLValue(IXMLDOMNode *pParam, T *pVal)
{
	CComBSTR bstrVal;
	HRESULT hr = AtlGetXMLValue(pParam, &bstrVal);
	if (SUCCEEDED(hr))
	{
		hr = AtlGetSAXValue(pVal, bstrVal, bstrVal.Length());
	}

	return hr;
}

// specialization for BSTR
template <>
inline HRESULT AtlGetXMLValue<BSTR>(IXMLDOMNode *pParam, BSTR *pbstrVal)
{
	if (pParam == NULL)
	{
		return E_INVALIDARG;
	}
	if (pbstrVal == NULL)
	{
		return E_POINTER;
	}

	CComPtr<IXMLDOMNode> spChild;
	if (pParam->get_firstChild(&spChild) == S_OK)
	{
		CComPtr<IXMLDOMNode> spXmlChild;
		if (spChild->get_firstChild(&spXmlChild) == S_OK)
		{
			return (pParam->get_xml(pbstrVal) == S_OK ? S_OK : E_FAIL);
		}
	}

	return (pParam->get_text(pbstrVal) == S_OK) ? S_OK : E_FAIL;
}

////////////////////////////////////////////////////////////////////////////////
//
// AtlGetSAXValue - (for SAX or generic) get the real type from the XML data
//
////////////////////////////////////////////////////////////////////////////////

template <>
inline HRESULT AtlGetSAXValue<bool>(bool *pVal, __in_z const wchar_t *wsz, int cch)
{
	ATLENSURE( wsz != NULL );

	if (!pVal)
	{
		return E_POINTER;
	}

	*pVal = false;

	HRESULT hr = E_FAIL;
	switch (wsz[0])
	{
		case L'1':
		{
			if (cch==1)
			{
				*pVal = true;
				hr = S_OK;
			}
			break;
		}
		case L'0':
		{
			if (cch==1)
			{
				*pVal = false;
				hr = S_OK;
			}
			break;
		}
		case L't':
		{
			if (cch==sizeof("true")-1 && !wcsncmp(wsz, L"true", cch))
			{
				*pVal = true;
				hr = S_OK;
			}
			break;
		}
		case L'f':
		{
			if (cch==sizeof("false")-1 && !wcsncmp(wsz, L"false", cch))
			{
				*pVal = false;
				hr = S_OK;
			}
			break;
		}
	}

	return hr;
}

template <>
inline HRESULT AtlGetSAXValue<__int64>(__int64 *pVal, __in_z const wchar_t *wsz, int cch)
{
	ATLENSURE_RETURN( wsz != NULL );

	if (!pVal)
	{
		return E_POINTER;
	}

	_ATLTRY
	{
		CFixedStringT<CStringW, 1024> wstr(wsz, cch);
		const wchar_t *pStart = ATL::SkipWhitespace(static_cast<LPCWSTR>(wstr));
		const wchar_t *pEnd;

		__int64 i = 0;
		errno_t errnoValue = AtlStrToNum(&i, pStart, const_cast<wchar_t **>(&pEnd), 10);
		if (errnoValue == ERANGE)
		{
			return E_FAIL;//overflow or underflow case 
		}
		pEnd = ATL::SkipWhitespace(pEnd);
		_ATL_VALIDATE_PARAMETER_END(pEnd);
		*pVal = i;
	}
	_ATLCATCHALL()
	{
		return E_OUTOFMEMORY;
	}

	return S_OK;
}

template <>
inline HRESULT AtlGetSAXValue<unsigned __int64>(unsigned __int64 *pVal, __in_z const wchar_t *wsz, int cch)
{
	ATLENSURE_RETURN( wsz != NULL );

	if (!pVal)
	{
		return E_POINTER;
	}

	_ATLTRY
	{
		CFixedStringT<CStringW, 1024> wstr(wsz, cch);
		const wchar_t *pStart = ATL::SkipWhitespace(static_cast<LPCWSTR>(wstr));
		const wchar_t *pEnd;

		unsigned __int64 i = 0;
		errno_t errnoValue = AtlStrToNum(&i, pStart, const_cast<wchar_t **>(&pEnd), 10);
		if (errnoValue == ERANGE)
		{
			 return E_FAIL;//overflow or underflow case 
		}
		pEnd = ATL::SkipWhitespace(pEnd);
		_ATL_VALIDATE_PARAMETER_END(pEnd);
		*pVal = i;
	}
	_ATLCATCHALL()
	{
		return E_OUTOFMEMORY;
	}

	return S_OK;
}
template <>
inline HRESULT AtlGetSAXValue<double>(double *pVal, __in_z const wchar_t *wsz, int cch)
{
	ATLENSURE_RETURN( wsz != NULL );

	if (!pVal)
	{
		return E_POINTER;
	}

	if ((cch == 3) && (wsz[0]==L'I') && (!wcsncmp(wsz, L"INF", cch)))
	{
		*(((int *) pVal)+0) = 0x0000000;
		*(((int *) pVal)+1) = 0x7FF00000;
	}
	else if ((cch == 3) && (wsz[0]==L'N') && (!wcsncmp(wsz, L"NaN", cch)))
	{
		*(((int *) pVal)+0) = 0x0000000;
		*(((int *) pVal)+1) = 0xFFF80000;
	}
	else if ((cch == 4) && (wsz[1]==L'I') && (!wcsncmp(wsz, L"-INF", cch)))
	{
		*(((int *) pVal)+0) = 0x0000000;
		*(((int *) pVal)+1) = 0xFFF00000;
	}
	else
	{
		errno_t errnoValue = 0;

		_ATLTRY
		{
			CFixedStringT<CStringW, 1024> wstr(wsz, cch);
			const wchar_t *pStart = ATL::SkipWhitespace(static_cast<LPCWSTR>(wstr));
			const wchar_t *pEnd;
			double d = 0.0;
			errnoValue = AtlStrToNum(&d, pStart, const_cast<wchar_t **>(&pEnd));
			pEnd = ATL::SkipWhitespace(pEnd);
			_ATL_VALIDATE_PARAMETER_END(pEnd);
			*pVal = d;
		}
		_ATLCATCHALL()
		{
			return E_OUTOFMEMORY;
		}

		if ((*pVal == -HUGE_VAL) || (*pVal == HUGE_VAL) || (errnoValue == ERANGE))
		{
			return E_FAIL;
		}
	}

	return S_OK;
}

template <>
inline HRESULT AtlGetSAXValue<float>(float *pVal, __in_z const wchar_t *wsz, int cch)
{
	ATLASSERT( wsz != NULL );

	if (!pVal)
	{
		return E_POINTER;
	}

	double d = *pVal;
	if (SUCCEEDED(AtlGetSAXValue(&d, wsz, cch)))
	{
#ifdef _ATL_SOAP_PARAMETER_VALIDATIONS
		if(d > FLT_MAX || d < -FLT_MAX)
			return E_FAIL;
#endif
		*pVal = (float) d;
		return S_OK;
	}

	return E_FAIL;
}

template <>
inline HRESULT AtlGetSAXValue<BSTR>(BSTR *pVal, __in_z const wchar_t *wsz, int cch)
{
	ATLASSERT( wsz != NULL );

	if (pVal == NULL)
	{
		return E_POINTER;
	}

	*pVal = SysAllocStringLen(wsz, cch);

	return ((*pVal != NULL) ? S_OK : E_OUTOFMEMORY);
}

inline HRESULT AtlGetSAXBlobValue(
	ATLSOAP_BLOB *pVal, 
	const wchar_t *wsz, 
	int cch, 
	IAtlMemMgr *pMemMgr, 
	bool bHex = false)
{
	ATLENSURE_RETURN( wsz != NULL );
	ATLENSURE_RETURN( pMemMgr != NULL );

	if (pVal == NULL)
	{
		return E_POINTER;
	}

	if (pVal->data != NULL)
	{
		return E_INVALIDARG;
	}

	pVal->data = NULL;
	pVal->size = 0;

	int nLength = AtlUnicodeToUTF8(wsz, cch, NULL, 0);

	if (nLength != 0)
	{
		char * pSrc = (char *) pMemMgr->Allocate(nLength);
		if (pSrc != NULL)
		{
			nLength = AtlUnicodeToUTF8(wsz, cch, pSrc, nLength);
			if (nLength != 0)
			{
				pVal->data = (unsigned char *) pMemMgr->Allocate(nLength);
				if (pVal->data != NULL)
				{
					BOOL bRet;
					int nDataLength = nLength;
					if (!bHex)
					{
						bRet = Base64Decode(pSrc, nLength, pVal->data, &nDataLength);
					}
					else
					{
						bRet = AtlHexDecode(pSrc, nLength, pVal->data, &nDataLength);
					}
					if (bRet)
					{
						pVal->size = nDataLength;
					}
				}
			}

			pMemMgr->Free(pSrc);
		}
	}

	if (pVal->size == 0)
	{
		if (pVal->data != NULL)
		{
			pMemMgr->Free(pVal->data);
			pVal->data = NULL;
		}
	}

	return S_OK;
}

////////////////////////////////////////////////////////////////////////////////
//
// AtlGenXMLValue template and specializations
//
////////////////////////////////////////////////////////////////////////////////

template <typename T>
inline HRESULT AtlGenXMLValue(__in IWriteStream *pStream, __in T *pVal)
{
	if ((pStream == NULL) || (pVal == NULL))
	{
		return E_INVALIDARG;
	}

	//
	// delegate to CWriteStreamHelper
	//
	CWriteStreamHelper s(pStream);

	return (s.Write(*pVal) == TRUE ? S_OK : E_FAIL);
}

#ifdef _NATIVE_WCHAR_T_DEFINED
template <>
inline HRESULT AtlGenXMLValue<wchar_t>(__in IWriteStream *pStream, __in wchar_t *pVal)
{
	return AtlGenXMLValue(pStream, (unsigned short *)pVal);
}
#endif

template <>
inline HRESULT AtlGenXMLValue<wchar_t *>(__in IWriteStream *pStream, __deref_inout_z wchar_t **pVal)
{
	if ((pStream == NULL) || (*pVal == NULL))
	{
		return E_INVALIDARG;
	}

	wchar_t *wszWrite = *pVal;
	int nSrcLen = (int)wcslen(*pVal);
	int nCnt = EscapeXML(*pVal, nSrcLen, NULL, 0);
	if (nCnt > nSrcLen)
	{
		nCnt++;
		wszWrite = (wchar_t *)calloc((nCnt),sizeof(wchar_t));
		if (wszWrite == NULL)
		{
			return E_OUTOFMEMORY;
		}

		nCnt = EscapeXML(*pVal, nSrcLen, wszWrite, nCnt);
		if (nCnt == 0)
		{
			free(wszWrite);
			return E_FAIL;
		}
		wszWrite[nCnt] = L'\0';
		nSrcLen = nCnt;
	}

	nCnt = AtlUnicodeToUTF8(wszWrite, nSrcLen, NULL, 0);
	HRESULT hr = E_FAIL;
	if ((nCnt == 0) || (nCnt == nSrcLen))
	{
		CWriteStreamHelper s(pStream);

		hr = (s.Write(wszWrite) == TRUE ? S_OK : E_FAIL);
	}
	else
	{
		nCnt++;
		CHeapPtr<char> szWrite;
		szWrite.AllocateBytes((size_t)(nCnt));
		if (szWrite != NULL)
		{
			nCnt = AtlUnicodeToUTF8(wszWrite, nSrcLen, szWrite, nCnt);
			if (nCnt != 0)
			{
				hr = pStream->WriteStream(szWrite, nCnt, NULL);
			}
		}
		else
		{
			ATLTRACE( _T("ATLSOAP: AtlGenXMLValue<wchar_t *> -- out of memory.\r\n") );

			hr = E_OUTOFMEMORY;
		}
	}

	if (wszWrite != *pVal)
	{
		free(wszWrite);
	}

	return hr;
}

template <>
inline HRESULT AtlGenXMLValue<double>(IWriteStream *pStream, double *pVal)
{
	if ((pStream == NULL) || (pVal == NULL))
	{
		return E_INVALIDARG;
	}

	HRESULT hr;
	switch (_fpclass(*pVal))
	{
		case _FPCLASS_SNAN: 
		case _FPCLASS_QNAN:
		{
			hr = pStream->WriteStream("NaN", 3, NULL);
			break;
		}
		case _FPCLASS_NINF:
		{
			hr = pStream->WriteStream("-INF", 4, NULL);
			break;
		}
		case _FPCLASS_PINF:
		{
			hr = pStream->WriteStream("INF", 3, NULL);
			break;
		}
		case _FPCLASS_NZ:
		{
			hr = pStream->WriteStream("-0", 2, NULL);
			break;
		}
		default:
		{
            /***
            * 2 = sign + decimal point
            * ndec = decimal digits
            * 5 = exponent letter (e or E), exponent sign, three digits exponent
            * 1 = extra space for rounding
            * 1 = string terminator '\0'
            ***/
            const int ndec = 512;
            CHAR szBuf[ndec+9];
            szBuf[0] = '\0';
            Checked::gcvt_s(szBuf, _countof(szBuf), *pVal, ndec);
            size_t nLen = strlen(szBuf);
            if (nLen && szBuf[nLen-1] == '.')
            {
                szBuf[--nLen] = '\0';
            }

            hr = pStream->WriteStream(szBuf, (int)nLen, NULL);
            break;
        }
    }

    return hr;
}

template <>
inline HRESULT AtlGenXMLValue<float>(IWriteStream *pStream, float *pVal)
{
	if ((pStream == NULL) || (pVal == NULL))
	{
		return E_INVALIDARG;
	}

	double d = *pVal;

	return AtlGenXMLValue(pStream, &d);
}

template <>
inline HRESULT AtlGenXMLValue<bool>(IWriteStream *pStream, bool *pVal)
{
	if ((pStream == NULL) || (pVal == NULL))
	{
		return E_INVALIDARG;
	}

	if (*pVal == true)
	{
		return pStream->WriteStream("true", sizeof("true")-1, NULL);
	}

	return pStream->WriteStream("false", sizeof("false")-1, NULL);
}

inline HRESULT AtlGenXMLBlobValue(
	IWriteStream *pStream, 
	ATLSOAP_BLOB *pVal, 
	IAtlMemMgr *pMemMgr, 
	bool bHex = false)
{
	if ((pStream == NULL) || (pVal == NULL) || (pMemMgr == NULL))
	{
		return E_INVALIDARG;
	}

	HRESULT hr = E_FAIL;
	int nLength;
	if (!bHex)
	{
		nLength = Base64EncodeGetRequiredLength(pVal->size, ATLSOAP_BASE64_FLAGS);
	}
	else
	{
		nLength = AtlHexEncodeGetRequiredLength(pVal->size);
	}

	char *pEnc = (char *) pMemMgr->Allocate(nLength);
	if (pEnc != NULL)
	{
		BOOL bRet;
		if (!bHex)
		{
			bRet = Base64Encode(pVal->data, pVal->size, pEnc, &nLength, ATLSOAP_BASE64_FLAGS);
		}
		else
		{
			bRet = AtlHexEncode(pVal->data, pVal->size, pEnc, &nLength);
		}
		if (bRet)
		{
			hr = pStream->WriteStream(pEnc, nLength, NULL);
		}

		pMemMgr->Free(pEnc);
	}

	return hr;
}

template <typename T>
inline HRESULT AtlCleanupValue(T * /*pVal*/)
{
	return S_OK;
}

inline HRESULT AtlCleanupBlobValue(ATLSOAP_BLOB *pVal, IAtlMemMgr *pMemMgr)
{
	if ((pVal == NULL) || (pMemMgr == NULL))
	{
		return E_INVALIDARG;
	}

	if (pVal->data != NULL)
	{
		pMemMgr->Free(pVal->data);
		pVal->data = NULL;
		pVal->size = 0;
	}

	return S_OK;
}

template <>
inline HRESULT AtlCleanupValue<ATLSOAP_BLOB>(ATLSOAP_BLOB *pVal)
{
	ATLTRACE( _T("Warning: AtlCleanupValue<ATLSOAP_BLOB> was called -- assuming CRT allocator.\r\n") );

	if (pVal == NULL)
	{
		return E_INVALIDARG;
	}

	if (pVal->data != NULL)
	{
		free(pVal->data);
		pVal->data = NULL;
		pVal->size = 0;
	}

	return S_OK;
}

template <>
inline HRESULT AtlCleanupValue<BSTR>(BSTR *pVal)
{
	if (pVal == NULL)
	{
		// should never happen
		ATLASSERT( FALSE );
		return E_INVALIDARG;
	}

	if ((*pVal) != NULL)
	{
		// null strings are okay
		SysFreeString(*pVal);
		*pVal = NULL;
	}

	return S_OK;
}

template <typename T>
inline HRESULT AtlCleanupValueEx(T *pVal, IAtlMemMgr *pMemMgr)
{
	pMemMgr;

	return AtlCleanupValue(pVal);
}

template <>
inline HRESULT AtlCleanupValueEx<ATLSOAP_BLOB>(ATLSOAP_BLOB *pVal, IAtlMemMgr *pMemMgr)
{
	return AtlCleanupBlobValue(pVal, pMemMgr);
}

// single dimensional arrays
template <typename T>
inline HRESULT AtlCleanupArray(T *pArray, int nCnt)
{
	if (pArray == NULL)
	{
		return E_INVALIDARG;
	}

	for (int i=0; i<nCnt; i++)
	{
		AtlCleanupValue(&pArray[i]);
	}

	return S_OK;
}


template <typename T>
inline HRESULT AtlCleanupArrayEx(T *pArray, int nCnt, IAtlMemMgr *pMemMgr)
{
	if (pArray == NULL)
	{
		return E_INVALIDARG;
	}

	for (int i=0; i<nCnt; i++)
	{
		AtlCleanupValueEx(&pArray[i], pMemMgr);
	}

	return S_OK;
}


// multi-dimensional arrays
template <typename T>
inline HRESULT AtlCleanupArrayMD(T *pArray, const int *pDims)
{
	if ((pArray == NULL) || (pDims == NULL))
	{
		return E_INVALIDARG;
	}

	// calculate size
	int nCnt = 1;
	for (int i=1; i<=pDims[0]; i++)
	{
		nCnt*= pDims[i];
	}

	return AtlCleanupArray(pArray, nCnt);
}

template <typename T>
inline HRESULT AtlCleanupArrayMDEx(T *pArray, const int *pDims, IAtlMemMgr *pMemMgr)
{
	if ((pArray == NULL) || (pDims == NULL))
	{
		return E_INVALIDARG;
	}

	// calculate size
	int nCnt = 1;
	for (int i=1; i<=pDims[0]; i++)
	{
		nCnt*= pDims[i];
	}

	return AtlCleanupArrayEx(pArray, nCnt, pMemMgr);
}


#pragma pack(push,_ATL_PACKING)
namespace ATL
{

////////////////////////////////////////////////////////////////////////////////
//
// CSAXSoapErrorHandler
//
////////////////////////////////////////////////////////////////////////////////

class CSAXSoapErrorHandler : public ISAXErrorHandler
{
private:

	CFixedStringT<CStringW, 256> m_strParseError;

public:
	virtual ~CSAXSoapErrorHandler()
	{
	}

	HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
	{
		if (!ppv)
		{
			return E_POINTER;
		}

		if (InlineIsEqualGUID(riid, __uuidof(ISAXErrorHandler)) ||
			InlineIsEqualGUID(riid, __uuidof(IUnknown)))
		{
			*ppv = static_cast<ISAXErrorHandler*>(this);
			return S_OK;
		}
		return E_NOINTERFACE;
	}

	ULONG __stdcall AddRef()
	{
		return 1;
	}

	ULONG __stdcall Release()
	{
		return 1;
	}

	const CStringW& GetParseError()
	{
		return m_strParseError;
	}

	HRESULT __stdcall error( 
		ISAXLocator *pLocator,
		const wchar_t *wszErrorMessage,
		HRESULT hrErrorCode)
	{
		(pLocator);
		(wszErrorMessage);
		(hrErrorCode);

		ATLTRACE( _T("ATLSOAP: parse error: %ws\r\n"), wszErrorMessage );

		_ATLTRY
		{
			m_strParseError = wszErrorMessage;
		}
		_ATLCATCHALL()
		{
			return E_FAIL;
		}

		return hrErrorCode;
	}

	HRESULT __stdcall fatalError(
		ISAXLocator  *pLocator,
		const wchar_t *wszErrorMessage,
		HRESULT hrErrorCode)
	{
		(pLocator);
		(wszErrorMessage);
		(hrErrorCode);

		ATLTRACE( _T("ATLSOAP: fatal parse error: %ws\r\n"), wszErrorMessage );

		_ATLTRY
		{
			m_strParseError = wszErrorMessage;
		}
		_ATLCATCHALL()
		{
			return E_FAIL;
		}

		return hrErrorCode;
	}

	HRESULT __stdcall ignorableWarning(
		ISAXLocator  *pLocator,
		const wchar_t *wszErrorMessage,
		HRESULT hrErrorCode)
	{
		(pLocator);
		(wszErrorMessage);
		(hrErrorCode);

		ATLTRACE( _T("ATLSOAP: ignorable warning: %ws\r\n"), wszErrorMessage );

		return hrErrorCode;
	}
};

////////////////////////////////////////////////////////////////////////////////
//
// ISAXContentHandlerImpl
//
////////////////////////////////////////////////////////////////////////////////

class ISAXContentHandlerImpl : 
	public ISAXContentHandler
{
public:

	//
	// ISAXContentHandler interface
	//

	HRESULT __stdcall putDocumentLocator(ISAXLocator  * /*pLocator*/)
	{
		return S_OK;
	}

	HRESULT __stdcall startDocument()
	{
		return S_OK;
	}

	HRESULT __stdcall endDocument()
	{
		return S_OK;
	}

	HRESULT __stdcall startPrefixMapping(
		 const wchar_t  * /*wszPrefix*/,
		 int /*cchPrefix*/,
		 const wchar_t  * /*wszUri*/,
		 int /*cchUri*/)
	{
		return S_OK;
	}

	HRESULT __stdcall endPrefixMapping( 
		 const wchar_t  * /*wszPrefix*/,
		 int /*cchPrefix*/)
	{
		return S_OK;
	}

	HRESULT __stdcall startElement( 
		 const wchar_t  * /*wszNamespaceUri*/,
		 int /*cchNamespaceUri*/,
		 const wchar_t  * /*wszLocalName*/,
		 int /*cchLocalName*/,
		 const wchar_t  * /*wszQName*/,
		 int /*cchQName*/,
		 ISAXAttributes  * /*pAttributes*/)
	{
		return S_OK;
	}

	HRESULT __stdcall endElement( 
		 const wchar_t  * /*wszNamespaceUri*/,
		 int /*cchNamespaceUri*/,
		 const wchar_t  * /*wszLocalName*/,
		 int /*cchLocalName*/,
		 const wchar_t  * /*wszQName*/,
		 int /*cchQName*/)
	{
		return S_OK;
	}

	HRESULT __stdcall characters( 
		 const wchar_t  * /*wszChars*/,
		 int /*cchChars*/)
	{
		return S_OK;
	}

	HRESULT __stdcall ignorableWhitespace( 
		 const wchar_t  * /*wszChars*/,
		 int /*cchChars*/)
	{
		return S_OK;
	}

	HRESULT __stdcall processingInstruction( 
		 const wchar_t  * /*wszTarget*/,
		 int /*cchTarget*/,
		 const wchar_t  * /*wszData*/,
		 int /*cchData*/)
	{
		return S_OK;
	}

	HRESULT __stdcall skippedEntity( 
		 const wchar_t  * /*wszName*/,
		 int /*cchName*/)
	{
		return S_OK;
	}
}; // class ISAXContentHandlerImpl

////////////////////////////////////////////////////////////////////////////////
//
// SAX skip element handler utility class
// (skip an element and all its child elements)
//
////////////////////////////////////////////////////////////////////////////////

class CSkipHandler : public ISAXContentHandlerImpl
{
public:
	virtual ~CSkipHandler()
	{
	}
	
	HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
	{
		if (ppv == NULL)
		{
			return E_POINTER;
		}

		*ppv = NULL;

		if (InlineIsEqualGUID(riid, IID_IUnknown) ||
			InlineIsEqualGUID(riid, IID_ISAXContentHandler))
		{
			*ppv = static_cast<ISAXContentHandler *>(this);
			return S_OK;
		}

		return E_NOINTERFACE;
	}

	ULONG __stdcall AddRef()
	{
		return 1;
	}

	ULONG __stdcall Release()
	{
		return 1;
	}

private:

	DWORD m_dwReset;
	CComPtr<ISAXXMLReader> m_spReader;
	CComPtr<ISAXContentHandler> m_spParent;

	DWORD DisableReset(DWORD dwCnt = 1)
	{
		m_dwReset += dwCnt;

		return m_dwReset;
	}

	DWORD EnableReset()
	{
		if (m_dwReset > 0)
		{
			--m_dwReset;
		}

		return m_dwReset;
	}

public:

	CSkipHandler(ISAXContentHandler *pParent = NULL, ISAXXMLReader *pReader = NULL)
		: m_spParent(pParent), m_spReader(pReader), m_dwReset(1)
	{
	}

	void SetParent(ISAXContentHandler *pParent)
	{
		m_spParent = pParent;
	}
	void DetachParent()
	{		
		m_spParent.Detach();
	}

	void SetReader(ISAXXMLReader *pReader)
	{
		m_spReader = pReader;
	}

	HRESULT __stdcall startElement( 
		 const wchar_t  * /*wszNamespaceUri*/,
		 int /*cchNamespaceUri*/,
		 const wchar_t  * /*wszLocalName*/,
		 int /*cchLocalName*/,
		 const wchar_t  * /*wszQName*/,
		 int /*cchQName*/,
		 ISAXAttributes  * /*pAttributes*/)
	{
		DisableReset();
		return S_OK;
	}

	HRESULT __stdcall endElement( 
		 const wchar_t  * /*wszNamespaceUri*/,
		 int /*cchNamespaceUri*/,
		 const wchar_t  * /*wszLocalName*/,
		 int /*cchLocalName*/,
		 const wchar_t  * /*wszQName*/,
		 int /*cchQName*/)
	{
		if (EnableReset() == 0)
		{
			m_spReader->putContentHandler(m_spParent);
		}

		return S_OK;
	}
}; // class CSkipHandler


////////////////////////////////////////////////////////////////////////////////
//
// SAX string builder class
//
////////////////////////////////////////////////////////////////////////////////

class CSAXStringBuilder : public ISAXContentHandlerImpl
{
public:

	HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
	{
		if (ppv == NULL)
		{
			return E_POINTER;
		}

		*ppv = NULL;

		if (InlineIsEqualGUID(riid, IID_IUnknown) ||
			InlineIsEqualGUID(riid, IID_ISAXContentHandler))
		{
			*ppv = static_cast<ISAXContentHandler *>(this);
			return S_OK;
		}

		return E_NOINTERFACE;
	}

	ULONG __stdcall AddRef()
	{
		return 1;
	}

	ULONG __stdcall Release()
	{
		return 1;
	}

private:

	ISAXContentHandler * m_pParent;
	ISAXXMLReader * m_pReader;
	DWORD m_dwReset;
	CFixedStringT<CStringW, 64> m_str;

	DWORD DisableReset(DWORD dwReset = 1)
	{
		m_dwReset+= dwReset;

		return m_dwReset;
	}

	DWORD EnableReset()
	{
		if (m_dwReset > 0)
		{
			--m_dwReset;
		}

		return m_dwReset;
	}

public:

	CSAXStringBuilder(ISAXXMLReader *pReader = NULL, ISAXContentHandler *pParent = NULL)
		:m_pReader(pReader), m_pParent(pParent), m_dwReset(0)
	{
	}
	
	virtual ~CSAXStringBuilder()
	{
	}

	void SetReader(ISAXXMLReader *pReader)
	{
		m_pReader = pReader;
	}

	void SetParent(ISAXContentHandler *pParent)
	{
		m_pParent = pParent;
	}

	const CStringW& GetString()
	{
		return m_str;
	}

	void Clear()
	{
		m_str.Empty();
		m_dwReset = 0;
	}

	HRESULT __stdcall startElement( 
		 const wchar_t  * /*wszNamespaceUri*/,
		 int /*cchNamespaceUri*/,
		 const wchar_t  * /*wszLocalName*/,
		 int /*cchLocalName*/,
		 const wchar_t  *wszQName,
		 int cchQName,
		 ISAXAttributes  *pAttributes)
	{
		if (m_dwReset == 0)
		{
			// if there is unescaped, nested XML, must disable 
			// an additional time for the first element
			DisableReset();
		}
		DisableReset();

		int nAttrs = 0;
		HRESULT hr = pAttributes->getLength(&nAttrs);

		_ATLTRY
		{
			if (SUCCEEDED(hr))
			{
				m_str.Append(L"<", 1);
				m_str.Append(wszQName, cchQName);

				const wchar_t *wszAttrNamespaceUri = NULL;
				const wchar_t *wszAttrLocalName = NULL;
				const wchar_t *wszAttrQName = NULL;
				const wchar_t *wszAttrValue = NULL;
				int cchAttrUri = 0;
				int cchAttrLocalName = 0;
				int cchAttrQName = 0;
				int cchAttrValue = 0;

				for (int i=0; i<nAttrs; i++)
				{
					hr = pAttributes->getName(i, &wszAttrNamespaceUri, &cchAttrUri, 
						&wszAttrLocalName, &cchAttrLocalName, &wszAttrQName, &cchAttrQName);

					if (FAILED(hr))
					{
						ATLTRACE( _T("ATLSOAP: CSAXStringBuilder::startElement -- MSXML error.\r\n") );

						break;
					}

					m_str.Append(L" ", 1);
					m_str.Append(wszAttrQName, cchAttrQName);

					hr = pAttributes->getValue(i, &wszAttrValue, &cchAttrValue);

					if (FAILED(hr))
					{
						ATLTRACE( _T("ATLSOAP: CSAXStringBuilder::startElement -- MSXML error.\r\n") );

						break;
					}

					m_str.Append(L"=\"", sizeof("=\"")-1);
					if (cchAttrValue != 0)
					{
						m_str.Append(wszAttrValue, cchAttrValue);
					}
					m_str.Append(L"\"", 1);
				}

				if (SUCCEEDED(hr))
				{
					m_str.Append(L">", 1);
				}
			}
		}
		_ATLCATCHALL()
		{
			ATLTRACE( _T("ATLSOAP: CSAXStringBuilder::startElement -- out of memory.\r\n") );

			hr = E_OUTOFMEMORY;
		}

		return hr;
	}

	HRESULT __stdcall endElement( 
		 const wchar_t  * wszNamespaceUri,
		 int cchNamespaceUri,
		 const wchar_t  * wszLocalName,
		 int cchLocalName,
		 const wchar_t  *wszQName,
		 int cchQName)
	{
		HRESULT hr = S_OK;
		_ATLTRY
		{
			if (EnableReset() == 0)
			{
				hr = m_pParent->characters((LPCWSTR) m_str, m_str.GetLength());
				if (SUCCEEDED(hr))
				{
					hr = m_pParent->endElement(wszNamespaceUri, cchNamespaceUri,
							wszLocalName, cchLocalName, wszQName, cchQName);
				}

				m_pReader->putContentHandler(m_pParent);
			}

			if (m_dwReset > 0)
			{
				m_str.Append(L"</", 2);
				m_str.Append(wszQName, cchQName);
				m_str.Append(L">", 1);
			}
		}
		_ATLCATCHALL()
		{
			ATLTRACE( _T("ATLSOAP: CSAXStringBuilder::endElement -- out of memory.\r\n") );

			hr = E_OUTOFMEMORY;
		}

		return hr;
	}

	HRESULT __stdcall characters(
		 const wchar_t  *wszChars,
		 int cchChars)
	{
		_ATLTRY
		{
			m_str.Append(wszChars, cchChars);
		}
		_ATLCATCHALL()
		{
			ATLTRACE( _T("ATLSOAP: CSAXStringBuilder::characters -- out of memory.\r\n") );

			return E_OUTOFMEMORY;
		}

		return S_OK;
	}

	HRESULT __stdcall ignorableWhitespace( 
		 const wchar_t  *wszChars,
		 int cchChars)
	{
		_ATLTRY
		{
			m_str.Append(wszChars, cchChars);
		}
		_ATLCATCHALL()
		{
			ATLTRACE( _T("ATLSOAP: CSAXStringBuilder::ignorableWhitespace -- out of memory.\r\n") );

			return E_OUTOFMEMORY;
		}

		return S_OK;
	}
}; // class CSAXStringBuilder

} // namespace ATL
#pragma pack(pop)

////////////////////////////////////////////////////////////////////////////////
//
// SOAP data structure definitions
//
////////////////////////////////////////////////////////////////////////////////

//
// *****************************  WARNING  *****************************
// THESE STRUCTURES ARE INTERNAL ONLY, FOR USE WITH THE ATL SERVER SOAP 
// ATTRIBUTES. USERS SHOULD NOT USE THESE TYPES DIRECTLY. ABSOLUTELY NO 
// GUARANTEES ARE MADE ABOUT BACKWARD COMPATIBILITY FOR DIRECT USE OF 
// THESE TYPES.
//

////////////////////////////////////////////////////////////////////////////////
//
// BEGIN PRIVATE DEFINITIONS
//
////////////////////////////////////////////////////////////////////////////////

inline HRESULT AtlSoapGetArraySize(ISAXAttributes *pAttributes, size_t *pnSize, 
	const wchar_t **pwszTypeStart = NULL, const wchar_t **pwszTypeEnd = NULL)
{
	if (pnSize == NULL)
	{
		return E_POINTER;
	}

	if (pAttributes == NULL)
	{
		return E_INVALIDARG;
	}

	*pnSize = 0;

	HRESULT hr = S_OK;

	_ATLTRY
	{
		const wchar_t *wszTmp;
		int cch;

		hr = GetAttribute(pAttributes, L"arrayType", sizeof("arrayType")-1, 
			&wszTmp, &cch, SOAPENC_NAMESPACEW, sizeof(SOAPENC_NAMESPACEA)-1);

		if ((SUCCEEDED(hr)) && (wszTmp != NULL))
		{
			hr = E_FAIL;

			CFixedStringT<CStringW, 1024> wstrArrayType(wszTmp, cch);
			const wchar_t *wsz = static_cast<LPCWSTR>(wstrArrayType);

			const wchar_t *wszTypeStart = NULL;
			const wchar_t *wszTypeEnd = NULL;

			// skip spaces
			while (iswspace(*wsz) != 0)
			{
				wsz++;
			}

			// no need to walk the string if the caller is not interested
			if ((pwszTypeStart != NULL) && (pwszTypeEnd != NULL))
			{
				wszTypeStart = wsz;
				wszTypeEnd = wcschr(wszTypeStart, L':');
				if (wszTypeEnd != NULL)
				{
					wszTypeStart = wszTypeEnd+1;
				}
			}

			// SOAP Section 5 encodings are of the form:
			//   <soap_enc namespace>:arrayType="<type_qname>[dim1(,dim_i)*]
			//   for example: SOAP-ENC:arrayType="xsd:string[2,4]"

			wsz = wcschr(wsz, L'[');
			if (wsz != NULL)
			{
				wszTypeEnd = wsz-1;
				if (wsz[1] == ']')
				{
					return S_FALSE;
				}

				*pnSize = 1;

				// get the size of each dimension
				while (wsz != NULL)
				{
					wsz++;
					int nDim = _wtoi(wsz);
					if (nDim < 0)
					{
						hr = E_FAIL;
						break;
					}
					*pnSize *= (size_t) nDim;
					if (!nDim)
					{
						break;
					}

					wsz = wcschr(wsz, L',');
				}

				if ((pwszTypeStart != NULL) && (pwszTypeEnd != NULL))
				{
					*pwszTypeStart = wszTypeStart;
					*pwszTypeEnd = wszTypeEnd;
				}

				hr = S_OK;
			}
		}
		else
		{
			// not a section-5 encoding
			hr = S_FALSE;
		}
	}
	_ATLCATCHALL()
	{
		hr = E_OUTOFMEMORY;
	}

	return hr;
}

inline size_t AtlSoapGetArrayDims(const int *pDims)
{
	if (pDims == NULL)
	{
		return 0;
	}

	size_t nRet = 1;
	for (int i=1; i<=pDims[0]; i++)
	{
		nRet *= pDims[i];
	}

	return nRet;
}

enum SOAPFLAGS
{
	SOAPFLAG_NONE           = 0x00000000,
	SOAPFLAG_IN             = 0x00000001,
	SOAPFLAG_OUT            = 0x00000002,
	SOAPFLAG_RETVAL         = 0x00000004,
	SOAPFLAG_DYNARR         = 0x00000008,
	SOAPFLAG_FIXEDARR       = 0x00000010,
	SOAPFLAG_MUSTUNDERSTAND = 0x00000020,
	SOAPFLAG_UNKSIZE        = 0x00000040,
	SOAPFLAG_READYSTATE     = 0x00000080,
	SOAPFLAG_FIELD          = 0x00000100,
	SOAPFLAG_NOMARSHAL      = 0x00000200,
	SOAPFLAG_NULLABLE       = 0x00000400,
	SOAPFLAG_DOCUMENT       = 0x00000800,
	SOAPFLAG_RPC            = 0x00001000,
	SOAPFLAG_LITERAL        = 0x00002000,
	SOAPFLAG_ENCODED        = 0x00004000,
	SOAPFLAG_PID            = 0x00008000,
	SOAPFLAG_PAD            = 0x00010000,
	SOAPFLAG_CHAIN          = 0x00020000,
	SOAPFLAG_SIZEIS         = 0x00040000,
	SOAPFLAG_DYNARRWRAPPER  = 0x00080000
};

enum SOAPMAPTYPE
{
	SOAPMAP_ERR = 0,
	SOAPMAP_ENUM,
	SOAPMAP_FUNC,
	SOAPMAP_STRUCT,
	SOAPMAP_UNION,
	SOAPMAP_HEADER,
	SOAPMAP_PARAM
};

struct _soapmap;

struct _soapmapentry
{
	ULONG nHash;
	const char * szField;
	const WCHAR * wszField;
	int cchField;
	int nVal;
	DWORD dwFlags;

	size_t nOffset;
	const int * pDims;

	const _soapmap * pChain;

	int nSizeIs;

	ULONG nNamespaceHash;
	const char *szNamespace;
	const wchar_t *wszNamespace;
	int cchNamespace;
};

struct _soapmap
{
	ULONG nHash;
	const char * szName;
	const wchar_t * wszName;
	int cchName;
	int cchWName;
	SOAPMAPTYPE mapType;
	const _soapmapentry * pEntries;
	size_t nElementSize;
	size_t nElements;
	int nRetvalIndex;

	DWORD dwCallFlags;

	ULONG nNamespaceHash;
	const char *szNamespace;
	const wchar_t *wszNamespace;
	int cchNamespace;
};

enum SOAPTYPES
{
	SOAPTYPE_ERR = -2,
	SOAPTYPE_UNK = -1,
	SOAPTYPE_STRING = 0,
	SOAPTYPE_BOOLEAN,
	SOAPTYPE_FLOAT,
	SOAPTYPE_DOUBLE,
	SOAPTYPE_DECIMAL,
	SOAPTYPE_DURATION,
	SOAPTYPE_HEXBINARY,
	SOAPTYPE_BASE64BINARY,
	SOAPTYPE_ANYURI,
	SOAPTYPE_ID,
	SOAPTYPE_IDREF,
	SOAPTYPE_ENTITY,
	SOAPTYPE_NOTATION,
	SOAPTYPE_QNAME,
	SOAPTYPE_NORMALIZEDSTRING,
	SOAPTYPE_TOKEN,
	SOAPTYPE_LANGUAGE,
	SOAPTYPE_IDREFS,
	SOAPTYPE_ENTITIES,
	SOAPTYPE_NMTOKEN,
	SOAPTYPE_NMTOKENS,
	SOAPTYPE_NAME,
	SOAPTYPE_NCNAME,
	SOAPTYPE_INTEGER,
	SOAPTYPE_NONPOSITIVEINTEGER,
	SOAPTYPE_NEGATIVEINTEGER,
	SOAPTYPE_LONG,
	SOAPTYPE_INT,
	SOAPTYPE_SHORT,
	SOAPTYPE_BYTE,
	SOAPTYPE_NONNEGATIVEINTEGER,
	SOAPTYPE_UNSIGNEDLONG,
	SOAPTYPE_UNSIGNEDINT,
	SOAPTYPE_UNSIGNEDSHORT,
	SOAPTYPE_UNSIGNEDBYTE,
	SOAPTYPE_POSITIVEINTEGER,
	SOAPTYPE_DATETIME,
	SOAPTYPE_TIME,
	SOAPTYPE_DATE,
	SOAPTYPE_GMONTH,
	SOAPTYPE_GYEARMONTH,
	SOAPTYPE_GYEAR,
	SOAPTYPE_GMONTHDAY,
	SOAPTYPE_GDAY,

	SOAPTYPE_USERBASE = 0x00001000
};

inline ULONG AtlSoapHashStr(const char * sz)
{
	ULONG nHash = 0;
	while (*sz != 0)
	{
		nHash = (nHash<<5)+nHash+(*sz);
		sz++;
	}

	return nHash;
}

inline ULONG AtlSoapHashStr(const wchar_t * sz)
{
	ULONG nHash = 0;
	while (*sz != 0)
	{
		nHash = (nHash<<5)+nHash+(*sz);
		sz++;
	}

	return nHash;
}

inline ULONG AtlSoapHashStr(const char * sz, int cch)
{
	ULONG nHash = 0;
	for (int i=0; i<cch; i++)
	{
		nHash = (nHash<<5)+nHash+(*sz);
		sz++;
	}

	return nHash;
}

inline ULONG AtlSoapHashStr(const wchar_t * sz, int cch)
{
	ULONG nHash = 0;
	for (int i=0; i<cch; i++)
	{
		nHash = (nHash<<5)+nHash+(*sz);
		sz++;
	}

	return nHash;
}

inline size_t AtlSoapGetElementSize(SOAPTYPES type)
{
	size_t nRet;
	switch (type)
	{
		case SOAPTYPE_BOOLEAN:
			nRet = sizeof(bool);
			break;
		case SOAPTYPE_FLOAT:
			nRet = sizeof(float);
			break;
		case SOAPTYPE_DOUBLE: 
		case SOAPTYPE_DECIMAL:
			nRet = sizeof(double);
			break;
		case SOAPTYPE_HEXBINARY:
		case SOAPTYPE_BASE64BINARY:
			nRet = sizeof(ATLSOAP_BLOB);
			break;
		case SOAPTYPE_INTEGER: 
		case SOAPTYPE_NONPOSITIVEINTEGER:
		case SOAPTYPE_NEGATIVEINTEGER:
		case SOAPTYPE_LONG:
			nRet = sizeof(__int64);
			break;
		case SOAPTYPE_INT:
			nRet = sizeof(int);
			break;
		case SOAPTYPE_SHORT:
			nRet = sizeof(short);
			break;
		case SOAPTYPE_BYTE:
			nRet = sizeof(char);
			break;
		case SOAPTYPE_POSITIVEINTEGER:
		case SOAPTYPE_NONNEGATIVEINTEGER:
		case SOAPTYPE_UNSIGNEDLONG:
			nRet = sizeof(unsigned __int64);
			break;
		case SOAPTYPE_UNSIGNEDINT:
			nRet = sizeof(unsigned int);
			break;
		case SOAPTYPE_UNSIGNEDSHORT:
			nRet = sizeof(unsigned short);
			break;
		case SOAPTYPE_UNSIGNEDBYTE:
			nRet = sizeof(unsigned char);
			break;
		default:
			if ((type != SOAPTYPE_ERR) && (type != SOAPTYPE_UNK) && (type != SOAPTYPE_USERBASE))
			{
				// treat as string
				nRet = sizeof(BSTR);
			}
			else
			{
				ATLTRACE( _T("ATLSOAP: AtlSoapGetElementSize -- internal error.\r\n") );
				// should never get here
				ATLASSERT( FALSE );
				nRet = 0;
			}
			break;
	}

	return nRet;
}

inline HRESULT AtlSoapGetElementValue(const wchar_t *wsz, int cch, 
	void *pVal, SOAPTYPES type, IAtlMemMgr *pMemMgr)
{
	HRESULT hr = E_FAIL;

	switch (type)
	{
		case SOAPTYPE_BOOLEAN:
			hr = AtlGetSAXValue((bool *)pVal, wsz, cch);
			break;
		case SOAPTYPE_FLOAT:
			hr = AtlGetSAXValue((float *)pVal, wsz, cch);
			break;
		case SOAPTYPE_DOUBLE: 
		case SOAPTYPE_DECIMAL:
			hr = AtlGetSAXValue((double *)pVal, wsz, cch);
			break;
		case SOAPTYPE_HEXBINARY:
			hr = AtlGetSAXBlobValue((ATLSOAP_BLOB *)pVal, wsz, cch, pMemMgr, true);
			break;
		case SOAPTYPE_BASE64BINARY:
			hr = AtlGetSAXBlobValue((ATLSOAP_BLOB *)pVal, wsz, cch, pMemMgr, false);
			break;

		case SOAPTYPE_INTEGER: 
		case SOAPTYPE_NONPOSITIVEINTEGER:
		case SOAPTYPE_NEGATIVEINTEGER:
		case SOAPTYPE_LONG:
			hr = AtlGetSAXValue((__int64 *)pVal, wsz, cch);
			break;
		case SOAPTYPE_INT:
			hr = AtlGetSAXValue((int *)pVal, wsz, cch);
			break;
		case SOAPTYPE_SHORT:
			hr = AtlGetSAXValue((short *)pVal, wsz, cch);
			break;
		case SOAPTYPE_BYTE:
			hr = AtlGetSAXValue((char *)pVal, wsz, cch);
			break;
		case SOAPTYPE_POSITIVEINTEGER:
		case SOAPTYPE_NONNEGATIVEINTEGER:
		case SOAPTYPE_UNSIGNEDLONG:
			hr = AtlGetSAXValue((unsigned __int64 *)pVal, wsz, cch);
			break;
		case SOAPTYPE_UNSIGNEDINT:
			hr = AtlGetSAXValue((unsigned int *)pVal, wsz, cch);
			break;
		case SOAPTYPE_UNSIGNEDSHORT:
			hr = AtlGetSAXValue((unsigned short *)pVal, wsz, cch);
			break;
		case SOAPTYPE_UNSIGNEDBYTE:
			hr = AtlGetSAXValue((unsigned char *)pVal, wsz, cch);
			break;
		default:
			if ((type != SOAPTYPE_ERR) && (type != SOAPTYPE_UNK) && (type != SOAPTYPE_USERBASE))
			{
				hr = AtlGetSAXValue((BSTR *)pVal, wsz, cch);
			}
#ifdef _DEBUG
			else
			{
				ATLTRACE( _T("ATLSOAP: AtlSoapGetElementValue -- internal error.\r\n") );

				// should never get here
				ATLASSERT( FALSE );
			}
#endif
			break;
	}

	return hr;
}

inline HRESULT AtlSoapGenElementValue(void *pVal, IWriteStream *pStream, SOAPTYPES type, IAtlMemMgr *pMemMgr)
{
	HRESULT hr = E_FAIL;

	switch (type)
	{
		case SOAPTYPE_BOOLEAN:
			hr = AtlGenXMLValue(pStream, (bool *)pVal);
			break;
		case SOAPTYPE_FLOAT:
			hr = AtlGenXMLValue(pStream, (float *)pVal);
			break;
		case SOAPTYPE_DOUBLE: 
		case SOAPTYPE_DECIMAL:
			hr = AtlGenXMLValue(pStream, (double *)pVal);
			break;
		case SOAPTYPE_HEXBINARY:
			hr = AtlGenXMLBlobValue(pStream, (ATLSOAP_BLOB *)pVal, pMemMgr, true);
			break;
		case SOAPTYPE_BASE64BINARY:
			hr = AtlGenXMLBlobValue(pStream, (ATLSOAP_BLOB *)pVal, pMemMgr, false);
			break;

		case SOAPTYPE_INTEGER: 
		case SOAPTYPE_NONPOSITIVEINTEGER:
		case SOAPTYPE_NEGATIVEINTEGER:
		case SOAPTYPE_LONG:
			hr = AtlGenXMLValue(pStream, (__int64 *)pVal);
			break;
		case SOAPTYPE_INT:
			hr = AtlGenXMLValue(pStream, (int *)pVal);
			break;
		case SOAPTYPE_SHORT:
			hr = AtlGenXMLValue(pStream, (short *)pVal);
			break;
		case SOAPTYPE_BYTE:
			hr = AtlGenXMLValue(pStream, (char *)pVal);
			break;
		case SOAPTYPE_POSITIVEINTEGER:
		case SOAPTYPE_NONNEGATIVEINTEGER:
		case SOAPTYPE_UNSIGNEDLONG:
			hr = AtlGenXMLValue(pStream, (unsigned __int64 *)pVal);
			break;
		case SOAPTYPE_UNSIGNEDINT:
			hr = AtlGenXMLValue(pStream, (unsigned int *)pVal);
			break;
		case SOAPTYPE_UNSIGNEDSHORT:
			hr = AtlGenXMLValue(pStream, (unsigned short *)pVal);
			break;
		case SOAPTYPE_UNSIGNEDBYTE:
			hr = AtlGenXMLValue(pStream, (unsigned char *)pVal);
			break;
		default:
			if ((type != SOAPTYPE_ERR) && (type != SOAPTYPE_UNK) && (type != SOAPTYPE_USERBASE))
			{
				hr = AtlGenXMLValue(pStream, (BSTR *)pVal);
			}
#ifdef _DEBUG
			else
			{
				ATLTRACE( _T("ATLSOAP: AtlSoapGenElementValue -- internal error.\r\n" ) );

				// should never get here
				ATLASSERT( FALSE );
			}
#endif
			break;
	}
	return hr;
}

inline HRESULT AtlSoapCleanupElement(void *pVal, SOAPTYPES type, IAtlMemMgr *pMemMgr)
{
	HRESULT hr = S_OK;

	switch (type)
	{
		case SOAPTYPE_BOOLEAN:
		case SOAPTYPE_FLOAT:
		case SOAPTYPE_DOUBLE: 
		case SOAPTYPE_DECIMAL:
		case SOAPTYPE_INT:
		case SOAPTYPE_INTEGER: 
		case SOAPTYPE_NONPOSITIVEINTEGER:
		case SOAPTYPE_NEGATIVEINTEGER:
		case SOAPTYPE_LONG:
		case SOAPTYPE_SHORT:
		case SOAPTYPE_BYTE:
		case SOAPTYPE_POSITIVEINTEGER:
		case SOAPTYPE_NONNEGATIVEINTEGER:
		case SOAPTYPE_UNSIGNEDLONG:
		case SOAPTYPE_UNSIGNEDINT:
		case SOAPTYPE_UNSIGNEDSHORT:
		case SOAPTYPE_UNSIGNEDBYTE:
			break;

		case SOAPTYPE_HEXBINARY:
		case SOAPTYPE_BASE64BINARY:
			hr = AtlCleanupBlobValue((ATLSOAP_BLOB *)pVal, pMemMgr);
			break;

		default:
			if ((type != SOAPTYPE_ERR) && (type != SOAPTYPE_UNK) && (type != SOAPTYPE_USERBASE))
			{
				// treat as string
				hr = AtlCleanupValue((BSTR *)pVal);
			}
#ifdef _DEBUG
			else
			{
				ATLTRACE( _T("ATLSOAP: AtlSoapCleanupElement -- internal error.\r\n" ) );

				// should never get here
				ATLASSERT( FALSE );
			}
#endif
			break;
	}

	return hr;
}

////////////////////////////////////////////////////////////////////////////////
//
// END PRIVATE DEFINITIONS
//
////////////////////////////////////////////////////////////////////////////////

#define SOAP_ENVELOPEA "Envelope"
#define SOAP_ENVELOPEW ATLSOAP_MAKEWIDESTR( SOAP_ENVELOPEA )

#define SOAP_HEADERA   "Header"
#define SOAP_HEADERW   ATLSOAP_MAKEWIDESTR( SOAP_HEADERA )

#define SOAP_BODYA     "Body"
#define SOAP_BODYW     ATLSOAP_MAKEWIDESTR( SOAP_BODYA )


#pragma pack(push,_ATL_PACKING)
namespace ATL
{

//
// SOAP fault helpers
//

enum SOAP_ERROR_CODE
{ 
	SOAP_E_UNK=0,
	SOAP_E_VERSION_MISMATCH=100,
	SOAP_E_MUST_UNDERSTAND=200,
	SOAP_E_CLIENT=300,
	SOAP_E_SERVER=400
};

// forward declaration of CSoapFault
class CSoapFault;

class CSoapFaultParser : public ISAXContentHandlerImpl
{
private:

	CSoapFault *m_pFault;

	DWORD m_dwState;

	const static DWORD STATE_ERROR       = 0;
	const static DWORD STATE_ENVELOPE    = 1;
	const static DWORD STATE_BODY        = 2;
	const static DWORD STATE_START       = 4;
	const static DWORD STATE_FAULTCODE   = 8;
	const static DWORD STATE_FAULTSTRING = 16;
	const static DWORD STATE_FAULTACTOR  = 32;
	const static DWORD STATE_DETAIL      = 64;
	const static DWORD STATE_RESET       = 128;
	const static DWORD STATE_SKIP        = 256;


	CComPtr<ISAXXMLReader> m_spReader;
	CSAXStringBuilder m_stringBuilder;
	CSkipHandler m_skipHandler;

	const wchar_t *m_wszSoapPrefix;
	int m_cchSoapPrefix;

public:
	virtual ~CSoapFaultParser()
	{
		m_skipHandler.DetachParent();		
	}
	
	// IUnknown interface
	HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
	{
		if (ppv == NULL)
		{
			return E_POINTER;
		}

		*ppv = NULL;

		if (InlineIsEqualGUID(riid, IID_IUnknown) ||
			InlineIsEqualGUID(riid, IID_ISAXContentHandler))
		{
			*ppv = static_cast<ISAXContentHandler *>(this);
			return S_OK;
		}

		return E_NOINTERFACE;
	}

	ULONG __stdcall AddRef()
	{
		return 1;
	}

	ULONG __stdcall Release()
	{
		return 1;
	}

	// constructor

	CSoapFaultParser(CSoapFault *pFault, ISAXXMLReader *pReader)
		:m_pFault(pFault), m_dwState(STATE_ERROR), m_spReader(pReader)
	{
		ATLASSERT( pFault != NULL );
		ATLASSERT( pReader != NULL );
	}

	// ISAXContentHandler interface
	HRESULT __stdcall startElement( 
		 const wchar_t  * wszNamespaceUri,
		 int cchNamespaceUri,
		 const wchar_t  * wszLocalName,
		 int cchLocalName,
		 const wchar_t  * /*wszQName*/,
		 int /*cchQName*/,
		 ISAXAttributes  * /*pAttributes*/)
	{
		struct _faultmap
		{
			const wchar_t *wszTag;
			int cchTag;
			DWORD dwState;
		};

		const static _faultmap s_faultParseMap[] =
		{
			{ L"Envelope", sizeof("Envelope")-1, CSoapFaultParser::STATE_ENVELOPE },
			{ L"Body", sizeof("Body")-1, CSoapFaultParser::STATE_BODY },
			{ L"Header", sizeof("Header")-1, CSoapFaultParser::STATE_BODY },
			{ L"Fault", sizeof("Fault")-1, CSoapFaultParser::STATE_START },
			{ L"faultcode", sizeof("faultcode")-1, CSoapFaultParser::STATE_FAULTCODE },
			{ L"faultstring", sizeof("faultstring")-1, CSoapFaultParser::STATE_FAULTSTRING },
			{ L"faultactor", sizeof("faultactor")-1, CSoapFaultParser::STATE_FAULTACTOR },
			{ L"detail", sizeof("detail")-1, CSoapFaultParser::STATE_DETAIL }
		};

		if (m_spReader.p == NULL)
		{
			ATLTRACE( _T("ATLSOAP: CSoapFaultParser::startElement -- ISAXXMLReader is NULL.\r\n" ) );

			return E_INVALIDARG;
		}

		m_dwState &= ~STATE_RESET;
		for (int i=0; i<(sizeof(s_faultParseMap)/sizeof(s_faultParseMap[0])); i++)
		{
			if ((cchLocalName == s_faultParseMap[i].cchTag) &&
				(!wcsncmp(wszLocalName, s_faultParseMap[i].wszTag, cchLocalName)))
			{
				DWORD dwState = s_faultParseMap[i].dwState;
				if ((dwState & (STATE_START | STATE_ENVELOPE | STATE_BODY)) == 0)
				{
					m_stringBuilder.SetReader(m_spReader);
					m_stringBuilder.SetParent(this);

					m_stringBuilder.Clear();
					m_spReader->putContentHandler( &m_stringBuilder );
				}
				else
				{
					if ((dwState <= m_dwState) || 
						(cchNamespaceUri != sizeof(SOAPENV_NAMESPACEA)-1) ||
						(wcsncmp(wszNamespaceUri, SOAPENV_NAMESPACEW, cchNamespaceUri)))
					{
						ATLTRACE( _T("ATLSOAP: CSoapFaultParser::startElement -- malformed SOAP fault.\r\n" ) );

						return E_FAIL;
					}
				}

				m_dwState = dwState;
				return S_OK;
			}
		}
		if (m_dwState > STATE_START)
		{
			m_dwState = STATE_SKIP;
			m_skipHandler.SetReader(m_spReader);
			m_skipHandler.SetParent(this);

			m_spReader->putContentHandler( &m_skipHandler );
			return S_OK;
		}

		ATLTRACE( _T("ATLSOAP: CSoapFaultParser::startElement -- malformed SOAP fault.\r\n" ) );

		return E_FAIL;
	}

	HRESULT __stdcall startPrefixMapping(
		 const wchar_t  * wszPrefix,
		 int cchPrefix,
		 const wchar_t  * wszUri,
		 int cchUri)
	{
		if ((cchUri == sizeof(SOAPENV_NAMESPACEA)-1) &&
			(!wcsncmp(wszUri, SOAPENV_NAMESPACEW, cchUri)))
		{
			m_wszSoapPrefix = wszPrefix;
			m_cchSoapPrefix = cchPrefix;
		}

		return S_OK;
	}

	HRESULT __stdcall characters( 
		 const wchar_t  * wszChars,
		 int cchChars);
};

extern __declspec(selectany) const int ATLS_SOAPFAULT_CNT = 4;

class CSoapFault
{
private:

	struct _faultcode
	{
		const wchar_t *wsz;
		int cch;
		const wchar_t *wszFaultString;
		int cchFaultString;
		SOAP_ERROR_CODE errCode;
	};

	static const _faultcode s_faultCodes[];

public:

	// members
	SOAP_ERROR_CODE m_soapErrCode;
	CStringW m_strFaultCode;
	CStringW m_strFaultString;
	CStringW m_strFaultActor;
	CStringW m_strDetail;

	CSoapFault()
		: m_soapErrCode(SOAP_E_UNK)
	{
	}

	HRESULT SetErrorCode(
		const wchar_t *wsz, 
		const wchar_t *wszSoapPrefix,
		int cch = -1, 
		int cchSoapPrefix = -1,
		bool bSetFaultString = true)
	{
		if ((wsz == NULL) || (wszSoapPrefix == NULL))
		{
			return E_INVALIDARG;
		}

		if (cch == -1)
		{
			cch = (int) wcslen(wsz);
		}

		while (*wsz && iswspace(*wsz))
		{
			++wsz;
			--cch;
		}

		if (cchSoapPrefix == -1)
		{
			cchSoapPrefix = (int) wcslen(wszSoapPrefix);
		}

		const wchar_t *wszLocalName = wcschr(wsz, L':');
		if (wszLocalName == NULL)
		{
			// faultCode must be QName

			ATLTRACE( _T("ATLSOAP: CSoapFault::SetErrorCode -- faultCode is not a QName.\r\n" ) );

			return E_FAIL;
		}

		// make sure the namespace of the fault is the
		// SOAPENV namespace
		if ((cchSoapPrefix != (int)(wszLocalName-wsz)) ||
			(wcsncmp(wsz, wszSoapPrefix, cchSoapPrefix)))
		{
			ATLTRACE( _T("ATLSOAP: CSoapFault::SetErrorCode -- fault namespace is incorrect.\r\n" ) );

			return E_FAIL;
		}

		wszLocalName++;
		cch -= (int) (wszLocalName-wsz);

		_ATLTRY
		{
			for (int i=0; i<ATLS_SOAPFAULT_CNT; i++)
			{
				if ((cch == s_faultCodes[i].cch) &&
					(!wcsncmp(wszLocalName, s_faultCodes[i].wsz, cch)))
				{
					m_soapErrCode = s_faultCodes[i].errCode;
					if (bSetFaultString != false)
					{
						m_strFaultString.SetString(s_faultCodes[i].wszFaultString, s_faultCodes[i].cchFaultString);
						break;
					}
				}
			}
			if (m_strFaultString.GetLength() == 0)
			{
				m_strFaultCode.SetString(wszLocalName, cch);
			}
		}
		_ATLCATCHALL()
		{
			ATLTRACE( _T("ATLSOAP: CSoapFault::SetErrorCode -- out of memory.\r\n" ) );

			return E_OUTOFMEMORY;
		}

		return S_OK;
	}

	HRESULT ParseFault(IStream *pStream, ISAXXMLReader *pReader = NULL)
	{
		if (pStream == NULL)
		{
			ATLTRACE( _T("ATLSOAP: CSoapFault::ParseFault -- NULL IStream was passed.\r\n" ) );

			return E_INVALIDARG;
		}

		CComPtr<ISAXXMLReader> spReader;
		if (pReader != NULL)
		{
			spReader = pReader;
		}
		else
		{
			if (FAILED(spReader.CoCreateInstance(ATLS_SAXXMLREADER_CLSID, NULL, CLSCTX_INPROC_SERVER)))
			{
				ATLTRACE( _T("ATLSOAP: CSoapFault::ParseFault -- CoCreateInstance of SAXXMLReader failed.\r\n" ) );

				return E_FAIL;
			}
		}

		Clear();
		CSoapFaultParser parser(const_cast<CSoapFault *>(this), spReader);
		spReader->putContentHandler(&parser);

		CComVariant varStream;
		varStream = static_cast<IUnknown*>(pStream);

		HRESULT hr = spReader->parse(varStream);
		spReader->putContentHandler(NULL);
		return hr;
	}

	HRESULT GenerateFault(IWriteStream *pWriteStream)
	{
		if ((pWriteStream == NULL) || (m_soapErrCode == SOAP_E_UNK))
		{
			return E_INVALIDARG;
		}

		ATLASSERT( (m_soapErrCode == SOAP_E_UNK) || 
				   (m_soapErrCode == SOAP_E_VERSION_MISMATCH) ||
				   (m_soapErrCode == SOAP_E_MUST_UNDERSTAND) || 
				   (m_soapErrCode == SOAP_E_CLIENT) ||
				   (m_soapErrCode == SOAP_E_SERVER) );

		HRESULT hr = S_OK;
		_ATLTRY
		{
			const wchar_t *wszFaultCode = NULL;
			if (m_strFaultCode.GetLength() == 0)
			{
				for (int i=0; i<4; i++)
				{
					if (s_faultCodes[i].errCode == m_soapErrCode)
					{
						if (m_strFaultString.GetLength() == 0)
						{
							m_strFaultString.SetString(s_faultCodes[i].wszFaultString, 
								s_faultCodes[i].cchFaultString);
						}

						wszFaultCode = s_faultCodes[i].wsz;
						break;
					}
				}
			}

			if (wszFaultCode == NULL)
			{
				if (m_strFaultCode.GetLength() != 0)
				{
					wszFaultCode = m_strFaultCode;
				}
				else
				{
					ATLTRACE( _T("CSoapFault::GenerateFault -- missing/invalid fault code.\r\n") );
					return E_FAIL;
				}
			}

			const LPCSTR s_szErrorFormat =
				"<SOAP:Envelope xmlns:SOAP=\"" SOAPENV_NAMESPACEA "\">"
				"<SOAP:Body>"
				"<SOAP:Fault>"
				"<faultcode>SOAP:%ws</faultcode>"
				"<faultstring>%ws</faultstring>"
				"%s%ws%s"
				"<detail>%ws</detail>"
				"</SOAP:Fault>"
				"</SOAP:Body>"
				"</SOAP:Envelope>";

			CStringA strFault;
			strFault.Format(s_szErrorFormat, wszFaultCode, m_strFaultString, 
				m_strFaultActor.GetLength() ? "<faultactor>" : "", m_strFaultActor, 
				m_strFaultActor.GetLength() ? "</faultactor>" : "",
				m_strDetail);

			hr = pWriteStream->WriteStream(strFault, strFault.GetLength(), NULL);
		}
		_ATLCATCHALL()
		{
			ATLTRACE( _T("ATLSOAP: CSoapFault::GenerateFault -- out of memory.\r\n" ) );
			hr = E_OUTOFMEMORY;
		}

		return hr;
	}

	void Clear()
	{
		m_soapErrCode = SOAP_E_UNK;
		m_strFaultCode.Empty();
		m_strFaultString.Empty();
		m_strFaultActor.Empty();
		m_strDetail.Empty();
	}
}; // class CSoapFault

#define DECLARE_SOAP_FAULT(__name, __faultstring, __errcode) \
	{ L ## __name, sizeof(__name)-1, L ## __faultstring, sizeof(__faultstring), __errcode },

__declspec(selectany) const CSoapFault::_faultcode CSoapFault::s_faultCodes[] =
{
	DECLARE_SOAP_FAULT("VersionMismatch", "SOAP Version Mismatch Error", SOAP_E_VERSION_MISMATCH)
	DECLARE_SOAP_FAULT("MustUnderstand", "SOAP Must Understand Error", SOAP_E_MUST_UNDERSTAND)
	DECLARE_SOAP_FAULT("Client", "SOAP Invalid Request", SOAP_E_CLIENT)
	DECLARE_SOAP_FAULT("Server", "SOAP Server Application Faulted", SOAP_E_SERVER)
};

ATL_NOINLINE inline HRESULT __stdcall CSoapFaultParser::characters( 
		 const wchar_t  * wszChars,
		 int cchChars)
{
	if (m_pFault == NULL)
	{
		return E_INVALIDARG;
	}

	if (m_dwState & STATE_RESET)
	{
		return S_OK;
	}

	HRESULT hr = E_FAIL;
	_ATLTRY
	{
		switch (m_dwState)
		{
			case STATE_FAULTCODE:
				if (m_pFault->m_soapErrCode == SOAP_E_UNK)
				{
					hr = m_pFault->SetErrorCode(wszChars, m_wszSoapPrefix, 
						cchChars, m_cchSoapPrefix, false);
				}
				break;
			case STATE_FAULTSTRING:
				if (m_pFault->m_strFaultString.GetLength() == 0)
				{
					m_pFault->m_strFaultString.SetString(wszChars, cchChars);
					hr = S_OK;
				}
				break;
			case STATE_FAULTACTOR:
				if (m_pFault->m_strFaultActor.GetLength() == 0)
				{
					m_pFault->m_strFaultActor.SetString(wszChars, cchChars);
					hr = S_OK;
				}
				break;
			case STATE_DETAIL:
				if (m_pFault->m_strDetail.GetLength() == 0)
				{
					m_pFault->m_strDetail.SetString(wszChars, cchChars);
					hr = S_OK;
				}
				break;
			case STATE_START: case STATE_ENVELOPE : case STATE_BODY : case STATE_SKIP:
				hr = S_OK;
				break;
			default:
				// should never get here
				ATLASSERT( FALSE );
				break;
		}
	}
	_ATLCATCHALL()
	{
		ATLTRACE( _T("ATLSOAP: CSoapFaultParser::characters -- out of memory.\r\n" ) );

		hr = E_OUTOFMEMORY;
	}

	m_dwState |= STATE_RESET;

	return hr;
}

////////////////////////////////////////////////////////////////////////////////
//
// CSoapRootHandler - the class that does most of the work
//
////////////////////////////////////////////////////////////////////////////////

#ifndef ATLSOAP_STACKSIZE
	// 16 will be plenty for the 99% case
	#define ATLSOAP_STACKSIZE 16
#endif

#ifndef ATLSOAP_GROWARRAY
	#define ATLSOAP_GROWARRAY 10
#endif

class CSoapRootHandler : public ISAXContentHandlerImpl
{
private:

	friend class _CSDLGenerator;

	//
	// state constants
	//
	const static DWORD SOAP_START        = 0;
	const static DWORD SOAP_ENVELOPE     = 1;
	const static DWORD SOAP_HEADERS      = 2;
	const static DWORD SOAP_BODY         = 3;
	const static DWORD SOAP_PARAMS       = 4;
	const static DWORD SOAP_CALLED       = 5;
	const static DWORD SOAP_RESPONSE     = 6;
	const static DWORD SOAP_HEADERS_DONE = 7;

	//
	// hash values for SOAP namespaces and elements
	//
	const static ULONG SOAP_ENV = 0x5D3574E2;
	const static ULONG SOAP_ENC = 0xBD62724B;
	const static ULONG ENVELOPE = 0xDBE6009E;
	const static ULONG HEADER   = 0xAF4DFFC9;
	const static ULONG BODY     = 0x0026168E;

	//
	// XSD Names
	//
	struct XSDEntry
	{
		wchar_t * wszName;
		char * szName;
		int cchName;
	};

	const static XSDEntry s_xsdNames[];

	//
	// CBitVector - a dynamically sized bit vector class
	//
	class CBitVector
	{
	private:

		// 64 bits will handle the 99% case
		unsigned __int64 m_nBits;

		// when we need to grow
		unsigned __int64 * m_pBits;

		size_t m_nSize;

		bool Grow(size_t nIndex)
		{
			// Think carefully
			// In our current implementation, CHAR_BIT==8, and sizeof(m_nBits)==8. Easy to confuse the two.

			// We do math in bits, so this is our max size
			ATLENSURE(nIndex<SIZE_MAX/((sizeof(m_nBits)*CHAR_BIT)));

			// round up to nearest 64 bits
			size_t nAllocSizeBits = nIndex+((sizeof(m_nBits)*CHAR_BIT)-(nIndex%(sizeof(m_nBits)*CHAR_BIT)));
			size_t nAllocSizeBytes = nAllocSizeBits/CHAR_BIT;

			if (m_pBits != &m_nBits)
			{
				unsigned __int64 * pNewBits=NULL;
				pNewBits = (unsigned __int64 *) realloc(m_pBits, nAllocSizeBytes );
				if(!pNewBits)
				{
					return false;
				}
				m_pBits=pNewBits;
			}
			else
			{
				m_pBits = (unsigned __int64 *) malloc(nAllocSizeBytes );
				if (m_pBits != NULL)
				{
					Checked::memcpy_s(m_pBits, nAllocSizeBytes, &m_nBits, sizeof(m_nBits));
				}
			}

			if (m_pBits != NULL)
			{
				// set new bits to 0
				memset(m_pBits+(m_nSize/(CHAR_BIT*sizeof(m_nBits))), 0x00, (nAllocSizeBits-m_nSize)/CHAR_BIT);
				m_nSize = nAllocSizeBits;
				return true;
			}

			ATLTRACE( _T("ATLSOAP: CBitVector::Grow -- out of memory.\r\n" ) );

			return false;
		}

	public:

		CBitVector()
			: m_nBits(0), m_nSize(sizeof(m_nBits)*CHAR_BIT)
		{
			m_pBits = &m_nBits;
		}

		CBitVector(const CBitVector&)
		{
			m_pBits = &m_nBits;
		}

		const CBitVector& operator=(const CBitVector& that)
		{
			if (this != &that)
			{
				m_pBits = &m_nBits;
			}

			return *this;
		}

		bool GetBit(size_t nIndex) const
		{
			if (nIndex >= m_nSize)
			{
				return false;
			}

			size_t i = nIndex/(sizeof(m_nBits)*CHAR_BIT);
			size_t nBits = nIndex-i*(sizeof(m_nBits)*CHAR_BIT);
			return ((m_pBits[i] >> nBits) & 0x01);
		}

		bool SetBit(size_t nIndex)
		{
			if (nIndex >= m_nSize)
			{
				if (!Grow(nIndex))
				{
					return false;
				}
			}

			size_t i = nIndex/(sizeof(m_nBits)*CHAR_BIT);
			size_t nBits = nIndex-i*(sizeof(m_nBits)*CHAR_BIT);
			m_pBits[i] |= (((unsigned __int64) 1) << nBits);

			return true;
		}

		void Clear()
		{
			if (m_pBits == &m_nBits)
			{
				m_nBits = 0;
			}
			else
			{
				memset(m_pBits, 0x00, (m_nSize/CHAR_BIT));
			}
		}

		~CBitVector()
		{
			if (m_pBits != &m_nBits)
			{
				free(m_pBits);
			}

			m_pBits = &m_nBits;
			m_nSize = sizeof(m_nBits)*CHAR_BIT;
		}

		void RelocateFixup()
		{
			if (m_nSize <= sizeof(m_nBits)*CHAR_BIT)
			{
				m_pBits = &m_nBits;
			}
		}
	}; // class CBitVector

	//
	// Parsing State
	//
	struct ParseState
	{
		void *pvElement;
		DWORD dwFlags;
		size_t nAllocSize;
		size_t nExpectedElements;
		size_t nElement;
		const _soapmap *pMap;
		const _soapmapentry *pEntry;

		// mark when we get an item
		CBitVector vec;

		size_t nDepth;

		ParseState(void *pvElement_ = NULL, DWORD dwFlags_ = 0, 
			size_t nAllocSize_ = 0, size_t nExpectedElements_ = 0, 
			size_t nElement_ = 0, const _soapmap *pMap_ = NULL, 
			const _soapmapentry *pEntry_ = NULL)
			: pvElement(pvElement_), dwFlags(dwFlags_), nAllocSize(nAllocSize_),
			  nExpectedElements(nExpectedElements_), nElement(nElement_), pMap(pMap_),
			  pEntry(pEntry_), nDepth(0)
		{
			vec.Clear();
		}

		ParseState(const ParseState& that)
		{
			pvElement = that.pvElement;
			dwFlags = that.dwFlags;
			nAllocSize = that.nAllocSize;
			nExpectedElements = that.nExpectedElements;
			nElement = that.nElement;
			pMap = that.pMap;
			pEntry = that.pEntry;
			nDepth = that.nDepth;
			vec.Clear();
		}

		~ParseState()
		{
			pvElement = NULL;
			dwFlags = 0;
			nAllocSize = 0;
			nExpectedElements = 0;
			nElement = 0;
			pMap = NULL;
			pEntry = NULL;
			nDepth = 0;
			vec.Clear();
		}

		void RelocateFixup()
		{
			vec.RelocateFixup();
		}
	}; // struct ParseState

	class CParseStateElementTraits : public CDefaultElementTraits<ParseState>
	{
	public:
		// CBitVector relocate fixup
		static void RelocateElements( ParseState* pDest, ParseState* pSrc, size_t nElements )
		{
			CDefaultElementTraits<ParseState>::RelocateElements(pDest, pSrc, nElements);

			// fixup CBitVector
			for (size_t i=0; i<nElements; i++)
			{
				pDest[i].RelocateFixup();
			}
		}
	};

	class CResponseGenerator
	{
	public:
		HRESULT StartEnvelope(IWriteStream *pStream)
		{
			ATLENSURE_RETURN( pStream != NULL );

			return pStream->WriteStream("<soap:Envelope "
				"xmlns:soap=\"" SOAPENV_NAMESPACEA "\" "
				"xmlns:xsi=\"" XSI_NAMESPACEA "\" "
				"xmlns:xsd=\"" XSD_NAMESPACEA "\" "
				"xmlns:soapenc=\"" SOAPENC_NAMESPACEA "\">",

				sizeof("<soap:Envelope "
					"xmlns:soap=\"" SOAPENV_NAMESPACEA "\" "
					"xmlns:xsi=\"" XSI_NAMESPACEA "\" "
					"xmlns:xsd=\"" XSD_NAMESPACEA "\" "
					"xmlns:soapenc=\"" SOAPENC_NAMESPACEA "\">")-1,

				NULL);
		}

		HRESULT StartHeaders(IWriteStream *pStream, const _soapmap *pMap)
		{
			ATLENSURE_RETURN( pStream != NULL );
			ATLENSURE_RETURN( pMap != NULL );

			HRESULT hr = pStream->WriteStream("<soap:Header", sizeof("<soap:Header")-1, NULL);
			if (SUCCEEDED(hr))
			{
				if ((pMap->dwCallFlags & (SOAPFLAG_RPC | SOAPFLAG_ENCODED)) != 
					(SOAPFLAG_RPC | SOAPFLAG_ENCODED))
				{
					// qualify document/literal by default
					// For this version, ATL Server will not respect 
					// the elementForm* attributes in an XSD schema

					hr = pStream->WriteStream(" xmlns=\"", sizeof(" xmlns=\"")-1, NULL);
					if (SUCCEEDED(hr))
					{
						hr = pStream->WriteStream(pMap->szNamespace, pMap->cchNamespace, NULL);
						if (SUCCEEDED(hr))
						{
							hr = pStream->WriteStream("\">", sizeof("\">")-1, NULL);
						}
					}					
				}
				else
				{
					// rpc/encoded
					hr = pStream->WriteStream(">", sizeof(">")-1, NULL);
				}
			}
			return hr;
		}

		HRESULT EndHeaders(IWriteStream *pStream)
		{
			ATLENSURE_RETURN( pStream != NULL );

			return pStream->WriteStream("</soap:Header>", sizeof("</soap:Header>")-1, NULL);
		}

		virtual HRESULT StartBody(IWriteStream *pStream)
		{
			ATLENSURE_RETURN( pStream != NULL );

			return pStream->WriteStream(
				"<soap:Body>", sizeof("<soap:Body>")-1, NULL);
		}

		HRESULT EndBody(IWriteStream *pStream)
		{
			ATLENSURE_RETURN( pStream != NULL );

			return pStream->WriteStream("</soap:Body>", sizeof("</soap:Body>")-1, NULL);
		}

		HRESULT EndEnvelope(IWriteStream *pStream)
		{
			ATLENSURE_RETURN( pStream != NULL );

			return pStream->WriteStream("</soap:Envelope>", sizeof("</soap:Envelope>")-1, NULL);
		}

		virtual HRESULT StartMap(IWriteStream *pStream, const _soapmap *pMap, bool bClient) = 0;
		virtual HRESULT EndMap(IWriteStream *pStream, const _soapmap *pMap, bool bClient) = 0;

		virtual HRESULT StartEntry(IWriteStream *pStream, const _soapmap *pMap, const _soapmapentry *pEntry)
		{
			ATLENSURE_RETURN( pStream != NULL );
			ATLENSURE_RETURN( pEntry != NULL );

			// output name
			HRESULT hr = pStream->WriteStream("<", 1, NULL);
			if (SUCCEEDED(hr))
			{
				const char *szHeaderNamespace = NULL;
				int cchHeaderNamespace = 0;

				if ((pMap != NULL) && (pMap->mapType == SOAPMAP_HEADER) &&
					((pEntry->pChain != NULL) && 
					 (pEntry->pChain->szNamespace !=NULL)) ||
					(pEntry->szNamespace != NULL))
				{
					hr = pStream->WriteStream("snp:", sizeof("snp:")-1, NULL);
					if (SUCCEEDED(hr))
					{
						szHeaderNamespace = pEntry->pChain ? 
							pEntry->pChain->szNamespace : pEntry->szNamespace;

						cchHeaderNamespace = pEntry->pChain ? 
							pEntry->pChain->cchNamespace : pEntry->cchNamespace;
					}
				}

				if (SUCCEEDED(hr))
				{
					if ((pEntry->dwFlags & SOAPFLAG_RETVAL)==0)
					{
						hr = pStream->WriteStream(pEntry->szField, pEntry->cchField, NULL);
					}
					else
					{
						hr = pStream->WriteStream("return", sizeof("return")-1, NULL);
					}
					if (SUCCEEDED(hr))
					{
						if (szHeaderNamespace != NULL)
						{
							ATLASSERT( cchHeaderNamespace != 0 );

							hr = pStream->WriteStream(" xmlns:snp=\"", sizeof(" xmlns:snp=\"")-1, NULL);
							if (SUCCEEDED(hr))
							{
								hr = pStream->WriteStream(szHeaderNamespace, cchHeaderNamespace, NULL);
								if (SUCCEEDED(hr))
								{
									hr = pStream->WriteStream("\"", sizeof("\"")-1, NULL);
								}
							}
						}
					}
				}
			}
			if (SUCCEEDED(hr))
			{
				if (pEntry->dwFlags & SOAPFLAG_MUSTUNDERSTAND)
				{
					// output mustUnderstand
					hr = pStream->WriteStream(" soap:mustUnderstand=\"1\"", sizeof(" soap:mustUnderstand=\"1\"")-1, NULL);
				}
			}
			return hr;
		}

		HRESULT EndEntry(IWriteStream *pStream, const _soapmap *pMap, const _soapmapentry *pEntry)
		{
			ATLENSURE_RETURN( pStream != NULL );
			ATLENSURE_RETURN( pEntry != NULL );

			HRESULT hr = pStream->WriteStream("</", 2, NULL);
			if (SUCCEEDED(hr))
			{
				if ((pMap != NULL) && 
					(pMap->mapType == SOAPMAP_HEADER) &&
					((pEntry->pChain != NULL) && 
					 (pEntry->pChain->szNamespace !=NULL)) ||
					(pEntry->szNamespace != NULL))
				{
					hr = pStream->WriteStream("snp:", sizeof("snp:")-1, NULL);
				}
				if ((pEntry->dwFlags & SOAPFLAG_RETVAL)==0)
				{
					hr = pStream->WriteStream(pEntry->szField, pEntry->cchField, NULL);
				}
				else
				{
					hr = pStream->WriteStream("return", sizeof("return")-1, NULL);
				}
				if (SUCCEEDED(hr))
				{
					hr = pStream->WriteStream(">", 1, NULL);
				}
			}
			return hr;
		}
	}; // class CResponseGenerator

	class CDocLiteralGenerator : public CResponseGenerator
	{
	public:

		HRESULT StartMap(IWriteStream *pStream, const _soapmap *pMap, bool bClient)
		{
			ATLENSURE_RETURN( pStream != NULL );
			ATLENSURE_RETURN( pMap != NULL );

			HRESULT hr = S_OK;
			// output type name
			hr = pStream->WriteStream("<", 1, NULL);
			if (SUCCEEDED(hr))
			{
				hr = pStream->WriteStream(pMap->szName, pMap->cchName, NULL);
				if (SUCCEEDED(hr))
				{
					if ((pMap->mapType == SOAPMAP_FUNC) && 
						(bClient == false) && 
						(pMap->dwCallFlags & SOAPFLAG_PID))
					{
						hr = pStream->WriteStream("Response", sizeof("Response")-1, NULL);
						if (FAILED(hr))
						{
							return hr;
						}
					}

					if (pMap->mapType == SOAPMAP_FUNC)
					{
						hr = pStream->WriteStream(" xmlns=\"", sizeof(" xmlns=\"")-1, NULL);
						if (SUCCEEDED(hr))
						{
							hr = pStream->WriteStream(pMap->szNamespace, pMap->cchNamespace, NULL);
							if (SUCCEEDED(hr))
							{
								hr = pStream->WriteStream("\">", sizeof("\">")-1, NULL);
							}
						}
					}
					else
					{
						hr = pStream->WriteStream(">", 1, NULL);
					}
				}
			}
			return hr;
		}

		HRESULT EndMap(IWriteStream *pStream, const _soapmap *pMap, bool bClient)
		{
			ATLENSURE_RETURN( pStream != NULL );
			ATLENSURE_RETURN( pMap != NULL );

			HRESULT hr = pStream->WriteStream("</", sizeof("</")-1, NULL);
			if (SUCCEEDED(hr))
			{
				hr = pStream->WriteStream(pMap->szName, pMap->cchName, NULL);
				if (SUCCEEDED(hr))
				{
					if ((pMap->mapType == SOAPMAP_FUNC) && 
						(bClient == false) && 
						(pMap->dwCallFlags & SOAPFLAG_PID))
					{
						hr = pStream->WriteStream("Response", sizeof("Response")-1, NULL);
						if (FAILED(hr))
						{
							return hr;
						}
					}
					hr = pStream->WriteStream(">", 1, NULL);
				}
			}

			return hr;
		}

	}; // class CDocLiteralGenerator

	class CPIDGenerator : public CDocLiteralGenerator
	{
	};

	class CPADGenerator : public CDocLiteralGenerator
	{
	public:

		virtual HRESULT StartEntry(IWriteStream *pStream, const _soapmap *pMap, const _soapmapentry *pEntry)
		{
			ATLENSURE_RETURN( pStream != NULL );
			ATLENSURE_RETURN( pEntry != NULL );

			HRESULT hr = __super::StartEntry(pStream, pMap, pEntry);
			if (SUCCEEDED(hr) && (pMap->dwCallFlags & SOAPFLAG_PAD))
			{
				hr = pStream->WriteStream(" xmlns=\"", sizeof(" xmlns=\"")-1, NULL);
				if (SUCCEEDED(hr))
				{
					hr = pStream->WriteStream(pMap->szNamespace, pMap->cchNamespace, NULL);
					if (SUCCEEDED(hr))
					{
						hr = pStream->WriteStream("\"", sizeof("\"")-1, NULL);
					}
				}
			}

			return hr;
		}
	}; // class CPADGenerator

	class CRpcEncodedGenerator : public CResponseGenerator
	{
	public:

		HRESULT StartBody(IWriteStream *pStream)
		{
			ATLENSURE_RETURN( pStream != NULL );

			return pStream->WriteStream(
				"<soap:Body soap:encodingStyle=\"" SOAPENC_NAMESPACEA "\">", 
				sizeof("<soap:Body soap:encodingStyle=\"" SOAPENC_NAMESPACEA "\">")-1, NULL);
		}

		HRESULT StartMap(IWriteStream *pStream, const _soapmap *pMap, bool bClient)
		{
			ATLENSURE_RETURN( pStream != NULL );
			ATLENSURE_RETURN( pMap != NULL );

			(bClient); // unused for rpc/encoded

			HRESULT hr = pStream->WriteStream("<snp:", sizeof("<snp:")-1, NULL);
			if (SUCCEEDED(hr))
			{
				hr = pStream->WriteStream(pMap->szName, pMap->cchName, NULL);
				if (SUCCEEDED(hr))
				{
					if (pMap->mapType == SOAPMAP_FUNC)
					{
						hr = pStream->WriteStream(" xmlns:snp=\"", sizeof(" xmlns:snp=\"")-1, NULL);
						if (SUCCEEDED(hr))
						{
							ATLASSERT( pMap->szNamespace != NULL );
							hr = pStream->WriteStream(pMap->szNamespace, pMap->cchNamespace, NULL);
							if (SUCCEEDED(hr))
							{
								hr = pStream->WriteStream("\">", sizeof("\">")-1, NULL);
							}
						}
					}
					else
					{
						hr = pStream->WriteStream(">", 1, NULL);
					}
				}
			}
			return hr;
		}

		HRESULT EndMap(IWriteStream *pStream, const _soapmap *pMap, bool bClient)
		{
			ATLENSURE_RETURN( pStream != NULL );
			ATLENSURE_RETURN( pMap != NULL );

			(bClient); // unused for rpc/encoded

			HRESULT hr = pStream->WriteStream("</snp:", sizeof("</snp:")-1, NULL);
			if (SUCCEEDED(hr))
			{
				hr = pStream->WriteStream(pMap->szName, pMap->cchName, NULL);
				if (SUCCEEDED(hr))
				{
					hr = pStream->WriteStream(">", 1, NULL);
				}
			}

			return hr;
		}
	}; // class CRpcEncodedGenerator

	//
	// members
	//
	CAtlArray<ParseState, CParseStateElementTraits> m_stateStack;
	size_t m_nState;

	DWORD m_dwState;

	CComPtr<ISAXXMLReader> m_spReader;

	CSAXStringBuilder m_stringBuilder;
	CSkipHandler m_skipHandler;

	IAtlMemMgr * m_pMemMgr;

	static CCRTHeap m_crtHeap;

	bool m_bClient;

	void *m_pvParam;

	bool m_bNullCheck;
	bool m_bChildCheck;
	bool m_bCharacters;
	size_t m_nDepth;

	typedef CFixedStringT<CStringW, 16> REFSTRING;

	// used for rpc/encoded messages with href's
	typedef CAtlMap<REFSTRING, ParseState, CStringRefElementTraits<REFSTRING> > REFMAP;
	REFMAP m_refMap;

	//
	// Implementation helpers
	//

	HRESULT PushState(void *pvElement = NULL, const _soapmap *pMap = NULL,
			const _soapmapentry *pEntry = NULL, DWORD dwFlags = 0, size_t nAllocSize = 0, 
			size_t nExpectedElements = 0, size_t nElement = 0)
	{
		if (m_stateStack.IsEmpty())
		{
			// 16 will be plenty for the 99% case
			if (!m_stateStack.SetCount(0, 16))
			{
				ATLTRACE( _T("ATLSOAP: CSoapRootHandler::PushState -- out of memory.\r\n" ) );

				return E_OUTOFMEMORY;
			}
		}

		size_t nCnt = m_stateStack.GetCount();
		m_nState = m_stateStack.Add();
		if (m_stateStack.GetCount() <= nCnt)
		{
			ATLTRACE( _T("ATLSOAP: CSoapRootHandler::PushState -- out of memory.\r\n" ) );

			return E_OUTOFMEMORY;
		}

		ParseState &state = m_stateStack[m_nState];

		state.pvElement = pvElement;
		state.dwFlags = dwFlags;
		state.nAllocSize = nAllocSize;
		state.nExpectedElements = nExpectedElements;
		state.nElement = nElement;
		state.pMap = pMap;
		state.pEntry = pEntry;
		state.nDepth = m_nDepth;

		return S_OK;
	}

	ParseState& GetState()
	{
		return m_stateStack[m_nState];
	}

	void PopState(bool bForce = false)
	{
		if ((m_nState != 0) || (bForce != false))
		{
			m_stateStack.RemoveAt(m_nState);
			--m_nState;
		}
	}

	BOOL IsEqualElement(int cchLocalNameCheck, const wchar_t *wszLocalNameCheck, 
		int cchNamespaceUriCheck, const wchar_t *wszNamespaceUriCheck,
		int cchLocalName, const wchar_t *wszLocalName,
		int cchNamespaceUri, const wchar_t *wszNamespaceUri)
	{
		ATLENSURE(wszLocalName);
		ATLENSURE(wszLocalNameCheck);
		ATLENSURE(wszNamespaceUri);
		ATLENSURE(wszNamespaceUriCheck);
		
		if (cchLocalName == cchLocalNameCheck &&
			cchNamespaceUri == cchNamespaceUriCheck &&
			!wcsncmp(wszLocalName, wszLocalNameCheck, cchLocalName) &&
			!wcsncmp(wszNamespaceUri, wszNamespaceUriCheck, cchNamespaceUri))
		{
			return TRUE;
		}

		return FALSE;
	}

	ATL_FORCEINLINE BOOL IsEqualString(const wchar_t *wszStr1, int cchStr1, const wchar_t *wszStr2, int cchStr2)
	{
		ATLENSURE( wszStr1 != NULL );
		ATLENSURE( wszStr2 != NULL );
		ATLENSURE( cchStr1 >= 0 );
		ATLENSURE( cchStr2 >= 0 );

		if (cchStr1 == cchStr2)
		{
			return !wcsncmp(wszStr1, wszStr2, cchStr2);
		}
		return FALSE;
	}

	ATL_FORCEINLINE BOOL IsEqualStringHash(const wchar_t *wszStr1, int cchStr1, ULONG nHash1, 
		const wchar_t *wszStr2, int cchStr2, ULONG nHash2)
	{
		ATLENSURE( wszStr1 != NULL );
		ATLENSURE( wszStr2 != NULL );
		ATLENSURE( cchStr1 >= 0 );
		ATLENSURE( cchStr2 >= 0 );

		if (nHash1 == nHash2)
		{
			return IsEqualString(wszStr1, cchStr1, wszStr2, cchStr2);
		}

		return FALSE;
	}

	BOOL IsEqualElement(int cchLocalNameCheck, const wchar_t *wszLocalNameCheck, 
		int cchLocalName, const wchar_t *wszLocalName)
	{
		if (cchLocalName == cchLocalNameCheck &&
			!wcsncmp(wszLocalName, wszLocalNameCheck, cchLocalName))
		{
			return TRUE;
		}

		return FALSE;
	}

	void SetOffsetValue(void *pBase, void *pSrc, size_t nOffset)
	{
		void **ppDest = (void **)(((unsigned char *)pBase)+nOffset);
		*ppDest = pSrc;
	}

	bool IsRpcEncoded()
	{
		if ((m_stateStack[0].pMap->dwCallFlags & (SOAPFLAG_RPC | SOAPFLAG_ENCODED)) ==
			(SOAPFLAG_RPC | SOAPFLAG_ENCODED))
		{
			return true;
		}
		return false;
	}


	HRESULT ValidateArrayEntry(
		ParseState& state,
		const wchar_t  *wszLocalName,
		int cchLocalName)
	{
		(cchLocalName);
		(wszLocalName);

		ATLASSERT( state.pEntry != NULL );

		// SOAP Section 5.4.2

		// check number of elements
		if (state.nElement == state.nExpectedElements)
		{
			// too many elements
			if ((state.dwFlags & SOAPFLAG_UNKSIZE)==0)
			{
				ATLTRACE( _T("ATLSOAP: CSoapRootHandler::ValidateArrayEntry -- too many elements.\r\n" ) );
				return E_FAIL;
			}

			ATLASSERT( IsRpcEncoded() == false );

			// see if we need to allocate more
			if (state.nElement == state.nAllocSize)
			{
				unsigned char **ppArr = (unsigned char **)state.pvElement;
				size_t nNewElement=0;
				HRESULT hr=E_FAIL;
				if(FAILED(hr=::ATL::AtlMultiply(&nNewElement, state.nElement, static_cast<size_t>(2))))
				{
					return hr;
				}
				hr = AllocateArray(state.pEntry, (void **)ppArr, __max(nNewElement, ATLSOAP_GROWARRAY), state.nElement);

				if (SUCCEEDED(hr))
				{
					state.nAllocSize = __max((state.nElement)*2, ATLSOAP_GROWARRAY);
				}

				return hr;
			}
		}

		return S_OK;
	}

	HRESULT CheckID(
		const wchar_t *wszNamespaceUri,
		const wchar_t *wszLocalName,
		int cchLocalName,
		ISAXAttributes *pAttributes)
	{
		(cchLocalName);
		(wszLocalName);
		(wszNamespaceUri);
		ATLASSERT( pAttributes != NULL );

		const wchar_t *wsz = NULL;
		int cch = 0;

		HRESULT hr = GetAttribute(pAttributes, L"id", sizeof("id")-1, &wsz, &cch);
		if ((hr == S_OK) && (wsz != NULL))
		{
			const REFMAP::CPair *p = NULL;
			_ATLTRY
			{
				REFSTRING strRef(wsz, cch);
				p = m_refMap.Lookup(strRef);
				if (p == NULL)
				{
					return S_FALSE;
				}
			}
			_ATLCATCHALL()
			{
				ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckID -- out of memory.\r\n" ) );

				return E_OUTOFMEMORY;
			}

			ATLASSERT( IsRpcEncoded() == true );

			const ParseState& state = p->m_value;

			// disallow href-chaining
			hr = CheckHref(state.pEntry, state.pvElement, pAttributes);
			if (hr != S_FALSE)
			{
				return E_FAIL;
			}

			hr = S_OK;

			// do array stuff
			if (state.dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR))
			{
				hr = GetSection5Info(state, state.pEntry, pAttributes);
			}
			else
			{
				// only structs and arrays are allowed for hrefs
				ATLASSERT( state.pEntry->pChain != NULL );
				ATLASSERT( state.pEntry->pChain->mapType == SOAPMAP_STRUCT );

				// structs must have child entries
				m_bChildCheck = state.pEntry->pChain->nElements != 0;

				if (S_OK != PushState(state.pvElement, state.pEntry->pChain, state.pEntry, 
								state.dwFlags, 0, state.pEntry->pChain->nElements))
				{
					ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckID -- out of memory.\n" ) );
					hr = E_OUTOFMEMORY;
				}
			}

			m_refMap.DisableAutoRehash();
			m_refMap.RemoveAtPos(const_cast<REFMAP::CPair*>(p));
			m_refMap.EnableAutoRehash();

			return hr;
		}

		return S_FALSE;
	}

	HRESULT GetElementEntry(
		ParseState& state,
		const wchar_t *wszNamespaceUri,
		const wchar_t *wszLocalName,
		int cchLocalName,
		ISAXAttributes *pAttributes,
		const _soapmapentry **ppEntry)
	{
		ATLENSURE_RETURN( state.pMap != NULL );
		ATLENSURE_RETURN( ppEntry != NULL );

		*ppEntry = NULL;
		const _soapmapentry *pEntries = state.pMap->pEntries;
		DWORD dwIncludeFlags;
		DWORD dwExcludeFlags;

		HRESULT hr = CheckID(wszNamespaceUri, wszLocalName, cchLocalName, pAttributes);
		if (hr != S_FALSE)
		{
			if (hr == S_OK)
			{
				hr = S_FALSE;
			}
			return hr;
		}

		if (m_bClient != false)
		{
			dwIncludeFlags = SOAPFLAG_OUT;
			dwExcludeFlags = SOAPFLAG_IN;
		}
		else
		{
			dwIncludeFlags = SOAPFLAG_IN;
			dwExcludeFlags = SOAPFLAG_OUT;
		}

		ULONG nHash = AtlSoapHashStr(wszLocalName, cchLocalName);

		for (size_t i=0; pEntries[i].nHash != 0; i++)
		{
			if (nHash == pEntries[i].nHash && 
				((pEntries[i].dwFlags & dwIncludeFlags) || 
				 ((pEntries[i].dwFlags & dwExcludeFlags) == 0)) &&
				IsEqualElement(pEntries[i].cchField, pEntries[i].wszField, 
				cchLocalName, wszLocalName)/* &&
				!wcscmp(wszNamespaceUri, wszNamespace)*/)
			{
				// check bit vector

				if (state.vec.GetBit(i) == false)
				{
					if (state.vec.SetBit(i) == false)
					{
						return E_OUTOFMEMORY;
					}
				}
				else
				{
					// already received this element
					ATLTRACE( _T("ATLSOAP: CSoapRootHandler::GetElementEntry -- duplicate element was sent.\r\n" ) );

					return E_FAIL;
				}

				state.nElement++;
				*ppEntry = &pEntries[i];

				return S_OK;
			}
		}

		ATLTRACE( _T("ATLSOAP: CSoapRootHandler::GetElementEntry -- element not found: %.*ws.\r\n" ), cchLocalName, wszLocalName );

		return E_FAIL;
	}

	HRESULT CheckMustUnderstandHeader(ISAXAttributes *pAttributes)
	{
		ATLASSERT( pAttributes != NULL );

		const wchar_t* wszMustUnderstand;
		int cchMustUnderstand;
		bool bMustUnderstand= false;

		if (SUCCEEDED(GetAttribute(pAttributes, L"mustUnderstand", sizeof("mustUnderstand")-1, 
				&wszMustUnderstand, &cchMustUnderstand,
				SOAPENV_NAMESPACEW, sizeof(SOAPENV_NAMESPACEA)-1)) && 
				(wszMustUnderstand != NULL))
		{
			if (FAILED(AtlGetSAXValue(&bMustUnderstand, wszMustUnderstand, cchMustUnderstand)))
			{
				bMustUnderstand = true;
			}
		}

		if (bMustUnderstand == false)
		{
			ATLASSERT( GetReader() != NULL );

			m_skipHandler.SetReader(GetReader());
			m_skipHandler.SetParent(this);

			return GetReader()->putContentHandler( &m_skipHandler );
		}
		else
		{
			SoapFault(SOAP_E_MUST_UNDERSTAND, NULL, 0);
		}

		ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckMustUnderstandHeader -- unknown \"mustUnderstand\" SOAP Header was received.\r\n" ) );

		return E_FAIL;
	}

	HRESULT AllocateArray(
		const _soapmapentry *pEntry, 
		void **ppArr, size_t nElements, 
		size_t nCurrElements = 0)
	{
		ATLENSURE_RETURN( ppArr != NULL );
		ATLENSURE_RETURN( pEntry != NULL );

		size_t nElementSize;
		if (pEntry->nVal != SOAPTYPE_UNK)
		{
			nElementSize = AtlSoapGetElementSize((SOAPTYPES) pEntry->nVal);
		}
		else // UDT
		{
			ATLENSURE_RETURN( pEntry->pChain != NULL );
			nElementSize = pEntry->pChain->nElementSize;
		}
		if (nElementSize != 0)
		{
			if (*ppArr == NULL)
			{
				ATLASSERT( nCurrElements == 0 );
				size_t nBytes=0;
				HRESULT hr=S_OK;
				if( FAILED(hr=::ATL::AtlMultiply(&nBytes, nElementSize, nElements)))
				{			
					return hr;
				}
				*ppArr = m_pMemMgr->Allocate(nBytes);
			}
			else // *ppArr != NULL
			{
				ATLASSERT( nCurrElements != 0 );
				size_t nBytes=0;
				HRESULT hr=S_OK;
				if(	FAILED(hr=::ATL::AtlAdd(&nBytes, nElements, nCurrElements)) ||			
					FAILED(hr=::ATL::AtlMultiply(&nBytes, nElementSize, nBytes)))
				{
					return hr;
				}
				*ppArr = m_pMemMgr->Reallocate(*ppArr, nBytes);
			}
		}
		else
		{
			// internal error
			ATLASSERT( FALSE );
			return E_FAIL;
		}

		if (*ppArr == NULL)
		{
			return E_OUTOFMEMORY;
		}

		memset(((unsigned char *)(*ppArr))+(nCurrElements*nElementSize), 0x00, nElements*nElementSize);

		return S_OK;
	}

	HRESULT GetSection5Info(
		const ParseState& state,
		const _soapmapentry *pEntry,
		ISAXAttributes *pAttributes)
	{
		ATLENSURE_RETURN( pEntry != NULL );
		ATLENSURE_RETURN( pAttributes != NULL );

		HRESULT hr;
		if (IsRpcEncoded() != false)
		{
			// check for href
			// we ONLY do this for rpc/encoded (required for interop)
			// NOTE: ATL Server does not support object graphs, so 
			// only single-reference elements are allowed
			hr = CheckHref(pEntry, state.pvElement, pAttributes, 
				pEntry->dwFlags, SOAPFLAG_READYSTATE);
			if (hr != S_FALSE)
			{
				return hr;
			}
		}

		size_t nElements;
		DWORD dwFlags = 0;
		hr = AtlSoapGetArraySize(pAttributes, &nElements);
		if (FAILED(hr))
		{
			return hr;
		}

		size_t nAllocSize = 0;
		size_t nElementsPush = 0;

		if (pEntry->dwFlags & SOAPFLAG_DYNARR)
		{
			// set size_is value
			ATLENSURE_RETURN( state.pMap != NULL );
			int *pnSizeIs = (int *)(((unsigned char *)state.pvElement)+
				(state.pMap->pEntries[pEntry->nSizeIs].nOffset));

			if (hr != S_OK)
			{
				if (IsRpcEncoded())
				{
					// rpc/encoded requires soapenc:arrayType attribute
					return E_FAIL;
				}

				nElements = ATLSOAP_GROWARRAY;
				nAllocSize = ATLSOAP_GROWARRAY;
				dwFlags |= SOAPFLAG_UNKSIZE;
				*pnSizeIs = 0;
			}
			else
			{
				*pnSizeIs = (int)nElements;
				if (nElements == 0)
				{
					// soapenc:arrayType="type[0]"
					// treat as null array

					m_bNullCheck = true;

					// push an emtpy state
					return PushState();
				}

				nElementsPush = nElements;
			}
			void *p = NULL;
			hr = AllocateArray(pEntry, &p, nElements);
			if (hr != S_OK)
			{
				return hr;
			}

			SetOffsetValue(state.pvElement, p, pEntry->nOffset);
		}
		else
		{
			// for fixed-size arrays, we know the number of elements
			ATLASSERT( pEntry->dwFlags & SOAPFLAG_FIXEDARR );
			if (hr == S_OK)
			{
				if (nElements != AtlSoapGetArrayDims(pEntry->pDims))
				{
					return E_FAIL;
				}
			}
			else
			{
				hr = S_OK;
				nElements = AtlSoapGetArrayDims(pEntry->pDims);
			}
			nElementsPush = nElements;
		}

		dwFlags |= pEntry->dwFlags;

		// push element with array flag

		if (S_OK != PushState(((unsigned char *)state.pvElement)+pEntry->nOffset, 
			state.pMap, pEntry, dwFlags & ~SOAPFLAG_READYSTATE, nAllocSize, nElementsPush))
		{
			return E_OUTOFMEMORY;
		}

		m_bChildCheck = true;

		return S_OK;
	}

	void * UpdateArray(ParseState& state, const _soapmapentry *pEntry)
	{
		ATLENSURE(pEntry);

		size_t nSize;
		void *pVal = NULL;

		if (pEntry->nVal != SOAPTYPE_UNK)
		{
			nSize = AtlSoapGetElementSize((SOAPTYPES) pEntry->nVal);
		}
		else
		{
			ATLENSURE( pEntry->pChain != NULL );

			nSize = pEntry->pChain->nElementSize;
		}

		if (state.dwFlags & SOAPFLAG_FIXEDARR)
		{
			unsigned char *ppArr = (unsigned char *)state.pvElement;
			pVal = ppArr+(state.nElement*nSize);
		}
		else
		{
			ATLASSERT( state.dwFlags & SOAPFLAG_DYNARR );

			unsigned char **ppArr = (unsigned char **)state.pvElement;
			pVal = (*ppArr)+(state.nElement*nSize);
			if (state.dwFlags & SOAPFLAG_UNKSIZE)
			{
				ATLASSERT( IsRpcEncoded() == false );

				// need to use the previous state's pvElement to update the size_is value
				ATLASSUME( m_nState > 0 );
				int *pnSizeIs = (int *)(((unsigned char *)m_stateStack[m_nState-1].pvElement)+
					(state.pMap->pEntries[pEntry->nSizeIs].nOffset));

				// update size_is parameter
				*pnSizeIs = (int)(state.nElement+1);
				state.nExpectedElements++;
			}
		}
		state.nElement++;

		return pVal;
	}

	HRESULT ProcessString(const _soapmapentry *pEntry, void *pVal)
	{
		ATLENSURE_RETURN( pEntry != NULL );

		//  set to the string builder class

		ATLASSERT( GetReader() != NULL );

		m_stringBuilder.SetReader(GetReader());
		m_stringBuilder.SetParent(this);

		m_stringBuilder.Clear();
		GetReader()->putContentHandler( &m_stringBuilder );

		if (S_OK != PushState(pVal, NULL, pEntry, SOAPFLAG_READYSTATE | pEntry->dwFlags))
		{
			return E_OUTOFMEMORY;
		}

		return S_OK;
	}


	HRESULT CheckHref(
		const _soapmapentry *pEntry,
		void *pVal,
		ISAXAttributes *pAttributes,
		DWORD dwIncludeFlags = 0,
		DWORD dwExcludeFlags = 0)
	{
		ATLASSERT( pEntry != NULL );
		ATLASSERT( pVal != NULL );
		ATLASSERT( pAttributes != NULL );

		const wchar_t *wsz = NULL;
		int cch = 0;

		HRESULT hr = GetAttribute(pAttributes, L"href", sizeof("href")-1, &wsz, &cch);
		if ((hr == S_OK) && (wsz != NULL))
		{
			// only allow hrefs on structs and arrays
			if (((pEntry->dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR))==0) &&
				(pEntry->pChain == NULL || pEntry->pChain->mapType != SOAPMAP_STRUCT))
			{
				ATLTRACE( _T("ATL Server only allows href's on arrays and structs.\r\n") );

				return E_FAIL;
			}

			ATLASSERT( IsRpcEncoded() == true );

			_ATLTRY
			{
				if (*wsz == L'#')
				{
					wsz++;
					cch--;
				}

				REFSTRING strRef(wsz, cch);
				if (m_refMap.Lookup(strRef) != NULL)
				{
					// ATL Server does not support multi-reference objects 
					ATLASSERT( FALSE );
					return E_FAIL;
				}

				ParseState& currState = GetState();
				if ((currState.pEntry != NULL) && (currState.pEntry->dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR)))
				{
					// it is an array item
					ATLASSERT( currState.nElement != 0 );

					// exclude array flags for href'd array elements
					dwExcludeFlags |= SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR;
				}

				ParseState state;
				state.pvElement = pVal;
				state.dwFlags = (pEntry->dwFlags | dwIncludeFlags) & ~dwExcludeFlags;
				state.nExpectedElements = 0;

				state.nElement = 0;
				state.pMap = GetState().pMap;
				state.pEntry = pEntry;

				if (!m_refMap.SetAt(strRef, state))
				{
					ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckHref -- out of memory.\r\n" ) );

					return E_OUTOFMEMORY;
				}

				// make sure there are no child elements
				m_bNullCheck = true;

				// push an emtpy state
				return PushState();
			}
			_ATLCATCHALL()
			{
				ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckHref -- out of memory.\r\n" ) );

				return E_OUTOFMEMORY;
			}
		}

		return S_FALSE;
	}

	HRESULT ProcessUDT(
		const _soapmapentry *pEntry, 
		void *pVal)
	{
		ATLENSURE_RETURN( pEntry != NULL );
		ATLENSURE_RETURN( pVal != NULL );
		ATLENSURE_RETURN( pEntry->nVal != SOAPTYPE_ERR );
		ATLENSURE_RETURN( pEntry->nVal != SOAPTYPE_USERBASE );

		// if it is a complex type, get the chain entry
		// and push the new state on the stack

		DWORD dwFlags = pEntry->dwFlags;
		if (pEntry->pChain->mapType != SOAPMAP_ENUM)
		{
			// struct
			dwFlags &= ~(SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR);
			m_bChildCheck = pEntry->pChain->nElements != 0;
		}
		else
		{
			// enum
			dwFlags |= SOAPFLAG_READYSTATE;

			// enums must not have child elements
			m_bNullCheck = true;

			// enums must be specified
			m_bCharacters = true;
		}

		if (S_OK != PushState(pVal, pEntry->pChain, pEntry, dwFlags, 0, pEntry->pChain->nElements))
		{
			return E_OUTOFMEMORY;
		}

		return S_OK;
	}

	HRESULT ChainEntry(
		const ParseState& state,
		const wchar_t  *wszNamespaceUri,
		int cchNamespaceUri,
		const wchar_t  *wszLocalName,
		int cchLocalName,
		ISAXAttributes  *pAttributes)
	{
		ATLENSURE_RETURN( state.pMap != NULL );

		// PAD is only supported on the client
		const _soapmap *pMap = state.pMap;
		if ((pMap->dwCallFlags & SOAPFLAG_CHAIN)==0)
		{
			return S_FALSE;
		}

		ATLENSURE_RETURN( pMap->dwCallFlags & SOAPFLAG_PAD );
		ATLASSUME( m_bClient == true );
		ATLENSURE_RETURN( pMap->nElements == 1 );
		const _soapmapentry *pEntries = pMap->pEntries;
		ATLENSURE_RETURN( pEntries != NULL );

		int nIndex;
		if (pEntries[0].dwFlags & SOAPFLAG_OUT)
		{
			nIndex = 0;
		}
		else
		{
			nIndex = 1;
		}

		const _soapmapentry *pEntry = &pEntries[nIndex];
		ATLENSURE_RETURN( pEntry->nHash != 0 );
		ATLENSURE_RETURN( pEntry->pChain != NULL );

		if (S_OK != PushState(state.pvElement, pEntry->pChain, pEntry, pEntry->dwFlags, 0, pEntry->pChain->nElements))
		{
			return E_OUTOFMEMORY;
		}

		return ProcessParams(wszNamespaceUri, cchNamespaceUri, wszLocalName, cchLocalName, pAttributes);
	}

	HRESULT IsNullEntry(const _soapmapentry *pEntry, ISAXAttributes *pAttributes)
	{
		ATLASSERT( pEntry != NULL );
		ATLASSERT( pAttributes != NULL );

		HRESULT hr = E_FAIL;
		bool bNull = false;
		const wchar_t *wszNull = NULL;
		int cchNull = 0;
		hr = GetAttribute(pAttributes, L"nil", sizeof("nil")-1, &wszNull, &cchNull,
				XSI_NAMESPACEW, sizeof(XSI_NAMESPACEA)-1);
		if ((hr == S_OK) && (wszNull != NULL))
		{
			hr = AtlGetSAXValue(&bNull, wszNull, cchNull);
			if (hr == S_OK)
			{
				if (bNull != false)
				{
					if (pEntry->dwFlags & SOAPFLAG_NULLABLE)
					{
						m_bNullCheck = true;

						// push an emtpy state
						return PushState();
					}

					// non-nullable element
					return E_FAIL;
				}
			}
		}

		return S_FALSE;
	}

	HRESULT ProcessParams(
		const wchar_t  *wszNamespaceUri,
		int cchNamespaceUri,
		const wchar_t  *wszLocalName,
		int cchLocalName,
		ISAXAttributes  *pAttributes)
	{
		(wszNamespaceUri);
		(cchNamespaceUri);

		if (m_stateStack.IsEmpty())
		{
			if (m_dwState == SOAP_HEADERS)
			{
				return CheckMustUnderstandHeader(pAttributes);
			}

			return E_FAIL;
		}

		ParseState &state = GetState();

		ATLASSERT( state.pvElement != NULL );
		HRESULT hr = E_FAIL;
		const _soapmapentry *pEntry = NULL;

		// if array element
		if (state.dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR))
		{
			hr = ValidateArrayEntry(state, wszLocalName, cchLocalName);

			if (SUCCEEDED(hr))
			{
				pEntry = state.pEntry;
			}
			else
			{
				return hr;
			}
		}
		else // not an array element
		{
			// special-case for PAD with type=
			hr = ChainEntry(state, wszNamespaceUri, cchNamespaceUri,
				wszLocalName, cchLocalName, pAttributes);

			if (hr == S_FALSE)
			{
				hr = GetElementEntry(state, wszNamespaceUri, wszLocalName, cchLocalName, pAttributes, &pEntry);
				if (hr != S_OK)
				{
					if (hr == S_FALSE)
					{
						hr = S_OK;
					}
					else if (m_dwState == SOAP_HEADERS)
					{
						hr = CheckMustUnderstandHeader(pAttributes);
					}
					return hr;
				}

				ATLASSERT( pEntry != NULL );
			}
			else
			{
				return hr;
			}
		}

		hr = IsNullEntry(pEntry, pAttributes);
		if (hr != S_FALSE)
		{
			return hr;
		}
		hr = S_OK;
		ATLENSURE_RETURN(pEntry);
		// if is array
		if (((pEntry->pDims != NULL) || (pEntry->dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR))) && 
			((state.dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR)) == 0))
		{
			// get SOAP section-5 info (if it is there)
			return GetSection5Info(state, pEntry, pAttributes);
		}
		else
		{
			// if it is a simple type, push a new (ready) state on the stack
			void *pVal;
			if (state.dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR))
			{
				pVal = UpdateArray(state, pEntry);
				ATLASSERT( pVal != NULL );
			}
			else
			{
				pVal = (((unsigned char *)state.pvElement)+pEntry->nOffset);
			}

			if (IsRpcEncoded() != false)
			{
				// check for href
				// we ONLY do this for rpc/encoded (required for interop)
				// NOTE: ATL Server does not support object graphs, so 
				// only single-reference elements are allowed
				hr = CheckHref(pEntry, pVal, pAttributes);
				if (hr != S_FALSE)
				{
					return hr;
				}
				hr = S_OK;
			}

			if (pEntry->nVal != SOAPTYPE_UNK)
			{
				// simple types should not have child elements
				m_bNullCheck = true;

				// if it is a string
				if ((pEntry->nVal == SOAPTYPE_STRING) || (pEntry->nVal == SOAPTYPE_BASE64BINARY))
				{
					hr = ProcessString(pEntry, pVal);
				}
				else
				{
					// expect characters for all non-string simple types
					m_bCharacters = true;

					// basic simple type
					if (S_OK != PushState(pVal, NULL, pEntry, SOAPFLAG_READYSTATE | pEntry->dwFlags))
					{
						hr = E_OUTOFMEMORY;
					}
				}
			}
			else
			{
				hr = ProcessUDT(pEntry, pVal);
				if (pEntry->dwFlags & (SOAPFLAG_DYNARRWRAPPER))
				{
					// We're moving to the **first** entry in the dynamic array wrapper.
					// We know it is the first entry because the dynamic array wrapper is created
					// by sproxy and it guarantees this layouts.
					++m_nDepth;
					ProcessParams (wszNamespaceUri, cchNamespaceUri, pEntry->pChain->pEntries[0].wszField, 
						pEntry->pChain->pEntries[0].cchField, pAttributes);
				}
			}
		}

		return hr;
	}

	size_t GetSizeIsValue(void *pvParam, const _soapmap *pMap, const _soapmapentry *pEntry)
	{
		ATLENSURE( pvParam != NULL );
		ATLENSURE( pMap != NULL );
		ATLENSURE( pEntry != NULL );

		int nSizeIs = pEntry->nSizeIs;
		size_t nOffset = pMap->pEntries[nSizeIs].nOffset;
		void *pVal = ((unsigned char *)pvParam)+nOffset;

		__int64 nVal = 0;
		switch(pMap->pEntries[nSizeIs].nVal)
		{
			case SOAPTYPE_INTEGER: 
			case SOAPTYPE_NONPOSITIVEINTEGER:
			case SOAPTYPE_NEGATIVEINTEGER:
			case SOAPTYPE_LONG:
				nVal = *((__int64 *)pVal);
				break;
			case SOAPTYPE_INT:
				nVal = *((int *)pVal);
				break;
			case SOAPTYPE_SHORT:
				nVal = *((short *)pVal);
				break;
			case SOAPTYPE_BYTE:
				nVal = *((char *)pVal);
				break;
			case SOAPTYPE_POSITIVEINTEGER:
			case SOAPTYPE_NONNEGATIVEINTEGER:
			case SOAPTYPE_UNSIGNEDLONG:
				unsigned __int64 n;
				n = *((unsigned __int64 *)pVal);
				if (n > _I64_MAX)
				{
					// come on ...
					nVal = 0;
				}
				else
				{
					nVal = (__int64)n;
				}
				break;
			case SOAPTYPE_UNSIGNEDINT:
				nVal = *((unsigned int *)pVal);
				break;
			case SOAPTYPE_UNSIGNEDSHORT:
				nVal = *((unsigned short *)pVal);
				break;
			case SOAPTYPE_UNSIGNEDBYTE:
				nVal = *((unsigned char *)pVal);
				break;
			default:
				nVal = 0;
		}

		if (nVal < 0)
		{
			nVal = 0;
		}

		return (size_t) nVal;
	}

	HRESULT GenerateArrayInfo(const _soapmapentry *pEntry, const int *pDims, IWriteStream *pStream)
	{
		ATLENSURE_RETURN( pEntry != NULL );
		ATLENSURE_RETURN( pStream != NULL );

		HRESULT hr = S_OK;
		if (pEntry->nVal != SOAPTYPE_UNK)
		{
			// xsd type
			hr = pStream->WriteStream(" soapenc:arrayType=\"xsd:", 
				sizeof(" soapenc:arrayType=\"xsd:")-1, NULL);
		}
		else
		{
			ATLENSURE_RETURN( pEntry->pChain != NULL );

			hr = pStream->WriteStream(" xmlns:q1=\"", sizeof(" xmlns:q1=\"")-1, NULL);
			if (SUCCEEDED(hr))
			{
				if (pEntry->pChain->szNamespace != NULL)
				{
					hr = pStream->WriteStream(pEntry->pChain->szNamespace, pEntry->pChain->cchNamespace, NULL);
				}
				else
				{
					hr = pStream->WriteStream(GetNamespaceUriA(), -1, NULL);
				}
				if (SUCCEEDED(hr))
				{
					hr = pStream->WriteStream("\"", 1, NULL);
					if (SUCCEEDED(hr))
					{
						hr = pStream->WriteStream(" soapenc:arrayType=\"q1:", 
							sizeof(" soapenc:arrayType=\"q1:")-1, NULL);
					}
				}
			}
		}

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

		if (pEntry->nVal != SOAPTYPE_UNK)
		{
			hr = pStream->WriteStream(s_xsdNames[pEntry->nVal].szName , 
				s_xsdNames[pEntry->nVal].cchName, NULL);
		}
		else
		{
			ATLASSERT( pEntry->pChain != NULL );

			hr = pStream->WriteStream(pEntry->pChain->szName, pEntry->pChain->cchName, NULL);
		}

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

		hr = pStream->WriteStream("[", 1, NULL);
		if (FAILED(hr))
		{
			return hr;
		}

		CWriteStreamHelper s( pStream );
		for (int i=1; i<=pDims[0]; i++)
		{
			if (!s.Write(pDims[i]) || 
				((i < pDims[0]) && (S_OK != pStream->WriteStream(", ", 2, NULL))))
			{
				return E_FAIL;
			}
		}

		hr = pStream->WriteStream("]\"", 2, NULL);
		if (FAILED(hr))
		{
			return hr;
		}

		return S_OK;
	}

	HRESULT GenerateXSDWrapper(bool bStart, int nVal, bool bNull, IWriteStream *pStream)
	{
		ATLENSURE_RETURN( pStream != NULL );

		HRESULT hr = pStream->WriteStream((bStart != false) ? "<" : "</", 
			(bStart != false) ? 1 : 2, NULL);
		if (SUCCEEDED(hr))
		{
			hr = pStream->WriteStream(s_xsdNames[nVal].szName, 
				s_xsdNames[nVal].cchName, NULL);
			if ((bNull != false) && (SUCCEEDED(hr)))
			{
				hr = pStream->WriteStream(" xsi:nil=\"1\"", sizeof(" xsi:nil=\"1\"")-1, NULL);
			}
			if (SUCCEEDED(hr))
			{
				hr = pStream->WriteStream(">", 1, NULL);
			}
		}

		return hr;
	}

	HRESULT GenerateGenericWrapper(bool bStart, const _soapmap *pMap, IWriteStream *pStream)
	{
		ATLENSURE_RETURN( pStream != NULL );
		ATLENSURE_RETURN( pMap != NULL );

		HRESULT hr = pStream->WriteStream((bStart != false) ? "<" : "</", 
			(bStart != false) ? 1 : 2, NULL);
		if (SUCCEEDED(hr))
		{
			hr = pStream->WriteStream(pMap->szName, pMap->cchName, NULL);
			if (SUCCEEDED(hr))
			{
				hr = pStream->WriteStream(">", 1, NULL);
			}
		}

		return hr;
	}

	HRESULT GetArrayInformation(
		IWriteStream *pStream, 
		const _soapmap *pMap, 
		const _soapmapentry *pEntry, 
		void *pvParam,
		size_t &nCnt, 
		size_t &nElementSize)
	{
		ATLENSURE_RETURN( pStream != NULL );
		ATLENSURE_RETURN( pMap != NULL );
		ATLENSURE_RETURN( pEntry != NULL );
		ATLENSURE_RETURN( pvParam != NULL );

		const int *pDims = NULL;
		int arrDims[2] = { 0 };

		if (pEntry->dwFlags & SOAPFLAG_FIXEDARR)
		{
			pDims = pEntry->pDims;
		}
		else
		{
			ATLASSERT( pEntry->dwFlags & SOAPFLAG_DYNARR );
			nCnt = GetSizeIsValue(pvParam, pMap, pEntry);

			if (nCnt == 0)
			{
				// array size should only be zero if array is NULL
				// did you forget to set the array size?
				ATLASSERT( FALSE );
				return E_FAIL;
			}

			arrDims[0] = 1;
			arrDims[1] = (int) nCnt;

			pDims = arrDims;
		}

		// output array information
		HRESULT hr = GenerateArrayInfo(pEntry, pDims, pStream);
		if (FAILED(hr))
		{
			return hr;
		}
		if (SUCCEEDED(hr))
		{
			nCnt = AtlSoapGetArrayDims(pDims);

			// did you forget to set the size_is value?
			ATLASSERT( nCnt != 0 );

			if (pEntry->nVal != SOAPTYPE_UNK)
			{
				nElementSize = AtlSoapGetElementSize((SOAPTYPES) pEntry->nVal);
			}
			else
			{
				ATLENSURE_RETURN( pEntry->pChain != NULL );

				nElementSize = pEntry->pChain->nElementSize;
			}
		}

		return hr;
	}

	HRESULT GenerateEnum(IWriteStream *pStream, void *pVal, const _soapmapentry *pEntry, bool bArray)
	{
		ATLENSURE_RETURN( pStream != NULL );
		ATLENSURE_RETURN( pVal != NULL );
		ATLENSURE_RETURN( pEntry != NULL );

		int nVal = *((int *)pVal);
		const _soapmapentry *pEnumEntries = pEntry->pChain->pEntries;

		ATLENSURE_RETURN( pEnumEntries != NULL );
		size_t j;
		HRESULT hr = E_FAIL;
		for (j=0; pEnumEntries[j].nHash != 0; j++)
		{
			if (nVal == pEnumEntries[j].nVal)
			{
				hr = pStream->WriteStream(pEnumEntries[j].szField, pEnumEntries[j].cchField, NULL);
				if ((bArray != false) && (SUCCEEDED(hr)))
				{
					hr = GenerateGenericWrapper(false, pEntry->pChain, pStream);
				}
				break;
			}
		}

		return hr;
	}

	HRESULT GenerateHeaders(CResponseGenerator *pGenerator, const _soapmap *pMap, IWriteStream *pStream)
	{
		ATLENSURE_RETURN( pStream != NULL );
		ATLENSURE_RETURN( pMap != NULL );

		ATLENSURE_RETURN( pGenerator != NULL );

		DWORD dwIncludeFlags = SOAPFLAG_OUT;
		if (m_bClient != false)
		{
			dwIncludeFlags = SOAPFLAG_IN;
		}

		size_t nCnt = 0;
		for (size_t i=0; pMap->pEntries[i].nHash != 0; i++)
		{
			if (pMap->pEntries[i].dwFlags & dwIncludeFlags)
			{
				nCnt++;
			}
		}

		// no headers to be sent
		if (nCnt == 0)
		{
			return S_OK;
		}

		HRESULT hr = pGenerator->StartHeaders(pStream, pMap);
		if (SUCCEEDED(hr))
		{
			hr = GenerateResponseHelper(pGenerator, pMap, GetHeaderValue(), pStream);
			if (SUCCEEDED(hr))
			{
				hr = pGenerator->EndHeaders(pStream);
			}
		}

		return hr;
	}

	bool IsNullElement(const _soapmapentry *pEntry, void *pVal, DWORD dwExcludeFlags=0)
	{
		ATLENSURE( pEntry != NULL );
		ATLENSURE( pVal != NULL );

		bool bNull = false;

		DWORD dwFlags = pEntry->dwFlags & ~dwExcludeFlags;

		if (dwFlags & SOAPFLAG_DYNARR)
		{
			unsigned char **ppArr = (unsigned char **)pVal;
			if (*ppArr == NULL)
			{
				bNull = true;
			}
		}
		else if (pEntry->nVal == SOAPTYPE_STRING)
		{
			BSTR *pBSTR = (BSTR *)pVal;
			if (*pBSTR == NULL)
			{
				bNull = true;
			}
		}
		else if ((pEntry->nVal == SOAPTYPE_BASE64BINARY) || (pEntry->nVal == SOAPTYPE_HEXBINARY))
		{
			if (((ATLSOAP_BLOB *)pVal)->data == NULL)
			{
				bNull = true;
			}
		}

		return bNull;
	}

	HRESULT GenerateNull(IWriteStream *pStream)
	{
		ATLENSURE_RETURN( pStream != NULL );
		return pStream->WriteStream(" xsi:nil=\"1\"/>", sizeof(" xsi:nil=\"1\"/>")-1, NULL);
	}

	HRESULT GenerateResponseHelper(CResponseGenerator *pGenerator, const _soapmap *pMap, void *pvParam, IWriteStream *pStream, 
		bool bArrayElement = false)
	{
		ATLENSURE_RETURN( pGenerator != NULL );
		ATLENSURE_RETURN( pMap != NULL );
		ATLENSURE_RETURN( pStream != NULL );

		HRESULT hr = S_OK;

		if ((bArrayElement != false) && 
			((pMap->dwCallFlags & SOAPFLAG_PAD)==0))
		{
			hr = pGenerator->StartMap(pStream, pMap, m_bClient);
			if (FAILED(hr))
			{
				return hr;
			}
		}

		ATLENSURE_RETURN( pMap->pEntries != NULL );

		const _soapmapentry *pEntries = pMap->pEntries;
		size_t i;

		DWORD dwIncludeFlags;
		DWORD dwExcludeFlags;
		if (m_bClient != false)
		{
			dwIncludeFlags = SOAPFLAG_IN;
			dwExcludeFlags = SOAPFLAG_OUT;
		}
		else
		{
			dwIncludeFlags = SOAPFLAG_OUT;
			dwExcludeFlags = SOAPFLAG_IN;
		}

		for (i=0; pEntries[i].nHash != 0; i++)
		{
			if (((pEntries[i].dwFlags & dwIncludeFlags) ||
				((pEntries[i].dwFlags & dwExcludeFlags)==0)) &&
				((pEntries[i].dwFlags & SOAPFLAG_NOMARSHAL)==0))
			{
				hr = pGenerator->StartEntry(pStream, pMap, &pEntries[i]);
				if (FAILED(hr))
				{
					return hr;
				}

				size_t nElementSize = 0;
				size_t nCnt = 1;

				ATLASSERT( pvParam != NULL );

				void *pvCurrent = ((unsigned char *)pvParam)+pEntries[i].nOffset;

				if (IsNullElement(&pEntries[i], pvCurrent))
				{
					hr = GenerateNull(pStream);
					if (SUCCEEDED(hr))
					{
						continue;
					}
					return hr;
				}

				bool bArray = (pEntries[i].dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR)) != 0;
				if (bArray != false)
				{
					hr = GetArrayInformation(pStream, pMap, &pEntries[i], pvParam, nCnt, nElementSize);
				}

				hr = pStream->WriteStream(">", 1, NULL);
				if (FAILED(hr))
				{
					return hr;
				}

				for (size_t nElement=0; nElement<nCnt; nElement++)
				{
					void *pVal;

					// get updated value
					if (bArray != false)
					{
						if (pEntries[i].dwFlags & SOAPFLAG_FIXEDARR)
						{
							unsigned char *ppArr = (unsigned char *)pvCurrent;
							pVal = ppArr+(nElement*nElementSize);
						}
						else
						{
							ATLASSERT( pEntries[i].dwFlags & SOAPFLAG_DYNARR );

							unsigned char **ppArr = (unsigned char **)pvCurrent;
							pVal = (*ppArr)+(nElement*nElementSize);
						}
					}
					else
					{
						pVal = pvCurrent;
					}

					if (pEntries[i].nVal != SOAPTYPE_UNK)
					{
						bool bNull = false;
						if (bArray != false)
						{
							bNull = IsNullElement(&pEntries[i], pVal, SOAPFLAG_DYNARR | SOAPFLAG_FIXEDARR);
							hr = GenerateXSDWrapper(true, pEntries[i].nVal, bNull, pStream);
							if (FAILED(hr))
							{
								return hr;
							}
						}
						if (bNull == false)
						{
							hr = AtlSoapGenElementValue(pVal, pStream, (SOAPTYPES) pEntries[i].nVal, GetMemMgr());
						}
						if ((SUCCEEDED(hr)) && (bArray != false))
						{
							hr = GenerateXSDWrapper(false, pEntries[i].nVal, false, pStream);
						}

						if (FAILED(hr))
						{
							return hr;
						}
					}
					else
					{
						ATLASSERT( pEntries[i].pChain != NULL );

						if (pEntries[i].pChain->mapType != SOAPMAP_ENUM)
						{
							// struct
							hr = GenerateResponseHelper(pGenerator, pEntries[i].pChain, pVal, pStream, bArray);
						}
						else
						{
							if (bArray != false)
							{
								hr = GenerateGenericWrapper(true, pEntries[i].pChain, pStream);
								if (FAILED(hr))
								{
									return hr;
								}
							}

							hr = GenerateEnum(pStream, pVal, &pEntries[i], bArray);
						}
					}
				}

				// output element close
				if (SUCCEEDED(hr))
				{
					hr = pGenerator->EndEntry(pStream, pMap, &pEntries[i]);
				}
			}

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

		if ((bArrayElement != false) && 
			((pMap->dwCallFlags & SOAPFLAG_PAD)==0))
		{
			// output type name
			hr = pGenerator->EndMap(pStream, pMap, m_bClient);
		}

		return hr;
	}

	void CleanupHelper(const _soapmap *pMap, void *pvParam)
	{
		ATLENSURE( pMap != NULL );
		ATLENSURE( pMap->pEntries != NULL );

		if (pvParam == NULL)
		{
			return;
		}

		const _soapmapentry *pEntries = pMap->pEntries;
		size_t i;

		for (i=0; pEntries[i].nHash != 0; i++)
		{
			if ((m_bClient != false) && ((pEntries[i].dwFlags & SOAPFLAG_OUT)==0))
			{
				// skip in-only headers on the client
				continue;
			}

			void *pvCheck = ((unsigned char *)pvParam)+pEntries[i].nOffset;
			if (IsNullElement(&pEntries[i], pvCheck))
			{
				continue;
			}

			size_t nElementSize = 0;
			size_t nCnt = 1;

			const int *pDims = NULL;
			int arrDims[2] = { 0 };

			bool bArray = (pEntries[i].dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR)) != 0;

			if (bArray != false)
			{
				if (pEntries[i].dwFlags & SOAPFLAG_FIXEDARR)
				{
					pDims = pEntries[i].pDims;
				}
				else
				{
					ATLASSERT( pEntries[i].dwFlags & SOAPFLAG_DYNARR );
					nCnt = GetSizeIsValue(pvParam, pMap, &pEntries[i]);

					arrDims[0] = 1;
					arrDims[1] = (int) nCnt;

					pDims = arrDims;
				}

				nCnt = AtlSoapGetArrayDims(pDims);

				if (pEntries[i].nVal != SOAPTYPE_UNK)
				{
					nElementSize = AtlSoapGetElementSize((SOAPTYPES) pEntries[i].nVal);
				}
				else
				{
					ATLENSURE( pEntries[i].pChain != NULL );

					nElementSize = pEntries[i].pChain->nElementSize;
				}					
			}

			void *pvCurrent = ((unsigned char *)pvParam)+pEntries[i].nOffset;

			for (size_t nElement=0; nElement<nCnt; nElement++)
			{
				void *pVal;

				// get updated value
				if (bArray != false)
				{
					if (pEntries[i].dwFlags & SOAPFLAG_FIXEDARR)
					{
						unsigned char *ppArr = (unsigned char *)pvCurrent;
						pVal = ppArr+(nElement*nElementSize);
					}
					else
					{
						ATLASSERT( pEntries[i].dwFlags & SOAPFLAG_DYNARR );

						unsigned char **ppArr = (unsigned char **)pvCurrent;
						if (*ppArr == NULL)
						{							
							break;
						}
						pVal = (*ppArr)+(nElement*nElementSize);
					}
				}
				else
				{
					pVal = pvCurrent;
				}

				if (pEntries[i].nVal != SOAPTYPE_UNK)
				{
					AtlSoapCleanupElement(pVal, (SOAPTYPES) pEntries[i].nVal, GetMemMgr());
				}
				else
				{
					ATLENSURE( pEntries[i].pChain != NULL );

					if (pEntries[i].pChain->mapType != SOAPMAP_ENUM)
					{
						CleanupHelper(pEntries[i].pChain, pVal);
					}
				}
			}

			if (pEntries[i].dwFlags & SOAPFLAG_DYNARR)
			{
				// free it
				unsigned char **ppArr = (unsigned char **)pvCurrent;

				ATLENSURE( ppArr != NULL );

				if (*ppArr != NULL)
				{
					m_pMemMgr->Free(*ppArr);
					*ppArr = NULL;
				}
			}
		}
	}

	const _soapmap * GetSoapMapFromName(
		const wchar_t * wszName, 
		int cchName = -1, 
		const wchar_t * wszNamespaceUri = NULL,
		int cchNamespaceUri = -1,
		int *pnVal = NULL,
		bool bHeader = false)
	{
		(cchNamespaceUri);

		const _soapmap ** pEntry = NULL;

		if (bHeader == false)
		{
			pEntry = GetFunctionMap();
		}
		else
		{
			pEntry = GetHeaderMap();
		}

		if (pEntry == NULL)
		{
			return NULL;
		}

		if (cchName < 0)
		{
			cchName = (int)wcslen(wszName);
		}
		if ((cchNamespaceUri < 0) && (wszNamespaceUri != NULL))
		{
			cchNamespaceUri = (int)wcslen(wszNamespaceUri);
		}

		ULONG nFunctionHash = AtlSoapHashStr(wszName, cchName);
		ULONG nNamespaceHash = wszNamespaceUri ? AtlSoapHashStr(wszNamespaceUri, cchNamespaceUri) : 0;

		int i;
		for (i=0; pEntry[i] != NULL; i++)
		{
			if ((IsEqualStringHash(wszName, cchName, nFunctionHash,
				   pEntry[i]->wszName, pEntry[i]->cchWName, pEntry[i]->nHash) != FALSE) &&
				(!wszNamespaceUri ||
				 IsEqualStringHash(wszNamespaceUri, cchNamespaceUri, nNamespaceHash, 
				   pEntry[i]->wszNamespace, pEntry[i]->cchNamespace, pEntry[i]->nNamespaceHash) != FALSE))
			{
				break;
			}
		}

		if (pnVal != NULL)
		{
			*pnVal = i;
		}
		return pEntry[i];
	}

	HRESULT CheckEndElement(const ParseState& state)
	{
		// check for all elements
		if (state.nElement == state.nExpectedElements)
		{
			return S_OK;
		}

		// error check for fixed arrays
		if (state.dwFlags & SOAPFLAG_FIXEDARR)
		{
			return E_FAIL;
		}

		// check for dynamic arrays
		if (state.dwFlags & SOAPFLAG_DYNARR)
		{
			// check for dynamic arrays with known size
			// (from soap:arrayType attribute)
			if ((state.dwFlags & SOAPFLAG_UNKSIZE)==0)
			{
				return E_FAIL;
			}
		}

		DWORD dwIncludeFlags;
		DWORD dwExcludeFlags;

		if (m_bClient != false)
		{
			dwIncludeFlags = SOAPFLAG_OUT;
			dwExcludeFlags = SOAPFLAG_IN;
		}
		else
		{
			dwIncludeFlags = SOAPFLAG_IN;
			dwExcludeFlags = SOAPFLAG_OUT;
		}

		if (state.pMap != NULL)
		{
			// ensure all omitted elements were nullable elements or nomarshal elements
			const _soapmapentry *pEntries = state.pMap->pEntries;
			for (size_t i=0; pEntries[i].nHash != 0; i++)
			{
				if ((pEntries[i].dwFlags & dwIncludeFlags) ||
					((pEntries[i].dwFlags & dwExcludeFlags)==0))
				{
					if (state.vec.GetBit(i) == false)
					{
						if (((pEntries[i].dwFlags & (SOAPFLAG_NULLABLE | SOAPFLAG_NOMARSHAL))==0) && (pEntries[i].nVal != SOAPTYPE_UNK))
						{
							ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckEndElement -- invalid number of elements for parameter/field\r\n") );
							return E_FAIL;
						}
					}
				}
			}
		}

		return S_OK;
	}

	HRESULT CheckSoapHeaders(const ParseState &state)
	{
		DWORD dwIncludeFlags;
		DWORD dwExcludeFlags;

		if (m_bClient != false)
		{
			dwIncludeFlags = SOAPFLAG_OUT;
			dwExcludeFlags = SOAPFLAG_IN;
		}
		else
		{
			dwIncludeFlags = SOAPFLAG_IN;
			dwExcludeFlags = SOAPFLAG_OUT;
		}

		if (state.pMap != NULL)
		{
			ATLASSERT( state.pMap->mapType == SOAPMAP_HEADER );

			// ensure all omitted elements were nullable elements, nomarshal elements, or non-required elements
			const _soapmapentry *pEntries = state.pMap->pEntries;
			for (size_t i=0; pEntries[i].nHash != 0; i++)
			{
				if ((pEntries[i].dwFlags & dwIncludeFlags) ||
					((pEntries[i].dwFlags & dwExcludeFlags)==0))
				{
					if (state.vec.GetBit(i) == false)
					{
						bool bNoOmit = (pEntries[i].dwFlags & (SOAPFLAG_NULLABLE | SOAPFLAG_NOMARSHAL))==0;

						if ((bNoOmit != false) || 
							((bNoOmit != false) && (pEntries[i].dwFlags & SOAPFLAG_MUSTUNDERSTAND)))
						{
							ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckSoapHeaders -- missing header\r\n") );
							return E_FAIL;
						}
					}
				}
			}
		}

		return S_OK;
	}

	HRESULT CheckEndHeaders(
		const wchar_t  * wszNamespaceUri,
		int cchNamespaceUri,
		const wchar_t  * wszLocalName,
		int cchLocalName)
	{
		if (IsEqualElement(sizeof(SOAP_HEADERA)-1, SOAP_HEADERW,
					sizeof(SOAPENV_NAMESPACEA)-1, SOAPENV_NAMESPACEW,
					cchLocalName, wszLocalName,
					cchNamespaceUri, wszNamespaceUri))
		{
			m_dwState = SOAP_HEADERS_DONE;
			return S_OK;
		}

		// some sort of error
		ATLTRACE( _T("ATLSOAP: CSoapRootHandler::endElement -- invalid SOAP message format while processing headers.\r\n" ) );

		return E_FAIL;
	}

protected:

	ISAXXMLReader * SetReader(ISAXXMLReader *pReader)
	{
		ISAXXMLReader *pPrevRdr = m_spReader;
		m_spReader = pReader;

		return pPrevRdr;
	}

	ISAXXMLReader * GetReader()
	{
		return m_spReader;
	}

	HRESULT SetSoapMapFromName(
		const wchar_t * wszName, 
		int cchName = -1, 
		const wchar_t * wszNamespaceUri = NULL,
		int cchNamespaceUri = -1,
		bool bHeader = false)
	{
		ATLENSURE_RETURN( wszName != NULL );

		int nVal;
		const _soapmap *pMap = NULL;
		if (m_stateStack.GetCount() != 0)
		{
			ATLASSUME( m_stateStack[0].pMap != NULL );
			nVal = (int) m_stateStack[0].nAllocSize;
			ATLASSERT( GetFunctionMap() != NULL );
			pMap = GetFunctionMap()[nVal];
		}
		else
		{
			pMap = GetSoapMapFromName(wszName, cchName,
				wszNamespaceUri, cchNamespaceUri, &nVal, bHeader);
		}

		if (pMap == NULL)
		{
			ATLTRACE( _T("ATLSOAP: CSoapRootHandler::SetSoapMapFromName -- _soapmap not found for: %.*ws, with namespace %.*ws\r\n"),
				(int)wcslen(wszName), wszName, wszNamespaceUri ? (int)wcslen(wszNamespaceUri) : 0, wszNamespaceUri ? wszNamespaceUri : L"");

			return E_FAIL;
		}

		HRESULT hr = E_OUTOFMEMORY;

		// allocate the parameter struct

		void *pvParam = NULL;
		if (bHeader != false)
		{
			pvParam = GetHeaderValue();
		}
		else 
		{
			if (m_bClient == false)
			{
				m_pvParam = m_pMemMgr->Allocate(pMap->nElementSize);
			}
			pvParam = m_pvParam;
		}

		if (pvParam != NULL)
		{
			if (bHeader == false)
			{
				memset(pvParam, 0x00, pMap->nElementSize);
			}

			// push initial state

			if (m_stateStack.GetCount() != 0)
			{
				m_stateStack.RemoveAll();
			}

			hr = PushState(pvParam, pMap, NULL, 0, nVal, pMap->nElements);

			if (FAILED(hr))
			{
				if ((m_bClient == false) && (bHeader == false))
				{
					m_pMemMgr->Free(pvParam);
				}
			}
		}

#ifdef _DEBUG
		if (hr == E_OUTOFMEMORY)
		{
			ATLTRACE( _T("ATLSOAP: CSoapRootHandler::SetSoapMapFromName -- out of memory.\r\n" ) );
		}
#endif // _DEBUG

		return hr;
	}

	// implementation
	virtual const _soapmap ** GetFunctionMap() = 0;
	virtual const _soapmap ** GetHeaderMap() = 0;
	virtual const wchar_t * GetNamespaceUri() = 0;
	virtual const char * GetServiceName() = 0;
	virtual const char * GetNamespaceUriA() = 0;
	virtual HRESULT CallFunction(
		void *pvParam, 
		const wchar_t *wszLocalName, int cchLocalName,
		size_t nItem) = 0;
	virtual void * GetHeaderValue() = 0;

public:

	CSoapRootHandler(ISAXXMLReader *pReader = NULL)
	:  m_pMemMgr(&m_crtHeap), m_spReader(pReader), m_bClient(false),
	   m_nState(0), m_pvParam(NULL), m_nDepth(0)
	{
		InitHandlerState();
	}
	virtual ~CSoapRootHandler()
	{
		m_skipHandler.DetachParent();		
	}

	IAtlMemMgr * SetMemMgr(IAtlMemMgr *pMemMgr)
	{
		IAtlMemMgr *pPrevMgr = m_pMemMgr;
		m_pMemMgr = pMemMgr;

		return pPrevMgr;
	}

	IAtlMemMgr * GetMemMgr()
	{
		return m_pMemMgr;
	}

	// override this function to do SOAP Fault handling
	virtual HRESULT SoapFault(
		SOAP_ERROR_CODE /*errCode*/, 
		const wchar_t * /*wszDetail*/, 
		int /*cchDetail*/)
	{
		if (m_bClient != false)
		{
			return S_OK;
		}

		// SOAP servers must implement this function
		ATLASSERT( FALSE );
		return E_FAIL;
	}

	//
	// implementation
	//

	void InitHandlerState()
	{
		m_bNullCheck = false;
		m_bCharacters = false;
		m_bChildCheck = false;	
		m_dwState = SOAP_START;		
	}
	HRESULT __stdcall startDocument()
	{
		InitHandlerState();
		return S_OK;
	}

	HRESULT __stdcall startElement( 
		 const wchar_t  *wszNamespaceUri,
		 int cchNamespaceUri,
		 const wchar_t  *wszLocalName,
		 int cchLocalName,
		 const wchar_t  * wszQName,
		 int cchQName,
		 ISAXAttributes  *pAttributes)
	{
		if (m_bNullCheck || m_bCharacters)
		{
			// make sure elements that aren't supposed to have child elements
			// do not have child elements, and where we were expecting
			// characters, we got them
			return E_FAIL;
		}

		m_bChildCheck = false;
		++m_nDepth;

		HRESULT hr = S_OK;
		switch (m_dwState)
		{
			case SOAP_PARAMS: case SOAP_HEADERS:
			{
				hr = ProcessParams(wszNamespaceUri, cchNamespaceUri, wszLocalName, 
					cchLocalName, pAttributes);

				break;
			}
			case SOAP_START: case SOAP_ENVELOPE: case SOAP_HEADERS_DONE:
			{
				ULONG nNamespaceHash = AtlSoapHashStr(wszNamespaceUri, 
					cchNamespaceUri);
				if (nNamespaceHash != SOAP_ENV)
				{
					ATLTRACE( _T("ATLSOAP: CSoapRootHandler::startElement -- incorrect SOAP-ENV namespace.\r\n" ) );

					return E_FAIL;
				}

				ULONG nElementHash = AtlSoapHashStr(wszLocalName, cchLocalName);

				if (nElementHash == ENVELOPE && 
					IsEqualElement(
						sizeof(SOAP_ENVELOPEA)-1, SOAP_ENVELOPEW,
						sizeof(SOAPENV_NAMESPACEA)-1, SOAPENV_NAMESPACEW,
						cchLocalName, wszLocalName,
						cchNamespaceUri, wszNamespaceUri))
				{
					// Envelope must be first element in package

					if (m_dwState != SOAP_START)
					{
						ATLTRACE( _T("ATLSOAP: CSoapRootHandler::startElement -- invalid SOAP message format: \"Envelope\" in unexpected location.\r\n" ) );

						hr = E_FAIL;
					}
					m_dwState = SOAP_ENVELOPE;
				}
				else if (nElementHash == HEADER &&
					IsEqualElement(sizeof(SOAP_HEADERA)-1, SOAP_HEADERW,
						sizeof(SOAPENV_NAMESPACEA)-1, SOAPENV_NAMESPACEW,
						cchLocalName, wszLocalName,
						cchNamespaceUri, wszNamespaceUri))
				{
					if (m_dwState != SOAP_ENVELOPE)
					{
						ATLTRACE( _T("ATLSOAP: CSoapRootHandler::startElement -- invalid SOAP message format: \"Headers\" in unexpected location.\r\n" ) );

						hr = E_FAIL;
					}

					m_dwState = SOAP_HEADERS;
				}
				else if (nElementHash == BODY &&
					IsEqualElement(sizeof(SOAP_BODYA)-1, SOAP_BODYW,
						sizeof(SOAPENV_NAMESPACEA)-1, SOAPENV_NAMESPACEW,
						cchLocalName, wszLocalName,
						cchNamespaceUri, wszNamespaceUri))
				{
					if (m_dwState == SOAP_START)
					{
						ATLTRACE( _T("ATLSOAP: CSoapRootHandler::startElement -- invalid SOAP message format: \"Body\" in unexpected location.\r\n" ) );

						hr = E_FAIL;
					}
					m_dwState = SOAP_BODY;
				}

				break;
			}
			case SOAP_BODY:
			{
				hr = DispatchSoapCall(wszNamespaceUri, cchNamespaceUri,
						wszLocalName, cchLocalName);

				m_dwState = SOAP_PARAMS;

				if (SUCCEEDED(hr))
				{
					if (GetState().pMap->dwCallFlags & SOAPFLAG_PAD)
					{
						hr = startElement(wszNamespaceUri, cchNamespaceUri,
								wszLocalName, cchLocalName, wszQName, cchQName,
								pAttributes);
					}
				}

				break;
			}

#ifdef _DEBUG

			default:
			{
				// should never get here -- internal error
				ATLASSERT( FALSE );
			}

#endif // _DEBUG
		}

		return hr;
	}

	HRESULT __stdcall characters( 
		 const wchar_t  *wszChars,
		 int cchChars)
	{
		m_bCharacters = false;

		// if it is a ready state, get the value
		if (m_stateStack.IsEmpty() == false)
		{
			ParseState& state = GetState();
			if ((state.dwFlags & SOAPFLAG_READYSTATE) &&
				((state.dwFlags & SOAPFLAG_SIZEIS)==0)) // don't marshal struct size_is elements -- should be filled in by array marshaling code
			{
				if ((state.pMap == NULL) || (state.pMap->mapType != SOAPMAP_ENUM))
				{
					return AtlSoapGetElementValue(wszChars, cchChars, 
						state.pvElement, (SOAPTYPES)state.pEntry->nVal, GetMemMgr());
				}
				else
				{
					// enum

					ATLASSERT( state.pMap != NULL );
					ATLASSERT( state.pMap->pEntries != NULL );

					ULONG nHash = AtlSoapHashStr(wszChars, cchChars);
					const _soapmapentry *pEntries = state.pMap->pEntries;

					size_t i;
					for (i=0; pEntries[i].nHash != 0; i++)
					{
						if ((nHash == pEntries[i].nHash) &&
							(cchChars == pEntries[i].cchField) &&
							(!wcsncmp(wszChars, pEntries[i].wszField, cchChars)))
						{
							break;
						}
					}

					if (pEntries[i].nHash != 0)
					{
						*((int *)state.pvElement) = pEntries[i].nVal;
						state.nElement++;
						return S_OK;
					}

					// no matching enum entry found
					ATLTRACE( _T("ATLSOAP: CSoapRootHandler::characters -- no matching enum entry found for: %.*ws.\r\n" ), cchChars, wszChars );

					return E_FAIL;
				}
			}
		}

		// otherwise, ignore

		return S_OK;
	}

	HRESULT __stdcall endElement( 
		 const wchar_t  * wszNamespaceUri,
		 int cchNamespaceUri,
		 const wchar_t  * wszLocalName,
		 int cchLocalName,
		 const wchar_t  * /*wszQName*/,
		 int /*cchQName*/)
	{
		static bool bDynArrWrapper = false;
		if (m_bCharacters)
		{			
			return E_FAIL;
		}

		m_bNullCheck = false;

		if (m_stateStack.IsEmpty() != false)
		{
			return S_OK;
		}

		if (!bDynArrWrapper && (m_nState > 1))
		{
			ParseState prevState = m_stateStack.GetAt(m_nState - 1);
			ParseState curState = m_stateStack.GetAt(m_nState);
			if (prevState.dwFlags & SOAPFLAG_DYNARRWRAPPER)
			{
				bDynArrWrapper = true;
				endElement (wszNamespaceUri, cchNamespaceUri, curState.pEntry->wszField,
				curState.pEntry->cchField, NULL, 0);
			}
		}
		else
		{
			bDynArrWrapper = false;
		}

		--m_nDepth;

		const ParseState& state = GetState();

		if ((m_dwState == SOAP_HEADERS) && (m_stateStack.GetCount() == 1))
		{
			return CheckEndHeaders(wszNamespaceUri, cchNamespaceUri, wszLocalName, cchLocalName);
		}

		if (state.dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR))
		{
			if (state.dwFlags & SOAPFLAG_READYSTATE)
			{
				PopState();
			}

			const ParseState& currstate = GetState();
			ATLENSURE_RETURN( currstate.pEntry != NULL );

			if (m_nDepth == (currstate.nDepth-1))
			{
				if (S_OK != CheckEndElement(currstate))
				{
					// invalid number of elements
					ATLTRACE( _T("ATLSOAP: CSoapRootHandler::endElement -- invalid number of array elements for array parameter %.*ws.\r\n"), 
							  currstate.pEntry->cchField, currstate.pEntry->wszField );

					return E_FAIL;
				}

				PopState();
			}
		}
		else
		{
			if (S_OK != CheckEndElement(state))
			{
				return E_FAIL;
			}

			PopState();
		}

		return S_OK;
	}

	HRESULT SetClientStruct(void *pvParam, int nMapIndex)
	{
		ATLENSURE_RETURN( pvParam != NULL );
		ATLENSURE_RETURN( nMapIndex >= 0 );

		// this is the params struct
		// store for later use
		m_pvParam = pvParam;

		const _soapmap ** pEntries = GetHeaderMap();
		ATLENSURE_RETURN( pEntries != NULL );

		// push header value
		return PushState(GetHeaderValue(), pEntries[nMapIndex], NULL, 0, nMapIndex, pEntries[nMapIndex]->nElements);
	}

	void ResetClientState(bool bFull = false)
	{
		m_stateStack.RemoveAll();
		m_nState = 0;
		if (bFull != false)
		{
			m_dwState = SOAP_START;
			m_pvParam = NULL;
		}
	}

	HRESULT CreateReader()
	{
		return m_spReader.CoCreateInstance(ATLS_SAXXMLREADER_CLSID, NULL, CLSCTX_INPROC_SERVER);
	}

	HRESULT InitializeSOAP(IServiceProvider *pProvider)
	{
		HRESULT hr = S_OK;

		if (m_spReader.p == NULL)
		{
			hr = E_FAIL;
			if (pProvider != NULL)
			{
				IAtlMemMgr *pMemMgr = NULL;
				hr = pProvider->QueryService(__uuidof(IAtlMemMgr), 
					__uuidof(IAtlMemMgr), (void **)&pMemMgr);
				if ((SUCCEEDED(hr)) && (pMemMgr != NULL))
				{
					SetMemMgr(pMemMgr);
				}

				hr = pProvider->QueryService(__uuidof(ISAXXMLReader), 
					__uuidof(ISAXXMLReader), (void **)&m_spReader);
			}

			if (FAILED(hr))
			{
				hr = CreateReader();
			}
		}

		if (SUCCEEDED(hr))
		{
			hr = m_spReader->putContentHandler(this);
		}

#ifdef _DEBUG
		else
		{
			ATLTRACE( _T("ATLSOAP: CSoapRootHandler::InitializeSOAP -- failed to get SAXXMLReader.\r\n" ) );
		}
#endif // _DEBUG

		return hr;
	}

	void UninitializeSOAP()
	{
		if (m_spReader.p != NULL)
		{
			m_spReader->putContentHandler(NULL);
			m_spReader.Release();
		}
	}

	virtual HRESULT DispatchSoapCall(const wchar_t *wszNamespaceUri,
		int cchNamespaceUri, const wchar_t *wszLocalName,
		int cchLocalName)
	{
		HRESULT hr = S_OK;

		if (m_stateStack.IsEmpty() == false)
		{
			ATLASSUME( m_stateStack[0].pMap != NULL );

			// check to see if all required and non-nullable SOAP headers were sent
			if (m_stateStack[0].pMap->mapType == SOAPMAP_HEADER)
			{
				hr = CheckSoapHeaders(m_stateStack[0]);
			}
			if (SUCCEEDED(hr))
			{
				hr = SetSoapMapFromName(wszLocalName, cchLocalName, 
						wszNamespaceUri, cchNamespaceUri);
			}
		}
		else
		{
			// get the appropriate function map
			hr = SetSoapMapFromName(wszLocalName, cchLocalName, 
					wszNamespaceUri, cchNamespaceUri);

			if (SUCCEEDED(hr))
			{
				// set the SOAP Header map for the function
				ATLASSUME( m_stateStack.IsEmpty() == false );

				const _soapmap **ppHeaderMap = GetHeaderMap();
				ATLENSURE_RETURN( ppHeaderMap != NULL );

				// create a temporary parse state for checking headers
				ParseState state;
				state.pMap = ppHeaderMap[m_stateStack[0].nAllocSize];
				ATLENSURE_RETURN( state.pMap != NULL );

				// check to see if all required and non-nullable SOAP headers were sent
				hr = CheckSoapHeaders(state);
			}
		}

		return hr;
	}

	virtual HRESULT BeginParse(IStream *pStream)
	{
		ATLASSERT( pStream != NULL );

		CComVariant varStream;
		varStream = static_cast<IUnknown*>(pStream);

		HRESULT hr = m_spReader->parse(varStream);
		if (SUCCEEDED(hr))
		{
			if (m_refMap.GetCount() != 0)
			{
				hr = E_FAIL;
			}
		}
		return hr;
	}

	HRESULT CallFunctionInternal()
	{
		HRESULT hr = E_FAIL;
		const ParseState& state = m_stateStack[0];
		hr = CallFunction(
			state.pvElement, 
			state.pMap->wszName,
			state.pMap->cchWName,
			state.nAllocSize);

		return hr;
	}

	virtual HRESULT GenerateResponse(IWriteStream *pStream)
	{
		ATLASSUME( m_stateStack.IsEmpty() == false );
		ATLASSUME( m_stateStack[0].pMap != NULL );
		ATLASSUME( m_stateStack[0].pvElement != NULL );

		const ParseState& state = m_stateStack[0];

		const _soapmap *pHeaderMap = NULL;
		if (m_bClient == false)
		{
			const _soapmap **ppHeaderMap = GetHeaderMap();
			if (ppHeaderMap != NULL)
			{
				pHeaderMap = ppHeaderMap[state.nAllocSize];
			}
		}
		else
		{
			pHeaderMap = state.pMap;
		}

		const _soapmap *pFuncMap = NULL;
		if (m_bClient == false)
		{
			pFuncMap = state.pMap;
		}
		else
		{
			const _soapmap **ppFuncMap = GetFunctionMap();
			ATLENSURE_RETURN( ppFuncMap != NULL );
			pFuncMap = ppFuncMap[state.nAllocSize];
		}

		ATLENSURE_RETURN( pFuncMap != NULL );

		CRpcEncodedGenerator rpcGen;
		CPADGenerator padGen;
		CPIDGenerator pidGen;

		CResponseGenerator *pGenerator = NULL;

		if ((pFuncMap->dwCallFlags & (SOAPFLAG_RPC | SOAPFLAG_ENCODED)) == (SOAPFLAG_RPC | SOAPFLAG_ENCODED))
		{
			pGenerator = &rpcGen;
		}
		else if (pFuncMap->dwCallFlags & SOAPFLAG_PID)
		{
			ATLASSERT( (pFuncMap->dwCallFlags & (SOAPFLAG_DOCUMENT | SOAPFLAG_LITERAL)) == (SOAPFLAG_DOCUMENT | SOAPFLAG_LITERAL) );
			pGenerator = &pidGen;
		}
		else
		{
			ATLASSERT( (pFuncMap->dwCallFlags & (SOAPFLAG_DOCUMENT | SOAPFLAG_LITERAL)) == (SOAPFLAG_DOCUMENT | SOAPFLAG_LITERAL) );
			ATLASSERT( pFuncMap->dwCallFlags & SOAPFLAG_PAD );
			pGenerator = &padGen;
		}

		HRESULT hr = pGenerator->StartEnvelope(pStream);
		if (SUCCEEDED(hr))
		{
			// generate headers if necessary
			hr = GenerateHeaders(pGenerator, pHeaderMap, pStream);
			if (SUCCEEDED(hr))
			{
				hr = pGenerator->StartBody(pStream);
				if (SUCCEEDED(hr))
				{
					hr = GenerateResponseHelper(pGenerator, pFuncMap, m_pvParam, pStream, true);
					if (SUCCEEDED(hr))
					{
						hr = pGenerator->EndBody(pStream);
						if (SUCCEEDED(hr))
						{
							hr = pGenerator->EndEnvelope(pStream);
						}
					}
				}
			}
		}

		return hr;
	}

	virtual void Cleanup()
	{
		// cleanup headers
		CleanupHeaders();

		if ((m_stateStack.IsEmpty() == false) && (m_pvParam != NULL))
		{
			const _soapmap **ppFuncMap = GetFunctionMap();
			ATLENSURE( ppFuncMap != NULL );

			const _soapmap *pFuncMap = ppFuncMap[m_stateStack[0].nAllocSize];
			ATLENSURE( pFuncMap != NULL );

			CleanupHelper(pFuncMap, m_pvParam);
			if (m_bClient == false)
			{
				m_pMemMgr->Free(m_pvParam);
				m_stateStack.RemoveAll();
			}
		}
	}

	virtual void CleanupHeaders()
	{
		if (m_stateStack.IsEmpty() == false)
		{
			const _soapmap **ppHeaderMap = GetHeaderMap();
			ATLENSURE( ppHeaderMap != NULL );

			const _soapmap *pHeaderMap = ppHeaderMap[m_stateStack[0].nAllocSize];
			ATLENSURE( pHeaderMap != NULL );

			CleanupHelper(pHeaderMap, GetHeaderValue());
		}
	}

	void SetClient(bool bClient)
	{
		m_bClient = bClient;
	}

}; // class CSoapRootHandler

#define DECLARE_XSD_ENTRY( __name ) \
	{ L ## __name, __name, sizeof(__name)-1 },

__declspec(selectany) const CSoapRootHandler::XSDEntry CSoapRootHandler::s_xsdNames[] =
{
	DECLARE_XSD_ENTRY("string")
	DECLARE_XSD_ENTRY("boolean")
	DECLARE_XSD_ENTRY("float")
	DECLARE_XSD_ENTRY("double")
	DECLARE_XSD_ENTRY("decimal")
	DECLARE_XSD_ENTRY("duration")
	DECLARE_XSD_ENTRY("hexBinary")
	DECLARE_XSD_ENTRY("base64Binary")
	DECLARE_XSD_ENTRY("anyURI")
	DECLARE_XSD_ENTRY("ID")
	DECLARE_XSD_ENTRY("IDREF")
	DECLARE_XSD_ENTRY("ENTITY")
	DECLARE_XSD_ENTRY("NOTATION")
	DECLARE_XSD_ENTRY("QName")
	DECLARE_XSD_ENTRY("normalizedString")
	DECLARE_XSD_ENTRY("token")
	DECLARE_XSD_ENTRY("language")
	DECLARE_XSD_ENTRY("IDREFS")
	DECLARE_XSD_ENTRY("ENTITIES")
	DECLARE_XSD_ENTRY("NMTOKEN")
	DECLARE_XSD_ENTRY("NMTOKENS")
	DECLARE_XSD_ENTRY("Name")
	DECLARE_XSD_ENTRY("NCName")
	DECLARE_XSD_ENTRY("integer")
	DECLARE_XSD_ENTRY("nonPositiveInteger")
	DECLARE_XSD_ENTRY("negativeInteger")
	DECLARE_XSD_ENTRY("long")
	DECLARE_XSD_ENTRY("int")
	DECLARE_XSD_ENTRY("short")
	DECLARE_XSD_ENTRY("byte")
	DECLARE_XSD_ENTRY("nonNegativeInteger")
	DECLARE_XSD_ENTRY("unsignedLong")
	DECLARE_XSD_ENTRY("unsignedInt")
	DECLARE_XSD_ENTRY("unsignedShort")
	DECLARE_XSD_ENTRY("unsignedByte")
	DECLARE_XSD_ENTRY("positiveInteger")
	DECLARE_XSD_ENTRY("dateTime")
	DECLARE_XSD_ENTRY("time")
	DECLARE_XSD_ENTRY("date")
	DECLARE_XSD_ENTRY("gMonth")
	DECLARE_XSD_ENTRY("gYearMonth")
	DECLARE_XSD_ENTRY("gYear")
	DECLARE_XSD_ENTRY("gMonthDay")
	DECLARE_XSD_ENTRY("gDay")
};

__declspec(selectany) CCRTHeap CSoapRootHandler::m_crtHeap;

template <typename THandler>
class CSoapHandler : 
	public CSoapRootHandler, 
	public CComObjectRootEx<CComMultiThreadModel>,
	public IRequestHandlerImpl<THandler>
{
protected:

	HTTP_CODE m_hcErr;
	CHttpResponse *m_pHttpResponse;

	// heap for SOAP requests
	CWin32Heap m_heap;

	// default heap is COM heap (SOAP Servers can double as COM objects)
	CComHeap m_comHeap;

public:

	BEGIN_COM_MAP(CSoapHandler<THandler>)
		COM_INTERFACE_ENTRY(ISAXContentHandler)
		COM_INTERFACE_ENTRY(IRequestHandler)
	END_COM_MAP()

	CSoapHandler()
		:m_pHttpResponse(NULL), m_hcErr(HTTP_SUCCESS)
	{
		SetMemMgr(&m_comHeap);
	}

	void SetHttpError(HTTP_CODE hcErr)
	{
		m_hcErr = hcErr;
	}

	HRESULT SoapFault(
		SOAP_ERROR_CODE errCode, 
		const wchar_t *wszDetail,
		int cchDetail)
	{
		ATLASSUME( m_pHttpResponse != NULL );

		SetHttpError(AtlsHttpError(500, SUBERR_NO_PROCESS));

		m_pHttpResponse->ClearHeaders();
		m_pHttpResponse->ClearContent();
		m_pHttpResponse->SetContentType("text/xml");
		m_pHttpResponse->SetStatusCode(500);

		CSoapFault fault;
		if (wszDetail != NULL)
		{
			if (cchDetail < 0)
			{
				cchDetail = (int) wcslen(wszDetail);
			}

			_ATLTRY
			{
				fault.m_strDetail.SetString(wszDetail, cchDetail);
			}
			_ATLCATCHALL()
			{
				ATLTRACE( _T("CSoapHandler::SoapFault -- out of memory.\r\n" ) );

				return E_OUTOFMEMORY;
			}
		}

		fault.m_soapErrCode = errCode;
		fault.GenerateFault(m_pHttpResponse);
		return S_OK;
	}

	HTTP_CODE InitializeHandler(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider)
	{
		m_hcErr = IRequestHandlerImpl<THandler>::InitializeHandler(pRequestInfo, pProvider);
		if (m_hcErr == HTTP_SUCCESS)
		{
			HRESULT hr = InitializeSOAP(m_spServiceProvider);
			if (SUCCEEDED(hr))
			{
				// try to use the per-thread heap
				CIsapiWorker *pWorker = pRequestInfo->pExtension->GetThreadWorker();
				if (pWorker != NULL)
				{
					m_heap.Attach(pWorker->m_hHeap, false);
					SetMemMgr(&m_heap);
				}

				return m_hcErr;
			}
		}

		// some initialization failure
		CHttpResponse HttpResponse(pRequestInfo->pServerContext);
		m_pHttpResponse = &HttpResponse;

		SoapFault(SOAP_E_SERVER, NULL, 0);

		m_pHttpResponse = NULL;

		return m_hcErr;
	}

	HTTP_CODE HandleRequest(AtlServerRequest *pRequestInfo, IServiceProvider * /*pProvider*/)
	{
		// SOAPACTION header is required per the SOAP 1.1
		// mainly so firewalls can filter on it.
		char szBuf[ATL_URL_MAX_URL_LENGTH+1];
		szBuf[0] = '\0';
		DWORD dwLen = ATL_URL_MAX_URL_LENGTH;
		if ( m_spServerContext->GetServerVariable("HTTP_SOAPACTION", szBuf, &dwLen) != FALSE )
		{
			if ( dwLen >= 2 )
			{
				// drop the last "
				szBuf[dwLen-2] = '\0';
				char *szMethod = strrchr(szBuf, '#');
				if (szMethod != NULL)
				{
					_ATLTRY
					{
						// ignore return code here
						SetSoapMapFromName(CA2W( szMethod+1 ), -1, GetNamespaceUri(), -1, true);
					}
					_ATLCATCHALL()
					{
						return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
					}
				}
			}
		}
		else
		{
			// SOAP requestion that use the HTTP transport
			// must have a SOAPACTION header.
			return HTTP_ERROR(500, ISE_SUBERR_SOAPNOSOAPACTION);
		}

		// set the header map
		CHttpResponse HttpResponse(pRequestInfo->pServerContext);
		m_pHttpResponse = &HttpResponse;

		CStreamOnServerContext s(pRequestInfo->pServerContext);

#ifdef _DEBUG

		CSAXSoapErrorHandler err;
		GetReader()->putErrorHandler(&err);

#endif // _DEBUG

		HRESULT hr = BeginParse(&s);

#ifdef _DEBUG
		// release the error handler
		GetReader()->putErrorHandler(NULL);
#endif // _DEBUG

		if (FAILED(hr))
		{
			Cleanup();
			if (m_hcErr == HTTP_SUCCESS)
			{
				SoapFault(SOAP_E_CLIENT, NULL, NULL);
			}

			return m_hcErr;
		}

		_ATLTRY
		{
			hr = CallFunctionInternal();
		}
		_ATLCATCHALL()
		{
			// cleanup before propagating user exception
			Cleanup();
			HttpResponse.Detach();
			_ATLRETHROW;
		}

		if (FAILED(hr))
		{
			Cleanup();
			HttpResponse.ClearHeaders();
			HttpResponse.ClearContent();
			if (m_hcErr != HTTP_SUCCESS)
			{
				HttpResponse.SetStatusCode(HTTP_ERROR_CODE(m_hcErr));
				return HTTP_SUCCESS_NO_PROCESS;
			}
			HttpResponse.SetStatusCode(500);
			GenerateAppError(&HttpResponse, hr);
			return AtlsHttpError(500, SUBERR_NO_PROCESS);
		}

		HttpResponse.SetContentType("text/xml");
		hr = GenerateResponse(&HttpResponse);
		Cleanup();
		if (FAILED(hr))
		{
			SoapFault(SOAP_E_SERVER, NULL, 0);
			return m_hcErr;
		}

		return HTTP_SUCCESS;
	}

	virtual ATL_NOINLINE HRESULT GenerateAppError(IWriteStream *pStream, HRESULT hr)
	{
		if (pStream == NULL)
		{
			return E_INVALIDARG;
		}

		LPWSTR pwszMessage = NULL;
		DWORD dwLen = ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
			NULL, hr, 0, (LPWSTR) &pwszMessage, 0, NULL);

		if (dwLen == 0)
		{
			pwszMessage = L"Application Error";
		}

		hr = SoapFault(SOAP_E_SERVER, pwszMessage, dwLen ? dwLen : -1);
		if (dwLen != 0)
		{
			::LocalFree(pwszMessage);
		}

		return hr;
	}

	void UninitializeHandler()
	{
		UninitializeSOAP();
	}
};


// client error states
enum SOAPCLIENT_ERROR
{
	SOAPCLIENT_SUCCESS=0,           // everything succeeded
	SOAPCLIENT_INITIALIZE_ERROR,    // initialization failed -- most likely an MSXML installation problem
	SOAPCLIENT_OUTOFMEMORY,         // out of memory
	SOAPCLIENT_GENERATE_ERROR,      // failed in generating the response
	SOAPCLIENT_CONNECT_ERROR,       // failed connecting to server
	SOAPCLIENT_SEND_ERROR,          // failed in sending message
	SOAPCLIENT_SERVER_ERROR,        // server error
	SOAPCLIENT_SOAPFAULT,           // a SOAP Fault was returned by the server
	SOAPCLIENT_PARSEFAULT_ERROR,    // failed in parsing SOAP fault
	SOAPCLIENT_READ_ERROR,          // failed in reading response
	SOAPCLIENT_PARSE_ERROR          // failed in parsing response
};

template <typename TSocketClass = ZEvtSyncSocket>
class CSoapSocketClientT
{
private:

	CUrl m_url;
	CWriteStreamOnCString m_writeStream;
	CReadStreamOnSocket<TSocketClass> m_readStream;
	DWORD m_dwTimeout;

	SOAPCLIENT_ERROR m_errorState;

protected:

	virtual HRESULT GetClientReader(ISAXXMLReader **pReader)
	{
		if (pReader == NULL)
		{
			return E_POINTER;
		}
		*pReader = NULL;

		CComPtr<ISAXXMLReader> spReader;
		HRESULT hr = spReader.CoCreateInstance(ATLS_SAXXMLREADER_CLSID, NULL, CLSCTX_INPROC_SERVER);
		if (SUCCEEDED(hr))
		{
			*pReader = spReader.Detach();
		}
		return hr;
	}

public:

	// note : not shared across stock client implementations
	CAtlHttpClientT<TSocketClass> m_socket;

	CSoapFault m_fault;

	// constructor
	CSoapSocketClientT(LPCTSTR szUrl)
		: m_dwTimeout(0), m_errorState(SOAPCLIENT_SUCCESS)
	{
		TCHAR szTmp[ATL_URL_MAX_URL_LENGTH];
		if(AtlEscapeUrl(szUrl,szTmp,0,ATL_URL_MAX_URL_LENGTH-1,ATL_URL_BROWSER_MODE))
			m_url.CrackUrl(szTmp);
	}

	CSoapSocketClientT(LPCTSTR szServer, LPCTSTR szUri, ATL_URL_PORT nPort=80)
		: m_dwTimeout(0), m_errorState(SOAPCLIENT_SUCCESS)
	{
		ATLASSERT( szServer != NULL );
		ATLASSERT( szUri != NULL );

		m_url.SetUrlPath(szUri);
		m_url.SetHostName(szServer);
		m_url.SetPortNumber(nPort);
	}

	~CSoapSocketClientT()
	{
		CleanupClient();
	}

	SOAPCLIENT_ERROR GetClientError()
	{
		return m_errorState;
	}

	void SetClientError(SOAPCLIENT_ERROR errorState)
	{
		m_errorState = errorState;
	}

	IWriteStream * GetWriteStream()
	{
		return &m_writeStream;
	}

	HRESULT GetReadStream(IStream **ppStream)
	{
		if (ppStream == NULL)
		{
			return E_POINTER;
		}

		*ppStream = &m_readStream;
		return S_OK;
	}

	void CleanupClient()
	{
		m_writeStream.Cleanup();
		m_fault.Clear();
		SetClientError(SOAPCLIENT_SUCCESS);
	}

	HRESULT SendRequest(LPCTSTR szAction)
	{
		HRESULT hr = E_FAIL;
		_ATLTRY
		{	
			// create extra headers to send with request
			CFixedStringT<CString, 256> strExtraHeaders(szAction);
			strExtraHeaders.Append(_T("Accept: text/xml\r\n"), sizeof("Accept: text/xml\r\n")-1);
			CAtlNavigateData navData;
			navData.SetMethod(ATL_HTTP_METHOD_POST);
			navData.SetPort(m_url.GetPortNumber());
			navData.SetExtraHeaders(strExtraHeaders);
			navData.SetPostData((LPBYTE)(LPCSTR) m_writeStream.m_str, m_writeStream.m_str.GetLength(), _T("text/xml; charset=utf-8"));

			ATLSOAP_TRACE( (LPBYTE)(LPCSTR)m_writeStream.m_str, m_writeStream.m_str.GetLength() );

			if (m_dwTimeout != 0)
			{
				navData.SetSocketTimeout(m_dwTimeout);
			}

			if (m_socket.Navigate(&m_url, &navData) != false)
			{
				if (GetStatusCode() == 200)
				{
					hr = (m_readStream.Init(&m_socket) != FALSE ? S_OK : E_FAIL);
					if (hr != S_OK)
					{
						SetClientError(SOAPCLIENT_READ_ERROR);
					}
				}
				else if (GetStatusCode() == 202)
				{
					// for one-way methods
					hr = S_OK;
				}
				else
				{
					SetClientError(SOAPCLIENT_SERVER_ERROR);
				}
			}
			else if (GetStatusCode() == 500)
			{
				SetClientError(SOAPCLIENT_SOAPFAULT);

				// if returned 500, get the SOAP fault
				if (m_readStream.Init(&m_socket) != FALSE)
				{
					CComPtr<ISAXXMLReader> spReader;
					if (SUCCEEDED(GetClientReader(&spReader)))
					{
						CComPtr<IStream> spReadStream;
						if (SUCCEEDED(GetReadStream(&spReadStream)))
						{
							if (FAILED(m_fault.ParseFault(spReadStream, spReader)))
							{
								SetClientError(SOAPCLIENT_PARSEFAULT_ERROR);
							}
						}
					}
				}
			}
			else
			{
				SetClientError(SOAPCLIENT_SEND_ERROR);
			}
		}
		_ATLCATCHALL()
		{
			hr = E_FAIL;
		}

		return hr;
	}

	HRESULT SetUrl(LPCTSTR szUrl)
	{
		TCHAR szTmp[ATL_URL_MAX_URL_LENGTH];
		if(!AtlEscapeUrl(szUrl,szTmp,0,ATL_URL_MAX_URL_LENGTH-1,ATL_URL_BROWSER_MODE))
		{
			return E_FAIL;
		}

		return (m_url.CrackUrl(szTmp) != FALSE) ? S_OK : E_FAIL;
	}

	HRESULT GetUrl(__out_ecount_part_z(*pdwLen, *pdwLen) LPTSTR szUrl, __inout LPDWORD pdwLen)
	{
		if ((szUrl == NULL) || (pdwLen == NULL))
		{
			return E_INVALIDARG;
		}

		return (m_url.CreateUrl(szUrl, pdwLen) != FALSE) ? S_OK : E_FAIL;
	}

	HRESULT SetProxy(LPCTSTR szProxy = NULL, short nProxyPort = 80)
	{
		BOOL bRet = m_socket.SetProxy(szProxy, nProxyPort);
		return (bRet != FALSE) ? S_OK : E_FAIL;
	}

	void SetTimeout(DWORD dwTimeout)
	{
		m_dwTimeout = dwTimeout;
	}

	int GetStatusCode()
	{
		return m_socket.GetStatus();
	}

}; // CSoapSocketClientT

#ifndef ATLSOAP_NOWININET

class CReadStreamOnInet : public IStreamImpl
{
public:

	HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
	{
		if (ppv == NULL)
		{
			return E_POINTER;
		}

		*ppv = NULL;

		if (InlineIsEqualGUID(riid, IID_IUnknown) ||
			InlineIsEqualGUID(riid, IID_IStream) ||
			InlineIsEqualGUID(riid, IID_ISequentialStream))
		{
			*ppv = static_cast<IStream *>(this);
			return S_OK;
		}

		return E_NOINTERFACE;
	}

	ULONG __stdcall AddRef()
	{
		return 1;
	}

	ULONG __stdcall Release()
	{
		return 1;
	}

private:

	HINTERNET m_hFile;

public:

	CReadStreamOnInet()
		:m_hFile(NULL)
	{
	}

	void Init(HINTERNET hFile)
	{
		m_hFile = hFile;
	}

	HRESULT STDMETHODCALLTYPE Read(void *pDest, ULONG dwMaxLen, ULONG *pdwRead)
	{
		BOOL bRet = InternetReadFile(m_hFile, pDest, dwMaxLen, pdwRead);
		return (bRet != FALSE) ? S_OK : E_FAIL;
	}

}; // CStreamOnInet

class CSoapWininetClient
{
private:

	CUrl m_url;
	CWriteStreamOnCString m_writeStream;
	CReadStreamOnInet m_readStream;
	CString m_strProxy;
	DWORD m_dwTimeout;
	CFixedStringT<CString, ATL_URL_MAX_URL_LENGTH+1> m_strUrl;
	SOAPCLIENT_ERROR m_errorState;

	void CloseAll()
	{
		if (m_hRequest != NULL)
		{
			InternetCloseHandle(m_hRequest);
			m_hRequest = NULL;
		}
		if (m_hConnection != NULL)
		{
			InternetCloseHandle(m_hConnection);
			m_hConnection = NULL;
		}
		if (m_hInternet != NULL)
		{
			InternetCloseHandle(m_hInternet);
			m_hInternet = NULL;
		}
	}

	HRESULT ConnectToServer()
	{
		if (m_hConnection != NULL)
		{
			return S_OK;
		}

		m_hInternet = InternetOpen(
			ATLSOAPINET_CLIENT, 
			m_strProxy.GetLength() ? (INTERNET_OPEN_TYPE_PRECONFIG | INTERNET_OPEN_TYPE_PROXY) : INTERNET_OPEN_TYPE_PRECONFIG,
			m_strProxy.GetLength() ? (LPCTSTR) m_strProxy : NULL,
			NULL, 0);

		if (m_hInternet != NULL)
		{
			if (m_dwTimeout != 0)
			{
				InternetSetOption(m_hInternet, INTERNET_OPTION_CONNECT_TIMEOUT,
					&m_dwTimeout, sizeof(m_dwTimeout));
				InternetSetOption(m_hInternet, INTERNET_OPTION_RECEIVE_TIMEOUT,
					&m_dwTimeout, sizeof(m_dwTimeout));
				InternetSetOption(m_hInternet, INTERNET_OPTION_SEND_TIMEOUT,
					&m_dwTimeout, sizeof(m_dwTimeout));
			}
			m_hConnection = InternetConnect(m_hInternet, m_url.GetHostName(), 
				(INTERNET_PORT) m_url.GetPortNumber(), NULL, NULL,
				INTERNET_SERVICE_HTTP, 0, NULL);

			if (m_hConnection != NULL)
			{
				return S_OK;
			}
		}
		CloseAll();
		return E_FAIL;
	}

protected:

	virtual HRESULT GetClientReader(ISAXXMLReader **pReader)
	{
		if (pReader == NULL)
		{
			return E_POINTER;
		}
		*pReader = NULL;

		CComPtr<ISAXXMLReader> spReader;
		HRESULT hr = spReader.CoCreateInstance(ATLS_SAXXMLREADER_CLSID, NULL, CLSCTX_INPROC_SERVER);
		if (SUCCEEDED(hr))
		{
			*pReader = spReader.Detach();
		}
		return hr;
	}

public:

	// note : not shared across stock client implementations
	HINTERNET m_hInternet;
	HINTERNET m_hConnection;
	HINTERNET m_hRequest;

	CSoapFault m_fault;

	CSoapWininetClient(LPCTSTR szUrl)
		:m_hInternet(NULL), m_hConnection(NULL), m_hRequest(NULL), m_dwTimeout(0), m_errorState(SOAPCLIENT_SUCCESS)
	{
		TCHAR szTmp[ATL_URL_MAX_URL_LENGTH];
		if(AtlEscapeUrl(szUrl,szTmp,0,ATL_URL_MAX_URL_LENGTH-1,ATL_URL_BROWSER_MODE))
		{
			if (m_url.CrackUrl(szTmp) != FALSE)
			{
				SetProxy();
				_ATLTRY
				{
					m_strUrl.SetString(m_url.GetUrlPath(), m_url.GetUrlPathLength());
					m_strUrl.Append(m_url.GetExtraInfo(), m_url.GetExtraInfoLength());
				}
				_ATLCATCHALL()
				{
				}
			}
		}
	}

	CSoapWininetClient(LPCTSTR szServer, LPCTSTR szUri, short nPort=80)
		:m_hInternet(NULL), m_hConnection(NULL), m_hRequest(NULL), m_dwTimeout(0), m_errorState(SOAPCLIENT_SUCCESS)
	{
		if (m_url.SetHostName(szServer) != FALSE)
		{
			if (m_url.SetUrlPath(szUri) != FALSE)
			{
				if (m_url.SetPortNumber((ATL_URL_PORT) nPort) != FALSE)
				{
					_ATLTRY
					{
						m_strUrl.SetString(m_url.GetUrlPath(), m_url.GetUrlPathLength());
						m_strUrl.Append(m_url.GetExtraInfo(), m_url.GetExtraInfoLength());
					}
					_ATLCATCHALL()
					{
					}
				}
			}
		}
	}

	virtual ~CSoapWininetClient()
	{
		CleanupClient();
		CloseAll();
	}

	SOAPCLIENT_ERROR GetClientError()
	{
		return m_errorState;
	}

	void SetClientError(SOAPCLIENT_ERROR errorState)
	{
		m_errorState = errorState;
	}

	IWriteStream * GetWriteStream()
	{
		return &m_writeStream;
	}

	HRESULT GetReadStream(IStream **ppStream)
	{
		if (ppStream == NULL)
		{
			return E_POINTER;
		}

		*ppStream = &m_readStream;
		return S_OK;
	}

	void CleanupClient()
	{
		m_writeStream.Cleanup();
		if (m_hRequest != NULL)
		{
			InternetCloseHandle(m_hRequest);
			m_hRequest = NULL;
		}
		m_fault.Clear();
		SetClientError(SOAPCLIENT_SUCCESS);
	}

	HRESULT SendRequest(LPCTSTR szAction)
	{
		if (ConnectToServer() != S_OK)
		{
			SetClientError(SOAPCLIENT_CONNECT_ERROR);
			return E_FAIL;
		}

		CString strHeaders;
		_ATLTRY
		{
			strHeaders.Append(szAction);
			strHeaders.Append(_T("Content-Type: text/xml; charset=utf-8\r\n"));
		}
		_ATLCATCHALL()
		{
			return E_OUTOFMEMORY;
		}

		static LPCTSTR s_szAcceptTypes[] = { _T("text/*"), NULL };
		m_hRequest = HttpOpenRequest(m_hConnection, _T("POST"), 
			m_strUrl, _T("HTTP/1.0"), NULL,
			s_szAcceptTypes, 
			INTERNET_FLAG_NO_UI | INTERNET_FLAG_KEEP_CONNECTION | ((m_url.GetScheme() == ATL_URL_SCHEME_HTTPS) ? INTERNET_FLAG_SECURE : 0)
			, NULL);

		if (m_hRequest != NULL)
		{
			if (FALSE != HttpSendRequest(m_hRequest, strHeaders, (DWORD) strHeaders.GetLength(),		
				(void *)(LPCSTR)m_writeStream.m_str, m_writeStream.m_str.GetLength()))
			{
				m_readStream.Init(m_hRequest);
				if (GetStatusCode() != HTTP_STATUS_SERVER_ERROR)
				{
					return S_OK;
				}
				else
				{
					SetClientError(SOAPCLIENT_SOAPFAULT);

					CComPtr<ISAXXMLReader> spReader;
					if (SUCCEEDED(GetClientReader(&spReader)))
					{
						CComPtr<IStream> spReadStream;
						if (SUCCEEDED(GetReadStream(&spReadStream)))
						{
							if (FAILED(m_fault.ParseFault(spReadStream, spReader)))
							{
								SetClientError(SOAPCLIENT_PARSEFAULT_ERROR);
							}
						}
					}
				}
			}
		}
		else
		{
			SetClientError(SOAPCLIENT_SEND_ERROR);
		}

		return E_FAIL;
	}

	HRESULT SetUrl(LPCTSTR szUrl)
	{
		CloseAll();
		TCHAR szTmp[ATL_URL_MAX_URL_LENGTH];
		if(!AtlEscapeUrl(szUrl,szTmp,0,ATL_URL_MAX_URL_LENGTH-1,ATL_URL_BROWSER_MODE))
		{
			return E_FAIL;
		}

		if (m_url.CrackUrl(szTmp) != FALSE)
		{
			_ATLTRY
			{
				m_strUrl.SetString(m_url.GetUrlPath(), m_url.GetUrlPathLength());
				m_strUrl.Append(m_url.GetExtraInfo(), m_url.GetExtraInfoLength());
			}
			_ATLCATCHALL()
			{
				return E_OUTOFMEMORY;
			}
			return S_OK;
		}
		return E_FAIL;
	}

	HRESULT GetUrl(LPTSTR szUrl, LPDWORD pdwLen)
	{
		if ((szUrl == NULL) || (pdwLen == NULL))
		{
			return E_INVALIDARG;
		}

		return (m_url.CreateUrl(szUrl, pdwLen) != FALSE) ? S_OK : E_FAIL;
	}

	HRESULT SetProxy(LPCTSTR szProxy = NULL, short nProxyPort = 80)
	{
		_ATLTRY
		{
			if (szProxy && szProxy[0])
			{
				m_strProxy.Format(_T("http=http://%s:%d https=http://%s:%d"), szProxy, nProxyPort, szProxy, nProxyPort);
			}
			else
			{
				m_strProxy.Empty();
			}
		}
		_ATLCATCHALL()
		{
			return E_OUTOFMEMORY;
		}

		return S_OK;
	}

	void SetTimeout(DWORD dwTimeout)
	{
		m_dwTimeout = dwTimeout;
	}

	int GetStatusCode()
	{
		DWORD dwLen = 255;
		TCHAR szBuf[256];
		if (HttpQueryInfo(m_hRequest, HTTP_QUERY_STATUS_CODE, szBuf, &dwLen, NULL))
		{
			szBuf[dwLen] = '\0';
			return _ttoi(szBuf);
		}
		return 0;
	}
}; // CSoapWininetClient
#endif

#ifndef ATLSOAP_NOMSXML_INET
class CSoapMSXMLInetClient
{
private:

	CUrl m_url;
	CWriteStreamOnCString m_writeStream;
	DWORD m_dwTimeout;
	SOAPCLIENT_ERROR m_errorState;

	HRESULT ConnectToServer()
	{
		TCHAR szURL[ATL_URL_MAX_URL_LENGTH];
		DWORD dwLen = ATL_URL_MAX_URL_LENGTH;
		HRESULT hr = E_FAIL;

		if (m_spHttpRequest)
			return S_OK;

		if (!m_url.CreateUrl(szURL, &dwLen))
			return E_FAIL;


		hr = m_spHttpRequest.CoCreateInstance(__uuidof(ServerXMLHTTP30));
		if (hr != S_OK)
			return hr;

		CComVariant vEmpty;
		hr = m_spHttpRequest->open( CComBSTR(L"POST"),
									CComBSTR(szURL),
									CComVariant(VARIANT_BOOL(VARIANT_FALSE)),
									vEmpty,
									vEmpty );
		if (hr != S_OK)
		{
			m_spHttpRequest.Release();
		   return hr;
		}

		return S_OK;
	}

protected:

	virtual HRESULT GetClientReader(ISAXXMLReader **pReader)
	{
		if (pReader == NULL)
		{
			return E_POINTER;
		}
		*pReader = NULL;

		CComPtr<ISAXXMLReader> spReader;
		HRESULT hr = spReader.CoCreateInstance(ATLS_SAXXMLREADER_CLSID, NULL, CLSCTX_INPROC_SERVER);
		if (SUCCEEDED(hr))
		{
			*pReader = spReader.Detach();
		}
		return hr;
	}

public:

	// note : not shared across stock client implementations
	CComPtr<IServerXMLHTTPRequest> m_spHttpRequest;

	CSoapFault m_fault;

	CSoapMSXMLInetClient(LPCTSTR szUrl)
		:m_dwTimeout(0), m_errorState(SOAPCLIENT_SUCCESS)
	{
		m_url.CrackUrl(szUrl);
	}

	CSoapMSXMLInetClient(LPCTSTR szServer, LPCTSTR szUri, short nPort=80)
		: m_dwTimeout(0), m_errorState(SOAPCLIENT_SUCCESS)
	{
		m_url.SetHostName(szServer);
		m_url.SetUrlPath(szUri);
		m_url.SetPortNumber((ATL_URL_PORT) nPort);
	}

	virtual ~CSoapMSXMLInetClient()
	{
		CleanupClient();
	}

	SOAPCLIENT_ERROR GetClientError()
	{
		return m_errorState;
	}

	void SetClientError(SOAPCLIENT_ERROR errorState)
	{
		m_errorState = errorState;
	}

	IWriteStream * GetWriteStream()
	{
		return &m_writeStream;
	}

	HRESULT GetReadStream(IStream **ppStream)
	{
		if (ppStream == NULL)
		{
			return E_POINTER;
		}

		*ppStream = NULL;
		HRESULT hr = E_FAIL;

		if (m_spHttpRequest)
		{
			VARIANT vResponseStream;
			VariantInit(&vResponseStream);
			hr = m_spHttpRequest->get_responseStream(&vResponseStream);
			if (S_OK == hr)
			{
				hr = E_FAIL;
				if ((vResponseStream.vt == VT_UNKNOWN) && (vResponseStream.punkVal != NULL))
				{
					// we return the refcount with the pointer!
					hr = vResponseStream.punkVal->QueryInterface(__uuidof(IStream), (void **)ppStream);
				}
				else
				{
					SetClientError(SOAPCLIENT_READ_ERROR);
				}
			}
			VariantClear(&vResponseStream);
		}
		return hr;
	}

	void CleanupClient()
	{
		m_writeStream.Cleanup();
		m_spHttpRequest.Release();
		m_fault.Clear();
		SetClientError(SOAPCLIENT_SUCCESS);
	}

	HRESULT SendRequest(LPCTSTR szAction)
	{
		if (ConnectToServer() != S_OK)
		{
			SetClientError(SOAPCLIENT_CONNECT_ERROR);
			return E_FAIL;
		}

		// set the action header
		LPCTSTR szColon = _tcschr(szAction, _T(':'));
		if (szColon != NULL)
		{
			do
			{
				szColon++;
			} while (_istspace(static_cast<unsigned char>(*szColon)));

			if (FAILED(m_spHttpRequest->setRequestHeader(
						CComBSTR( L"SOAPAction" ), CComBSTR( szColon ))))
			{
				SetClientError(SOAPCLIENT_SEND_ERROR);
				return E_FAIL;
			}
		} // if SOAPAction header not properly formed, attempt to send anyway

		if (FAILED(m_spHttpRequest->setRequestHeader(CComBSTR( L"Content-Type" ), CComBSTR(L"text/xml; charset=utf-8"))))
		{
			SetClientError(SOAPCLIENT_SEND_ERROR);
			return E_FAIL;
		}

		// set timeout
		if (m_dwTimeout != 0)
		{
			long nTimeout = (long) m_dwTimeout;
			m_spHttpRequest->setTimeouts(nTimeout, nTimeout, nTimeout, nTimeout);
			// reset timeout
			m_dwTimeout = 0;
		}

		CComVariant vBody(m_writeStream.m_str);
		HRESULT hr = m_spHttpRequest->send(vBody);
		if ((SUCCEEDED(hr)) && (GetStatusCode() == 500))
		{
			hr = E_FAIL;
			CComPtr<ISAXXMLReader> spReader;
			if (SUCCEEDED(GetClientReader(&spReader)))
			{
				SetClientError(SOAPCLIENT_SOAPFAULT);

				CComPtr<IStream> spReadStream;
				if (SUCCEEDED(GetReadStream(&spReadStream)))
				{
					if (FAILED(m_fault.ParseFault(spReadStream, spReader)))
					{
						SetClientError(SOAPCLIENT_PARSEFAULT_ERROR);
					}
				}
			}
		}
		else if (FAILED(hr))
		{
			SetClientError(SOAPCLIENT_SEND_ERROR);
		}

		return hr;
	}

	HRESULT SetUrl(LPCTSTR szUrl)
	{
		CleanupClient();
		return (m_url.CrackUrl(szUrl) != FALSE ? S_OK : E_FAIL);
	}

	HRESULT GetUrl(LPTSTR szUrl, LPDWORD pdwLen)
	{
		if ((szUrl == NULL) || (pdwLen == NULL))
		{
			return E_INVALIDARG;
		}

		return (m_url.CreateUrl(szUrl, pdwLen) != FALSE) ? S_OK : E_FAIL;
	}

	void SetTimeout(DWORD dwTimeout)
	{
		m_dwTimeout = dwTimeout;
	}

	int GetStatusCode()
	{
		long lStatus;
		if (m_spHttpRequest->get_status(&lStatus) == S_OK)
		{
			return (int) lStatus;
		}
		return 0;
	}

	HRESULT SetProxy(LPCTSTR szProxy = NULL, short nProxyPort = 80)
	{
		(szProxy);
		(nProxyPort);

		ATLTRACE( _T("CSoapMSXMLInetClient does not support SetProxy") );

		return S_OK;
	}
}; // CSoapMSXMLInetClient
#endif


class _CSDLGenerator : public ITagReplacerImpl<_CSDLGenerator>
{
private:

	typedef CAtlMap<CStringA, const _soapmap *, CStringElementTraits<CStringA> >  WSDLMAP;
	typedef CAtlMap<CStringA, const _soapmapentry *, CStringElementTraits<CStringA> > HEADERMAP;

	HRESULT GenerateWSDLHelper(const _soapmap *pMap, WSDLMAP& structMap, WSDLMAP& enumMap)
	{
		ATLENSURE_RETURN( pMap != NULL );

		const _soapmapentry *pEntries = pMap->pEntries;
		ATLENSURE_RETURN( pEntries != NULL );

		HRESULT hr = S_OK;

		for (int i=0; pEntries[i].nHash != 0; i++)
		{
			if (pEntries[i].nVal == SOAPTYPE_UNK)
			{
				ATLENSURE_RETURN( pEntries[i].pChain != NULL );

				_ATLTRY
				{
					POSITION pos = NULL;
					CStringA strName(pEntries[i].pChain->szName, pEntries[i].pChain->cchName);
					if (pEntries[i].pChain->mapType == SOAPMAP_STRUCT)
					{
						pos = structMap.SetAt(strName, pEntries[i].pChain);
					}
					else if (pEntries[i].pChain->mapType == SOAPMAP_ENUM)
					{
						pos = enumMap.SetAt(strName, pEntries[i].pChain);
					}
					if (pos == NULL)
					{
						hr = E_OUTOFMEMORY;
						break;
					}
				}
				_ATLCATCHALL()
				{
					hr = E_OUTOFMEMORY;
					break;
				}

				hr = GenerateWSDLHelper(pEntries[i].pChain, structMap, enumMap);
				if (FAILED(hr))
				{
					break;
				}
			}
		}

		return hr;
	}

	HTTP_CODE IsUDT(const _soapmapentry *pEntry)
	{
		ATLENSURE( pEntry != NULL );
		return (pEntry->nVal != SOAPTYPE_UNK) ? HTTP_S_FALSE : HTTP_SUCCESS;
	}

	HTTP_CODE GetSoapDims(const _soapmapentry *pEntry)
	{
		ATLENSURE( pEntry != NULL );
		if (pEntry->pDims[0] != 0)
		{
			if (SUCCEEDED(m_pWriteStream->WriteStream("[", 1, NULL)))
			{
				for (int i=1; i<=pEntry->pDims[0]; i++)
				{
					if (m_writeHelper.Write(pEntry->pDims[i]) != FALSE)
					{
						if (i < pEntry->pDims[0])
						{
							if (FAILED(m_pWriteStream->WriteStream(", ", 2, NULL)))
							{
								return HTTP_FAIL;
							}
						}
					}
				}
				if (SUCCEEDED(m_pWriteStream->WriteStream("]", 1, NULL)))
				{
					return HTTP_SUCCESS;
				}
			}
		}
		return HTTP_FAIL;
	}

	const _soapmap **m_pFuncs;
	const _soapmap **m_pHeaders;
	int m_nFunc;
	int m_nParam;
	int m_nHeader;
	WSDLMAP m_structMap;
	WSDLMAP m_enumMap;
	POSITION m_currUDTPos;
	int m_nCurrUDTField;

	HEADERMAP m_headerMap;
	POSITION m_currHeaderPos;

	CWriteStreamHelper m_writeHelper;

	CStringA m_strServiceName;
	CStringA m_strNamespaceUri;

	IWriteStream *m_pWriteStream;

	CComPtr<IHttpServerContext> m_spHttpServerContext;

	DWORD m_dwCallFlags;

protected:

	void SetWriteStream(IWriteStream *pStream)
	{
		m_pWriteStream = pStream;
		m_writeHelper.Attach(m_pWriteStream);
	}

	void SetHttpServerContext(IHttpServerContext *pServerContext)
	{
		m_spHttpServerContext = pServerContext;
	}

	static HTTP_CODE GetSoapType(int nVal, IWriteStream *pStream)
	{
		return (pStream->WriteStream(CSoapRootHandler::s_xsdNames[nVal].szName, 
			CSoapRootHandler::s_xsdNames[nVal].cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_FAIL;
	}


	HRESULT InitializeSDL(CSoapRootHandler *pHdlr)
	{
		m_pFuncs = pHdlr->GetFunctionMap();

		if (m_pFuncs == NULL)
		{
			return E_FAIL;
		}

		ATLASSUME( m_pFuncs[0] != NULL );

		m_dwCallFlags = m_pFuncs[0]->dwCallFlags;

		size_t i;
		for (i=0; m_pFuncs[i] != NULL; i++)
		{
			const _soapmap *pMap = m_pFuncs[i];
			HRESULT hr = GenerateWSDLHelper(pMap, m_structMap, m_enumMap);
			if (FAILED(hr))
			{
				return hr;
			}
		}

		m_pHeaders = pHdlr->GetHeaderMap();
		if (m_pHeaders != NULL)
		{
			for (i=0; m_pHeaders[i] != NULL; i++)
			{
				const _soapmap *pMap = m_pHeaders[i];
				HRESULT hr = GenerateWSDLHelper(pMap, m_structMap, m_enumMap);
				if (FAILED(hr))
				{
					return hr;
				}
			}

			for (i=0; m_pHeaders[i] != NULL; i++)
			{
				const _soapmap *pMap = m_pHeaders[i];
				for (size_t j=0; pMap->pEntries[j].nHash != 0; j++)
				{
					HRESULT hr = S_OK;
					_ATLTRY
					{
						if (m_headerMap.SetAt(pMap->pEntries[j].szField, &pMap->pEntries[j]) == NULL)
						{
							hr = E_OUTOFMEMORY;
						}
					}
					_ATLCATCHALL()
					{
						hr = E_OUTOFMEMORY;
					}
					if (FAILED(hr))
					{
						return hr;
					}
				}
			}
		}

		_ATLTRY
		{
			m_strServiceName = pHdlr->GetServiceName();
			m_strNamespaceUri = pHdlr->GetNamespaceUriA();
		}
		_ATLCATCHALL()
		{
			return E_OUTOFMEMORY;
		}
		return S_OK;
	}

	virtual const char * GetHandlerName() = 0;

public:

	_CSDLGenerator()
		:m_pFuncs(NULL), m_nFunc(-1), m_nParam(-1),
		 m_currUDTPos(NULL), m_nCurrUDTField(-1),
		 m_pWriteStream(NULL), m_nHeader(-1), m_currHeaderPos(NULL)
	{
	}
	virtual ~_CSDLGenerator()
	{
	}

	HTTP_CODE OnGetURL()
	{
		char szURL[ATL_URL_MAX_URL_LENGTH];
		DWORD dwUrlSize = sizeof(szURL);
		char szServer[ATL_URL_MAX_HOST_NAME_LENGTH];
		DWORD dwServerSize = sizeof(szServer);
		char szHttps[16];
		DWORD dwHttpsLen = sizeof(szHttps);
		char szPort[ATL_URL_MAX_PORT_NUMBER_LENGTH+1];
		DWORD dwPortLen = sizeof(szPort);

		if (m_spHttpServerContext->GetServerVariable("URL", szURL, &dwUrlSize) != FALSE)
		{
			if (m_spHttpServerContext->GetServerVariable("SERVER_NAME", szServer, &dwServerSize) != FALSE)
			{
				bool bHttps = false;
				if ((m_spHttpServerContext->GetServerVariable("HTTPS", szHttps, &dwHttpsLen) != FALSE) &&
					(!_stricmp(szHttps, "ON")))
				{
					bHttps = true;
				}

				if (m_spHttpServerContext->GetServerVariable("SERVER_PORT", szPort, &dwPortLen) != FALSE)
				{
					_ATLTRY
					{
						CStringA strUrl;
						strUrl.Format("http%s://%s:%s%s?Handler=%s", bHttps ? "s" : "", szServer, szPort, szURL, GetHandlerName());
						
						CA2W wszUrl(strUrl);
						wchar_t	*pwszUrl = wszUrl;
						HRESULT	hr = AtlGenXMLValue(m_pWriteStream, &pwszUrl);
						return SUCCEEDED(hr) ? HTTP_SUCCESS : HTTP_FAIL;
					}
					_ATLCATCHALL()
					{
						return HTTP_FAIL;
					}
				}
			}
		}
		return HTTP_FAIL;
	}

	HTTP_CODE OnGetNamespace()
	{
		return (m_pWriteStream->WriteStream(m_strNamespaceUri, 
			m_strNamespaceUri.GetLength(), NULL) == S_OK) ? HTTP_SUCCESS : HTTP_FAIL;
	}

	HTTP_CODE OnGetNextFunction()
	{
		m_nFunc++;
		if (m_pFuncs[m_nFunc] == NULL)
		{
			m_nFunc = -1;
			return HTTP_S_FALSE;
		}
		return HTTP_SUCCESS;
	}

	HTTP_CODE OnGetFunctionName()
	{
		return (m_pWriteStream->WriteStream(m_pFuncs[m_nFunc]->szName, 
			m_pFuncs[m_nFunc]->cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE; 
	}

	HTTP_CODE OnGetNextParameter()
	{
		++m_nParam;
		if (m_pFuncs[m_nFunc]->pEntries[m_nParam].nHash != 0)
		{
			if (m_pFuncs[m_nFunc]->pEntries[m_nParam].dwFlags & SOAPFLAG_NOMARSHAL)
			{
				return OnGetNextParameter();
			}
			return HTTP_SUCCESS;
		}
		m_nParam = -1;
		return HTTP_S_FALSE;
	}

	HTTP_CODE OnIsInParameter()
	{
		return (m_pFuncs[m_nFunc]->pEntries[m_nParam].dwFlags & SOAPFLAG_IN) ? HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnGetParameterName()
	{
		HRESULT hr = S_OK;
		if (m_pFuncs[m_nFunc]->pEntries[m_nParam].dwFlags & SOAPFLAG_RETVAL)
		{
			hr = m_pWriteStream->WriteStream("return", sizeof("return")-1, NULL);
		}
		else
		{
			hr = m_pWriteStream->WriteStream(m_pFuncs[m_nFunc]->pEntries[m_nParam].szField, 
					m_pFuncs[m_nFunc]->pEntries[m_nParam].cchField, NULL);
		}

		return (hr == S_OK) ? HTTP_SUCCESS : HTTP_FAIL; 
	}

	HTTP_CODE OnNotIsArrayParameter()
	{
		return (m_pFuncs[m_nFunc]->pEntries[m_nParam].dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR)) 
			? HTTP_S_FALSE: HTTP_SUCCESS;
	}

	HTTP_CODE OnIsParameterUDT()
	{
		return IsUDT(&m_pFuncs[m_nFunc]->pEntries[m_nParam]);
	}

	HTTP_CODE OnGetParameterSoapType()
	{
		if (m_pFuncs[m_nFunc]->pEntries[m_nParam].nVal != SOAPTYPE_UNK)
		{
			return GetSoapType(m_pFuncs[m_nFunc]->pEntries[m_nParam].nVal, m_pWriteStream);
		}
		ATLASSUME( m_pFuncs[m_nFunc]->pEntries[m_nParam].pChain != NULL );
		return (m_pWriteStream->WriteStream(m_pFuncs[m_nFunc]->pEntries[m_nParam].pChain->szName, 
			m_pFuncs[m_nFunc]->pEntries[m_nParam].pChain->cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE; 
	}

	HTTP_CODE OnIsParameterDynamicArray()
	{
		return (m_pFuncs[m_nFunc]->pEntries[m_nParam].dwFlags & SOAPFLAG_DYNARR) ? HTTP_SUCCESS: HTTP_S_FALSE;
	}

	HTTP_CODE OnIsArrayParameter()
	{
		return (OnNotIsArrayParameter() != HTTP_SUCCESS) ? HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnIsParameterOneDimensional()
	{
		return (m_pFuncs[m_nFunc]->pEntries[m_nParam].pDims[0] == 1) ? HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnGetParameterArraySize()
	{
		return (m_writeHelper.Write(m_pFuncs[m_nFunc]->pEntries[m_nParam].pDims[1]) != FALSE)
			? HTTP_SUCCESS : HTTP_FAIL;
	}

	HTTP_CODE OnGetParameterArraySoapDims()
	{
		return GetSoapDims(&m_pFuncs[m_nFunc]->pEntries[m_nParam]);
	}

	HTTP_CODE OnIsOutParameter()
	{
		return (m_pFuncs[m_nFunc]->pEntries[m_nParam].dwFlags & SOAPFLAG_OUT) ? HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnGetNextEnum()
	{
		if (m_currUDTPos == NULL)
		{
			m_currUDTPos = m_enumMap.GetStartPosition();
		}
		else
		{
			m_enumMap.GetNext(m_currUDTPos);
		}

		return (m_currUDTPos != NULL) ? HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnGetEnumName()
	{
		const _soapmap *pMap = m_enumMap.GetValueAt(m_currUDTPos);
		return (m_pWriteStream->WriteStream(pMap->szName, pMap->cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnGetNextEnumElement()
	{
		const _soapmap *pMap = m_enumMap.GetValueAt(m_currUDTPos);
		++m_nCurrUDTField;
		if (pMap->pEntries[m_nCurrUDTField].nHash != 0)
		{
			return HTTP_SUCCESS;
		}
		m_nCurrUDTField = -1;
		return HTTP_S_FALSE;
	}

	HTTP_CODE OnGetEnumElementName()
	{
		const _soapmap *pMap = m_enumMap.GetValueAt(m_currUDTPos);
		return (m_pWriteStream->WriteStream(pMap->pEntries[m_nCurrUDTField].szField,
			pMap->pEntries[m_nCurrUDTField].cchField, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnGetNextStruct()
	{
		if (m_currUDTPos == NULL)
		{
			m_currUDTPos = m_structMap.GetStartPosition();
		}
		else
		{
			m_structMap.GetNext(m_currUDTPos);
		}

		return (m_currUDTPos != NULL) ? HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnGetStructName()
	{
		const _soapmap *pMap = m_enumMap.GetValueAt(m_currUDTPos);
		return (m_pWriteStream->WriteStream(pMap->szName, pMap->cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnGetNextStructField()
	{
		const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos);
		++m_nCurrUDTField;
		if (pMap->pEntries[m_nCurrUDTField].nHash != 0)
		{
			return HTTP_SUCCESS;
		}
		m_nCurrUDTField = -1;
		return HTTP_S_FALSE;
	}

	HTTP_CODE OnGetStructFieldName()
	{
		const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos);
		return (m_pWriteStream->WriteStream(pMap->pEntries[m_nCurrUDTField].szField,
			pMap->pEntries[m_nCurrUDTField].cchField, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnNotIsArrayField()
	{
		const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos);
		return (pMap->pEntries[m_nCurrUDTField].dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR)) ? HTTP_S_FALSE : HTTP_SUCCESS;
	}

	HTTP_CODE OnIsFieldUDT()
	{
		const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos);
		return IsUDT(&pMap->pEntries[m_nCurrUDTField]);
	}

	HTTP_CODE OnGetStructFieldSoapType()
	{
		const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos);
		if (pMap->pEntries[m_nCurrUDTField].nVal != SOAPTYPE_UNK)
		{
			return GetSoapType(pMap->pEntries[m_nCurrUDTField].nVal, m_pWriteStream);
		}
		ATLASSERT( pMap->pEntries[m_nCurrUDTField].pChain != NULL );
		return (m_pWriteStream->WriteStream(pMap->pEntries[m_nCurrUDTField].pChain->szName, 
			pMap->pEntries[m_nCurrUDTField].pChain->cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE; 
	}

	HTTP_CODE OnIsArrayField()
	{
		return (OnNotIsArrayField() != HTTP_SUCCESS) ? HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnIsFieldDynamicArray()
	{
		const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos);
		return (pMap->pEntries[m_nCurrUDTField].dwFlags & SOAPFLAG_DYNARR) ? HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnGetFieldSizeIsName()
	{
		const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos);
		int nIndex = pMap->pEntries[m_nCurrUDTField].nSizeIs;
		ATLASSERT( nIndex >= 0 );
		return (m_pStream->WriteStream(pMap->pEntries[nIndex].szField, 
			pMap->pEntries[nIndex].cchField, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnIsFieldOneDimensional()
	{
		const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos);
		return (pMap->pEntries[m_nCurrUDTField].pDims[0] == 1) ? HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnGetFieldArraySize()
	{
		const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos);
		return (m_writeHelper.Write(pMap->pEntries[m_nCurrUDTField].pDims[1]) != FALSE) ? 
				HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnGetFieldArraySoapDims()
	{
		const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos);
		return GetSoapDims(&pMap->pEntries[m_nCurrUDTField]);
	}

	HTTP_CODE OnGetServiceName()
	{
		return (m_pWriteStream->WriteStream(m_strServiceName, 
			m_strServiceName.GetLength(), NULL) == S_OK) ? HTTP_SUCCESS : HTTP_FAIL;
	}

	HTTP_CODE OnGetNextHeader()
	{	
		if (m_currHeaderPos == NULL)
		{
			m_currHeaderPos = m_headerMap.GetStartPosition();
		}
		else
		{
			m_headerMap.GetNext(m_currHeaderPos);
		}

		return (m_currHeaderPos != NULL) ? HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnIsInHeader()
	{
		return (m_pHeaders[m_nFunc]->pEntries[m_nHeader].dwFlags & SOAPFLAG_IN) 
			? HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnIsOutHeader()
	{
		return (m_pHeaders[m_nFunc]->pEntries[m_nHeader].dwFlags & SOAPFLAG_OUT) 
			? HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnIsRequiredHeader()
	{
		return (m_pHeaders[m_nFunc]->pEntries[m_nHeader].dwFlags & SOAPFLAG_MUSTUNDERSTAND) 
			? HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnGetHeaderName()
	{
		const _soapmapentry *pEntry = m_headerMap.GetValueAt(m_currHeaderPos);
		return (m_pWriteStream->WriteStream(pEntry->szField, 
			pEntry->cchField, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_FAIL;
	}

	HTTP_CODE OnNotIsArrayHeader()
	{
		const _soapmapentry *pEntry = m_headerMap.GetValueAt(m_currHeaderPos);
		return (pEntry->dwFlags & SOAPFLAG_FIXEDARR) ? HTTP_S_FALSE : HTTP_SUCCESS;
	}

	HTTP_CODE OnIsHeaderUDT()
	{
		return IsUDT(m_headerMap.GetValueAt(m_currHeaderPos));
	}

	HTTP_CODE OnGetHeaderSoapType()
	{
		const _soapmapentry *pEntry = m_headerMap.GetValueAt(m_currHeaderPos);
		if (pEntry->nVal != SOAPTYPE_UNK)
		{
			return GetSoapType(pEntry->nVal, m_pWriteStream);
		}
		ATLENSURE( pEntry->pChain != NULL );
		return (m_pWriteStream->WriteStream(pEntry->pChain->szName, 
			pEntry->pChain->cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE; 
	}

	HTTP_CODE OnIsHeaderOneDimensional()
	{
		const _soapmapentry *pEntry = m_headerMap.GetValueAt(m_currHeaderPos);
		return (pEntry->pDims[0] == 1) ? HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnGetHeaderArraySize()
	{
		const _soapmapentry *pEntry = m_headerMap.GetValueAt(m_currHeaderPos);
		return (m_writeHelper.Write(pEntry->pDims[1]) != FALSE) ? 
				HTTP_SUCCESS : HTTP_S_FALSE;
	}

	HTTP_CODE OnGetHeaderArraySoapDims()
	{
		return GetSoapDims(m_headerMap.GetValueAt(m_currHeaderPos));
	}

	HTTP_CODE OnGetNextFunctionHeader()
	{
		++m_nHeader;
		if (m_pHeaders[m_nFunc]->pEntries[m_nHeader].nHash != 0)
		{
			if (m_pHeaders[m_nFunc]->pEntries[m_nHeader].dwFlags & SOAPFLAG_NOMARSHAL)
			{
				return OnGetNextHeader();
			}
			return HTTP_SUCCESS;
		}
		m_nHeader = -1;
		return HTTP_S_FALSE;
	}

	HTTP_CODE OnGetFunctionHeaderName()
	{
		return (m_pWriteStream->WriteStream(
					m_pHeaders[m_nFunc]->pEntries[m_nHeader].szField,
					m_pHeaders[m_nFunc]->pEntries[m_nHeader].cchField,
					NULL) == S_OK) ? HTTP_SUCCESS : HTTP_FAIL;
	}

	HTTP_CODE OnIsArrayHeader()
	{
		return (OnNotIsArrayHeader() == HTTP_SUCCESS) ? HTTP_S_FALSE : HTTP_SUCCESS;
	}

	HTTP_CODE OnIsDocumentLiteral()
	{
		if ((m_dwCallFlags & (SOAPFLAG_DOCUMENT | SOAPFLAG_LITERAL)) ==	
			(SOAPFLAG_DOCUMENT | SOAPFLAG_LITERAL))
		{
			return HTTP_SUCCESS;
		}
		return HTTP_S_FALSE;
	}

	HTTP_CODE OnIsRpcEncoded()
	{
		if ((m_dwCallFlags & (SOAPFLAG_RPC | SOAPFLAG_ENCODED)) ==	
			(SOAPFLAG_RPC | SOAPFLAG_ENCODED))
		{
			return HTTP_SUCCESS;
		}
		return HTTP_S_FALSE;
	}

#pragma warning (push)
#pragma warning (disable : 4640)	// construction of local static object is not thread-safe

	BEGIN_REPLACEMENT_METHOD_MAP(_CSDLGenerator)
		REPLACEMENT_METHOD_ENTRY("GetNamespace", OnGetNamespace)
		REPLACEMENT_METHOD_ENTRY("GetNextFunction", OnGetNextFunction)
		REPLACEMENT_METHOD_ENTRY("GetFunctionName", OnGetFunctionName)
		REPLACEMENT_METHOD_ENTRY("GetNextParameter", OnGetNextParameter)
		REPLACEMENT_METHOD_ENTRY("IsInParameter", OnIsInParameter)
		REPLACEMENT_METHOD_ENTRY("GetParameterName", OnGetParameterName)
		REPLACEMENT_METHOD_ENTRY("NotIsArrayParameter", OnNotIsArrayParameter)
		REPLACEMENT_METHOD_ENTRY("IsParameterUDT", OnIsParameterUDT)
		REPLACEMENT_METHOD_ENTRY("GetParameterSoapType", OnGetParameterSoapType)
		REPLACEMENT_METHOD_ENTRY("IsParameterDynamicArray", OnIsParameterDynamicArray)
		REPLACEMENT_METHOD_ENTRY("IsArrayParameter", OnIsArrayParameter)
		REPLACEMENT_METHOD_ENTRY("IsParameterOneDimensional", OnIsParameterOneDimensional)
		REPLACEMENT_METHOD_ENTRY("GetParameterArraySize", OnGetParameterArraySize)
		REPLACEMENT_METHOD_ENTRY("GetParameterArraySoapDims", OnGetParameterArraySoapDims)
		REPLACEMENT_METHOD_ENTRY("IsOutParameter", OnIsOutParameter)
		REPLACEMENT_METHOD_ENTRY("GetNextEnum", OnGetNextEnum)
		REPLACEMENT_METHOD_ENTRY("GetEnumName", OnGetEnumName)
		REPLACEMENT_METHOD_ENTRY("GetNextEnumElement", OnGetNextEnumElement)
		REPLACEMENT_METHOD_ENTRY("GetEnumElementName", OnGetEnumElementName)
		REPLACEMENT_METHOD_ENTRY("GetNextStruct", OnGetNextStruct)
		REPLACEMENT_METHOD_ENTRY("GetStructName", OnGetStructName)
		REPLACEMENT_METHOD_ENTRY("GetNextStructField", OnGetNextStructField)
		REPLACEMENT_METHOD_ENTRY("GetStructFieldName", OnGetStructFieldName)
		REPLACEMENT_METHOD_ENTRY("NotIsArrayField", OnNotIsArrayField)
		REPLACEMENT_METHOD_ENTRY("IsFieldUDT", OnIsFieldUDT)
		REPLACEMENT_METHOD_ENTRY("GetStructFieldSoapType", OnGetStructFieldSoapType)
		REPLACEMENT_METHOD_ENTRY("IsArrayField", OnIsArrayField)
		REPLACEMENT_METHOD_ENTRY("IsFieldOneDimensional", OnIsFieldOneDimensional)
		REPLACEMENT_METHOD_ENTRY("GetFieldArraySize", OnGetFieldArraySize)
		REPLACEMENT_METHOD_ENTRY("GetFieldArraySoapDims", OnGetFieldArraySoapDims)
		REPLACEMENT_METHOD_ENTRY("GetServiceName", OnGetServiceName)
		REPLACEMENT_METHOD_ENTRY("GetURL", OnGetURL)

		REPLACEMENT_METHOD_ENTRY("GetNextHeader", OnGetNextHeader)
		REPLACEMENT_METHOD_ENTRY("GetHeaderName", OnGetHeaderName)
		REPLACEMENT_METHOD_ENTRY("NotIsArrayHeader", OnNotIsArrayHeader)
		REPLACEMENT_METHOD_ENTRY("IsArrayHeader", OnIsArrayHeader)
		REPLACEMENT_METHOD_ENTRY("IsHeaderUDT", OnIsHeaderUDT)
		REPLACEMENT_METHOD_ENTRY("GetHeaderSoapType", OnGetHeaderSoapType)
		REPLACEMENT_METHOD_ENTRY("IsHeaderOneDimensional", OnIsHeaderOneDimensional)
		REPLACEMENT_METHOD_ENTRY("GetHeaderArraySize", OnGetHeaderArraySize)
		REPLACEMENT_METHOD_ENTRY("GetHeaderArraySoapDims", OnGetHeaderArraySoapDims)
		REPLACEMENT_METHOD_ENTRY("GetNextFunctionHeader", OnGetNextFunctionHeader)
		REPLACEMENT_METHOD_ENTRY("GetFunctionHeaderName", OnGetFunctionHeaderName)
		REPLACEMENT_METHOD_ENTRY("IsInHeader", OnIsInHeader)
		REPLACEMENT_METHOD_ENTRY("IsOutHeader", OnIsOutHeader)
		REPLACEMENT_METHOD_ENTRY("IsRequiredHeader", OnIsRequiredHeader)

		REPLACEMENT_METHOD_ENTRY("IsDocumentLiteral", OnIsDocumentLiteral)
		REPLACEMENT_METHOD_ENTRY("IsRpcEncoded", OnIsRpcEncoded)
		REPLACEMENT_METHOD_ENTRY("IsFieldDynamicArray", OnIsFieldDynamicArray)
		REPLACEMENT_METHOD_ENTRY("GetFieldSizeIsName", OnGetFieldSizeIsName)
	END_REPLACEMENT_METHOD_MAP()
	
#pragma warning (pop)

}; // class _CSDLGenerator

#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
	#error atlsoap.h requires Windows CE platform to have DCOM support or _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA needs to be defined.
#endif

template <class THandler, const char *szHandlerName>
class CSDLGenerator :
	public _CSDLGenerator,
	public IRequestHandlerImpl< CSDLGenerator<THandler,szHandlerName> >,
	public CComObjectRootEx<CComSingleThreadModel>
{
private:

public:
	typedef CSDLGenerator<THandler, szHandlerName> _sdlGenerator;

	BEGIN_COM_MAP(_sdlGenerator)
		COM_INTERFACE_ENTRY(IRequestHandler)
		COM_INTERFACE_ENTRY(ITagReplacer)
	END_COM_MAP()

	HTTP_CODE InitializeHandler(AtlServerRequest *pRequestInfo, IServiceProvider *pServiceProvider)
	{
		IRequestHandlerImpl<CSDLGenerator>::InitializeHandler(pRequestInfo, pServiceProvider);

		CComObjectStack<THandler> handler;
		if (FAILED(InitializeSDL(&handler)))
		{
			return HTTP_FAIL;
		}

		CStencil s;
		HTTP_CODE hcErr = s.LoadFromString(s_szAtlsWSDLSrf, (DWORD) strlen(s_szAtlsWSDLSrf));
		if (hcErr == HTTP_SUCCESS)
		{
			hcErr = HTTP_FAIL;
			CHttpResponse HttpResponse(pRequestInfo->pServerContext);
			HttpResponse.SetContentType("text/xml");
			if (s.ParseReplacements(this) != false)
			{
				s.FinishParseReplacements();

				SetStream(&HttpResponse);
				SetWriteStream(&HttpResponse);
				SetHttpServerContext(m_spServerContext);

				ATLASSERT( s.ParseSuccessful() != false );

				hcErr = s.Render(this, &HttpResponse);
			}
		}

		return hcErr;
	}

	const char * GetHandlerName()
	{
		return szHandlerName;
	}
}; // class CSDLGenerator

} // namespace ATL
#pragma pack(pop)

#pragma warning(pop)

#endif // __ATLSOAP_H__
