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%s\n",   
    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



Comments

  • Another little Bug

    Posted by Legacy on 08/12/2002 12:00am

    Originally posted by: Bent Kuhre

    In the function OnMultiDaoMessage in the switch statement MultiDaoGetLastModifiedBookmark: 
    
    

    case MultiDaoGetLastModifiedBookmark:
    cVar = pSet->GetLastModifiedBookmark();
    lRet = (LONG)&cVar;
    break;

    The result from the GetLastModifiedBookmark() function is stored in the variable cVar. A pointer to the cVar is returned as a result. This is the bug as cVar is a local variable. When OnMultiDaoMessage returns, cVar is deleted as it is out of scope. Sometime when the memory is not overwritten it works, but it depends of the operating system used and the applications running.

    The solution is the to have a COleVariant member variable in pSet (CMultiDaoRecordSet) and use it to store the result into.

    Thanks
    Bent Kuhre


    Reply
  • Win 2000

    Posted by Legacy on 05/14/2002 12:00am

    Originally posted by: new

    does not work on 2000, any idea?

    Reply
  • A few bugs....

    Posted by Legacy on 12/01/2000 12:00am

    Originally posted by: Mikael Aronsson

    This code works great, but there are a few bugs that needs to be fixed:

    In the FindNext, FindPrev and son methods you have:

    lRet = (LONG)pSet->FindLast(*(LPCTSTR*)pSet->pParam1);

    This does not work, the LPCTSTR pointers are incorrect and should of course be:

    lRet = (LONG)pSet->FindLast((LPCTSTR)pSet->pParam1);

    The second problem is the GetBookMark method:

    You return the pointer value (LONG) as a COleVariant when you should return the contents of the pointer, so it needs to be changed to something like this:

    LONG l = SendMessage( hMainWindowHandle, WM_MULTIDAOMESSAGE, MultiDaoGetBookmark, (LPARAM)this);
    COleVariant* pp = (COleVariant*) l;
    return( *pp);

    Thats it for now,
    Mikael

    Reply
  • DAO in Multithread

    Posted by Legacy on 04/19/1999 12:00am

    Originally posted by: youcef

    Is is possible to inherit from CDAORecordSet and just add a static member critical section like m_protect,
    since most of the DAO function are virtual we only need to put our own implementation which will simply call
    :
    
    m_protect.Lock();
    base Daofunction();
    m_protect.Unlock();

    this will ensure the thread safety and keep the access to the underlining database synchronised.


    Reply
  • A slightly better way

    Posted by Legacy on 02/03/1999 12:00am

    Originally posted by: Jeff Miller

    After this class got up and working, I thought of a slightly better way to do it.
    
    

    Really, the DAO should have its OWN thread. This way it will not cause slowdown problems in the GUI thread.

    If I have time, I will implement the class this way. It won't be soon, though.

    ---jeff

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Event Date: April 15, 2014 The ability to effectively set sales goals, assign quotas and territories, bring new people on board and quickly make adjustments to the sales force is often crucial to success--and to the field experience! But for sales operations leaders, managing the administrative processes, systems, data and various departments to get it all right can often be difficult, inefficient and manually intensive. Register for this webinar and learn how you can: Align sales goals, quotas and …

  • Corporate e-Learning technology has a long and diverse pedigree. As far back as the 1980s, companies were adopting computer-based training to supplement traditional classroom activities. More recently, rich web-based applications have added streaming audio and video, real-time collaboration and other new tools to the e-Learning mix. At the same time, the growing availability of informal learning tools--a category that includes everything from web searches to social media posts--are having a major impact on …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds