How to Use DAO in Visual C++ without MFC

If you ever used Data Access Objects (DAO) both in Visual Basic and Visual C++ with MFC, you must have noticed that in Visual Basic manipulating with Microsoft Access MDB files is a more enjoyable way of spending time than doing it in Visual C++ with MFC. And, you must have wondered whether a workaround exists to make work with MDB in Visual C++ so flexible as in Visual Basic.

To solve this problem, try to use in Visual C++ the same mechanism as it is used in Visual Basic. This mechanism assumes that you should work directly with type libraries. Unlike Visual Basic, which is more adapted for rapid application development and has its own methods for accessing to type libraries, in Visual C++ working with type libraries is more preferable through converting the content of the type library into C++ classes with the #import directive.

The best way to understand this is reading a sample source:

// daocpp.cpp
// Describe the full path to the dao360.dll file. Interface files
// dao360.tlh and dao360.tli will be generated automatically
// during compilation.
// Usually, using namespaces is highly recommended. But, in this
// simple example, it's not required (no_namespace). For more
// about the #import directive, read in MSDN.
#import <C:\Program Files\Common Files\Microsoft Shared\DAO\dao360.dll>
   no_namespace

#include <stdio.h>
#include <tchar.h>

// The following procedure dump_com_error and structure StartOle
// are borrowed from MSDN samples.

// Dump all COM errors to the console
void dump_com_error(_com_error &e)
{
   _tprintf(_T("Oops - hit an error!\n"));
   _tprintf(_T("\tCode = %08lx\n"), e.Error());
   _tprintf(_T("\tCode meaning = %s\n"), e.ErrorMessage());
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());
   _tprintf(_T("\tSource = %s\n"), (LPCTSTR) bstrSource);
   _tprintf(_T("\a\tDescription = %s\n"), (LPCTSTR) bstrDescription);
}

// If this is placed in the scope of the smart pointers, they must
// be explicitly Release(d) before CoUninitialize() is called.
// If any reference count is non-zero, a protection fault will
// occur.
struct StartOle {
   StartOle() { CoInitialize(NULL); }
   ~StartOle() { CoUninitialize(); }
} _inst_StartOle;

// From this line, everything will be commented with its VB analogue
void main()
{
   // Dim Dbe As DBEngine
   _DBEnginePtr Dbe(__uuidof(DBEngine));
   // Dim CurrDB As Database
   DatabasePtr CurrDB;
   // Dim stmp As String
   char stmp[1024];    // wide chars not used here for simplicity
   // (COM errors must be handled by C++ try/catch block)
   try {
      // Dbe.CreateDatabase("test.mdb", ";
      //                    LANGID=0x0419;CP=1251;COUNTRY=7;",
      // dbVersion40). 
      // (";COUNTRY=7" expression allows using "dd.mm.yy" date
      // format in queries.
      // dbVersion40 constant creates a database with Access 2000
      // file format.
      // The value for Access 97 format format is dbVersion30.)
      CurrDB = Dbe->CreateDatabase("test.mdb", 
       ";LANGID=0x0419;CP=1251;COUNTRY=7;",
          _variant_t((short)dbVersion40));
      // Set CurrDB = Dbe.OpenDatabase("test.mdb")
      CurrDB = Dbe->OpenDatabase("test.mdb");
      // for i=0 to CurrDB.TableDefs.Count
      // (for all tables in database including system tables)
      for (int i=0; i < CurrDB->TableDefs->Count; i++)
      {
         // Debug.Print CurrDB.TableDefs(i).Name
         _variant_t vI = _variant_t((long) i);
         printf("%s\n", (char *) CurrDB->TableDefs->Item[vI]->Name);
      }
      // stmp = "CREATE TABLE ....."
      strcpy(stmp,"CREATE TABLE Test ( \
                   ARTIST Char(40), \
                   TITLE1 Char(60), \
                   FORMAT1 Char(9), \
                   CATNO Numeric, \
                   PRICE Numeric, \
                   DATEIN DateTime, \
                   NOTES Char(10) \
                   );");
      // CurrDB.Execute(stmp)
      CurrDB->Execute(stmp);
      // stmp = "INSERT INTO ....."
      strcpy(stmp,"INSERT INTO Test VALUES( \
                   \"Artist\", \
                   \"Title\", \
                   \"Format1\", \
                   1223, \
                   3231.54, \
                   '21.09.04', \
                   \"Notes\" \
                   );");
      // CurrDB.Execute(stmp)
      CurrDB->Execute(stmp);
   } catch(_com_error &e) {
        dump_com_error(e);
   }
}

Compile this example in the command line as

cl -GX daocpp.cpp

and run daocpp.exe. The database file test.mdb will be created. This file will contain the Test table with one record of data.

As you can see, the types DBEngine and Database are mapped to _DBEnginePtr and DatabasePtr C++ classes after incorporating information from the type library. Type TableDef will be mapped to _TableDefPtr, respectively.

Note: Using DAO requires converting C++ types to the variant type. In this sample, you can see that C++ type long is converted to type _variant_t:

_variant_t vI = _variant_t((long) i);

In the same way, for example, the logical values True and False of the bool type will be converted to type _variant_t as:

_variant_t vTrue  = _variant_t((bool) -1);
_variant_t vFalse = _variant_t((bool) 0);

I hope this short article will help you write more efficient code in your applications.



About the Author

Yuriy Tkachenko

Software Engineer, Kiev Computer Laboratory, Okhtyrka, Ukraine

Comments

  • How to retrieve data from tables and queries in C++?

    Posted by hesham_1682001 on 07/28/2007 11:29am

    Really it is a good article. I want to know how to retrieve rows from tables and queries using Recordsets.

    • Very quick answer. See DAO docs and MSDN (variant data types).

      Posted by yurtk on 07/31/2007 04:41pm

      DAO::TableDefsPtr tabdefs = CurrDB->TableDefs;


      _variant_t vTn = _variant_t((const char* ) tablename);


      DAO::FieldsPtr fields = tabdefs->Item[vTn]->GetFields();


      DAO::RecordsetPtr rs = CurrDB->OpenRecordset(tablename);

      int total = rs->RecordCount;

      for (int j=0; j<total; j++ )


      {

      for (i=0; i<fields->Count; i++)


      {


      vI = _variant_t((long) i);



      fv = rs->Fields->Item[vI]->Value;


      if (fv.vt != VT_NULL )


      {


      strcpy(buff, _bstr_t(fv)); // string representation

      of the value

      }

      }

      rs->MoveNext();


      }

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

Top White Papers and Webcasts

  • As mobile devices have pushed their way into the enterprise, they have brought cloud apps along with them. This app explosion means account passwords are multiplying, which exposes corporate data and leads to help desk calls from frustrated users. This paper will discover how IT can improve user productivity, gain visibility and control over SaaS and mobile apps, and stop password sprawl. Download this white paper to learn: How you can leverage your existing AD to manage app access. Key capabilities to …

  • Live Event Date: September 10, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild". This loop of continuous delivery and continuous feedback is …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds