现在的位置: 首页 > 综合 > 正文

ATLDBCLI.H

2013年12月11日 ⁄ 综合 ⁄ 共 52015字 ⁄ 字号 评论关闭
// This is a part of the Active Template Library.
// Copyright (C) 1996-1998 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.

// ATLDBCLI.H : ATL consumer code for OLEDB

#ifndef __ATLDBCLI_H_
#define __ATLDBCLI_H_

#ifndef __cplusplus
#error ATL requires C++ compilation (use a .cpp suffix)
#endif

#ifndef _ATLBASE_H
#include <atlbase.h>
#endif

#ifndef __oledb_h__
#include <oledb.h>
#endif // __oledb_h__

#include <msdaguid.h>
#include <msdasc.h>

namespace ATL
{

#define DEFINE_OLEDB_TYPE_FUNCTION(ctype, oledbtype) \
inline DBTYPE _GetOleDBType(ctype&) \
{ \
return oledbtype; \
}
inline DBTYPE _GetOleDBType(BYTE[])
{
return DBTYPE_BYTES;
}
inline DBTYPE _GetOleDBType(CHAR[])
{
return DBTYPE_STR;
}
inline DBTYPE _GetOleDBType(WCHAR[])
{
return DBTYPE_WSTR;
}

DEFINE_OLEDB_TYPE_FUNCTION(signed char ,DBTYPE_I1)
DEFINE_OLEDB_TYPE_FUNCTION(SHORT ,DBTYPE_I2) // DBTYPE_BOOL
DEFINE_OLEDB_TYPE_FUNCTION(int ,DBTYPE_I4)
DEFINE_OLEDB_TYPE_FUNCTION(LONG ,DBTYPE_I4) // DBTYPE_ERROR (SCODE)
DEFINE_OLEDB_TYPE_FUNCTION(LARGE_INTEGER ,DBTYPE_I8) // DBTYPE_CY
DEFINE_OLEDB_TYPE_FUNCTION(BYTE ,DBTYPE_UI1)
DEFINE_OLEDB_TYPE_FUNCTION(unsigned short ,DBTYPE_UI2)
DEFINE_OLEDB_TYPE_FUNCTION(unsigned int ,DBTYPE_UI4)
DEFINE_OLEDB_TYPE_FUNCTION(unsigned long ,DBTYPE_UI4)
DEFINE_OLEDB_TYPE_FUNCTION(ULARGE_INTEGER ,DBTYPE_UI8)
DEFINE_OLEDB_TYPE_FUNCTION(float ,DBTYPE_R4)
DEFINE_OLEDB_TYPE_FUNCTION(double ,DBTYPE_R8) // DBTYPE_DATE
DEFINE_OLEDB_TYPE_FUNCTION(DECIMAL ,DBTYPE_DECIMAL)
DEFINE_OLEDB_TYPE_FUNCTION(DB_NUMERIC ,DBTYPE_NUMERIC)
DEFINE_OLEDB_TYPE_FUNCTION(VARIANT ,DBTYPE_VARIANT)
DEFINE_OLEDB_TYPE_FUNCTION(IDispatch* ,DBTYPE_IDISPATCH)
DEFINE_OLEDB_TYPE_FUNCTION(IUnknown* ,DBTYPE_IUNKNOWN)
DEFINE_OLEDB_TYPE_FUNCTION(GUID ,DBTYPE_GUID)
DEFINE_OLEDB_TYPE_FUNCTION(SAFEARRAY* ,DBTYPE_ARRAY)
DEFINE_OLEDB_TYPE_FUNCTION(DBVECTOR ,DBTYPE_VECTOR)
DEFINE_OLEDB_TYPE_FUNCTION(DBDATE ,DBTYPE_DBDATE)
DEFINE_OLEDB_TYPE_FUNCTION(DBTIME ,DBTYPE_DBTIME)
DEFINE_OLEDB_TYPE_FUNCTION(DBTIMESTAMP ,DBTYPE_DBTIMESTAMP)
DEFINE_OLEDB_TYPE_FUNCTION(FILETIME ,DBTYPE_FILETIME)
DEFINE_OLEDB_TYPE_FUNCTION(PROPVARIANT ,DBTYPE_PROPVARIANT)
DEFINE_OLEDB_TYPE_FUNCTION(DB_VARNUMERIC ,DBTYPE_VARNUMERIC)

// Internal structure containing the accessor handle and a flag
// indicating whether the data for the accessor is automatically
// retrieved
struct _ATL_ACCESSOR_INFO
{
HACCESSOR hAccessor;
bool bAutoAccessor;
};

class _CNoOutputColumns
{
public:
static bool HasOutputColumns()
{
return false;
}
static ULONG _GetNumAccessors()
{
return 0;
}
static HRESULT _GetBindEntries(ULONG*, DBBINDING*, ULONG, bool*, BYTE* pBuffer = NULL)
{
pBuffer;
return E_FAIL;
}
};

class _CNoParameters
{
public:
static bool HasParameters()
{
return false;
}
static HRESULT _GetParamEntries(ULONG*, DBBINDING*, BYTE* pBuffer = NULL)
{
pBuffer;
return E_FAIL;
}
};

class _CNoCommand
{
public:
static HRESULT GetDefaultCommand(LPCTSTR* /*ppszCommand*/)
{
return S_OK;
}
};

typedef _CNoOutputColumns _OutputColumnsClass;
typedef _CNoParameters _ParamClass;
typedef _CNoCommand _CommandClass;

#define BEGIN_ACCESSOR_MAP(x, num) \
public: \
typedef x _classtype; \
typedef x _OutputColumnsClass; \
static ULONG _GetNumAccessors() { return num; } \
static bool HasOutputColumns() { return true; } \
/* If pBindings == NULL means we only return the column number */ \
/* If pBuffer != NULL then it points to the accessor buffer and */ \
/* we release any appropriate memory e.g. BSTR's or interface pointers */ \
inline static HRESULT _GetBindEntries(ULONG* pColumns, DBBINDING *pBinding, ULONG nAccessor, bool* pAuto, BYTE* pBuffer = NULL) \
{ \
ATLASSERT(pColumns != NULL); \
DBPARAMIO eParamIO = DBPARAMIO_NOTPARAM; \
ULONG nColumns = 0; \
pBuffer;

#define BEGIN_ACCESSOR(num, bAuto) \
if (nAccessor == num) \
{ \
if (pBinding != NULL) \
*pAuto = bAuto;

#define END_ACCESSOR() \
} \
else

#define END_ACCESSOR_MAP() \
; \
*pColumns = nColumns; \
return S_OK; \
}

#define BEGIN_COLUMN_MAP(x) \
BEGIN_ACCESSOR_MAP(x, 1) \
BEGIN_ACCESSOR(0, true)

#define END_COLUMN_MAP() \
END_ACCESSOR() \
END_ACCESSOR_MAP()

#define offsetbuf(m) offsetof(_classtype, m)
#define _OLEDB_TYPE(data) _GetOleDBType(((_classtype*)0)->data)
#define _SIZE_TYPE(data) sizeof(((_classtype*)0)->data)

#define _COLUMN_ENTRY_CODE(nOrdinal, wType, nLength, nPrecision, nScale, dataOffset, lengthOffset, statusOffset) \
if (pBuffer != NULL) \
{ \
CAccessorBase::FreeType(wType, pBuffer + dataOffset); \
} \
else if (pBinding != NULL) \
{ \
CAccessorBase::Bind(pBinding, nOrdinal, wType, nLength, nPrecision, nScale, eParamIO, \
dataOffset, lengthOffset, statusOffset); \
pBinding++; \
} \
nColumns++;

#define COLUMN_ENTRY_EX(nOrdinal, wType, nLength, nPrecision, nScale, data, length, status) \
_COLUMN_ENTRY_CODE(nOrdinal, wType, nLength, nPrecision, nScale, offsetbuf(data), offsetbuf(length), offsetbuf(status))

#define COLUMN_ENTRY_TYPE(nOrdinal, wType, data) \
COLUMN_ENTRY_TYPE_SIZE(nOrdinal, wType, _SIZE_TYPE(data), data)

#define COLUMN_ENTRY_TYPE_SIZE(nOrdinal, wType, nLength, data) \
_COLUMN_ENTRY_CODE(nOrdinal, wType, nLength, 0, 0, offsetbuf(data), 0, 0)

// Standard macros where type and size is worked out
#define COLUMN_ENTRY(nOrdinal, data) \
COLUMN_ENTRY_TYPE(nOrdinal, _OLEDB_TYPE(data), data)

#define COLUMN_ENTRY_LENGTH(nOrdinal, data, length) \
_COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), 0, 0, offsetbuf(data), offsetbuf(length), 0)

#define COLUMN_ENTRY_STATUS(nOrdinal, data, status) \
_COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), 0, 0, offsetbuf(data), 0, offsetbuf(status))

#define COLUMN_ENTRY_LENGTH_STATUS(nOrdinal, data, length, status) \
_COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), 0, 0, offsetbuf(data), offsetbuf(length), offsetbuf(status))

// Follow macros are used if precision and scale need to be specified
#define COLUMN_ENTRY_PS(nOrdinal, nPrecision, nScale, data) \
_COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), nPrecision, nScale, offsetbuf(data), 0, 0)

#define COLUMN_ENTRY_PS_LENGTH(nOrdinal, nPrecision, nScale, data, length) \
_COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), nPrecision, nScale, offsetbuf(data), offsetbuf(length), 0)

#define COLUMN_ENTRY_PS_STATUS(nOrdinal, nPrecision, nScale, data, status) \
_COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), nPrecision, nScale, offsetbuf(data), 0, offsetbuf(status))

#define COLUMN_ENTRY_PS_LENGTH_STATUS(nOrdinal, nPrecision, nScale, data, length, status) \
_COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), nPrecision, nScale, offsetbuf(data), offsetbuf(length), offsetbuf(status))

#define BOOKMARK_ENTRY(variable) \
COLUMN_ENTRY_TYPE_SIZE(0, DBTYPE_BYTES, _SIZE_TYPE(variable##.m_rgBuffer), variable##.m_rgBuffer)

#define _BLOB_ENTRY_CODE(nOrdinal, IID, flags, dataOffset, statusOffset) \
if (pBuffer != NULL) \
{ \
CAccessorBase::FreeType(DBTYPE_IUNKNOWN, pBuffer + dataOffset); \
} \
else if (pBinding != NULL) \
{ \
DBOBJECT* pObject = NULL; \
ATLTRY(pObject = new DBOBJECT); \
if (pObject == NULL) \
return E_OUTOFMEMORY; \
pObject->dwFlags = flags; \
pObject->iid = IID; \
CAccessorBase::Bind(pBinding, nOrdinal, DBTYPE_IUNKNOWN, sizeof(IUnknown*), 0, 0, eParamIO, \
dataOffset, 0, statusOffset, pObject); \
pBinding++; \
} \
nColumns++;

#define BLOB_ENTRY(nOrdinal, IID, flags, data) \
_BLOB_ENTRY_CODE(nOrdinal, IID, flags, offsetbuf(data), 0);

#define BLOB_ENTRY_STATUS(nOrdinal, IID, flags, data, status) \
_BLOB_ENTRY_CODE(nOrdinal, IID, flags, offsetbuf(data), offsetbuf(status));

#define BEGIN_PARAM_MAP(x) \
public: \
typedef x _classtype; \
typedef x _ParamClass; \
static bool HasParameters() { return true; } \
static HRESULT _GetParamEntries(ULONG* pColumns, DBBINDING *pBinding, BYTE* pBuffer = NULL) \
{ \
ATLASSERT(pColumns != NULL); \
DBPARAMIO eParamIO = DBPARAMIO_INPUT; \
int nColumns = 0; \
pBuffer;

#define END_PARAM_MAP() \
*pColumns = nColumns; \
return S_OK; \
}

#define SET_PARAM_TYPE(type) \
eParamIO = type;

#define DEFINE_COMMAND(x, szCommand) \
typedef x _CommandClass; \
static HRESULT GetDefaultCommand(LPCTSTR* ppszCommand) \
{ \
*ppszCommand = szCommand; \
return S_OK; \
}

///////////////////////////////////////////////////////////////////////////
// class CDBErrorInfo

class CDBErrorInfo
{
public:
// Use to get the number of error record when you want to explicitly check that
// the passed interface set the error information
HRESULT GetErrorRecords(IUnknown* pUnk, const IID& iid, ULONG* pcRecords)
{
CComPtr<ISupportErrorInfo> spSupportErrorInfo;
HRESULT hr = pUnk->QueryInterface(&spSupportErrorInfo);
if (FAILED(hr))
return hr;

hr = spSupportErrorInfo->InterfaceSupportsErrorInfo(iid);
if (FAILED(hr))
return hr;

return GetErrorRecords(pcRecords);
}
// Use to get the number of error records
HRESULT GetErrorRecords(ULONG* pcRecords)
{
ATLASSERT(pcRecords != NULL);
HRESULT hr;
m_spErrorInfo.Release();
m_spErrorRecords.Release();
hr = ::GetErrorInfo(0, &m_spErrorInfo);
if (hr == S_FALSE)
return E_FAIL;

hr = m_spErrorInfo->QueryInterface(IID_IErrorRecords, (void**)&m_spErrorRecords);
if (FAILED(hr))
{
// Well we got the IErrorInfo so we'll just treat that as
// the one record
*pcRecords = 1;
return S_OK;
}

return m_spErrorRecords->GetRecordCount(pcRecords);
}
// Get the error information for the passed record number. GetErrorRecords must
// be called before this function is called.
HRESULT GetAllErrorInfo(ULONG ulRecordNum, LCID lcid, BSTR* pbstrDescription,
BSTR* pbstrSource = NULL, GUID* pguid = NULL, DWORD* pdwHelpContext = NULL,
BSTR* pbstrHelpFile = NULL) const
{
CComPtr<IErrorInfo> spErrorInfo;

// If we have the IErrorRecords interface pointer then use it, otherwise
// we'll just default to the IErrorInfo we have already retrieved in the call
// to GetErrorRecords
if (m_spErrorRecords != NULL)
{
HRESULT hr = m_spErrorRecords->GetErrorInfo(ulRecordNum, lcid, &spErrorInfo);
if (FAILED(hr))
return hr;
}
else
{
ATLASSERT(m_spErrorInfo != NULL);
spErrorInfo = m_spErrorInfo;
}

if (pbstrDescription != NULL)
spErrorInfo->GetDescription(pbstrDescription);

if (pguid != NULL)
spErrorInfo->GetGUID(pguid);

if (pdwHelpContext != NULL)
spErrorInfo->GetHelpContext(pdwHelpContext);

if (pbstrHelpFile != NULL)
spErrorInfo->GetHelpFile(pbstrHelpFile);

if (pbstrSource != NULL)
spErrorInfo->GetSource(pbstrSource);

return S_OK;
}
// Get the error information for the passed record number
HRESULT GetBasicErrorInfo(ULONG ulRecordNum, ERRORINFO* pErrorInfo) const
{
return m_spErrorRecords->GetBasicErrorInfo(ulRecordNum, pErrorInfo);
}
// Get the custom error object for the passed record number
HRESULT GetCustomErrorObject(ULONG ulRecordNum, REFIID riid, IUnknown** ppObject) const
{
return m_spErrorRecords->GetCustomErrorObject(ulRecordNum, riid, ppObject);
}
// Get the IErrorInfo interface for the passed record number
HRESULT GetErrorInfo(ULONG ulRecordNum, LCID lcid, IErrorInfo** ppErrorInfo) const
{
return m_spErrorRecords->GetErrorInfo(ulRecordNum, lcid, ppErrorInfo);
}
// Get the error parameters for the passed record number
HRESULT GetErrorParameters(ULONG ulRecordNum, DISPPARAMS* pdispparams) const
{
return m_spErrorRecords->GetErrorParameters(ulRecordNum, pdispparams);
}

// Implementation
CComPtr<IErrorInfo> m_spErrorInfo;
CComPtr<IErrorRecords> m_spErrorRecords;
};

#ifdef _DEBUG
inline void AtlTraceErrorRecords(HRESULT hrErr = S_OK)
{
CDBErrorInfo ErrorInfo;
ULONG cRecords;
HRESULT hr;
ULONG i;
CComBSTR bstrDesc, bstrHelpFile, bstrSource;
GUID guid;
DWORD dwHelpContext;
WCHAR wszGuid[40];
USES_CONVERSION;

// If the user passed in an HRESULT then trace it
if (hrErr != S_OK)
ATLTRACE2(atlTraceDBClient, 0, _T("OLE DB Error Record dump for hr = 0x%x\n"), hrErr);

LCID lcLocale = GetSystemDefaultLCID();

hr = ErrorInfo.GetErrorRecords(&cRecords);
if (FAILED(hr) && ErrorInfo.m_spErrorInfo == NULL)
{
ATLTRACE2(atlTraceDBClient, 0, _T("No OLE DB Error Information found: hr = 0x%x\n"), hr);
}
else
{
for (i = 0; i < cRecords; i++)
{
hr = ErrorInfo.GetAllErrorInfo(i, lcLocale, &bstrDesc, &bstrSource, &guid,
&dwHelpContext, &bstrHelpFile);
if (FAILED(hr))
{
ATLTRACE2(atlTraceDBClient, 0,
_T("OLE DB Error Record dump retrieval failed: hr = 0x%x\n"), hr);
return;
}
StringFromGUID2(guid, wszGuid, sizeof(wszGuid) / sizeof(WCHAR));
ATLTRACE2(atlTraceDBClient, 0,
_T("Row #: %4d Source: \"%s\" Description: \"%s\" Help File: \"%s\" Help Context: %4d GUID: %s\n"),
i, OLE2T(bstrSource), OLE2T(bstrDesc), OLE2T(bstrHelpFile), dwHelpContext, OLE2T(wszGuid));
bstrSource.Empty();
bstrDesc.Empty();
bstrHelpFile.Empty();
}
ATLTRACE2(atlTraceDBClient, 0, _T("OLE DB Error Record dump end\n"));
}
}
#else
inline void AtlTraceErrorRecords(HRESULT hrErr = S_OK) { hrErr; }
#endif

///////////////////////////////////////////////////////////////////////////
// class CDBPropSet

class CDBPropSet : public tagDBPROPSET
{
public:
CDBPropSet()
{
rgProperties = NULL;
cProperties = 0;
}
CDBPropSet(const GUID& guid)
{
rgProperties = NULL;
cProperties = 0;
guidPropertySet = guid;
}
CDBPropSet(const CDBPropSet& propset)
{
InternalCopy(propset);
}
~CDBPropSet()
{
for (ULONG i = 0; i < cProperties; i++)
VariantClear(&rgProperties[i].vValue);

CoTaskMemFree(rgProperties);
}
CDBPropSet& operator=(CDBPropSet& propset)
{
this->~CDBPropSet();
InternalCopy(propset);
return *this;
}
// Set the GUID of the property set this class represents.
// Use if you didn't pass the GUID to the constructor.
void SetGUID(const GUID& guid)
{
guidPropertySet = guid;
}
// Add the passed property to the property set
bool AddProperty(DWORD dwPropertyID, const VARIANT& var)
{
HRESULT hr;
if (!Add())
return false;
rgProperties[cProperties].dwPropertyID = dwPropertyID;
hr = ::VariantCopy(&(rgProperties[cProperties].vValue), const_cast<VARIANT*>(&var));
if (FAILED(hr))
return false;
cProperties++;
return true;
}
// Add the passed property to the property set
bool AddProperty(DWORD dwPropertyID, LPCSTR szValue)
{
USES_CONVERSION;
if (!Add())
return false;
rgProperties[cProperties].dwPropertyID = dwPropertyID;
rgProperties[cProperties].vValue.vt = VT_BSTR;
rgProperties[cProperties].vValue.bstrVal = SysAllocString(A2COLE(szValue));
if (rgProperties[cProperties].vValue.bstrVal == NULL)
return false;
cProperties++;
return true;
}
// Add the passed property to the property set
bool AddProperty(DWORD dwPropertyID, LPCWSTR szValue)
{
USES_CONVERSION;
if (!Add())
return false;
rgProperties[cProperties].dwPropertyID = dwPropertyID;
rgProperties[cProperties].vValue.vt = VT_BSTR;
rgProperties[cProperties].vValue.bstrVal = SysAllocString(W2COLE(szValue));
if (rgProperties[cProperties].vValue.bstrVal == NULL)
return false;
cProperties++;
return true;
}
// Add the passed property to the property set
bool AddProperty(DWORD dwPropertyID, bool bValue)
{
if (!Add())
return false;
rgProperties[cProperties].dwPropertyID = dwPropertyID;
rgProperties[cProperties].vValue.vt = VT_BOOL;
#pragma warning(disable: 4310) // cast truncates constant value
rgProperties[cProperties].vValue.boolVal = (bValue) ? VARIANT_TRUE : VARIANT_FALSE;
#pragma warning(default: 4310)
cProperties++;
return true;
}
// Add the passed property to the property set
bool AddProperty(DWORD dwPropertyID, BYTE bValue)
{
if (!Add())
return false;
rgProperties[cProperties].dwPropertyID = dwPropertyID;
rgProperties[cProperties].vValue.vt = VT_UI1;
rgProperties[cProperties].vValue.bVal = bValue;
cProperties++;
return true;
}
// Add the passed property to the property set
bool AddProperty(DWORD dwPropertyID, short nValue)
{
if (!Add())
return false;
rgProperties[cProperties].dwPropertyID = dwPropertyID;
rgProperties[cProperties].vValue.vt = VT_I2;
rgProperties[cProperties].vValue.iVal = nValue;
cProperties++;
return true;
}
// Add the passed property to the property set
bool AddProperty(DWORD dwPropertyID, long nValue)
{
if (!Add())
return false;
rgProperties[cProperties].dwPropertyID = dwPropertyID;
rgProperties[cProperties].vValue.vt = VT_I4;
rgProperties[cProperties].vValue.lVal = nValue;
cProperties++;
return true;
}
// Add the passed property to the property set
bool AddProperty(DWORD dwPropertyID, float fltValue)
{
if (!Add())
return false;
rgProperties[cProperties].dwPropertyID = dwPropertyID;
rgProperties[cProperties].vValue.vt = VT_R4;
rgProperties[cProperties].vValue.fltVal = fltValue;
cProperties++;
return true;
}
// Add the passed property to the property set
bool AddProperty(DWORD dwPropertyID, double dblValue)
{
if (!Add())
return false;
rgProperties[cProperties].dwPropertyID = dwPropertyID;
rgProperties[cProperties].vValue.vt = VT_R8;
rgProperties[cProperties].vValue.dblVal = dblValue;
cProperties++;
return true;
}
// Add the passed property to the property set
bool AddProperty(DWORD dwPropertyID, CY cyValue)
{
if (!Add())
return false;
rgProperties[cProperties].dwPropertyID = dwPropertyID;
rgProperties[cProperties].vValue.vt = VT_CY;
rgProperties[cProperties].vValue.cyVal = cyValue;
cProperties++;
return true;
}
// Implementation
// Create memory to add a new property
bool Add()
{
rgProperties = (DBPROP*)CoTaskMemRealloc(rgProperties, (cProperties + 1) * sizeof(DBPROP));
if (rgProperties != NULL)
{
rgProperties[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
rgProperties[cProperties].colid = DB_NULLID;
rgProperties[cProperties].vValue.vt = VT_EMPTY;
return true;
}
else
return false;
}
// Copies in the passed value now it this value been cleared
void InternalCopy(const CDBPropSet& propset)
{
cProperties = propset.cProperties;
guidPropertySet = propset.guidPropertySet;
rgProperties = (DBPROP*)CoTaskMemAlloc(cProperties * sizeof(DBPROP));
if (rgProperties != NULL)
{
for (ULONG i = 0; i < cProperties; i++)
{
rgProperties[i].dwPropertyID = propset.rgProperties[i].dwPropertyID;
rgProperties[i].dwOptions = DBPROPOPTIONS_REQUIRED;
rgProperties[i].colid = DB_NULLID;
rgProperties[i].vValue.vt = VT_EMPTY;
VariantCopy(&rgProperties[i].vValue, &propset.rgProperties[i].vValue);
}
}
else
{
// The memory allocation failed so set the count
// of properties to zero
cProperties = 0;
}
}
};

///////////////////////////////////////////////////////////////////////////
// class CDBPropIDSet

class CDBPropIDSet : public tagDBPROPIDSET
{
// Constructors and Destructors
public:
CDBPropIDSet()
{
rgPropertyIDs = NULL;
cPropertyIDs = 0;
}
CDBPropIDSet(const GUID& guid)
{
rgPropertyIDs = NULL;
cPropertyIDs = 0;
guidPropertySet = guid;
}
CDBPropIDSet(const CDBPropIDSet& propidset)
{
InternalCopy(propidset);
}
~CDBPropIDSet()
{
if (rgPropertyIDs != NULL)
free(rgPropertyIDs);
}
CDBPropIDSet& operator=(CDBPropIDSet& propset)
{
this->~CDBPropIDSet();
InternalCopy(propset);
return *this;
}
// Set the GUID of the property ID set
void SetGUID(const GUID& guid)
{
guidPropertySet = guid;
}
// Add a property ID to the set
bool AddPropertyID(DBPROPID propid)
{
if (!Add())
return false;
rgPropertyIDs[cPropertyIDs] = propid;
cPropertyIDs++;
return true;
}
// Implementation
bool Add()
{
rgPropertyIDs = (DBPROPID*)realloc(rgPropertyIDs, (cPropertyIDs + 1) * sizeof(DBPROPID));
return (rgPropertyIDs != NULL) ? true : false;
}
void InternalCopy(const CDBPropIDSet& propidset)
{
cPropertyIDs = propidset.cPropertyIDs;
guidPropertySet = propidset.guidPropertySet;
rgPropertyIDs = (DBPROPID*)malloc(cPropertyIDs * sizeof(DBPROPID));
if (rgPropertyIDs != NULL)
{
for (ULONG i = 0; i < cPropertyIDs; i++)
rgPropertyIDs[i] = propidset.rgPropertyIDs[i];
}
else
{
// The memory allocation failed so set the count
// of properties to zero
cPropertyIDs = 0;
}
}
};

///////////////////////////////////////////////////////////////////////////
// class CBookmarkBase

class ATL_NO_VTABLE CBookmarkBase
{
public:
virtual ULONG GetSize() const = 0;
virtual BYTE* GetBuffer() const = 0;
};

///////////////////////////////////////////////////////////////////////////
// class CBookmark

template <ULONG nSize = 0>
class CBookmark : public CBookmarkBase
{
public:
virtual ULONG GetSize() const { return nSize; }
virtual BYTE* GetBuffer() const { return (BYTE*)m_rgBuffer; }

// Implementation
BYTE m_rgBuffer[nSize];
};

// Size of 0 means that the memory for the bookmark will be allocated
// at run time.
template <>
class CBookmark<0> : public CBookmarkBase
{
public:
CBookmark()
{
m_nSize = 0;
m_pBuffer = NULL;
}
CBookmark(ULONG nSize)
{
m_pBuffer = NULL;
ATLTRY(m_pBuffer = new BYTE[nSize]);
m_nSize = (m_pBuffer == NULL) ? 0 : nSize;
}
~CBookmark()
{
delete [] m_pBuffer;
}
CBookmark& operator=(const CBookmark& bookmark)
{
SetBookmark(bookmark.GetSize(), bookmark.GetBuffer());
return *this;
}
virtual ULONG GetSize() const { return m_nSize; }
virtual BYTE* GetBuffer() const { return m_pBuffer; }
// Sets the bookmark to the passed value
HRESULT SetBookmark(ULONG nSize, BYTE* pBuffer)
{
ATLASSERT(pBuffer != NULL);
delete [] m_pBuffer;
m_pBuffer = NULL;
ATLTRY(m_pBuffer = new BYTE[nSize]);
if (m_pBuffer != NULL)
{
memcpy(m_pBuffer, pBuffer, nSize);
m_nSize = nSize;
return S_OK;
}
else
{
m_nSize = 0;
return E_OUTOFMEMORY;
}
}
ULONG m_nSize;
BYTE* m_pBuffer;
};

///////////////////////////////////////////////////////////////////////////
// class CAccessorBase

class CAccessorBase
{
public:
CAccessorBase()
{
m_pAccessorInfo = NULL;
m_nAccessors = 0;
m_pBuffer = NULL;
}
void Close()
{
// If Close is called then ReleaseAccessors must have been
// called first
ATLASSERT(m_nAccessors == 0);
ATLASSERT(m_pAccessorInfo == NULL);
}
// Get the number of accessors that have been created
ULONG GetNumAccessors() const { return m_nAccessors; }
// Get the handle of the passed accessor (offset from 0)
HACCESSOR GetHAccessor(ULONG nAccessor) const
{
ATLASSERT(nAccessor<m_nAccessors);
return m_pAccessorInfo[nAccessor].hAccessor;
};
// Called during Close to release the accessor information
HRESULT ReleaseAccessors(IUnknown* pUnk)
{
ATLASSERT(pUnk != NULL);
HRESULT hr = S_OK;
if (m_nAccessors > 0)
{
CComPtr<IAccessor> spAccessor;
hr = pUnk->QueryInterface(IID_IAccessor, (void**)&spAccessor);
if (SUCCEEDED(hr))
{
ATLASSERT(m_pAccessorInfo != NULL);
for (ULONG i = 0; i < m_nAccessors; i++)
spAccessor->ReleaseAccessor(m_pAccessorInfo[i].hAccessor, NULL);
}
m_nAccessors = 0;
delete [] m_pAccessorInfo;
m_pAccessorInfo = NULL;
}
return hr;
}
// Returns true or false depending upon whether data should be
// automatically retrieved for the passed accessor.
bool IsAutoAccessor(ULONG nAccessor) const
{
ATLASSERT(nAccessor < m_nAccessors);
ATLASSERT(m_pAccessorInfo != NULL);
return m_pAccessorInfo[nAccessor].bAutoAccessor;
}

// Implementation
// Used by the rowset class to find out where to place the data
BYTE* GetBuffer() const
{
return m_pBuffer;
}
// Set the buffer that is used to retrieve the data
void SetBuffer(BYTE* pBuffer)
{
m_pBuffer = pBuffer;
}

// Allocate internal memory for the passed number of accessors
HRESULT AllocateAccessorMemory(int nAccessors)
{
// Can't be called twice without calling ReleaseAccessors first
ATLASSERT(m_pAccessorInfo == NULL);
m_nAccessors = nAccessors;
m_pAccessorInfo = NULL;
ATLTRY(m_pAccessorInfo = new _ATL_ACCESSOR_INFO[nAccessors]);
if (m_pAccessorInfo == NULL)
return E_OUTOFMEMORY;
else
return S_OK;
}
// BindParameters will be overriden if parameters are used
HRESULT BindParameters(HACCESSOR*, ICommand*, void**) { return S_OK; }

// Create an accessor for the passed binding information. The created accessor is
// returned through the pHAccessor parameter.
static HRESULT BindEntries(DBBINDING* pBindings, int nColumns, HACCESSOR* pHAccessor,
ULONG nSize, IAccessor* pAccessor)
{
ATLASSERT(pBindings != NULL);
ATLASSERT(pHAccessor != NULL);
ATLASSERT(pAccessor != NULL);
HRESULT hr;
int i;
DWORD dwAccessorFlags = (pBindings->eParamIO == DBPARAMIO_NOTPARAM) ?
DBACCESSOR_ROWDATA : DBACCESSOR_PARAMETERDATA;

#ifdef _DEBUG
// In debug builds we will retrieve the status flags and trace out
// any errors that may occur.
DBBINDSTATUS* pStatus = NULL;
ATLTRY(pStatus = new DBBINDSTATUS[nColumns]);
hr = pAccessor->CreateAccessor(dwAccessorFlags, nColumns,
pBindings, nSize, pHAccessor, pStatus);
if (FAILED(hr) && pStatus != NULL)
{
for (i=0; i<nColumns; i++)
{
if (pStatus[i] != DBBINDSTATUS_OK)
ATLTRACE2(atlTraceDBClient, 0, _T("Binding entry %d failed. Status: %d\n"), i, pStatus[i]);
}
}
delete [] pStatus;
#else
hr = pAccessor->CreateAccessor(dwAccessorFlags, nColumns,
pBindings, nSize, pHAccessor, NULL);
#endif
for (i=0; i<nColumns; i++)
delete pBindings[i].pObject;

return hr;
}
// Set up the binding structure pointed to by pBindings based upon
// the other passed parameters.
static void Bind(DBBINDING* pBinding, ULONG nOrdinal, DBTYPE wType,
ULONG nLength, BYTE nPrecision, BYTE nScale, DBPARAMIO eParamIO,
ULONG nDataOffset, ULONG nLengthOffset = NULL, ULONG nStatusOffset = NULL,
DBOBJECT* pdbobject = NULL)
{
ATLASSERT(pBinding != NULL);

// If we are getting a pointer to the data then let the provider
// own the memory
if (wType & DBTYPE_BYREF)
pBinding->dwMemOwner = DBMEMOWNER_PROVIDEROWNED;
else
pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED;

pBinding->pObject = pdbobject;

pBinding->eParamIO = eParamIO;
pBinding->iOrdinal = nOrdinal;
pBinding->wType = wType;
pBinding->bPrecision = nPrecision;
pBinding->bScale = nScale;
pBinding->dwFlags = 0;

pBinding->obValue = nDataOffset;
pBinding->obLength = 0;
pBinding->obStatus = 0;
pBinding->pTypeInfo = NULL;
pBinding->pBindExt = NULL;
pBinding->cbMaxLen = nLength;

pBinding->dwPart = DBPART_VALUE;
if (nLengthOffset != NULL)
{
pBinding->dwPart |= DBPART_LENGTH;
pBinding->obLength = nLengthOffset;
}
if (nStatusOffset != NULL)
{
pBinding->dwPart |= DBPART_STATUS;
pBinding->obStatus = nStatusOffset;
}
}

// Free memory if appropriate
static inline void FreeType(DBTYPE wType, BYTE* pValue, IRowset* pRowset = NULL)
{
switch (wType)
{
case DBTYPE_BSTR:
SysFreeString(*((BSTR*)pValue));
break;
case DBTYPE_VARIANT:
VariantClear((VARIANT*)pValue);
break;
case DBTYPE_IUNKNOWN:
case DBTYPE_IDISPATCH:
(*(IUnknown**)pValue)->Release();
break;
case DBTYPE_ARRAY:
SafeArrayDestroy((SAFEARRAY*)pValue);
break;

case DBTYPE_HCHAPTER:
CComQIPtr<IChapteredRowset> spChapteredRowset = pRowset;
if (spChapteredRowset != NULL)
spChapteredRowset->ReleaseChapter(*(HCHAPTER*)pValue, NULL);
break;
}
if ((wType & DBTYPE_VECTOR) && ~(wType & DBTYPE_BYREF))
CoTaskMemFree(((DBVECTOR*)pValue)->ptr);
}

_ATL_ACCESSOR_INFO* m_pAccessorInfo;
ULONG m_nAccessors;
BYTE* m_pBuffer;
};

///////////////////////////////////////////////////////////////////////////
// class CRowset

class CRowset
{
// Constructors and Destructors
public:
CRowset()
{
m_pAccessor = NULL;
m_hRow = NULL;
}
CRowset(IRowset* pRowset)
{
m_spRowset = pRowset;
CRowset();
}
~CRowset()
{
Close();
}
// Release any retrieved row handles and then release the rowset
void Close()
{
if (m_spRowset != NULL)
{
ReleaseRows();
m_spRowset.Release();
m_spRowsetChange.Release();
}
}
// Addref the current row
HRESULT AddRefRows()
{
ATLASSERT(m_spRowset != NULL);
return m_spRowset->AddRefRows(1, &m_hRow, NULL, NULL);
}
// Release the current row
HRESULT ReleaseRows()
{
ATLASSERT(m_spRowset != NULL);
HRESULT hr = S_OK;

if (m_hRow != NULL)
{
hr = m_spRowset->ReleaseRows(1, &m_hRow, NULL, NULL, NULL);
m_hRow = NULL;
}
return hr;
}
// Compare two bookmarks with each other
HRESULT Compare(const CBookmarkBase& bookmark1, const CBookmarkBase& bookmark2,
DBCOMPARE* pComparison) const
{
ATLASSERT(m_spRowset != NULL);
CComPtr<IRowsetLocate> spLocate;
HRESULT hr = m_spRowset.QueryInterface(&spLocate);
if (FAILED(hr))
return hr;

return spLocate->Compare(NULL, bookmark1.GetSize(), bookmark1.GetBuffer(),
bookmark2.GetSize(), bookmark2.GetBuffer(), pComparison);
}
// Compare the passed hRow with the current row
HRESULT IsSameRow(HROW hRow) const
{
ATLASSERT(m_spRowset != NULL);
CComPtr<IRowsetIdentity> spRowsetIdentity;
HRESULT hr = m_spRowset.QueryInterface(&spRowsetIdentity);
if (FAILED(hr))
return hr;

return spRowsetIdentity->IsSameRow(m_hRow, hRow);
}
// Move to the previous record
HRESULT MovePrev()
{
return MoveNext(-2, true);
}
// Move to the next record
HRESULT MoveNext()
{
return MoveNext(0, true);
}
// Move lSkip records forward or backward
HRESULT MoveNext(LONG lSkip, bool bForward)
{
HRESULT hr;
ULONG ulRowsFetched = 0;

// Check the data was opened successfully and the accessor
// has been set.
ATLASSERT(m_spRowset != NULL);
ATLASSERT(m_pAccessor != NULL);

// Release a row if one is already around
ReleaseRows();

// Get the row handle
HROW* phRow = &m_hRow;
hr = m_spRowset->GetNextRows(NULL, lSkip, (bForward) ? 1 : -1, &ulRowsFetched, &phRow);
if (hr != S_OK)
return hr;

// Get the data
hr = GetData();
if (FAILED(hr))
{
ATLTRACE2(atlTraceDBClient, 0, _T("GetData failed - HRESULT = 0x%X\n"),hr);
ReleaseRows();
}
return hr;
}
// Move to the first record
HRESULT MoveFirst()
{
HRESULT hr;

// Check the data was opened successfully and the accessor
// has been set.
ATLASSERT(m_spRowset != NULL);
ATLASSERT(m_pAccessor != NULL);

// Release a row if one is already around
ReleaseRows();

hr = m_spRowset->RestartPosition(NULL);
if (FAILED(hr))
return hr;

// Get the data
return MoveNext();
}
// Move to the last record
HRESULT MoveLast()
{
// Check the data was opened successfully and the accessor
// has been set.
ATLASSERT(m_spRowset != NULL);
ATLASSERT(m_pAccessor != NULL);

// Release a row if one is already around
ReleaseRows();

HRESULT hr;
ULONG ulRowsFetched = 0;
HROW* phRow = &m_hRow;
// Restart the rowset position and then move backwards
m_spRowset->RestartPosition(NULL);
hr = m_spRowset->GetNextRows(NULL, -1, 1, &ulRowsFetched, &phRow);
if (hr != S_OK)
return hr;

// Get the data
hr = GetData();
if (FAILED(hr))
{
ATLTRACE2(atlTraceDBClient, 0, _T("GetData from MoveLast failed - HRESULT = 0x%X\n"),hr);
ReleaseRows();
}

return S_OK;
}
// Move to the passed bookmark
HRESULT MoveToBookmark(const CBookmarkBase& bookmark, LONG lSkip = 0)
{
// Check the data was opened successfully and the accessor
// has been set.
ATLASSERT(m_spRowset != NULL);
ATLASSERT(m_pAccessor != NULL);

CComPtr<IRowsetLocate> spLocate;
HRESULT hr = m_spRowset.QueryInterface(&spLocate);
if (FAILED(hr))
return hr;

// Release a row if one is already around
ReleaseRows();

ULONG ulRowsFetched = 0;
HROW* phRow = &m_hRow;
hr = spLocate->GetRowsAt(NULL, NULL, bookmark.GetSize(), bookmark.GetBuffer(),
lSkip, 1, &ulRowsFetched, &phRow);
// Note we're not using SUCCEEDED here, because we could get DB_S_ENDOFROWSET
if (hr != S_OK)
return hr;

// Get the data
hr = GetData();
if (FAILED(hr))
{
ATLTRACE2(atlTraceDBClient, 0, _T("GetData from Bookmark failed - HRESULT = 0x%X\n"),hr);
ReleaseRows();
}

return S_OK;
}
// Get the data for the current record
HRESULT GetData()
{
HRESULT hr = S_OK;
ATLASSERT(m_pAccessor != NULL);

ULONG nAccessors = m_pAccessor->GetNumAccessors();
for (ULONG i=0; i<nAccessors; i++)
{
if (m_pAccessor->IsAutoAccessor(i))
{
hr = GetData(i);
if (FAILED(hr))
return hr;
}
}
return hr;
}
// Get the data for the passed accessor. Use for a non-auto accessor
HRESULT GetData(int nAccessor)
{
ATLASSERT(m_spRowset != NULL);
ATLASSERT(m_pAccessor != NULL);
ATLASSERT(m_hRow != NULL);

// Note that we are using the specified buffer if it has been set,
// otherwise we use the accessor for the data.
return m_spRowset->GetData(m_hRow, m_pAccessor->GetHAccessor(nAccessor), m_pAccessor->GetBuffer());
}
// Get the data for the passed accessor. Use for a non-auto accessor
HRESULT GetDataHere(int nAccessor, void* pBuffer)
{
ATLASSERT(m_spRowset != NULL);
ATLASSERT(m_pAccessor != NULL);
ATLASSERT(m_hRow != NULL);

// Note that we are using the specified buffer if it has been set,
// otherwise we use the accessor for the data.
return m_spRowset->GetData(m_hRow, m_pAccessor->GetHAccessor(nAccessor), pBuffer);
}
HRESULT GetDataHere(void* pBuffer)
{
HRESULT hr = S_OK;

ULONG nAccessors = m_pAccessor->GetNumAccessors();
for (ULONG i=0; i<nAccessors; i++)
{
hr = GetDataHere(i, pBuffer);
if (FAILED(hr))
return hr;
}
return hr;
}

// Insert the current record
HRESULT Insert(int nAccessor = 0, bool bGetHRow = false)
{
ATLASSERT(m_pAccessor != NULL);
HRESULT hr;
if (m_spRowsetChange != NULL)
{
HROW* pHRow;
if (bGetHRow)
{
ReleaseRows();
pHRow = &m_hRow;
}
else
pHRow = NULL;

hr = m_spRowsetChange->InsertRow(NULL, m_pAccessor->GetHAccessor(nAccessor),
m_pAccessor->GetBuffer(), pHRow);

}
else
hr = E_NOINTERFACE;

return hr;
}
// Delete the current record
HRESULT Delete() const
{
ATLASSERT(m_pAccessor != NULL);
HRESULT hr;
if (m_spRowsetChange != NULL)
hr = m_spRowsetChange->DeleteRows(NULL, 1, &m_hRow, NULL);
else
hr = E_NOINTERFACE;

return hr;
}
// Update the current record
HRESULT SetData() const
{
ATLASSERT(m_pAccessor != NULL);
HRESULT hr = S_OK;

ULONG nAccessors = m_pAccessor->GetNumAccessors();
for (ULONG i=0; i<nAccessors; i++)
{
hr = SetData(i);
if (FAILED(hr))
return hr;
}
return hr;
}
// Update the current record with the data in the passed accessor
HRESULT SetData(int nAccessor) const
{
ATLASSERT(m_pAccessor != NULL);
HRESULT hr;
if (m_spRowsetChange != NULL)
{
hr = m_spRowsetChange->SetData(m_hRow, m_pAccessor->GetHAccessor(nAccessor),
m_pAccessor->GetBuffer());
}
else
hr = E_NOINTERFACE;

return hr;
}

// Get the data most recently fetched from or transmitted to the data source.
// Does not get values based on pending changes.
HRESULT GetOriginalData()
{
ATLASSERT(m_spRowset != NULL);
ATLASSERT(m_pAccessor != NULL);

HRESULT hr = S_OK;
CComPtr<IRowsetUpdate> spRowsetUpdate;
hr = m_spRowset->QueryInterface(&spRowsetUpdate);
if (FAILED(hr))
return hr;

ULONG nAccessors = m_pAccessor->GetNumAccessors();
for (ULONG i = 0; i < nAccessors; i++)
{
hr = spRowsetUpdate->GetOriginalData(m_hRow, m_pAccessor->GetHAccessor(i), m_pAccessor->GetBuffer());
if (FAILED(hr))
return hr;
}
return hr;
}
// Get the status of the current row
HRESULT GetRowStatus(DBPENDINGSTATUS* pStatus) const
{
ATLASSERT(m_spRowset != NULL);
ATLASSERT(pStatus != NULL);

CComPtr<IRowsetUpdate> spRowsetUpdate;
HRESULT hr = m_spRowset->QueryInterface(&spRowsetUpdate);
if (FAILED(hr))
return hr;

return spRowsetUpdate->GetRowStatus(NULL, 1, &m_hRow, pStatus);
}
// Undo any changes made to the current row since it was last fetched or Update
// was called for it
HRESULT Undo(ULONG* pcRows = NULL, HROW* phRow = NULL, DBROWSTATUS* pStatus = NULL)
{
ATLASSERT(m_spRowset != NULL);

CComPtr<IRowsetUpdate> spRowsetUpdate;
HRESULT hr = m_spRowset->QueryInterface(&spRowsetUpdate);
if (FAILED(hr))
return hr;

HROW* prgRows;
DBROWSTATUS* pRowStatus;
if (phRow != NULL)
hr = spRowsetUpdate->Undo(NULL, 1, &m_hRow, pcRows, &prgRows, &pRowStatus);
else
hr = spRowsetUpdate->Undo(NULL, 1, &m_hRow, pcRows, NULL, &pRowStatus);
if (FAILED(hr))
return hr;

if (phRow != NULL)
{
*phRow = *prgRows;
CoTaskMemFree(prgRows);
}
if (pStatus != NULL)
*pStatus = *pRowStatus;

CoTaskMemFree(pRowStatus);
return hr;
}
// Transmits any pending changes made to a row since it was last fetched or Update was
// called for it. Also see SetData.
HRESULT Update(ULONG* pcRows = NULL, HROW* phRow = NULL, DBROWSTATUS* pStatus = NULL)
{
ATLASSERT(m_spRowset != NULL);

CComPtr<IRowsetUpdate> spRowsetUpdate;
HRESULT hr = m_spRowset->QueryInterface(&spRowsetUpdate);
if (FAILED(hr))
return hr;

HROW* prgRows;
DBROWSTATUS* pRowStatus;
if (phRow != NULL)
hr = spRowsetUpdate->Update(NULL, 1, &m_hRow, pcRows, &prgRows, &pRowStatus);
else
hr = spRowsetUpdate->Update(NULL, 1, &m_hRow, pcRows, NULL, &pRowStatus);
if (FAILED(hr))
return hr;

if (phRow != NULL)
{
*phRow = *prgRows;
CoTaskMemFree(prgRows);
}
if (pStatus != NULL)
*pStatus = *pRowStatus;

CoTaskMemFree(pRowStatus);
return hr;
}

// Get the approximate position of the row corresponding to the passed bookmark
HRESULT GetApproximatePosition(const CBookmarkBase* pBookmark, ULONG* pPosition, ULONG* pcRows)
{
ATLASSERT(m_spRowset != NULL);

CComPtr<IRowsetScroll> spRowsetScroll;
HRESULT hr = m_spRowset->QueryInterface(&spRowsetScroll);
if (SUCCEEDED(hr))
{
if (pBookmark != NULL)
hr = spRowsetScroll->GetApproximatePosition(NULL, pBookmark->GetSize(), pBookmark->GetBuffer(),
pPosition, pcRows);
else
hr = spRowsetScroll->GetApproximatePosition(NULL, 0, NULL, pPosition, pcRows);

}
return hr;

}
// Move to a fractional position in the rowset
HRESULT MoveToRatio(ULONG nNumerator, ULONG nDenominator, bool bForward = true)
{
ATLASSERT(m_spRowset != NULL);
ULONG nRowsFetched;

CComPtr<IRowsetScroll> spRowsetScroll;
HRESULT hr = m_spRowset->QueryInterface(&spRowsetScroll);
if (FAILED(hr))
return hr;

ReleaseRows();
HROW* phRow = &m_hRow;
hr = spRowsetScroll->GetRowsAtRatio(NULL, NULL, nNumerator, nDenominator, (bForward) ? 1 : -1,
&nRowsFetched, &phRow);
// Note we're not using SUCCEEDED here, because we could get DB_S_ENDOFROWSET
if (hr == S_OK)
hr = GetData();

return hr;
}

// Implementation
static const IID& GetIID()
{
return IID_IRowset;
}
IRowset* GetInterface() const
{
return m_spRowset;
}
IRowset** GetInterfacePtr()
{
return &m_spRowset;
}
void SetupOptionalRowsetInterfaces()
{
// Cache IRowsetChange if available
if (m_spRowset != NULL)
m_spRowset->QueryInterface(&m_spRowsetChange);
}
HRESULT BindFinished() const { return S_OK; }
void SetAccessor(CAccessorBase* pAccessor)
{
m_pAccessor = pAccessor;
}

CComPtr<IRowset> m_spRowset;
CComPtr<IRowsetChange> m_spRowsetChange;
CAccessorBase* m_pAccessor;
HROW m_hRow;
};

///////////////////////////////////////////////////////////////////////////
// class CBulkRowset

class CBulkRowset : public CRowset
{
public:
CBulkRowset()
{
// Default the number of rows to bulk fetch to 10
m_nRows = 10;
m_hr = S_OK;
m_phRow = NULL;
}
CBulkRowset::~CBulkRowset()
{
Close();

delete [] m_phRow;
}
// Set the number of row handles that will be retrieved in each
// bulk row fetch. The default is 10 and this function must be called
// before Open if you wish to change it.
void SetRows(ULONG nRows)
{
// This function must be called before the memory is allocated
// during binding
ATLASSERT(m_phRow == NULL);
m_nRows = nRows;
}
// AddRef all the currently retrieved row handles
HRESULT AddRefRows()
{
ATLASSERT(m_spRowset != NULL);
return m_spRowset->AddRefRows(m_nCurrentRows, m_phRow, NULL, NULL);
}
// Release all the currently retrieved row handles
HRESULT ReleaseRows()
{
ATLASSERT(m_spRowset != NULL);
// We're going to Release the rows so reset the current row position
m_nCurrentRow = 0;
m_hRow = NULL;
return m_spRowset->ReleaseRows(m_nCurrentRows, m_phRow, NULL, NULL, NULL);
}
// Move to the first record
HRESULT MoveFirst()
{
ATLASSERT(m_spRowset != NULL);
ReleaseRows();

// Cause MoveNext to perform a new bulk fetch
m_nCurrentRow = m_nRows;

HRESULT hr = m_spRowset->RestartPosition(NULL);
if (FAILED(hr))
return hr;

// Get the data
return MoveNext();
}
// Move to the next record
HRESULT MoveNext()
{
ATLASSERT(m_spRowset != NULL);
ATLASSERT(m_phRow != NULL);

// Move to the next record in the buffer
m_nCurrentRow++;

// Have we reached the end of the buffer?
if (m_nCurrentRow >= m_nCurrentRows)
{
// If we've reached the end of the buffer and we had a non S_OK HRESULT from
// the last call to GetNextRows then return that HRESULT now.
if (m_hr != S_OK)
return m_hr;

// We've finished with these rows so we need some more
// First release any HROWs that we have
ReleaseRows();

m_hr = m_spRowset->GetNextRows(NULL, 0, m_nRows, &m_nCurrentRows, &m_phRow);
// If we have an error HRESULT or we haven't retrieved any rows then return
// the HRESULT now.
if (FAILED(m_hr) || m_nCurrentRows == 0)
return m_hr;
}

// Get the data for the current row
m_hRow = m_phRow[m_nCurrentRow];
return GetData();
}
// Move to the previous record
HRESULT MovePrev()
{
ATLASSERT(m_spRowset != NULL);
ATLASSERT(m_phRow != NULL);

// Check if we're at the start of the block
if (m_nCurrentRow == 0)
{
ReleaseRows();

// Go back the amount of rows in the block - 1 and fetch forward
m_hr = m_spRowset->GetNextRows(NULL, -(LONG)m_nRows-1, m_nRows, &m_nCurrentRows, &m_phRow);

// Set the current record to the end of the new block
m_nCurrentRow = m_nCurrentRows - 1;

// If we have an error HRESULT or we haven't retrieved any rows then return
// the HRESULT now.
if (FAILED(m_hr) || m_nCurrentRows == 0)
return m_hr;
}
else
{
// Move back a row in the block
m_nCurrentRow--;
}

// Get the data for the current row
m_hRow = m_phRow[m_nCurrentRow];
return GetData();
}
// Move to the last record
HRESULT MoveLast()
{
ReleaseRows();
return CRowset::MoveLast();
}
// Move to the passed bookmark
HRESULT MoveToBookmark(const CBookmarkBase& bookmark, LONG lSkip = 0)
{
ATLASSERT(m_spRowset != NULL);
CComPtr<IRowsetLocate> spLocate;
HRESULT hr = m_spRowset->QueryInterface(&spLocate);
if (FAILED(hr))
return hr;

ReleaseRows();
m_hr = spLocate->GetRowsAt(NULL, NULL, bookmark.GetSize(), bookmark.GetBuffer(),
lSkip, m_nRows, &m_nCurrentRows, &m_phRow);
if (m_hr != S_OK || m_nCurrentRows == 0)
return m_hr;

// Get the data
m_hRow = m_phRow[m_nCurrentRow];
return GetData();
}
// Move to a fractional position in the rowset
HRESULT MoveToRatio(ULONG nNumerator, ULONG nDenominator)
{
ATLASSERT(m_spRowset != NULL);

CComPtr<IRowsetScroll> spRowsetScroll;
HRESULT hr = m_spRowset->QueryInterface(&spRowsetScroll);
if (FAILED(hr))
return hr;

ReleaseRows();
m_hr = spRowsetScroll->GetRowsAtRatio(NULL, NULL, nNumerator, nDenominator, m_nRows, &m_nCurrentRows, &m_phRow);
if (m_hr != S_OK || m_nCurrentRows == 0)
return m_hr;

// Get the data
m_hRow = m_phRow[m_nCurrentRow];
return GetData();
}
// Insert the current record
HRESULT Insert(int nAccessor = 0, bool bGetHRow = false)
{
ReleaseRows();
return CRowset::Insert(nAccessor, bGetHRow);
}

// Implementation
HRESULT BindFinished()
{
// No rows in the buffer yet
m_nCurrentRows = 0;
// Cause MoveNext to automatically perform a new bulk fetch the first time
m_nCurrentRow = m_nRows;

m_phRow = NULL;
ATLTRY(m_phRow = new HROW[m_nRows]);
if (m_phRow == NULL)
return E_OUTOFMEMORY;

return S_OK;
}

HRESULT m_hr; // HRESULT to return from MoveNext at end of buffer
HROW* m_phRow; // Pointer to array of HROWs for each row in buffer
ULONG m_nRows; // Number of rows that will fit in the buffer
ULONG m_nCurrentRows; // Number of rows currently in the buffer
ULONG m_nCurrentRow;
};

///////////////////////////////////////////////////////////////////////////
// class CArrayRowset
//
// Allows you to access a rowset with an array syntax

template <class T, class TRowset = CRowset>
class CArrayRowset :
public CVirtualBuffer<T>,
public TRowset
{
public:
CArrayRowset(int nMax = 100000) : CVirtualBuffer<T>(nMax)
{
m_nRowsRead = 0;
}
T& operator[](int nRow)
{
ATLASSERT(nRow >= 0);
HRESULT hr = S_OK;
T* m_pCurrent = m_pBase + m_nRowsRead;

// Retrieve the row if we haven't retrieved it already
while ((ULONG)nRow >= m_nRowsRead)
{
m_pAccessor->SetBuffer((BYTE*)m_pCurrent);
__try
{
// Get the row
hr = MoveNext();
if (hr != S_OK)
break;
}
__except(Except(GetExceptionInformation()))
{
}
m_nRowsRead++;
m_pCurrent++;
}
if (hr != S_OK)
*((char*)0) = 0; // Force exception

return *(m_pBase + nRow);
}

HRESULT Snapshot()
{
ATLASSERT(m_nRowsRead == 0);
ATLASSERT(m_spRowset != NULL);
HRESULT hr = MoveFirst();
if (FAILED(hr))
return hr;
do
{
Write(*(T*)m_pAccessor->GetBuffer());
m_nRowsRead++;
hr = MoveNext();
} while (SUCCEEDED(hr) && hr != DB_S_ENDOFROWSET);

return (hr == DB_S_ENDOFROWSET) ? S_OK : hr;
}

// Implementation
ULONG m_nRowsRead;
};

// Used when you don't need any parameters or output columns
class CNoAccessor
{
public:
// We don't need any typedef's here as the default
// global typedef is not to have any parameters and
// output columns.
HRESULT BindColumns(IUnknown*) { return S_OK; }
HRESULT BindParameters(HACCESSOR*, ICommand*, void**) { return S_OK; }
void Close() { }
HRESULT ReleaseAccessors(IUnknown*) { return S_OK; }
};

// Used when a rowset will not be returned from the command
class CNoRowset
{
public:
HRESULT BindFinished() { return S_OK; }
void Close() { }
static const IID& GetIID() { return IID_NULL; }
IRowset* GetInterface() const { return NULL; }
IRowset** GetInterfacePtr() { return NULL; }
void SetAccessor(void*) { }
void SetupOptionalRowsetInterfaces() { }
};

///////////////////////////////////////////////////////////////////////////
// class CAccessor

// T is the class that contains the data that will be accessed.
template <class T>
class CAccessor :
public T,
public CAccessorBase
{
public:
// Implementation
// Free's any columns in the current record that need to be freed.
// E.g. Calls SysFreeString on any BSTR's and Release on any interfaces.
void FreeRecordMemory(IRowset* /* pRowset */)
{
ULONG nColumns;
ULONG i;

for (i = 0; i < GetNumAccessors(); i++)
{
// Passing in m_pBuffer tells the column entry maps to free the
// memory for the types if appropriate
_GetBindEntries(&nColumns, NULL, i, NULL, m_pBuffer);
}
}
HRESULT BindColumns(IUnknown* pUnk)
{
HRESULT hr;
ULONG nAccessors;
ULONG nSize;
nAccessors = _OutputColumnsClass::_GetNumAccessors();

SetBuffer((BYTE*)this);

nSize = sizeof(T);
hr = BindAccessors(nAccessors, nSize, pUnk);
return hr;
}
HRESULT BindAccessors(ULONG nAccessors, ULONG nSize, IUnknown* pUnk)
{
ATLASSERT(pUnk != NULL);
HRESULT hr;

CComPtr<IAccessor> spAccessor;
hr = pUnk->QueryInterface(&spAccessor);
if (SUCCEEDED(hr))
{
// Allocate the accessor memory if we haven't done so yet
if (m_pAccessorInfo == NULL)
{
hr = AllocateAccessorMemory(nAccessors);
if (FAILED(hr))
return hr;
}

for (ULONG i=0; i<nAccessors && SUCCEEDED(hr); i++)
hr = BindAccessor(spAccessor, i, nSize);
}

return hr;
}

HRESULT BindAccessor(IAccessor* pAccessor, ULONG nAccessor, ULONG nSize)
{
DBBINDING* pBindings = NULL;
ULONG nColumns;
bool bAuto;
HRESULT hr;

// First time just get the number of entries by passing in &nColumns
_OutputColumnsClass::_GetBindEntries(&nColumns, NULL, nAccessor, NULL);

// Now allocate the binding structures
ATLTRY(pBindings = new DBBINDING[nColumns]);
if (pBindings == NULL)
return E_OUTOFMEMORY;

// Now get the bind entries
hr = _OutputColumnsClass::_GetBindEntries(&nColumns, pBindings, nAccessor, &bAuto);
if (FAILED(hr))
return hr;

m_pAccessorInfo[nAccessor].bAutoAccessor = bAuto;
hr = BindEntries(pBindings, nColumns, &m_pAccessorInfo[nAccessor].hAccessor, nSize, pAccessor);
delete [] pBindings;
return hr;
}

HRESULT BindParameters(HACCESSOR* pHAccessor, ICommand* pCommand, void** ppParameterBuffer)
{
HRESULT hr = S_OK;
// In the static accessor case, the parameter buffer will be T
*ppParameterBuffer = this;

// Only bind the parameters if we haven't already done it
if (*pHAccessor == NULL)
{
ULONG nColumns;
_ParamClass::_GetParamEntries(&nColumns, NULL);

DBBINDING* pBinding = NULL;
ATLTRY(pBinding = new DBBINDING[nColumns]);
if (pBinding == NULL)
return E_OUTOFMEMORY;

hr = _ParamClass::_GetParamEntries(&nColumns, pBinding);
if (SUCCEEDED(hr))
{
// Get the IAccessor from the passed IUnknown
CComPtr<IAccessor> spAccessor;
hr = pCommand->QueryInterface(&spAccessor);
if (SUCCEEDED(hr))
{
hr = BindEntries(pBinding, nColumns, pHAccessor, sizeof(T),
spAccessor);
}
}
delete [] pBinding;
}
return hr;
}
};

///////////////////////////////////////////////////////////////////////////
// CDynamicAccessor

class CDynamicAccessor :
public CAccessorBase
{
public:
CDynamicAccessor()
{
m_nColumns = 0;
m_pColumnInfo = NULL;
m_pStringsBuffer = NULL;
};
~CDynamicAccessor()
{
Close();
}
void Close()
{
if (m_pColumnInfo != NULL)
{
CoTaskMemFree(m_pColumnInfo);
m_pColumnInfo = NULL;
}

// Free the memory for the string buffer returned by IColumnsInfo::GetColumnInfo,
// if necessary
if (m_pStringsBuffer != NULL)
{
CoTaskMemFree(m_pStringsBuffer);
m_pStringsBuffer = NULL;
}

delete [] m_pBuffer;
m_pBuffer = NULL;
m_nColumns = 0;

CAccessorBase::Close();
}
bool GetColumnType(ULONG nColumn, DBTYPE* pType) const
{
if (TranslateColumnNo(nColumn))
{
*pType = m_pColumnInfo[nColumn].wType;
return true;
}
else
return false;
}
bool GetColumnFlags(ULONG nColumn, DBCOLUMNFLAGS* pFlags) const
{
if (TranslateColumnNo(nColumn))
{
*pFlags = m_pColumnInfo[nColumn].dwFlags;
return true;
}
else
return false;
}
bool GetOrdinal(TCHAR* pColumnName, ULONG* pOrdinal) const
{
ATLASSERT(pColumnName != NULL);
ULONG nColumn;
if (GetInternalColumnNo(pColumnName, &nColumn))
{
*pOrdinal = m_pColumnInfo[nColumn].iOrdinal;
return true;
}
else
return false;
}

void* GetValue(ULONG nColumn) const
{
if (TranslateColumnNo(nColumn))
return _GetDataPtr(nColumn);
else
return NULL;
}

void* GetValue(TCHAR* pColumnName) const
{
ATLASSERT(pColumnName != NULL);
ULONG nColumn;
if (GetInternalColumnNo(pColumnName, &nColumn))
return _GetDataPtr(nColumn);
else
return NULL; // Not Found
}

template <class ctype>
void _GetValue(ULONG nColumn, ctype* pData) const
{
ATLASSERT(pData != NULL);
ATLASSERT(m_pColumnInfo[nColumn].ulColumnSize == sizeof(ctype));
ctype* pBuffer = (ctype*)_GetDataPtr(nColumn);
*pData = *pBuffer;
}
template <class ctype>
void _SetValue(ULONG nColumn, const ctype& data)
{
ATLASSERT(m_pColumnInfo[nColumn].ulColumnSize == sizeof(ctype));
ctype* pBuffer = (ctype*)_GetDataPtr(nColumn);
*pBuffer = (ctype)data;
}
template <class ctype>
bool GetValue(ULONG nColumn, ctype* pData) const
{
if (TranslateColumnNo(nColumn))
{
_GetValue(nColumn, pData);
return true;
}
return false;
}
template <class ctype>
bool SetValue(ULONG nColumn, const ctype& data)
{
if (TranslateColumnNo(nColumn))
{
_SetValue(nColumn, data);
return true;
}
return false;
}
template <class ctype>
bool GetValue(TCHAR *pColumnName, ctype* pData) const
{
ATLASSERT(pColumnName != NULL);
ULONG nColumn;
if (GetInternalColumnNo(pColumnName, &nColumn))
{
_GetValue(nColumn, pData);
return true;
}
return false;
}
template <class ctype>
bool SetValue(TCHAR *pColumnName, const ctype& data)
{
ATLASSERT(pColumnName != NULL);
ULONG nColumn;
if (GetInternalColumnNo(pColumnName, &nColumn))
{
_SetValue(nColumn, data);
return true;
}
return false;
}
bool GetLength(ULONG nColumn, ULONG* pLength) const
{
ATLASSERT(pLength != NULL);
if (TranslateColumnNo(nColumn))
{
*pLength = *(ULONG*)(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize));
return true;
}
else
return false;
}
bool SetLength(ULONG nColumn, ULONG nLength)
{
if (TranslateColumnNo(nColumn))
{
*(ULONG*)(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize)) = nLength;
return true;
}
else
return false;
}
bool GetLength(TCHAR* pColumnName, ULONG* pLength) const
{
ATLASSERT(pColumnName != NULL);
ATLASSERT(pLength != NULL);
ULONG nColumn;
if (GetInternalColumnNo(pColumnName, &nColumn))
{
*pLength = *(ULONG*)(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize));
return true;
}
else
return false;
}
bool SetLength(TCHAR* pColumnName, ULONG nLength)
{
ATLASSERT(pColumnName != NULL);
ULONG nColumn;
if (GetInternalColumnNo(pColumnName, &nColumn))
{
*(ULONG*)(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize)) = nLength;
return true;
}
else
return false;
}
bool GetStatus(ULONG nColumn, DBSTATUS* pStatus) const
{
ATLASSERT(pStatus != NULL);
if (TranslateColumnNo(nColumn))
{
*pStatus = *(ULONG*)(AddOffset(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize), sizeof(ULONG)));
return true;
}
else
return false;
}
bool SetStatus(ULONG nColumn, DBSTATUS status)
{
if (TranslateColumnNo(nColumn))
{
*(ULONG*)(AddOffset(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize), sizeof(ULONG))) = status;
return true;
}
else
return false;
}
bool GetStatus(TCHAR* pColumnName, DBSTATUS* pStatus) const
{
ATLASSERT(pColumnName != NULL);
ATLASSERT(pStatus != NULL);
ULONG nColumn;
if (GetInternalColumnNo(pColumnName, &nColumn))
{
*pStatus = *(ULONG*)((BYTE*)_GetDataPtr(nColumn) + m_pColumnInfo[nColumn].ulColumnSize + sizeof(ULONG));
return true;
}
else
return false;
}
bool SetStatus(TCHAR* pColumnName, DBSTATUS status)
{
ATLASSERT(pColumnName != NULL);
ULONG nColumn;
if (GetInternalColumnNo(pColumnName, &nColumn))
{
*(ULONG*)((BYTE*)_GetDataPtr(nColumn) + m_pColumnInfo[nColumn].ulColumnSize + sizeof(ULONG)) = status;
return true;
}
else
return false;
}

// Returns true if a bookmark is available
HRESULT GetBookmark(CBookmark<>* pBookmark) const
{
HRESULT hr;
if (m_pColumnInfo->iOrdinal == 0)
hr = pBookmark->SetBookmark(m_pColumnInfo->ulColumnSize, (BYTE*)_GetDataPtr(0));
else
hr = E_FAIL;
return hr;
}

ULONG GetColumnCount() const
{
return m_nColumns;
}

LPOLESTR GetColumnName(ULONG nColumn) const
{
if (TranslateColumnNo(nColumn))
return m_pColumnInfo[nColumn].pwszName;
else
return NULL;
}

HRESULT GetColumnInfo(IRowset* pRowset, ULONG* pColumns, DBCOLUMNINFO** ppColumnInfo)
{
CComPtr<IColumnsInfo> spColumnsInfo;
HRESULT hr = pRowset->QueryInterface(&spColumnsInfo);
if (SUCCEEDED(hr))
hr = spColumnsInfo->GetColumnInfo(pColumns, ppColumnInfo, &m_pStringsBuffer);

return hr;
}

HRESULT AddBindEntry(const DBCOLUMNINFO& info)
{
m_pColumnInfo = (DBCOLUMNINFO*)CoTaskMemRealloc(m_pColumnInfo, (m_nColumns + 1) * sizeof(DBCOLUMNINFO));
if (m_pColumnInfo == NULL)
return E_OUTOFMEMORY;

m_pColumnInfo[m_nColumns] = info;
m_nColumns++;

return S_OK;
}

// Implementation
// Free's any columns in the current record that need to be freed.
// E.g. Calls SysFreeString on any BSTR's and Release on any interfaces.
void FreeRecordMemory(IRowset* pRowset)
{
ULONG i;

for (i = 0; i < m_nColumns; i++)
CAccessorBase::FreeType(m_pColumnInfo[i].wType, (BYTE*)_GetDataPtr(i), pRowset);
}
void* _GetDataPtr(ULONG nColumn) const
{
return m_pBuffer + (ULONG)m_pColumnInfo[nColumn].pTypeInfo;
}
bool GetInternalColumnNo(TCHAR* pColumnName, ULONG* pColumn) const
{
ATLASSERT(pColumnName != NULL);
ATLASSERT(pColumn != NULL);
USES_CONVERSION;
ULONG i;
ULONG nSize = (lstrlen(pColumnName) + 1) * sizeof(OLECHAR);
OLECHAR* pOleColumnName = T2OLE(pColumnName);

// Search through the columns trying to find a match
for (i = 0; i < m_nColumns; i++)
{
if (m_pColumnInfo[i].pwszName != NULL &&
memcmp(m_pColumnInfo[i].pwszName, pOleColumnName, nSize) == 0)
break;
}
if (i < m_nColumns)
{
*pColumn = i;
return true;
}
else
return false; // Not Found
}
HRESULT BindColumns(IUnknown* pUnk)
{
ATLASSERT(pUnk != NULL);
CComPtr<IAccessor> spAccessor;
HRESULT hr = pUnk->QueryInterface(&spAccessor);
if (FAILED(hr))
return hr;

ULONG i;
ULONG nOffset = 0, nLengthOffset, nStatusOffset;

// If the user hasn't specifed the column information to bind by calling AddBindEntry then
// we get it ourselves
if (m_pColumnInfo == NULL)
{
CComPtr<IColumnsInfo> spColumnsInfo;
hr = pUnk->QueryInterface(&spColumnsInfo);
if (FAILED(hr))
return hr;

hr = spColumnsInfo->GetColumnInfo(&m_nColumns, &m_pColumnInfo, &m_pStringsBuffer);
if (FAILED(hr))
return hr;

m_bOverride = false;
}
else
m_bOverride = true;

DBBINDING* pBinding = NULL;
ATLTRY(pBinding= new DBBINDING[m_nColumns]);
if (pBinding == NULL)
return E_OUTOFMEMORY;

DBBINDING* pCurrent = pBinding;
DBOBJECT* pObject;
for (i = 0; i < m_nColumns; i++)
{
// If it's a BLOB or the column size is large enough for us to treat it as
// a BLOB then we also need to set up the DBOBJECT structure.
if (m_pColumnInfo[i].ulColumnSize > 1024 || m_pColumnInfo[i].wType == DBTYPE_IUNKNOWN)
{
pObject = NULL;
ATLTRY(pObject = new DBOBJECT);
if (pObject == NULL)
return E_OUTOFMEMORY;
pObject->dwFlags = STGM_READ;
pObject->iid = IID_ISequentialStream;
m_pColumnInfo[i].wType = DBTYPE_IUNKNOWN;
m_pColumnInfo[i].ulColumnSize = sizeof(IUnknown*);
}
else
pObject = NULL;

// If column is of type STR or WSTR increase length by 1
// to accommodate the NULL terminator.
if (m_pColumnInfo[i].wType == DBTYPE_STR ||
m_pColumnInfo[i].wType == DBTYPE_WSTR)
m_pColumnInfo[i].ulColumnSize += 1;

nLengthOffset = AddOffset(nOffset, m_pColumnInfo[i].ulColumnSize);
nStatusOffset = AddOffset(nLengthOffset, sizeof(ULONG));
Bind(pCurrent, m_pColumnInfo[i].iOrdinal, m_pColumnInfo[i].wType,
m_pColumnInfo[i].ulColumnSize, m_pColumnInfo[i].bPrecision, m_pColumnInfo[i].bScale,
DBPARAMIO_NOTPARAM, nOffset,
nLengthOffset, nStatusOffset, pObject);
pCurrent++;

// Note that, as we're not using this for anything else, we're using the
// pTypeInfo element to store the offset to our data.
m_pColumnInfo[i].pTypeInfo = (ITypeInfo*)nOffset;

nOffset = AddOffset(nStatusOffset, sizeof(DBSTATUS));
}
// Allocate the accessor memory if we haven't done so yet
if (m_pAccessorInfo == NULL)
{
hr = AllocateAccessorMemory(1); // We only have one accessor
if (FAILED(hr))
{
delete [] pBinding;
return hr;
}
m_pAccessorInfo->bAutoAccessor = TRUE;
}

// Allocate enough memory for the data buffer and tell the rowset
// Note that the rowset will free the memory in its destructor.
m_pBuffer = NULL;
ATLTRY(m_pBuffer = new BYTE[nOffset]);
if (m_pBuffer == NULL)
{
delete [] pBinding;
return E_OUTOFMEMORY;
}
hr = BindEntries(pBinding, m_nColumns, &m_pAccessorInfo->hAccessor,
nOffset, spAccessor);
delete [] pBinding;

return hr;
}

static ULONG AddOffset(ULONG nCurrent, ULONG nAdd)
{
struct foobar
{
char foo;
long bar;
};
ULONG nAlign = offsetof(foobar, bar);

return nCurrent + nAdd + (nAdd % nAlign);;
}

// Translate the column number to the index into the column info array
bool TranslateColumnNo(ULONG& nColumn) const
{
ATLASSERT(m_pColumnInfo != NULL);
// If the user has overriden the binding then we need to search
// through the column info for the ordinal number
if (m_bOverride)
{
for (ULONG i = 0; i < m_nColumns; i++)
{
if (m_pColumnInfo[i].iOrdinal == nColumn)
{
nColumn = i;
return true;
}
}
return false;
}
else
{
// Note that m_pColumnInfo->iOrdinal will be zero if have bound
// a bookmark as the first entry, otherwise it will be 1.
// If the column is out of range then return false
if (nColumn > (m_nColumns - 1 + m_pColumnInfo->iOrdinal))
return false;

// otherwise translate the column to an index into our internal
// binding entries array
nColumn -= m_pColumnInfo->iOrdinal;
return true;
}
}
typedef CDynamicAccessor _OutputColumnsClass;
static bool HasOutputColumns() { return true; }

ULONG m_nColumns;
DBCOLUMNINFO* m_pColumnInfo;
OLECHAR* m_pStringsBuffer;
bool m_bOverride;
};

///////////////////////////////////////////////////////////////////////////
// class CDynamicParameterAccessor

class CDynamicParameterAccessor : public CDynamicAccessor
{
// Constructors and Destructors
public:
typedef CDynamicParameterAccessor _ParamClass;
CDynamicParameterAccessor()
{
m_pParameterEntry = NULL;
m_pParameterBuffer = NULL;
m_ppParamName = NULL;
m_nParameterBufferSize = 0;
m_nParams = 0;
};

~CDynamicParameterAccessor()
{
delete [] m_pParameterEntry;
if (m_ppParamName != NULL)
{
if (*m_ppParamName != NULL)
CoTaskMemFree(*m_ppParamName);
delete [] m_ppParamName;
}
delete m_pParameterBuffer;
};
// nParam is the parameter number (offset from 1)
bool GetParamType(ULONG nParam, DBTYPE* pType) const
{
ATLASSERT(pType != NULL);
if (nParam == 0 || nParam > m_nParams)
return false;

*pType = m_pParameterEntry[nParam-1].wType;
return true;
}
template <class ctype>
bool GetParam(ULONG nParam, ctype* pData) const
{
ATLASSERT(pData != NULL);
ctype* pBuffer = (ctype*)GetParam(nParam);
if (pBuffer == NULL)
return false;
*pData = *pBuffer;
return true;

}
template <class ctype>
bool SetParam(ULONG nParam, ctype* pData)
{
ATLASSERT(pData != NULL);
ctype* pBuffer = (ctype*)GetParam(nParam);
if (pBuffer == NULL)
return false;
*pBuffer = *pData;
return true;

}
template <class cty

抱歉!评论已关闭.