Creating an OLE DB Consumer

Creating an OLE DB consumer is pretty straightforward—most of the support comes through the ATL Object Wizard. You can see an example of a consumer in the x33a folder on the companion CD. Here are the steps for creating a consumer using the ATL Object Wizard.

  1. Create an application or a control to drive the data consumption. For example, you might want to create an ActiveX control.

  2. While inside the IDE, use the ATL Object Wizard to insert a data consumer. Do this by either selecting New ATL Object from the Insert menu or by right-clicking on the project icon in ClassView and selecting New ATL Object from the context menu to start the ATL Object Wizard.

  3. From the ATL Object Wizard, select the Data Access category of objects. Then select Consumer and click Next. This will cause the ATL Object Wizard Properties dialog, shown in Figure 33-1, to appear. There will be only one page in it, for naming the class and selecting the data source.

    Figure 33-1. The ATL Object Wizard Properties.

  4. Click Select Datasource to configure the data consumer. Once you've picked out a data source, choose OK. The ATL Object Wizard will create an OLE DB Consumer template ready for you to use.

As an example, we took the BIBLIO.MDB database (a Microsoft Access database) that comes in the Visual Studio VB98 directory and made a data consumer out of it. The BIBLIO database includes the titles and the authors of various programming texts. Using the ATL Object Wizard to create the OLE DB Consumer template for the authors in the database yielded these classes:

// Authors.H : Declaration of the CAuthors class

#ifndef __AUTHORS_H_
#define __AUTHORS_H_

class CAuthorsAccessor
{
public:
    LONG m_AuID;
    TCHAR m_Author[51];
    SHORT m_YearBorn;

BEGIN_COLUMN_MAP(CAuthorsAccessor)
    COLUMN_ENTRY(1, m_AuID)
    COLUMN_ENTRY(2, m_Author)
    COLUMN_ENTRY(3, m_YearBorn)
END_COLUMN_MAP()

DEFINE_COMMAND(CAuthorsAccessor, _T("SELECT * FROM Authors"))
};

class CAuthors : public CCommand<CAccessor<CAuthorsAccessor> >
{
public:
    HRESULT Open()
    {
        HRESULT   hr;

        hr = OpenDataSource();
        if (FAILED(hr))
            return hr;

        return OpenRowset();
    }
    HRESULT OpenDataSource()
    {
        HRESULT      hr;
        CDataSource  db;
        CDBPropSet   dbinit(DBPROPSET_DBINIT);

        dbinit.AddProperty(DBPROP_AUTH_CACHE_AUTHINFO, true);
        dbinit.AddProperty(DBPROP_AUTH_ENCRYPT_PASSWORD, false);
        dbinit.AddProperty(DBPROP_AUTH_MASK_PASSWORD, false);
        dbinit.AddProperty(DBPROP_AUTH_PASSWORD, OLESTR(""));
        dbinit.AddProperty(DBPROP_AUTH_PERSIST_ENCRYPTED, false);
        dbinit.AddProperty(DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO,
                           false);
        dbinit.AddProperty(DBPROP_AUTH_USERID, OLESTR("Admin"));
        dbinit.AddProperty(DBPROP_INIT_DATASOURCE,
                           OLESTR("c:\\biblio.mdb"));
        dbinit.AddProperty(DBPROP_INIT_MODE, (long)16);
        dbinit.AddProperty(DBPROP_INIT_PROMPT, (short)4);
        dbinit.AddProperty(DBPROP_INIT_PROVIDERSTRING, OLESTR
                           (";COUNTRY=0;CP=1252;LANGID=0x0409"));
        dbinit.AddProperty(DBPROP_INIT_LCID, (long)1033);
        hr = db.Open(_T("Microsoft.Jet.OLEDB.3.51"), &dbinit);
        if (FAILED(hr))
            return hr;

        return m_session.Open(db);
    }
    HRESULT OpenRowset()
    {
        return CCommand<CAccessor<CAuthorsAccessor> 
               >::Open(m_session, _T("Authors"));
    }
    Csession   m_session;
};

#endif // __AUTHORS_H_

The CAuthorsAccessor class defines the structure of the author record. Notice that the class includes an author ID field, a name field, and a field indicating when the author was born.

The CAuthors class represents the actual data consumer class that connects to the database. Notice that it's derived from CCommand. Remember that command objects represent a command (such as a SQL statement) and generate rowsets. The COLUMN_MAP represents data returned in the rowset. The PARAM_MAP represents parameter data for a command.

The column maps and the parameter maps represent the user's view of the accessor. As with many data structures in ATL and MFC, these maps are built up with macros. Here's how the maps work: when running against a database, the data that comes back is contained in a contiguous block of memory. OLE DB templates work with this block of memory to extract the data. The data members in the entries represent offsets into that block of memory. The entries in the maps filter out the data from the database. That way, you as a developer do not have to worry about doing anything funky like performing pointer arithmetic on the block to get information.