Multi-Threaded DAO

In order to get around the limitations of using DAO recordsets in multiple threads, I have designed a class that forces all of the DAO calls to go through the GUI thread. Using the GUI message queue, all calls that come in to CMultiDAORecordset are forced to check the current thread using AfxGetThread(). A pointer to the GUI thread is saved at the beginning of the InitInstance function. If the incoming call request is not running in the GUI thread, then CMultiDAORecordSet sends a WM_MULTIDAOMESSAGE message to AfxGetMainWnd() (Mainfrm.cpp). Mainfrm recieves this message, and the process repeats. This time, however, the message was received in the GUI thread, and the base CDaoRecordset class gets called.

To use this class you must change your recordset classes to inherit from CMultiDAORecordset and not CDaoRecordset. Like This:


class CMySet : public CMultiDaoRecordSet

and also change these in the .cpp file:


IMPLEMENT_DYNAMIC(CMySet, CMultiDaoRecordSet)

CMySet::CMySet (CDaoDatabase* pdb) : CMultiDaoRecordSet(pdb)

To process the received WM_MULTIDAOMESSAGE messages, the following code was added to MainFrm:

In MainFrm.h:


#ifdef MAINFRAME_CPP
UINT WM_MULTIDAOMESSAGE = RegisterWindowMessage(“WM_MULTIDAOMESSAGE”);
#else
extern UINT WM_MULTIDAOMESSAGE;
#endif

afx_msg LONG OnMultiDaoMessage( UINT uParam, LONG lParam);

In MainFrm.cpp:


#define MAINFRAME_CPP

#include “MutliDaoRecordset.h”

//added to the message map

BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_REGISTERED_MESSAGE(WM_MULTIDAOMESSAGE, OnMultiDaoMessage)
END_MESSAGE_MAP()

//this function added
LONG CMainFrame::OnMultiDaoMessage( UINT uParam, LONG lParam)
{
//jtm
//based on switch, perform operation…
CMultiDaoRecordSet *pSet = (CMultiDaoRecordSet *)lParam;
LONG lRet = 0;

CString cRet = “”;
COleVariant cVar;

try
{
//jtm——-debug————————————–
CString cTraceMessage = cDAOMessageArray[uParam];
cTraceMessage += “n”;
TRACE(cTraceMessage);
//jtm——-debug————————————–

switch(uParam)
{
case MultiDaoOpen:
pSet->Open();
break;
case MultiDaoClose:
pSet->Close();
break;
case MultiDaoIsOpen:
lRet = (LONG)pSet->IsOpen();
break;
case MultiDaoIsBOF:
lRet = (LONG)pSet->IsBOF();
break;
case MultiDaoIsEOF:
lRet = (LONG)pSet->IsEOF();
break;
case MultiDaoIsDeleted:
lRet = (LONG)pSet->IsDeleted();
break;
case MultiDaoIsFieldDirty:
lRet = (LONG)pSet->IsFieldDirty(pSet->pParam1);
break;
case MultiDaoIsFieldNull:
lRet = (LONG)pSet->IsFieldNull(pSet->pParam1);
break;
case MultiDaoIsFieldNullable:
lRet = (LONG)pSet->IsFieldNullable(pSet->pParam1);
break;
case MultiDaoGetName:
cRet = pSet->GetName();
lRet = (LONG)&cRet;
break;
case MultiDaoGetType:
lRet = (LONG)pSet->GetType();
break;
case MultiDaoGetEditMode:
lRet = (LONG)pSet->GetEditMode();
break;
case MultiDaoGetLastModifiedBookmark:
cVar = pSet->GetLastModifiedBookmark();
lRet = (LONG)&cVar;
break;
case MultiDaoGetRecordCount:
lRet = (LONG)pSet->GetRecordCount();
break;
case MultiDaoMoveNext:
pSet->MoveNext();
break;
case MultiDaoMovePrev:
pSet->MovePrev();
break;
case MultiDaoMoveFirst:
pSet->MoveFirst();
break;
case MultiDaoMoveLast:
pSet->MoveLast();
break;
case MultiDaoMove:
pSet->Move(*(LONG *)pSet->pParam1);
break;
case MultiDaoFindNext:
lRet = (LONG)pSet->FindNext(*(LPCTSTR *)pSet->pParam1);
break;
case MultiDaoFindPrev:
lRet = (LONG)pSet->FindPrev(*(LPCTSTR*)pSet->pParam1);
break;
case MultiDaoFindFirst:
lRet = (LONG)pSet->FindFirst(*(LPCTSTR *)pSet->pParam1);
break;
case MultiDaoFindLast:
lRet = (LONG)pSet->FindLast(*(LPCTSTR *)pSet->pParam1);
break;
case MultiDaoFind:
lRet = (LONG)pSet->Find(*(LONG *)pSet->pParam1, *(LPCTSTR*)pSet->pParam2);
break;
case MultiDaoGetBookmark:
cVar = pSet->GetBookmark();
lRet = (LONG)&cVar;
break;
case MultiDaoSetBookmark:
pSet->SetBookmark(*(COleVariant*)pSet->pParam1);
break;
case MultiDaoAddNew:
pSet->AddNew();
break;
case MultiDaoEdit:
pSet->Edit();
break;
case MultiDaoUpdate:
pSet->Update();
break;
case MultiDaoDelete:
pSet->Delete();
break;
case MultiDaoCancelUpdate:
pSet->CancelUpdate();
break;
case MultiDaoRequery:
pSet->Requery();
break;
}
}
catch (CDaoException *e)
{
TRACE(“Database Multithread Operation Failed%sn”,
e->m_pErrorInfo->m_strDescription);
}
return lRet;
}

The following code was added to the app object header file:



public:
CWinThread *pGUIThread;

And this to the constructor in the app .cpp file:


CMyApp::CMyApp()
{
pGUIThread = AfxGetThread();
}

Without the above definitions, the app will die a short stack overflow death.


The entire DAO public function list was not implemented. However, the above functions should cover the majority of usage of CDaoRecordset. If you need to add a function, you can insert it into the enumerated function list, and also add it to the debug string list. These were defined in MultiDaorecordset.h.


The trace function in mainfrm.cpp can be eliminated, once you have verified that it is the multi-threaded aspect that is causing the DAO errors.


To support the DAO multi threaded recordset classes, the follinw files were implemented:

Multidaorecordset.cpp:


// MultiDaoRecordSet.cpp : implementation file
//

#define MULTIDAORECORDSET_CPP

#include “stdafx.h”

#include “MyApp.h”

#include “MultiDaoRecordSet.h”

#include “mainfrm.h”

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CMultiDaoRecordSet

IMPLEMENT_DYNAMIC(CMultiDaoRecordSet, CDaoRecordset)

CMultiDaoRecordSet::CMultiDaoRecordSet(CDaoDatabase* pdb)
: CDaoRecordset(pdb)
{
//{{AFX_FIELD_INIT(CMultiDaoRecordSet)
//}}AFX_FIELD_INIT
m_nDefaultType = dbOpenDynaset;
}

//jtm
//thread safe destructor….
CMultiDaoRecordSet::~CMultiDaoRecordSet()
{
if (IsOpen())
Close();

// Clean up database if necessary
if (m_pDatabase != NULL && (m_nStatus & AFX_DAO_IMPLICIT_DB))
{
m_pDatabase->Close();
delete m_pDatabase;
m_pDatabase = NULL;
}
}

CString CMultiDaoRecordSet::GetDefaultDBName()
{
CMyApp *pApp = ((CMYApp *)AfxGetApp());
return pApp->GetDatabaseFullPath();
}

/////////////////////////////////////////////////////////////////////////////
// CMultiDaoRecordSet diagnostics

//jtm
//multi thread safe functoins
void CMultiDaoRecordSet::Open(int nOpenType, LPCTSTR lpszSQL, int nOptions)
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
CDaoRecordset::Open(nOpenType, lpszSQL, nOptions);
}
else
{
pParam1 = (void *)&nOpenType;
pParam2 = (void *)lpszSQL;
pParam3 = (void *)&nOptions;
AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoOpen, (LPARAM)this);
}
}

void CMultiDaoRecordSet::Close()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
CDaoRecordset::Close();
}
else
{
AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoClose, (LPARAM)this);
}
}

BOOL CMultiDaoRecordSet::IsOpen()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
return CDaoRecordset::IsOpen();
}
else
{
return (BOOL)AfxGetMainWnd()->
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoIsOpen, (LPARAM)this);
}
}

BOOL CMultiDaoRecordSet::IsBOF()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
return CDaoRecordset::IsBOF();
}
else
{
return (BOOL)AfxGetMainWnd()->
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoIsBOF, (LPARAM)this);
}
}

BOOL CMultiDaoRecordSet::IsEOF()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
return CDaoRecordset::IsEOF();
}
else
{
return (BOOL)AfxGetMainWnd()->
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoIsEOF, (LPARAM)this);
}
}

BOOL CMultiDaoRecordSet::IsDeleted()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
return CDaoRecordset::IsDeleted();
}
else
{
return (BOOL)AfxGetMainWnd()->
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoIsDeleted, (LPARAM)this);
}
}

BOOL CMultiDaoRecordSet::IsFieldDirty(void* pv)
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
return CDaoRecordset::IsFieldDirty(pv);
}
else
{
pParam1 = pv;
return (BOOL)AfxGetMainWnd()->
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoIsFieldDirty, (LPARAM)this);
}
}

BOOL CMultiDaoRecordSet::IsFieldNull(void* pv)
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
return CDaoRecordset::IsFieldNull(pv);
}
else
{
pParam1 = pv;
return (BOOL)AfxGetMainWnd()->
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoIsFieldNull, (LPARAM)this);
}
}

BOOL CMultiDaoRecordSet::IsFieldNullable(void* pv)
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
return CDaoRecordset::IsFieldNullable(pv);
}
else
{
pParam1 = pv;
return (BOOL)AfxGetMainWnd()->
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoIsFieldNullable, (LPARAM)this);
}
}

CString CMultiDaoRecordSet::GetName()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
return CDaoRecordset::GetName();
}
else
{
return (CString)*(CString *)AfxGetMainWnd()->
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoGetName, (LPARAM)this);
}
}

short CMultiDaoRecordSet::GetType()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
return CDaoRecordset::GetType();
}
else
{
return (short)AfxGetMainWnd()->
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoGetType, (LPARAM)this);
}
}

short CMultiDaoRecordSet::GetEditMode()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
return CDaoRecordset::GetEditMode();
}
else
{
return (short)AfxGetMainWnd()->
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoGetEditMode, (LPARAM)this);
}
}

CString CMultiDaoRecordSet::GetSQL()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
return CDaoRecordset::GetSQL();
}
else
{
return (CString)*(CString *)AfxGetMainWnd()->
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoGetSQL, (LPARAM)this);
}
}

COleVariant CMultiDaoRecordSet::GetLastModifiedBookmark()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
return CDaoRecordset::GetLastModifiedBookmark();
}
else
{
return (COleVariant)AfxGetMainWnd()->
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoGetLastModifiedBookmark, (LPARAM)this);
}
}

long CMultiDaoRecordSet::GetRecordCount()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
return CDaoRecordset::GetRecordCount();
}
else
{
return (long)AfxGetMainWnd()->
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoGetRecordCount, (LPARAM)this);
}
}

void CMultiDaoRecordSet::MoveNext()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
CDaoRecordset::MoveNext();
}
else
{
AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoMoveNext,
(LPARAM)this);
}
}

void CMultiDaoRecordSet::MovePrev()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
CDaoRecordset::MovePrev();
}
else
{
AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoMovePrev,
(LPARAM)this);
}
}

void CMultiDaoRecordSet::MoveFirst()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
CDaoRecordset::MoveFirst();
}
else
{
AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoMoveFirst,
(LPARAM)this);
}
}

void CMultiDaoRecordSet::MoveLast()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
CDaoRecordset::MoveLast();
}
else
{
AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoMoveLast,
(LPARAM)this);
}
}

void CMultiDaoRecordSet::Move(long lRows)
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
CDaoRecordset::Move(lRows);
}
else
{
pParam1 = (void *)&lRows;
AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoMove,
(LPARAM)this);
}
}

BOOL CMultiDaoRecordSet::FindNext(LPCTSTR lpszFilter)
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
return CDaoRecordset::FindNext(lpszFilter);
}
else
{
pParam1 = (void *)lpszFilter;
return (BOOL)AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoFindNext,
(LPARAM)this);
}
}

BOOL CMultiDaoRecordSet::FindPrev(LPCTSTR lpszFilter)
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
return CDaoRecordset::FindPrev(lpszFilter);
}
else
{
pParam1 = (void *)lpszFilter;
return (BOOL)AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoFindPrev,
(LPARAM)this);
}
}

BOOL CMultiDaoRecordSet::FindFirst(LPCTSTR lpszFilter)
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
return CDaoRecordset::FindFirst(lpszFilter);
}
else
{
pParam1 = (void *)lpszFilter;
return (BOOL)AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoFindFirst,
(LPARAM)this);
}
}

BOOL CMultiDaoRecordSet::FindLast(LPCTSTR lpszFilter)
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
return CDaoRecordset::FindLast(lpszFilter);
}
else
{
pParam1 = (void *)lpszFilter;
return (BOOL)AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoFindLast,
(LPARAM)this);
}
}

BOOL CMultiDaoRecordSet::Find(long lFindType, LPCTSTR lpszFilter)
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
return CDaoRecordset::Find(lFindType, lpszFilter);
}
else
{
pParam1 = (void *)&lFindType;
pParam2 = (void *)lpszFilter;
return (BOOL)AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoFind,
(LPARAM)this);
}
}

COleVariant CMultiDaoRecordSet::GetBookmark()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
return CDaoRecordset::GetBookmark();
}
else
{
return AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoGetBookmark,
(LPARAM)this);
}
}

void CMultiDaoRecordSet::SetBookmark(COleVariant varBookmark)
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
CDaoRecordset::SetBookmark(varBookmark);
}
else
{
pParam1 = (void *)&varBookmark;
AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoSetBookmark,
(LPARAM)this);
}
}

void CMultiDaoRecordSet::AddNew()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
CDaoRecordset::AddNew();
}
else
{
AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoAddNew,
(LPARAM)this);
}
}

void CMultiDaoRecordSet::Edit()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
CDaoRecordset::Edit();
}
else
{
AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoEdit,
(LPARAM)this);
}
}

void CMultiDaoRecordSet::Update()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
CDaoRecordset::Update();
}
else
{
AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoUpdate,
(LPARAM)this);
}
}

void CMultiDaoRecordSet::Delete()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
CDaoRecordset::Delete();
}
else
{
AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoDelete,
(LPARAM)this);
}
}

void CMultiDaoRecordSet::CancelUpdate()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
CDaoRecordset::CancelUpdate();
}
else
{
AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoCancelUpdate,
(LPARAM)this);
}
}

void CMultiDaoRecordSet::Requery()
{
CMYApp *pApp = ((CMYApp *)AfxGetApp());
if (pApp->pGUIThread == AfxGetThread())
{
CDaoRecordset::Requery();
}
else
{
AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoRequery,
(LPARAM)this);
}
}

and, Multidaorecordset.h


#if !defined(AFX_MULTIDAORECORDSET_H__BECC8DC3_A967_11D2_BA4C_006097808646__INCLUDED_)
#define AFX_MULTIDAORECORDSET_H__BECC8DC3_A967_11D2_BA4C_006097808646__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
// MultiDaoRecordSet.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CMultiDaoRecordSet DAO recordset

enum {
MultiDaoOpen,
MultiDaoClose,
MultiDaoIsOpen,
MultiDaoIsBOF,
MultiDaoIsEOF,
MultiDaoIsDeleted,
MultiDaoIsFieldDirty,
MultiDaoIsFieldNull,
MultiDaoIsFieldNullable,
MultiDaoGetName,
MultiDaoGetType,
MultiDaoGetSQL,
MultiDaoGetEditMode,
MultiDaoGetLastModifiedBookmark,
MultiDaoGetRecordCount,
MultiDaoMoveNext,
MultiDaoMovePrev,
MultiDaoMoveFirst,
MultiDaoMoveLast,
MultiDaoMove,
MultiDaoFindNext,
MultiDaoFindPrev,
MultiDaoFindFirst,
MultiDaoFindLast,
MultiDaoFind,
MultiDaoGetBookmark,
MultiDaoSetBookmark,
MultiDaoAddNew,
MultiDaoEdit,
MultiDaoUpdate,
MultiDaoDelete,
MultiDaoCancelUpdate,
MultiDaoRequery,
};

#ifdef MULTIDAORECORDSET_CPP
CString cDAOMessageArray[] =
{
“MultiDaoOpen”,
“MultiDaoClose”,
“MultiDaoIsOpen”,
“MultiDaoIsBOF”,
“MultiDaoIsEOF”,
“MultiDaoIsDeleted”,
“MultiDaoIsFieldDirty”,
“MultiDaoIsFieldNull”,
“MultiDaoIsFieldNullable”,
“MultiDaoGetName”,
“MultiDaoGetType”,
“MultiDaoGetSQL”,
“MultiDaoGetEditMode”,
“MultiDaoGetLastModifiedBookmark”,
“MultiDaoGetRecordCount”,
“MultiDaoMoveNext”,
“MultiDaoMovePrev”,
“MultiDaoMoveFirst”,
“MultiDaoMoveLast”,
“MultiDaoMove”,
“MultiDaoFindNext”,
“MultiDaoFindPrev”,
“MultiDaoFindFirst”,
“MultiDaoFindLast”,
“MultiDaoFind”,
“MultiDaoGetBookmark”,
“MultiDaoSetBookmark”,
“MultiDaoAddNew”,
“MultiDaoEdit”,
“MultiDaoUpdate”,
“MultiDaoDelete”,
“MultiDaoCancelUpdate”,
“MultiDaoRequery”,
};
#else
extern CString cDAOMessageArray[];
#endif

class CMultiDaoRecordSet : public CDaoRecordset
{
public:

//jtm
//FORCE user to pass database…
CMultiDaoRecordSet(CDaoDatabase* pDatabase);

~CMultiDaoRecordSet();

DECLARE_DYNAMIC(CMultiDaoRecordSet)

// Field/Param Data
//{{AFX_FIELD(CMultiDaoRecordSet, CDaoRecordset)
//}}AFX_FIELD

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMultiDaoRecordSet)
public:
virtual CString GetDefaultDBName(); // Default database name
//}}AFX_VIRTUAL

// Implementation
//jtm
//redefined public functions
//Note: I am only redefining SOME
//of the public functions this is NOT
//a FULL implementation !!!!!!
public:
//jtm — only supporting this open…..
virtual void Open(int nOpenType = AFX_DAO_USE_DEFAULT_TYPE,
LPCTSTR lpszSQL = NULL, int nOptions = 0);
virtual void Close();
BOOL IsOpen();
BOOL IsBOF();
BOOL IsEOF();
BOOL IsDeleted();
BOOL IsFieldDirty(void* pv);
BOOL IsFieldNull(void* pv);
BOOL IsFieldNullable(void* pv);
CString GetName();
short GetType();
short GetEditMode();
CString GetSQL();
COleVariant GetLastModifiedBookmark();
long GetRecordCount();
void MoveNext();
void MovePrev();
void MoveFirst();
void MoveLast();
virtual void Move(long lRows);
BOOL FindNext(LPCTSTR lpszFilter);
BOOL FindPrev(LPCTSTR lpszFilter);
BOOL FindFirst(LPCTSTR lpszFilter);
BOOL FindLast(LPCTSTR lpszFilter);
virtual BOOL Find(long lFindType, LPCTSTR lpszFilter);
COleVariant GetBookmark();
void SetBookmark(COleVariant varBookmark);
virtual void AddNew();
virtual void Edit();
virtual void Update();
virtual void Delete();
virtual void CancelUpdate();
virtual void Requery();

void *pParam1;
void *pParam2;
void *pParam3;
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_MULTIDAORECORDSET_H__BECC8DC3_A967_11D2_BA4C_006097808646__INCLUDED_)

The only version of the constructor that is currently supported is the one that takes the database pointer as a parameter.


As stated earlier, you may remove the debugging strings and trace calls without affecting the operation of the code. Do not make calls directly to the CDaoRecordset base class, or you will have problems.


There is a slight slowdown in multithreaded operations using this class. I believe it is negligible.


There is a potential problem with deadlock in mutlithreaded apps using this class. Do not put a critical section around code in the GUI that will be waiting for another threads DAO operation to complete. Since the DAO calls are going through the GUI, this will result in deadlock.

Download source – 252 KB

More by Author

Get the Free Newsletter!

Subscribe to Data Insider for top news, trends & analysis

Must Read