Saturday, February 25, 2012

C++ oledb templates

Below, you'll see a parameter map and parts of a data class.
It works (that's the good thing). The parameter was, originally, an image
datatype, but unfortunately, I had to make it varbinary(8000) because of
runtime oledb errors (I have oledb 2.8 and use SQL 2000 as a storage
medium). Does anybody have a clue why I cannot just use a SequentialStream
without errors? (ps: Visual C++ 7.1 has even more problems with this when
you use attributes)
(some code has been omitted to shorten...)
BYTE m_PtrStatePar[16];
DBLENGTH m_dwPtrStateLength;
//ISequentialStream *pStream; //does not work
PBYTE m_BLOB;
DBLENGTH m_dwStateLength;
DBSTATUS m_dwStateStatus;
LONG insertptr;
BEGIN_PARAM_MAP(CpSessionUpdate1Accessor
)
SET_PARAM_TYPE(DBPARAMIO_INPUT)
COLUMN_ENTRY_LENGTH(1, m_PtrStatePar, m_dwPtrStateLength)
SET_PARAM_TYPE(DBPARAMIO_INPUT)
COLUMN_ENTRY(2, insertptr)
SET_PARAM_TYPE(DBPARAMIO_INPUT)
//returns 'you must bind parameters first' error and E_UNEXPECTED
//BLOB_ENTRY_LENGTH_STATUS(3, IID_ISequentialStream, STGM_READ, pStream,
m_dwStateLength, m_dwStateStatus)
_COLUMN_ENTRY_CODE(3, DBTYPE_BYTES | DBTYPE_BYREF,
_SIZE_TYPE(m_BLOB),0,0,offsetbuf(m_BLOB)
,offsetbuf(m_dwStateLength),0)
END_PARAM_MAP()
};
class CpSessionUpdate1 : public
CCommand<CAccessor<CpSessionUpdate1Accessor>, CNoRowset >
HRESULT OpenRowset(ATL::CSession ms)
hr = Open(ms, L"UPDATETEXT mytable.myBLOB ? ? NULL ?");Hi,
I just completed OLEDB project that included BLOB functionality. Everything
works very well (with SQLOLEDB provider and MSSQL2000 database). However, I
can't help you here because I didn't use ATL OLEDB template classes at all:
I used OLEDB interfaces directly. There're some good BLOB-handling samples
in SQL2000 Books Online.
Boris
"Egbert Nierop (MVP for IIS)" <egbert_nierop@.nospam.invalid> wrote in
message news:%23jSkVkVIEHA.3248@.TK2MSFTNGP12.phx.gbl...
> Below, you'll see a parameter map and parts of a data class.
> It works (that's the good thing). The parameter was, originally, an image
> datatype, but unfortunately, I had to make it varbinary(8000) because of
> runtime oledb errors (I have oledb 2.8 and use SQL 2000 as a storage
> medium). Does anybody have a clue why I cannot just use a SequentialStream
> without errors? (ps: Visual C++ 7.1 has even more problems with this when
> you use attributes)
> (some code has been omitted to shorten...)
> BYTE m_PtrStatePar[16];
> DBLENGTH m_dwPtrStateLength;
> //ISequentialStream *pStream; //does not work
> PBYTE m_BLOB;
> DBLENGTH m_dwStateLength;
> DBSTATUS m_dwStateStatus;
> LONG insertptr;
>
> BEGIN_PARAM_MAP(CpSessionUpdate1Accessor
)
> SET_PARAM_TYPE(DBPARAMIO_INPUT)
> COLUMN_ENTRY_LENGTH(1, m_PtrStatePar, m_dwPtrStateLength)
> SET_PARAM_TYPE(DBPARAMIO_INPUT)
> COLUMN_ENTRY(2, insertptr)
> SET_PARAM_TYPE(DBPARAMIO_INPUT)
> //returns 'you must bind parameters first' error and E_UNEXPECTED
> //BLOB_ENTRY_LENGTH_STATUS(3, IID_ISequentialStream, STGM_READ, pStream,
> m_dwStateLength, m_dwStateStatus)
> _COLUMN_ENTRY_CODE(3, DBTYPE_BYTES | DBTYPE_BYREF,
> _SIZE_TYPE(m_BLOB),0,0,offsetbuf(m_BLOB)
,offsetbuf(m_dwStateLength),0)
> END_PARAM_MAP()
> };
> class CpSessionUpdate1 : public
> CCommand<CAccessor<CpSessionUpdate1Accessor>, CNoRowset >
>
> HRESULT OpenRowset(ATL::CSession ms)
> hr = Open(ms, L"UPDATETEXT mytable.myBLOB ? ? NULL ?");
>
>|||I have a solution, however, it has a problem. If you can help me with the
problem you can use the code. I have written a template OLEDB class to
support reading and writing BLOB data to and from an SQL 2000 database.
This code works perfectly when compiled on VC6.0. Porting to VC7.0 however
has generated a serious problem. When writing to the database all goes well
until my CCommand object begins to destroy itself. When FreeType() is
called in the clean up routine for the BLOB column, the program crashes with
an access violation. FreeType() is trying to clean up an IUNKNOWN pointer
which points to somewhere. (I have no idea where). Thoughts? Help? Some
code is below, as well as a link to a good sample. Let me know if you run
into the same problem.
James
// The following MACRO was required for VC6.0 but was included in VC7.0 The
code is very similar.
#define _BLOB_ENTRY_CODE_EX(nOrdinal, IID, flags, dataOffset, lengthOffset,
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, lengthOffset, statusOffset, pObject); \
pBinding++; \
} \
nColumns++;
#ifdef BLOB_ENTRY_LENGTH_STATUS
#undef BLOB_ENTRY_LENGTH_STATUS
#define BLOB_ENTRY_LENGTH_STATUS(nOrdinal, IID, flags, data, length, status)
\
_BLOB_ENTRY_CODE_EX(nOrdinal, IID, flags, offsetbuf(data),
offsetbuf(length), offsetbuf(status));
#endif
enum
{
HEADER = 1,
CONTENTSIZE,
CONTENT,
NUM_COLUMNS_PLUS_ONE
};
class CdboMessagesAccessor
{
public:
TCHAR m_header[4097];
LONG m_contentSize;
ISequentialStream* m_messageContent; ULONG m_messageContentStatus; ULONG
m_messageContentLength;
BEGIN_COLUMN_MAP(CdboMessagesAccessor)
COLUMN_ENTRY(HEADER, m_header)
COLUMN_ENTRY(CONTENTSIZE, m_contentSize)
BLOB_ENTRY_LENGTH_STATUS(CONTENT,
IID_ISequentialStream,
STGM_READ,
m_messageContent,
m_messageContentLength,
m_messageContentStatus)
END_COLUMN_MAP()
DEFINE_COMMAND(CdboMessagesAccessor, _T(" \
SELECT \
header, \
contentSize, \
messageContent, \
FROM dbo.Messages"))
// You may wish to call this function if you are inserting a record and wish
to
// initialize all the fields, if you are not going to explicitly set all of
them.
void ClearRecord()
{
memset(this, 0, sizeof(*this));
m_sentStatus = DBSTATUS_S_ISNULL;
m_receivedStatus = DBSTATUS_S_ISNULL;
}
};
// Write a data element
// CMyISSImp is a wrapper class which has a base class of ISequentialStream
CdboMessages dbo(&m_session);
hr = dbo.Open();
if (SUCCEEDED(hr))
{
dbo.ClearRecord();
// Create the message object and write to the database
COPYSTRING(dbo.m_header, GetHeader());
// The entire block below is needed to write to the messageContent
column
std::string s = GetContent();
try
{
CMyISSImp *pStream = new CMyISSImp(s);
if (pStream)
{
dbo.m_messageContentLength = s.size();
dbo.m_messageContentStatus = DBSTATUS_S_OK;
dbo.m_messageContent = (ISequentialStream*)pStream;
dbo.m_contentSize = s.size();
}
else
{
_com_issue_error(E_OUTOFMEMORY);
}
}
catch(_com_error &e)
{
// Something failed
hr = e.Error();
}
}
if (!Check(hr))
{
dbo.m_messageContentStatus = DBSTATUS_S_ISNULL;
}
// Write the data to the database
hr = dbo.Insert();
// Read data from the database
CdboMessages dbo(&m_session);
HRESULT hr = dbo.Open(m_switchID, messageID);
if (Check(hr))
{
hr = dbo.MoveFirst(); // and only...but we won't check, the database
should enforce this
if (Check(hr))
{
std::string content;
if (dbo.m_messageContentStatus == DBSTATUS_S_OK &&
dbo.m_messageContent)
{
if (dbo.m_contentSize > 0)
{
content.resize(dbo.m_contentSize);
int pos = 0;
while(1)
{
// Documentation is unclear as to what can happen when we
reach the end of
// a stream. Although it is stated that Read() can return an
error, it doesn't
// indicates what this means about the buffer or the number
of bytes read.
// I have determined that OLEDB seems to return success with
a bytes count < size
// when the end of the stream is encountered. This allows
the loop to exit properly
// If however a future implementation returns an error with
some uninitialized byte
// count this could cause errors. No one implementation of
this function can account
// for all possiblities so I will leave it like this for
now.
static const size = 1024;
char buffer[size];
ULONG read;
dbo.m_messageContent->Read((LPVOID)buffer, size, &read);
// Make sure we don't overflow the buffer
if (content.size() < pos + size)
{
content.resize(content.size() + size);
}
for(int i = 0; i<read; i++)
{
content[pos++] = buffer[i];
}
if (read < size)
{
break;
}
}
content.resize(pos);
}
}
}
}
The basic design for this came from here:
http://support.microsoft.com/?kbid=190958&sd=msdn
"Boris Dynin" <spam@.noplease.com> wrote in message
news:OK7q$7pJEHA.752@.tk2msftngp13.phx.gbl...
> Hi,
> I just completed OLEDB project that included BLOB functionality.
Everything
> works very well (with SQLOLEDB provider and MSSQL2000 database). However,
I
> can't help you here because I didn't use ATL OLEDB template classes at
all:
> I used OLEDB interfaces directly. There're some good BLOB-handling samples
> in SQL2000 Books Online.
> Boris
> "Egbert Nierop (MVP for IIS)" <egbert_nierop@.nospam.invalid> wrote in
> message news:%23jSkVkVIEHA.3248@.TK2MSFTNGP12.phx.gbl...
image[vbcol=seagreen]
SequentialStream[vbcol=seagreen]
when[vbcol=seagreen]
>|||What kind of accessor are you using? If you use CDynamicAccessor, you will
have to call SetBlobHandling and SetBlobSizeLimit (new in 7.0). I'm using
table.SetBlobSizeLimit(1024);
table. SetBlobHandling(DBBLOBHANDLING_NOSTREAMS
);
This will make all blob bindings by ref.
Joo Paulo Figueira
Embedded MVP
"James Ryan" <__@._jryan@.__icescape.__com> wrote in message
news:%235AFa3KTEHA.1544@.TK2MSFTNGP09.phx.gbl...
> I have a solution, however, it has a problem. If you can help me with the
> problem you can use the code. I have written a template OLEDB class to
> support reading and writing BLOB data to and from an SQL 2000 database.
> This code works perfectly when compiled on VC6.0. Porting to VC7.0
however
> has generated a serious problem. When writing to the database all goes
well
> until my CCommand object begins to destroy itself. When FreeType() is
> called in the clean up routine for the BLOB column, the program crashes
with
> an access violation. FreeType() is trying to clean up an IUNKNOWN pointer
> which points to somewhere. (I have no idea where). Thoughts? Help?
Some
> code is below, as well as a link to a good sample. Let me know if you
run
> into the same problem.
> James
> // The following MACRO was required for VC6.0 but was included in VC7.0
The
> code is very similar.
>
> #define _BLOB_ENTRY_CODE_EX(nOrdinal, IID, flags, dataOffset,
lengthOffset,
> 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, lengthOffset, statusOffset, pObject); \
> pBinding++; \
> } \
> nColumns++;
> #ifdef BLOB_ENTRY_LENGTH_STATUS
> #undef BLOB_ENTRY_LENGTH_STATUS
> #define BLOB_ENTRY_LENGTH_STATUS(nOrdinal, IID, flags, data, length,
status)
> \
> _BLOB_ENTRY_CODE_EX(nOrdinal, IID, flags, offsetbuf(data),
> offsetbuf(length), offsetbuf(status));
> #endif
> enum
> {
> HEADER = 1,
> CONTENTSIZE,
> CONTENT,
> NUM_COLUMNS_PLUS_ONE
> };
> class CdboMessagesAccessor
> {
> public:
> TCHAR m_header[4097];
> LONG m_contentSize;
> ISequentialStream* m_messageContent; ULONG m_messageContentStatus; ULONG
> m_messageContentLength;
> BEGIN_COLUMN_MAP(CdboMessagesAccessor)
> COLUMN_ENTRY(HEADER, m_header)
> COLUMN_ENTRY(CONTENTSIZE, m_contentSize)
> BLOB_ENTRY_LENGTH_STATUS(CONTENT,
> IID_ISequentialStream,
> STGM_READ,
> m_messageContent,
> m_messageContentLength,
> m_messageContentStatus)
> END_COLUMN_MAP()
> DEFINE_COMMAND(CdboMessagesAccessor, _T(" \
> SELECT \
> header, \
> contentSize, \
> messageContent, \
> FROM dbo.Messages"))
> // You may wish to call this function if you are inserting a record and
wish
> to
> // initialize all the fields, if you are not going to explicitly set all
of
> them.
> void ClearRecord()
> {
> memset(this, 0, sizeof(*this));
> m_sentStatus = DBSTATUS_S_ISNULL;
> m_receivedStatus = DBSTATUS_S_ISNULL;
> }
> };
>
> // Write a data element
> // CMyISSImp is a wrapper class which has a base class of
ISequentialStream
> CdboMessages dbo(&m_session);
> hr = dbo.Open();
> if (SUCCEEDED(hr))
> {
> dbo.ClearRecord();
> // Create the message object and write to the database
> COPYSTRING(dbo.m_header, GetHeader());
> // The entire block below is needed to write to the messageContent
> column
> std::string s = GetContent();
> try
> {
> CMyISSImp *pStream = new CMyISSImp(s);
> if (pStream)
> {
> dbo.m_messageContentLength = s.size();
> dbo.m_messageContentStatus = DBSTATUS_S_OK;
> dbo.m_messageContent = (ISequentialStream*)pStream;
> dbo.m_contentSize = s.size();
> }
> else
> {
> _com_issue_error(E_OUTOFMEMORY);
> }
> }
> catch(_com_error &e)
> {
> // Something failed
> hr = e.Error();
> }
> }
> if (!Check(hr))
> {
> dbo.m_messageContentStatus = DBSTATUS_S_ISNULL;
> }
> // Write the data to the database
> hr = dbo.Insert();
>
> // Read data from the database
> CdboMessages dbo(&m_session);
> HRESULT hr = dbo.Open(m_switchID, messageID);
> if (Check(hr))
> {
> hr = dbo.MoveFirst(); // and only...but we won't check, the database
> should enforce this
> if (Check(hr))
> {
> std::string content;
> if (dbo.m_messageContentStatus == DBSTATUS_S_OK &&
> dbo.m_messageContent)
> {
> if (dbo.m_contentSize > 0)
> {
> content.resize(dbo.m_contentSize);
> int pos = 0;
> while(1)
> {
> // Documentation is unclear as to what can happen when we
> reach the end of
> // a stream. Although it is stated that Read() can return
an
> error, it doesn't
> // indicates what this means about the buffer or the
number
> of bytes read.
> // I have determined that OLEDB seems to return success
with
> a bytes count < size
> // when the end of the stream is encountered. This allows
> the loop to exit properly
> // If however a future implementation returns an error
with
> some uninitialized byte
> // count this could cause errors. No one implementation of
> this function can account
> // for all possiblities so I will leave it like this for
> now.
> static const size = 1024;
> char buffer[size];
> ULONG read;
> dbo.m_messageContent->Read((LPVOID)buffer, size, &read);
> // Make sure we don't overflow the buffer
> if (content.size() < pos + size)
> {
> content.resize(content.size() + size);
> }
> for(int i = 0; i<read; i++)
> {
> content[pos++] = buffer[i];
> }
> if (read < size)
> {
> break;
> }
> }
> content.resize(pos);
> }
> }
> }
> }
> The basic design for this came from here:
> http://support.microsoft.com/?kbid=190958&sd=msdn
>
> "Boris Dynin" <spam@.noplease.com> wrote in message
> news:OK7q$7pJEHA.752@.tk2msftngp13.phx.gbl...
> Everything
However,[vbcol=seagreen]
> I
> all:
samples[vbcol=seagreen]
> image
of[vbcol=seagreen]
> SequentialStream
> when
pStream,[vbcol=seagreen]
>|||Interesting, however, this class is using the CAccessor template class as I
have defined the database structure specific to this app.
I have managed to determine that my stream object is being released more
times than it is being AddRef'd. I have created an initial reference when I
pass the object to the insert() call. During the insert and the subsequent
object destruction, I see TWO AddRef's by the oledb dll (sqloledb.dll)
followed by THREE releases. At this point the object is destroyed.
However, the template class still has a reference to the object and it tries
to release() as well. This causes the crash. I have "fixed" the problem by
setting the object pointer in the Accessor class to zero after the insert
call so the template class will not try to release it. This hardly sounds
like the proper solution however, at this point I don't know if I am looking
at a bug in OLEDB or some error in my own code.
James
"Joo Paulo Figueira [eMVP]" <joao.figueira@.primeworks.takethisout.pt> w
rote
in message news:uQMh5qMTEHA.2480@.TK2MSFTNGP10.phx.gbl...
> What kind of accessor are you using? If you use CDynamicAccessor, you will
> have to call SetBlobHandling and SetBlobSizeLimit (new in 7.0). I'm using
> table.SetBlobSizeLimit(1024);
> table. SetBlobHandling(DBBLOBHANDLING_NOSTREAMS
);
> This will make all blob bindings by ref.
> --
> Joo Paulo Figueira
> Embedded MVP
> "James Ryan" <__@._jryan@.__icescape.__com> wrote in message
> news:%235AFa3KTEHA.1544@.TK2MSFTNGP09.phx.gbl...
the[vbcol=seagreen]
> however
> well
> with
pointer[vbcol=seagreen]
> Some
> run
> The
> lengthOffset,
> status)
> wish
> of
> ISequentialStream
we[vbcol=seagreen]
return[vbcol=seagreen]
> an
> number
> with
allows[vbcol=seagreen]
> with
of[vbcol=seagreen]
> However,
> samples
because[vbcol=seagreen]
> of
> pStream,
_SIZE_TYPE(m_BLOB),0,0,offsetbuf(m_BLOB)
,offsetbuf(m_dwStateLength),0)[vbcol=sea
green]
>

No comments:

Post a Comment