// The code provided here works with MFC 4.1.
// Code to work with other versions of MFC before 4.2 will be very
// similar. You just need to copy the source for CDatabase::Open() from
// Dbcore.cpp, make the changes described in this article, and then
// determine which file specific definitions and header files to add
// to your .cpp file.
// MyDataBase.h
// Derive a class from CDatabase
class CMyDatabase : public CDatabase
{
public:
Open(LPCTSTR lpszDSN, BOOL bExclusive = FALSE,
BOOL bReadonly = FALSE, LPCTSTR lpszConnect = "ODBC;",
BOOL bUseCursorLib = TRUE);
};
//MyDataBase.cpp
#include "stdafx.h"
#include <afxpriv.h>
#include <afxdisp.h>
#include "MyDataBase.h"
// This line of code is borrowed from Dbcore.cpp
static const TCHAR szODBC[] = _T("ODBC;");
// These two lines are borrowed from Afximpl.h
// determine number of elements in an array (not bytes)
#define _countof(array) (sizeof(array)/sizeof(array[0]))
BOOL CMyDatabase::Open(LPCTSTR lpszDSN, BOOL bExclusive,
BOOL bReadonly, LPCTSTR lpszConnect, BOOL bUseCursorLib)
{
UCHAR szConnectOutput[MAX_CONNECT_LEN];
#ifdef _68K_
if(nClassObject == 0)
AfxThrowDBException(AFX_SQL_ERROR_ODBC_LOAD_FAILED, NULL,
SQL_NULL_HSTMT);
#endif
ASSERT_VALID(this);
ASSERT(lpszDSN == NULL || AfxIsValidString(lpszDSN));
ASSERT(lpszConnect == NULL || AfxIsValidString(lpszConnect));
// Exclusive access not supported.
ASSERT(!bExclusive);
UNUSED(bExclusive); // unused in release builds
m_bUpdatable = !bReadonly;
TRY
{
if (lpszConnect != NULL)
m_strConnect = lpszConnect;
// For Visual Basic & Microsoft Access compatibility, use the
// "ODBC;" (or "odbc;") prefix to the connect string
if (_tcsnicmp(m_strConnect, szODBC, lstrlen(szODBC)) != 0)
{
TRACE0("Error: Missing 'ODBC' prefix on connect string.\n");
return FALSE;
}
// Strip "ODBC;"
m_strConnect = m_strConnect.Right(m_strConnect.GetLength()
- lstrlen(szODBC));
if (lpszDSN != NULL && lstrlen(lpszDSN) != 0)
{
// Append "DSN=" lpszDSN
m_strConnect += ";DSN=";
m_strConnect += lpszDSN;
}
AllocConnect();
RETCODE nRetCode;
// Turn on cursor lib support
if (bUseCursorLib)
{
AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc,
SQL_ODBC_CURSORS, SQL_CUR_USE_ODBC));
// With cursor library added records immediately in result set
m_bIncRecordCountOnAdd = TRUE;
}
HWND hWndTop;
CWnd* pWnd = CWnd::GetSafeOwner(NULL, &hWndTop);
if (pWnd == NULL)
pWnd = CWnd::GetDesktopWindow();
ASSERT_VALID(pWnd);
SWORD nResult;
#ifndef _MAC
USES_CONVERSION;
// Code before it was changed:
// AFX_SQL_SYNC(::SQLDriverConnect(m_hdbc, pWnd->m_hWnd,
// (UCHAR*)T2A((LPCTSTR)m_strConnect), SQL_NTS,
// szConnectOutput, _countof(szConnectOutput),
// &nResult, SQL_DRIVER_COMPLETE));
AFX_SQL_SYNC(::SQLDriverConnect(m_hdbc, NULL,
(UCHAR*)(const char*)m_strConnect, SQL_NTS,
szConnectOutput, _countof(szConnectOutput),
&nResult, SQL_DRIVER_NOPROMPT));
#else
AFX_SQL_SYNC(::SQLDriverConnect(m_hdbc, GetWrapperWindow
(pWnd->m_hWnd), (UCHAR*)(const char*)m_strConnect, SQL_NTS,
szConnectOutput, _countof(szConnectOutput),
&nResult, SQL_DRIVER_COMPLETE));
#endif
if (hWndTop != NULL)
::EnableWindow(hWndTop, TRUE);
// Code before it was changed:
// if (nRetCode == SQL_NO_DATA_FOUND)
// Now need to check for SQL_ERROR too
if ((nRetCode == SQL_NO_DATA_FOUND) || (nRetCode == SQL_ERROR))
{
Free();
return FALSE;
}
if (!Check(nRetCode))
{
#ifdef _DEBUG
if (pWnd->m_hWnd == NULL)
TRACE0("Error: No default window (AfxGetApp()->m_pMainWnd) for
SQLDriverConnect.\n");
#endif
ThrowDBException(nRetCode);
}
// Connect strings must have "ODBC;"
m_strConnect = szODBC;
// Save connect string returned from ODBC
m_strConnect += (char*)szConnectOutput;
SWORD nAPIConformance;
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_ODBC_API_CONFORMANCE,
&nAPIConformance, sizeof(nAPIConformance), &nResult));
if (!Check(nRetCode))
ThrowDBException(nRetCode);
if (nAPIConformance < SQL_OAC_LEVEL1)
ThrowDBException(AFX_SQL_ERROR_API_CONFORMANCE);
SWORD nSQLConformance;
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_ODBC_SQL_CONFORMANCE,
&nSQLConformance, sizeof(nSQLConformance), &nResult));
if (!Check(nRetCode))
ThrowDBException(nRetCode);
if (nSQLConformance < SQL_OSC_MINIMUM)
ThrowDBException(AFX_SQL_ERROR_SQL_CONFORMANCE);
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_CURSOR_COMMIT_BEHAVIOR,
&m_nCursorCommitBehavior, sizeof(m_nCursorCommitBehavior),
&nResult));
if (!Check(nRetCode))
m_nCursorCommitBehavior = SQL_ERROR;
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_CURSOR_ROLLBACK_BEHAVIOR,
&m_nCursorRollbackBehavior, sizeof(m_nCursorRollbackBehavior),
&nResult));
if (!Check(nRetCode))
m_nCursorRollbackBehavior = SQL_ERROR;
UDWORD dwGetDataExtensions;
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_GETDATA_EXTENSIONS,
&dwGetDataExtensions, sizeof(dwGetDataExtensions),
&nResult));
if (!Check(nRetCode))
dwGetDataExtensions = 0;
if (dwGetDataExtensions & SQL_GD_BOUND)
m_dwUpdateOptions = AFX_SQL_GDBOUND;
else
m_dwUpdateOptions = 0;
// Set required transaction support for CRecordset cursors
if ((m_nCursorCommitBehavior == SQL_CB_PRESERVE) &&
(m_nCursorRollbackBehavior == SQL_CB_PRESERVE))
m_bTransactions = TRUE;
if (m_bUpdatable)
{
// Make sure data source is Updatable
char szReadOnly[10];
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_DATA_SOURCE_READ_ONLY,
szReadOnly, _countof(szReadOnly), &nResult));
if (Check(nRetCode) && nResult == 1)
m_bUpdatable = !(lstrcmpA(szReadOnly, "Y") == 0);
else
m_bUpdatable = FALSE;
#ifdef _DEBUG
if (!m_bUpdatable && (afxTraceFlags & traceDatabase))
TRACE0("Warning: data source is readonly.\n");
#endif
}
else
{
// Make data source is Updatable
AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc,
SQL_ACCESS_MODE, SQL_MODE_READ_ONLY));
}
char szIDQuoteChar[2];
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_IDENTIFIER_QUOTE_CHAR,
szIDQuoteChar, _countof(szIDQuoteChar), &nResult));
if (Check(nRetCode) && nResult == 1)
m_chIDQuoteChar = szIDQuoteChar[0];
else
m_chIDQuoteChar = ' ';
#ifdef _DEBUG
if (afxTraceFlags & traceDatabase)
{
char szInfo[64];
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_DBMS_NAME,
szInfo, _countof(szInfo), &nResult));
if (Check(nRetCode))
{
CString strInfo = szInfo;
TRACE1("DBMS: %s\n", strInfo);
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_DBMS_VER,
szInfo, _countof(szInfo), &nResult));
if (Check(nRetCode))
{
strInfo = szInfo;
TRACE1(", Version: %s\n", strInfo);
}
}
}
#endif
}
CATCH_ALL(e)
{
Free();
THROW_LAST();
}
END_CATCH_ALL
return TRUE;
}