I have a legacy code base where I connect with OLEDB to a MSSQL server.
Now here is the code:
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0A00
#endif
#include <afx.h>
#include <atlbase.h>
#include <atldbcli.h>
// -------------------------------------------------------------------
// OpenWithUDL - connect and open database using an udl file
// -------------------------------------------------------------------
HRESULT OpenWithUDL(CSession& m_session, const CString& pathToUdl)
{
CDataSource db;
USES_CONVERSION;
HRESULT hr = db.OpenFromFileName(T2COLE(pathToUdl));
if (FAILED(hr)) { return hr; }
CDBPropSet dbinit(DBPROPSET_DBINIT);
dbinit.AddProperty(DBPROP_INIT_OLEDBSERVICES, (short)DBPROPVAL_OS_ENABLEALL);
return m_session.Open(db);
}
// -------------------------------------------------------------------
// Connect to database
//
// SetConnStr() or SetUdlStr() must be called first to define the db
// to open - when not set use hardcoded db details for connection
// -------------------------------------------------------------------
HRESULT Open(CSession& m_session, const CString& pathToUdl)
{
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr)) { return E_FAIL; }
return OpenWithUDL(m_session, pathToUdl);
}
// -------------------------------------------------------------------
// Return: true db connection ok - session param not NULL
// false db connection failed
// -------------------------------------------------------------------
bool GetDBCon(const CSession& p_session)
{
if (p_session.m_spOpenRowset == NULL) { return false; }
HRESULT hr;
XACTTRANSINFO pInfo;
try
{
hr = p_session.GetTransactionInfo(&pInfo);
}
catch (...)
{
return false;
}
return hr == S_OK || hr == XACT_E_NOTRANSACTION;
}
int main()
{
CSession session;
CString pathToUdl = "SomePathToUDL";
HRESULT hr = Open(session, pathToUdl);
if (FAILED(hr)) { return -1; }
try
{
if (!GetDBCon(session)) { return -2; }
}
catch (...)
{
return -3;
}
CString sqlStatement = "SELECT GETDATE();"; // Some select statement
CCommand<CDynamicAccessor> d_tab; // <<-- Here is the Problem
hr = d_tab.Open(session, sqlStatement);
if (FAILED(hr))
{
d_tab.Close();
return -4;
}
hr = d_tab.MoveFirst(); // <<-- throws exception because ATLASSUME(m_pAccessor != NULL) fails
}
Now the interesting part is, at the process of instantiation of d_tab, CAccessorRowset gets its ctor called. There it registers itself as accessor (see code listing).
///////////////////////////////////////////////////////////////////////////
// CAccessorRowset
template <class TAccessor = CNoAccessor, template <typename T> class TRowset = CRowset>
class CAccessorRowset :
public TAccessor,
public TRowset<TAccessor>
{
public:
template <class _Self = CAccessorRowset>
CAccessorRowset()
{
// Give the rowset a pointer to the accessor
#ifdef _ATL_MODULES
if constexpr(_Has_m_nAccessors<_Self>::value)
#else // ^^ _ATL_MODULES / !_ATL_MODULES vv
__if_exists(m_nAccessors)
#endif // _ATL_MODULES
{
static_cast<_Self*>(this)->SetAccessor(this); // <<--- register as accessor
}
}
//...
};
The provided code at the top throws the exception regardless of the C++ version.
But in my legacy code the access to the DB is compiled to a .lib with C++17. Excecuting the calls in main results in registering as accessor, but not so, when it was compiled in C++20. Which to me makes no sense.
Does anyone know a solution to that?