The type DBTYPE_BYREF can still be used, but modifications must be made to the ATL code to prevent it from setting the the DBBINDING structure to DBMEMOWNER_PROVIDEROWNED. Furthermore, if you do specify DBMEMOWNER_CLIENTOWNED and use DBTYPE_BYREF, the consumer is responsible for freeing the memory referenced by using
IMalloc::Free/
CoTaskMemFree.
The easiest way to work around this problem is to copy the Atldbcli.h file into your project so that you have your own custom build of the header. Modify the
Bind method of
CAccessorBase by commenting the code that sets the dwMemOwner member to DBMEMOWNER_PROVIDEROWNED. For example:
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;
Use this header file instead of the Visual C++ copy.
NOTE: You must free the memory returned to you by the provider. You may want to create a function called FreeRecordMemory() in your class where you call
CoTaskMemFree on any of the pointers you get back with BYREF. For example:
void FreeRecordMemory()
{
CoTaskMemFree((BYTE *)m_pszField2);
}
If you don't want to modify the header file, there is a more difficult approach to work around this problem. You can write your own macro that calls your own
Bind function, and that always sets pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED.
The code for your data class might resemble the following:
#define _COLUMN_ENTRY_CODE_BYREF(nOrdinal, wType, nLength, nPrecision, nScale, dataOffset, lengthOffset, statusOffset) \
if (pBuffer != NULL) \
{ \
CAccessorBase::FreeType(wType, pBuffer + dataOffset); \
} \
else if (pBinding != NULL) \
{ \
Bind(pBinding, nOrdinal, wType, nLength, nPrecision, nScale, eParamIO, \
dataOffset, lengthOffset, statusOffset); \
pBinding++; \
} \
nColumns++;
#define COLUMN_ENTRY_EX_BYREF(nOrdinal, wType, nLength, nPrecision, nScale, data, length, status) \
_COLUMN_ENTRY_CODE_BYREF(nOrdinal, wType, nLength, nPrecision, nScale, offsetbuf(data), offsetbuf(length), offsetbuf(status))
class CdbotexttestAccessor
{
public:
TCHAR m_szfield1[11];
char * m_pszField2;
long m_nField2Len;
DBSTATUS m_Field2Status;
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.
// (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;
}
}
void FreeRecordMemory()
{
CoTaskMemFree((BYTE *)m_pField2);
}
BEGIN_COLUMN_MAP(CdbotexttestAccessor)
COLUMN_ENTRY(1, m_szField1)
COLUMN_ENTRY_EX_BYREF(2, DBTYPE_STR | DBTYPE_BYREF, sizeof(char *), 0, 0, m_pszField2, m_szField2Len, m_szField2Status)
END_COLUMN_MAP()
};
You still can use BYREF, however, you must specify client owned by modifying the ATL code. You are also responsible for freeing the memory using
IMalloc::Free/
CoTaskMemFree.