Figure 3   DBDAOINT.H Excerpt
 DECLARE_INTERFACE_(_DAODBEngine, _DAO)
{
  STDMETHOD(get_Version)         (THIS_ BSTR FAR* pbstr) PURE;
  STDMETHOD(get_IniPath)         (THIS_ BSTR FAR* pbstr) PURE;
  STDMETHOD(put_IniPath)         (THIS_ BSTR path) PURE;
  STDMETHOD(put_DefaultUser)     (THIS_ BSTR user) PURE;
  STDMETHOD(put_DefaultPassword) (THIS_ BSTR pw) PURE;
  STDMETHOD(get_LoginTimeout)    (THIS_ short FAR* ps) PURE;
  STDMETHOD(put_LoginTimeout)    (THIS_ short Timeout) PURE;
  STDMETHOD(get_Workspaces)      (THIS_ DAOWorkspaces FAR* FAR* ppworks) PURE;
  STDMETHOD(get_Errors)          (THIS_ DAOErrors FAR* FAR* pperrs) PURE;
  STDMETHOD(CompactDatabase)     (THIS_ BSTR SrcName, BSTR DstName,
     VARIANT DstConnect, VARIANT Options, VARIANT SrcConnect) PURE;
  STDMETHOD(CreateWorkspace)     (THIS_ BSTR Name, BSTR UserName, 
     BSTR Password, DAOWorkspace FAR* FAR* ppwrk) PURE;
  STDMETHOD(OpenDatabase)        (THIS_ BSTR Name, VARIANT Exclusive, 
     VARIANT ReadOnly, VARIANT Connect, DAODatabase FAR* FAR* ppdb) PURE;
  STDMETHOD(CreateDatabase)      (THIS_ BSTR Name, BSTR Connect, 
     VARIANT Option, DAODatabase FAR* FAR* ppdb) PURE;
};

Figure 6   CDAOEMPDoc

 class CDAOEMPDoc : public COleDocument {
protected:
   CDAOEMPDoc();
   DECLARE_DYNCREATE(CDAOEMPDoc)

   CdbDBEngine    m_cDBEngine;
   CdbDatabase    m_cEmpDatabase;
   CdbBookmark    m_cLastGoodRecord;

public:
   CdbRecordset   m_cEmpRecordSet;
   BOOL           m_bConnected;
   BOOL ConnectToDatabase(); //Opens a database 

   // ClassWizard generated virtual function overrides
   //{{AFX_VIRTUAL(CDAOEMPDoc)
   public:
   virtual BOOL OnNewDocument();
   //}}AFX_VIRTUAL

public:
   virtual ~CDAOEMPDoc();
   virtual void Serialize(CArchive& ar);   // overridden for document i/o
   BOOL OKToMove();

   void UpdateEmpRec(long m_nEmpNum, LPCTSTR lpszFirstName, 
                     LPCTSTR lpszHomePhone, LPCTSTR lpszLastName, 
                     LPCTSTR lpszNotes, DATE HireDate);

protected:
   //{{AFX_MSG(CDAOEMPDoc)
   afx_msg void OnEditNext();
   afx_msg void OnEditPrevious();
   afx_msg void OnEditAdd();
   afx_msg void OnEditDelete();
   afx_msg void OnUpdateEditDelete(CCmdUI* pCmdUI);
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};

Figure 7   CDAOEMPDoc::ConnectToDatabase

 //////////////////
// Create new document
//
BOOL CDAOEMPDoc::OnNewDocument()
{
   if (!COleDocument::OnNewDocument())
      return FALSE;
   
   if (!ConnectToDatabase()) {
      m_bConnected = FALSE;
      return FALSE;
   }

   m_bConnected = TRUE;
   return TRUE;
}

////////////////
// When the document is created, connect to the database and open the
// Employee recordset.
//
BOOL CDAOEMPDoc::ConnectToDatabase()
{
   // Ask the user for the name of the EMPLOYEE database
   CFileDialog cOpenFile( TRUE,
                    _T("MDB"), 
                    _T("employee.mdb"), OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, 
                    (_T("Access Files (*.mdb) | *.mdb ||")));

   // Get the location of the database (assume it's the Employee example)
   cOpenFile.DoModal();

   //Open the database and the recordset
   try {
      // NOTE: Using default collection rather than Workspaces.Items
      m_cEmpDatabase = m_cDBEngine.OpenDatabase(cOpenFile.m_ofn.lpstrFile);
      m_cEmpRecordSet = m_cEmpDatabase.OpenRecordset(_T("Employees"), dbOpenTable);

   } catch (CdbException e) {
      CdbLastOLEError exError;
      TCHAR szBuf[256];

      wsprintf(szBuf, _T("Error %d : %s\n"), DBERR(e.m_hr), 
               (LPCTSTR) exError.GetDescription());
      AfxMessageBox(szBuf);
      return (FALSE);
   }
   return TRUE;;
}

Figure 8   CDAOEMPView::OnUpdate

 void CDAOEMPView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) 
{
   CDAOEMPDoc* pDoc = GetDocument();
   ASSERT_VALID(pDoc);

   try {
              // Employee number/ID
      m_nEmpNum = 
         VTOLONG(pDoc->m_cEmpRecordSet.Fields[EMP_EMPLOYEE_ID].GetValue());
      
      // Convert the variant strings into CStrings
      VarToCStr(&m_strFirstName, 
                &pDoc->m_cEmpRecordSet.Fields[EMP_FIRST_NAME].GetValue());
      VarToCStr(&m_strHomePhone, 
                &pDoc->m_cEmpRecordSet.Fields[EMP_HOME_PHONE].GetValue());
      VarToCStr(&m_strLastName,  
                &pDoc->m_cEmpRecordSet.Fields[EMP_LAST_NAME].GetValue());
      VarToCStr(&m_strNotes,     
                &pDoc->m_cEmpRecordSet.Fields[EMP_NOTES].GetValue());

      // Hire date is a COleDateTime 
      m_HireDate = pDoc->m_cEmpRecordSet.Fields[EMP_HIRE_DATE].GetValue();

   } catch (CdbException e) {
      CdbLastOLEError exError;
      TCHAR szBuf[256];

      wsprintf(szBuf, _T("Error %d : %s\n"), 
               DBERR(e.m_hr), (LPCTSTR) exError.GetDescription());
      AfxMessageBox(szBuf);
   }
   UpdateData(FALSE);   // Invoke DDX (copy member data to form controls)
   Invalidate();        // Tell Windows to repaint
}

Figure 9   CDAOEMPDoc::UpdateEmpRec

 ////////////////
// Update the current record
//
void CDAOEMPDoc::UpdateEmpRec(long m_nEmpNum, LPCTSTR lpszFirstName, 
                              LPCTSTR lpszHomePhone, LPCTSTR lpszLastName, 
                              LPCTSTR lpszNotes, DATE HireDate)
{
   // Convert the date to a dbVariant
   COleVariant cdbHireDate;
   cdbHireDate.date = HireDate;
   cdbHireDate.vt = VT_DATE;

   try {
      // The recordset must be in edit mode
      if(m_cEmpRecordSet.GetEditMode() == dbEditNone)
         m_cEmpRecordSet.Edit();

      m_cEmpRecordSet.SetField(EMP_FIRST_NAME, 
                               COleVariant(lpszFirstName, VT_BSTRT));
      m_cEmpRecordSet.SetField(EMP_HOME_PHONE, 
                               COleVariant(lpszHomePhone, VT_BSTRT));
      m_cEmpRecordSet.SetField(EMP_LAST_NAME, 
                               COleVariant(lpszLastName, VT_BSTRT));
      m_cEmpRecordSet.SetField(EMP_NOTES, 
                               COleVariant(lpszNotes, VT_BSTRT));
      m_cEmpRecordSet.SetField(EMP_HIRE_DATE, cdbHireDate);

      // Commit the changes
      m_cEmpRecordSet.Update();

      // Return to the edited record
      CdbBookmark cBookmark = m_cEmpRecordSet.GetLastModified();
      m_cEmpRecordSet.SetBookmark(cBookmark);

   } catch (CdbException e) {
      CdbLastOLEError exError;
      TCHAR szBuf[256];

      wsprintf(szBuf, _T("Error 0x%lx : %s\n"), e.m_hr, 
               (LPCTSTR)exError.GetDescription());
      AfxMessageBox(szBuf);
   }
}

Figure 11   DAORDDLG.CPP

 ////////////////
// When the Execute button is pressed, run through the selected methods 
// and perform each, reporting an approximate execution time and listing
// the first field returned (only if it's a string or numeric)
//
void CDAOReadDlg::OnExecute() 
{
       DWORD             dwStart, dwEnd;
       DWORD             dwDuration;
       LONG              lIndex;
       COleVariant       dbVar;
       CListBox          *pList = ((CListBox *)(GetDlgItem(IDC_ROWSLIST)));

       // Update the parameters
       if (!UpdateData(TRUE))
              return;

       // This could take a while, 
       CWaitCursor wait;

       // Clear the list
       pList->ResetContent();


       // Following is a series of database table retrieves using variations
       // on the Recordset object. This isn't a great programming example,
       // but it shows each method clearly.
       
       // Execute each the requested read methods, recording a time for each
       try {
           // Open the database
           (GetDlgItem(IDC_DATABASENAME))->GetWindowText(m_strDatabase);
           m_dbDatabase = m_dbEngine.OpenDatabase( m_strDatabase, m_bExclusive,             
                                                   m_bReadOnly, m_strConnect);

           // Run through the selected methods, recording each time
           if (m_bOpenRecordset) {
              // Recordset as a Table
              if (m_bTable) {
                 CdbRecordset RSetTable; 
                 pList->AddString(_T("Recordset/Table"));
                 pList->AddString(_T("=========================="));

                 dwStart = timeGetTime();
              
                 // Open recordset on a table
                 try {
                     RSetTable = m_dbDatabase. OpenRecordset(m_strTableQuery, 
                                                             dbOpenTable);
                     } catch(CdbException dbError) {

                     // If the object was not found, assume it was something 
                     // not openable as a "table"
                     //
                     if (dbError.m_hr != E_DAO_ObjectNotFound)
                        throw dbError;
                        }
                        // Check that recordset was created
                        if (RSetTable.Exists()) {
                           // Get requested number of records
                           for (lIndex = 0L;
                                !RSetTable.GetEOF() && lIndex < m_lNumRows;          
                                lIndex++) {
                                // Get the first returned column
                                dbVar = RSetTable.GetField(0L);
                                AddFieldToList(lIndex, &dbVar);
                                RSetTable.MoveNext();
                                }
       
                           // Report the estimated execute time (in milliseconds)
                           dwEnd = timeGetTime();
                           dwDuration = dwEnd - dwStart;
                           DisplayQueryTime(IDC_TABLETIME, IDC_UNITTABLE,   
                                            dwDuration);
                       } else {
                           (GetDlgItem(IDC_TABLETIME))-         
                                       >SetWindowText(_T("ERROR"));}
                     }

                     // Recordset as a Dynaset
                 if (m_bDynaSet) {
                    CdbRecordset RSetDyna; 
                    pList->AddString(_T("Recordset/Dynaset"));
                    pList->AddString(_T("=========================="));

                    dwStart = timeGetTime();
              
                    // Open recordset on as a Dynaset
                    RSetDyna = m_dbDatabase.OpenRecordset(m_strTableQuery, 
                                                          dbOpenDynaset);

                    // Check that recordset was created
                    if (RSetDyna.Exists()) {

                       // Get requested number of records
                       for (lIndex = 0L; 
                            !RSetDyna.GetEOF() && lIndex < m_lNumRows; lIndex++) {

                            // Get the first returned column
                            dbVar = RSetDyna.GetField(0L);
                            AddFieldToList(lIndex, &dbVar);
                            RSetDyna.MoveNext();
                            }
       
                       // Report the estimated execution time (in milliseconds)
                       dwEnd = timeGetTime();
                       dwDuration = dwEnd - dwStart;
                       DisplayQueryTime(IDC_DYNASETTIME,IDC_UNITDYNA,dwDuration);

                    } else {
                       (GetDlgItem(IDC_DYNASETTIME))->SetWindowText(_T("ERROR"));
                       }
                 }

                 // Recordset as a Dynaset with caching
                 if (m_bDynaSet && m_bDynaSetCache) {
                    CdbRecordset       RSetDynaCache; 
                    CdbBookmark              DynaSetBookmark;

                    pList->AddString(_T("Recordset/Dynaset cached"));
                    pList->AddString(_T("=========================="));

                    dwStart = timeGetTime();
              
                    // Open recordset on as a Dynaset
                    RSetDynaCache = 
                      m_dbDatabase.OpenRecordset(m_strTableQuery, dbOpenDynaset);
                            
                    // Check that recordset was created
                    if (RSetDynaCache.Exists()) {

                       // Setthecachesizetovaluesuppliedbyuser.
                       RSetDynaCache.SetCacheSize(m_lCacheSize);
                       RSetDynaCache.FillCache(); 
                                   
                       // Get requested number of records
                       for (lIndex = 0L; 
                       !RSetDynaCache.GetEOF() && lIndex < m_lNumRows; lIndex++) {

                           // Get the first returned column
                           dbVar = RSetDynaCache.GetField(0L);
                           AddFieldToList(lIndex, &dbVar);
                           RSetDynaCache.MoveNext();

                           //Watch for cache run-out
                           if (lIndex % m_lCacheSize) {       
                              DynaSetBookmark = RSetDynaCache.GetBookmark();
                              RSetDynaCache.SetCacheStart(DynaSetBookmark);
                              RSetDynaCache.FillCache(); 
                            }
                        }
       
                        // Report the estimated execute time (in milliseconds)
                        dwEnd = timeGetTime();
                        dwDuration = dwEnd - dwStart;
                        DisplayQueryTime(IDC_CACHETIME,IDC_UNITCACHE,dwDuration);
                    } else {
                        (GetDlgItem(IDC_CACHETIME))->SetWindowText(_T("ERROR"));
                        }
                     }                     

                     // Recordset as a Snapshot
                     if (m_bSnapshot) {
                        // Bidirectional Snapshot
                        CdbRecordset RSetSnap; 
                        pList->AddString(_T("Recordset/Snapshot"));
                        pList->AddString(_T("=========================="));

                        dwStart = timeGetTime();
              
                        // Open recordset on as a Snapshot
                        RSetSnap =
                            m_dbDatabase.OpenRecordset(m_strTableQuery,        
                                                       dbOpenSnapshot);

                        // Check that recordset was created
                        if (RSetSnap.Exists()) {

                           // Get requested number of records
                           for (lIndex = 0L; 
                               !RSetSnap.GetEOF() && lIndex < m_lNumRows;         
                               lIndex++) {
                               // Get the first returned column
                               dbVar = RSetSnap.GetField(0L);
                               AddFieldToList(lIndex, &dbVar);
                               RSetSnap.MoveNext();
                               }
       
                           // Report the estimated execute time (in milliseconds)
                           dwEnd = timeGetTime();
                           dwDuration = dwEnd - dwStart;
                           DisplayQueryTime(IDC_SNAPSHOTTIME, IDC_UNITSNAP, 
                                            dwDuration);

                        } else {
                             (GetDlgItem(IDC_SNAPSHOTTIME))-           
                                         >SetWindowText(_T("ERROR"));
                             }

                        // Forward Only snapshot
                        CdbRecordset RSetSnapForward; 
                        pList->AddString(_T("Recordset/Snapshot forward only"));
                        pList->AddString(_T("=========================="));

                        dwStart = timeGetTime();
              
                        // Open recordset on as a Forward only Snapshot
                        RSetSnapForward =  
                            m_dbDatabase.OpenRecordset(m_strTableQuery, 
                                                  dbOpenSnapshot, dbForwardOnly);

                        // Check that recordset was created
                        if (RSetSnapForward.Exists()) {
                           // Get requested number of records
                           for (lIndex = 0L; 
                                !RSetSnapForward.GetEOF() && lIndex < m_lNumRows; 
                                lIndex++){

                                // Get the first returned column
                                dbVar = RSetSnapForward.GetField(0L);
                                AddFieldToList(lIndex, &dbVar);
                                RSetSnapForward.MoveNext();
                                }
       
                           // Report the estimated execute time (in milliseconds)
                           dwEnd = timeGetTime();
                           dwDuration = dwEnd - dwStart;
                           DisplayQueryTime(IDC_FORWARDTIME, IDC_UNITSNAPF, 
                                            dwDuration);
                        } else {
                            (GetDlgItem(IDC_FORWARDTIME))-
                            >SetWindowText(_T("ERROR"));
                            }

                        }

                     // Getrows
                     if (m_bGetRows) {
                        CdbRecordset     RSetGetRows; 
                        COleVariant      cRows; // Returned rows (in SafeArray)
                        LONG             lActual;
                      LONG         lGetRowsIndex[2];

                      pList->AddString(_T("GetRows"));
                      pList->AddString(_T("=========================="));

                      dwStart = timeGetTime();
              
                      // Open recordset on as a Forward only Snapshot
                      RSetGetRows = m_dbDatabase.OpenRecordset(m_strTableQuery, 
                                                 dbOpenSnapshot, dbForwardOnly);

                      // Check that recordset was created
                      if (RSetGetRows.Exists()) {
                         cRows = RSetGetRows.GetRows(m_lNumRows); 
                                                          // get requested rows
                         // Find out how many records were actually retrieved
                         SafeArrayGetUBound(cRows.parray, 1, &lActual);
                         if (lActual > m_lNumRows)
                            lActual = m_lNumRows;

                         lGetRowsIndex[0] = 0L;

                         for (lGetRowsIndex[1] = 0; 
                             lGetRowsIndex[1] < lActual; lGetRowsIndex[1]++) {
                     
                             // Use OLE safe array function to access fields
                             SafeArrayGetElement(cRows.parray,
                                                 &lGetRowsIndex[0],&dbVar);
                             AddFieldToList(lGetRowsIndex[1], &dbVar);
                             }
       
                         // Report the estimated execute time (in millisecs)
                         dwEnd = timeGetTime();
                         dwDuration = dwEnd - dwStart;
                         DisplayQueryTime(IDC_GETROWSTIME, IDC_UNITGETROWS, 
                                          dwDuration);

                      } else {
                           (GetDlgItem(IDC_GETROWSTIME))-
                                       >SetWindowText(_T("ERROR"));
                            }
                       }                                                 
                   }

              // Disconnect the database

              } catch (CdbException dbError) {
                  CdbLastOLEError exError;
                  TCHAR szBuf[256];

                  wsprintf(szBuf, _T("Error %d : %s\n"), DBERR(dbError.m_hr), 
                           (LPCTSTR) exError.GetDescription());
                  AfxMessageBox(szBuf);
                  }
 }

Figure 12   Using Table-type Recordsets

 // Open recordset on a table
CdbRecordset RSetTable; . . . try {
RSetTable = m_dbDatabase.
OpenRecordset(m_strTableQuery, dbOpenTable);
} catch(CdbException dbError) {
// If the object was not found, assume it
// was something not openable as a "table"
if(dbError.m_hr != E_DAO_ObjectNotFound)
throw dbError;
}
// Check that recordset was created
if (RSetTable.Exists()) {
// Get requested number of records
for (lIndex = 0L;
!RSetTable.GetEOF() && lIndex < m_lNumRows;
lIndex++) {
// Get the first returned column
dbVar = RSetTable.GetField(0L);
AddFieldToList(lIndex, &dbVar);
RSetTable.MoveNext();
}
}

Figure 3   DBDAOINT.H Excerpt

Figure 13   Using Dynaset Recordsets

 // Open recordset on as a Dynaset
CdbRecordset RSetDyna; . . . RSetDyna = m_dbDatabase.
OpenRecordset(m_strTableQuery, dbOpenDynaset);

// Check that recordset was created
if (RSetDyna.Exists()) {
// Get requested number of records
for (lIndex = 0L;
!RSetDyna.GetEOF() && lIndex < m_lNumRows;
lIndex++) {
// Get the first returned column
dbVar = RSetDyna.GetField(0L);
AddFieldToList(lIndex, &dbVar);
RSetDyna.MoveNext();
}
}

Figure 3   DBDAOINT.H Excerpt

Figure 14   Using Forward-only Snapshots

 // Open recordset on as a Forward-only Snapshot
CdbRecordset RSetSnapForward; . . . RSetSnapForward = m_dbDatabase.
OpenRecordset(m_strTableQuery, dbOpenSnapshot,
dbForwardOnly);

// Check that recordset was created if (RSetSnapForward.Exists()) { // Get requested number of records
for (lIndex = 0L;
!RSetSnapForward.GetEOF() && lIndex < m_lNumRows;
lIndex++) {
// Get the first returned column
dbVar = RSetSnapForward.GetField(0L);
AddFieldToList(lIndex, &dbVar);
RSetSnapForward.MoveNext();
}
}

Figure 15   CGetRowsDlg::DoGetRows

 ////////////////////////////////////////////////////////////////
// (From DAO GETROWS sample)
// Perform standard GetRows against the Employee table
//
void CGetRowsDlg::DoGetRows() 
{
   COleVariant    cRows;
   COleVariant    varField;
   CString        strLBRow;
   TCHAR          szId[16];
   LONG           lNumRecords;
   LONG           lIndex[2];
   HRESULT        hResult;
   CListBox       *pListBox = (CListBox *)GetDlgItem(IDD_GETROWSLIST);

   // Perform GetRows on Employee table
   // This GetRows uses VARIANTS
   // Arbitrarily get MAX_EMP_REC rows
   cRows = m_cEmpRecordSet.GetRows(MAX_EMP_REC); 

   // Find out how many records were actually retrieved
   // (NOTE: SafeArrays are 1-based)
   //
   SafeArrayGetUBound(cRows.parray, 2, &lNumRecords);

   // Clear the listbox
   pListBox->ResetContent();
   
   for (lIndex[1] = 0; lIndex[1] <= lNumRecords; lIndex[1]++) {
      strLBRow.Empty();    // Clear the string

      lIndex[0] = EMP_ID;  // Employee ID
         
      // Use OLE safe array function to access fields
      hResult = SafeArrayGetElement(cRows.parray, &lIndex[0], &varField);

      // Watch out for bum variants
      if(FAILED(hResult))
         break;

      if(varField.vt == VT_I4) {    // Gotta be a long 
         wsprintf(szId, _T("%d,  "), varField.iVal);
      } else {
         lstrcpy(szId, _T("Unexpected Data Type"));
      }

      strLBRow += (LPCTSTR)szId;

      // Get last name
      lIndex[0] = EMP_LNAME;
      SafeArrayGetElement(cRows.parray, &lIndex[0], &varField);
      strLBRow += (LPCTSTR)varField.bstrVal;

      // Get first name
      strLBRow += _T(", ");
      lIndex[0] = EMP_FNAME;
      SafeArrayGetElement(cRows.parray, &lIndex[0], &varField);
      strLBRow += (LPCTSTR)varField.bstrVal;

      pListBox->AddString(strLBRow);
   }
}

Figure 16   CGetRowsDlg::DoGetRowsEx

 ////////////////
// Perform C++ GetRowsEx against the Employee table
//
void CGetRowsDlg::DoGetRowsEx() 
{
   LPEMP       pEmpRows = new EMP[MAX_EMP_REC];
   CListBox    *pListBox = (CListBox *)GetDlgItem(IDD_GETROWSLISTEX);
   CString     strLBRow;
   TCHAR       szId[16];
   LONG        lNumRecords;
   LONG        lCount;
   TCHAR       pBuf[MAX_EMP_REC * 15];    // allow average of 15 chars/name

   // Perform GetRows on Employee table
   // This GetRows uses a specific C++ structure
   try {
      lNumRecords = m_cEmpRecordSet.GetRowsEx(pEmpRows, sizeof(EMP),
                       &Bindings[0], sizeof(Bindings) / sizeof(DAORSETBINDING),
                       pBuf, sizeof(pBuf),
                       MAX_EMP_REC); //arbitrarily get MAX_EMP_REC rows

   } catch (CdbException e) {
      // Differentiate between GetRowsEx Errors and other CdbExceptions
      // see defines in DAOGETRW.H
      if( e.m_hr == E_ROWTOOSHORT ||
          e.m_hr == E_BADBINDINFO ||         
          e.m_hr == E_COLUMNUNAVAILABLE ) {
         AfxMessageBox(_T("Error in GetRowsEx call."));
      } else {
         AfxMessageBox(_T("General CdbException"));
      }
      delete [] pEmpRows;
      return;
   }

   // Step through the returned rows 
   for (lCount = 0; lCount < lNumRecords; lCount++) {
      strLBRow.Empty();
      wsprintf(szId, _T("%d,  "), pEmpRows[lCount].lEmpId);
      strLBRow += szId;
      strLBRow += pEmpRows[lCount].strLastName;
      strLBRow += _T(", ");
      strLBRow += (LPCTSTR) pEmpRows[lCount].strFirstName;
      pListBox->AddString(strLBRow);
   }
   delete [] pEmpRows;
}

Figure 17   Using GetRowsEx

 ////////////////////////////////////////////////////////////////
// (From MSDEV\DAOSDK\SAMPLES\GETROWS)
// Structure for GetRowsEx
//
// structure to receive one row of data
typedef struct {
LONG lEmpId;
TCHAR *strLastName;
TCHAR strFirstName[20];
} EMP, *LPEMP; // Binding maps table fields to C struct members
DAORSETBINDING Bindings[] =
{
// { Index Type, Column, Type,
// Offset, Size }
{dbBindIndexINT, EMP_ID, dbBindI4,
offsetof(EMP,lEmpId), sizeof(LONG)},
{dbBindIndexINT, EMP_LNAME,dbBindLPSTRING,
offsetof(EMP,strLastName), sizeof(TCHAR *)},
{dbBindIndexINT, EMP_FNAME,dbBindSTRING,
offsetof(EMP,strFirstName), sizeof(TCHAR)*20 }
};

Figure 3   DBDAOINT.H Excerpt

Figure 19   SAMP.BAS

 Sub SetDate()
       dim db as database, rs as Recordset
   dim vBkmk as variant
   on error goto lblErrorHndlr
   set db = OpenDatabase("employees.mdb")
   set rs = db.OpenRecordset("employees", dbOpenDynaset)
   vBkmk = rs.bookmark
   rs.FindFirst "[Hire Date] < #10/10/1990#"
   do while rs.nomatch = False
     rs.edit
     rs![Hire Date] = #10/10/1990#
     rs.update
     rs.FindFirst "[Hire Date] < #10/10/1990#"
   loop
   rs.bookmark = vBkmk
   rs.Close
   db.Close
lblQuit:
   exit sub
lblErrorHndlr:
   if (dbengine.errors.count <> 0) then
     msgbox dbengine.Errors(0).Description
   else
     msgbox "Unexpected Error"
   endif
   resume lblQuit
end sub

Figure 20   SAMP.CPP

 void SetDate(void)
{
       CdbDBEngine dben;
       CdbDatabase db;
       CdbRecordset rs;
       CdbBookmark bk;
       COleDateTime dt;
       try {
              db = dben[0L].OpenDatabase(_T("employees.mdb"));
              rs = db.OpenRecordset(_T("employees"));
              bk = rs.GetBookmark();
              rs.FindFirst(_T("[Hire Date] < #10/10/1990#"));
              while (rs.GetNoMatch() == FALSE) {
                     rs.Edit();
                     rs.SetValue(_T("[Hire Date]"), COleDateTime(1990, 10, 10, 
                                                                 0, 0, 0));
                     rs.Update();
                     rs.FindFirst(_T("[Hire Date] < #10/10/1990#"));
              }
              rs.SetBookmark(bk);
              rs.Close();
              db.Close();

       } catch(CdbException e) {
              if (dben.Errors.GetCount() != 0)
                     MessageBox(NULL, dben.Errors[0L].GetDescription(), NULL, 
                                0);
              else
                     MessageBox(NULL, _T("Unexpected Error"), NULL, 0);
       }
}
Figure A   VARIANT Definition
 /*
 * Definition of VARIANT, from OAIDL.H
 */

typedef unsigned short VARTYPE;
typedef struct tagVARIANT VARIANT;

struct tagVARIANT {
   VARTYPE vt;
   WORD wReserved1;
   WORD wReserved2;
   WORD wReserved3;
   union {
      long          lVal;           /* VT_I4                */
      unsigned char bVal;           /* VT_UI1               */
      short         iVal;           /* VT_I2                */
      float         fltVal;         /* VT_R4                */
      double        dblVal;         /* VT_R8                */
      VARIANT_BOOL  bool;           /* VT_BOOL              */
      SCODE         scode;          /* VT_ERROR             */
      CY            cyVal;          /* VT_CY                */
      DATE          date;           /* VT_DATE              */
      BSTR          bstrVal;        /* VT_BSTR              */
      IUnknown      *punkVal;       /* VT_UNKNOWN           */
      IDispatch     *pdispVal;      /* VT_DISPATCH          */
      SAFEARRAY     *parray;        /* VT_ARRAY|*           */
      unsigned char *pbVal;         /* VT_BYREF|VT_UI1      */
      short         *piVal;         /* VT_BYREF|VT_I2       */
      long          *plVal;         /* VT_BYREF|VT_I4       */
      float         *pfltVal;       /* VT_BYREF|VT_R4       */
      double        *pdblVal;       /* VT_BYREF|VT_R8       */
      VARIANT_BOOL  *pbool;         /* VT_BYREF|VT_BOOL     */
      SCODE         *pscode;        /* VT_BYREF|VT_ERROR    */
      CY            *pcyVal;        /* VT_BYREF|VT_CY       */
      DATE          *pdate;         /* VT_BYREF|VT_DATE     */
      BSTR          *pbstrVal;      /* VT_BYREF|VT_BSTR     */
      IUnknown      **ppunkVal;     /* VT_BYREF|VT_UNKNOWN  */
      IDispatch     **ppdispVal;    /* VT_BYREF|VT_DISPATCH */
      SAFEARRAY     **pparray;      /* VT_BYREF|VT_ARRAY|*  */
      VARIANT       *pvarVal;       /* VT_BYREF|VT_VARIANT  */
      void          *byref;         /* Generic Bytes        */
   } u;
};