Appendix A

The following is a complete source code listing for the example presented in "Basic OLE DB." The example selects the CompanyName, City, Phone and FAX columns from the Customers table of the Access Northwind sample database. The database is shipped with the OLE DB SDK and is installed as the ODBC data source OLE_DB_NWind_Jet.

To build the file using Microsoft Visual C++ ® 4.0 or later:

  1. Create a new console application.

  2. Copy the following code to a new .cpp file.

  3. Ensure that your build directory settings reference the OLE DB SDK include directory.

  4. Alter the build link settings to include linking to Oledb.lib.
/********************************************************************
* OLE DB
********************************************************************/
#define UNICODE
#define _UNICODE
#define DBINITCONSTANTS // Initialize OLE constants...
#define INITGUID        // ...once in each app

#include <windows.h>
#include <stdio.h>

#include <oledb.h>      // OLE DB include files
#include <oledberr.h> 
#include <msdaguid.h>   // ODBC provider include files
#include <msdasql.h>

// Macros--number of row identifiers to retrieve
#define NUMROWS_CHUNK               35

// Prototypes
HRESULT myInitDSO(IDBInitialize** ppIDBI);
HRESULT mySetInitProps(IDBInitialize* pIDBInitialize);
HRESULT myCommand(IDBInitialize* pIDBI, IRowset** ppIRowset);
void    myGetData(IRowset* pIRowset);
void    DumpError(LPSTR lpStr);
HRESULT myGetColumnsInfo(IRowset* pIRowset, ULONG* pnCols,
                 DBCOLUMNINFO** ppColumnsInfo, OLECHAR** ppColumnStrings);
void    myCreateDBBindings(ULONG nCols, DBCOLUMNINFO* pColumnsInfo,
                 DBBINDING** ppDBBindings, char** ppRowValues);

// Global task memory allocator
IMalloc*        g_pIMalloc = NULL;

/********************************************************************
* General OLE DB application main()
********************************************************************/
int main()
    {
    IDBInitialize*  pIDBInitialize = NULL;
    IRowset*        pIRowset = NULL;

    // Init OLE and set up the DLLs
    CoInitialize(NULL);

    // Get the task memory allocator.
    if (FAILED(CoGetMalloc(MEMCTX_TASK, &g_pIMalloc)))
        goto EXIT;

    // Connect to the data source.
    if (FAILED(myInitDSO(&pIDBInitialize)))
        goto EXIT;

    // Get a session, set and execute a command.
    if (FAILED(myCommand(pIDBInitialize, &pIRowset)))
        goto EXIT;

    // Retrieve data from rowset.
    myGetData(pIRowset);

EXIT:
    // Clean up and disconnect.
    if (pIRowset != NULL)
        pIRowset->Release();

    if (pIDBInitialize != NULL)
        {
        if (FAILED(pIDBInitialize->Uninitialize()))
            {
            // Uninitialize is not required, but it will fail if an 
            // interface has not been released; we can use it for
            // debugging.
            DumpError("Someone forgot to release something!");
            }
        pIDBInitialize->Release();
        }

    if (g_pIMalloc != NULL)
        g_pIMalloc->Release();

    CoUninitialize();
    
    return (0);
    }

/********************************************************************
* Initialize the data source.
********************************************************************/
HRESULT myInitDSO
    (
    IDBInitialize** ppIDBInitialize  // [out]
    )
    {
    // Create an instance of the MSDASQL (ODBC) provider.
    CoCreateInstance(CLSID_MSDASQL, NULL, CLSCTX_INPROC_SERVER,
        IID_IDBInitialize, (void**)ppIDBInitialize);

    if (*ppIDBInitialize == NULL)
        {
        return (E_FAIL);
        }

    if (FAILED(mySetInitProps(*ppIDBInitialize)))
        {
        return (E_FAIL);
        }

    if (FAILED((*ppIDBInitialize)->Initialize()))
        {
        DumpError("IDBInitialze->Initialize failed.");
        return (E_FAIL);
        }

    return (NOERROR);
    }

/********************************************************************
* Set initialization properties on a data source.
********************************************************************/
HRESULT mySetInitProps
    (
    IDBInitialize*  pIDBInitialize  // [in]
    )
    {
    const ULONG     nProps = 4;
    IDBProperties*  pIDBProperties;
    DBPROP          InitProperties[nProps];
    DBPROPSET       rgInitPropSet;
    HRESULT         hr;

    // Initialize common property options.
    for (ULONG i = 0; i < nProps; i++ )
        {
        VariantInit(&InitProperties[i].vValue);
        InitProperties[i].dwOptions = DBPROPOPTIONS_REQUIRED;
        InitProperties[i].colid = DB_NULLID;
        }

    // Level of prompting that will be done to complete the 
    // connection process
    InitProperties[0].dwPropertyID = DBPROP_INIT_PROMPT;
    InitProperties[0].vValue.vt = VT_I2;
    InitProperties[0].vValue.iVal = DBPROMPT_NOPROMPT;     

    // Data source name--see the sample source included with the OLE
    // DB SDK.
    InitProperties[1].dwPropertyID = DBPROP_INIT_DATASOURCE;    
    InitProperties[1].vValue.vt = VT_BSTR;
    InitProperties[1].vValue.bstrVal = 
        SysAllocString(OLESTR("OLE_DB_NWind_Jet"));

    // User ID
    InitProperties[2].dwPropertyID = DBPROP_AUTH_USERID;
    InitProperties[2].vValue.vt = VT_BSTR;
    InitProperties[2].vValue.bstrVal = SysAllocString(OLESTR(""));

    // Password
    InitProperties[3].dwPropertyID = DBPROP_AUTH_PASSWORD;
    InitProperties[3].vValue.vt = VT_BSTR;
    InitProperties[3].vValue.bstrVal = SysAllocString(OLESTR(""));

    rgInitPropSet.guidPropertySet = DBPROPSET_DBINIT;
    rgInitPropSet.cProperties = nProps;
    rgInitPropSet.rgProperties = InitProperties;

    // Set initialization properties.
    pIDBInitialize->QueryInterface(IID_IDBProperties, (void**) 
        &pIDBProperties);
    hr = pIDBProperties->SetProperties(1, &rgInitPropSet);

    SysFreeString(InitProperties[1].vValue.bstrVal);
    SysFreeString(InitProperties[2].vValue.bstrVal);
    SysFreeString(InitProperties[3].vValue.bstrVal);

    pIDBProperties->Release();

    if (FAILED(hr))
        {
        DumpError("Set properties failed.");
        }

    return (hr);
    }

/********************************************************************
* Execute a command, retrieve a rowset interface pointer.
********************************************************************/
HRESULT myCommand
    (
    IDBInitialize*  pIDBInitialize, // [in]
    IRowset**       ppIRowset       // [out]
    ) 
    {
    IDBCreateSession*   pIDBCreateSession;
    IDBCreateCommand*   pIDBCreateCommand;
    IRowset*            pIRowset;
    ICommandText*       pICommandText;
    LPCTSTR             wSQLString = OLESTR("SELECT CompanyName, 
                                         City, Phone, Fax")
                                     OLESTR(" FROM Customers")
                                     OLESTR(" ORDER BY CompanyName, 
                                         City");
    LONG                cRowsAffected;
    HRESULT             hr;

    // Get the DB session object.
    if (FAILED(pIDBInitialize->QueryInterface(IID_IDBCreateSession,
            (void**) &pIDBCreateSession)))
        {
        DumpError("Session initialization failed.");
        return (E_FAIL);
        }

    // Create the session, getting an interface for command creation.
    hr = pIDBCreateSession->CreateSession(NULL, IID_IDBCreateCommand,
        (IUnknown**) &pIDBCreateCommand);
    pIDBCreateSession->Release();
    if (FAILED(hr))
        {
        DumpError("Create session failed.");
        return (hr);
        }

    // Create the command object.
    hr = pIDBCreateCommand->CreateCommand(NULL, IID_ICommandText,
        (IUnknown**) &pICommandText);
    if (FAILED(hr))
        {
        DumpError("Create command failed.");
        return (hr);
        }
    pIDBCreateCommand->Release();

    // The command requires the actual text as well as an indicator
    // of its language and dialect.
    pICommandText->SetCommandText(DBGUID_DBSQL, wSQLString);

    // Execute the command.
    hr = pICommandText->Execute(NULL, IID_IRowset, NULL, 
         &cRowsAffected, (IUnknown**) &pIRowset);
    if (FAILED(hr))
        {
        DumpError("Command execution failed.");
        }
    pICommandText->Release();

    *ppIRowset = pIRowset;
    return (hr);
    }

/********************************************************************
* Get the characteristics of the rowset (the ColumnsInfo interface).
********************************************************************/
HRESULT myGetColumnsInfo
    (
    IRowset*        pIRowset,        // [in]
    ULONG*          pnCols,          // [out]
    DBCOLUMNINFO**  ppColumnsInfo,   // [out]
    OLECHAR**       ppColumnStrings  // [out]
    )
    {
    IColumnsInfo*   pIColumnsInfo;
    HRESULT         hr;

    if (FAILED(pIRowset->QueryInterface(IID_IColumnsInfo, (void**) &pIColumnsInfo)))
        {
        DumpError("Query rowset interface for IColumnsInfo failed");
        return (E_FAIL);
        }

    hr = pIColumnsInfo->GetColumnInfo(pnCols, ppColumnsInfo, ppColumnStrings);
    if (FAILED(hr))
        {
        DumpError("GetColumnInfo failed.");
        *pnCols = 0;
        }

    pIColumnsInfo->Release();
    return (hr);
    }

/********************************************************************
* Create binding structures from column information. Binding
* structures will be used to create an accessor that allows row value 
* retrieval.
********************************************************************/
void myCreateDBBindings
    (
    ULONG nCols,                 // [in]
    DBCOLUMNINFO* pColumnsInfo,  // [in]
    DBBINDING** ppDBBindings,    // [out]
    char** ppRowValues           // [out]
    )
    {
    ULONG       nCol;
    ULONG       cbRow = 0;
    DBBINDING*  pDBBindings;
    char*       pRowValues;

    pDBBindings = new DBBINDING[nCols];

    for (nCol = 0; nCol < nCols; nCol++)
        {
        pDBBindings[nCol].iOrdinal = nCol+1;
        pDBBindings[nCol].obValue = cbRow;
        pDBBindings[nCol].obLength = 0;
        pDBBindings[nCol].obStatus = 0;
        pDBBindings[nCol].pTypeInfo = NULL;
        pDBBindings[nCol].pObject = NULL;
        pDBBindings[nCol].pBindExt = NULL;
        pDBBindings[nCol].dwPart = DBPART_VALUE;
        pDBBindings[nCol].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
        pDBBindings[nCol].eParamIO = DBPARAMIO_NOTPARAM;
        pDBBindings[nCol].cbMaxLen = pColumnsInfo[nCol].ulColumnSize;
        pDBBindings[nCol].dwFlags = 0;
        pDBBindings[nCol].wType = pColumnsInfo[nCol].wType;
        pDBBindings[nCol].bPrecision = pColumnsInfo[nCol].bPrecision;
        pDBBindings[nCol].bScale = pColumnsInfo[nCol].bScale;

        cbRow += pDBBindings[nCol].cbMaxLen;
        }

    pRowValues = new char[cbRow];

    *ppDBBindings = pDBBindings;
    *ppRowValues = pRowValues;

    return;
    }

/********************************************************************
* Retrieve data from a rowset.
********************************************************************/
void myGetData
    (
    IRowset*    pIRowset    // [in]
    ) 
    {
    ULONG           nCols;
    DBCOLUMNINFO*   pColumnsInfo = NULL;
    OLECHAR*        pColumnStrings = NULL;
    ULONG           nCol;
    ULONG           cRowsObtained;         // Number of rows obtained
    ULONG           iRow;                  // Row count
    HROW            rghRows[NUMROWS_CHUNK];// Row handles
    HROW*           pRows = &rghRows[0];   // Pointer to the row 
                                           // handles
    IAccessor*      pIAccessor;            // Pointer to the accessor
    HACCESSOR       hAccessor;             // Accessor handle
    DBBINDSTATUS*   pDBBindStatus = NULL;
    DBBINDING*      pDBBindings = NULL;
    char*           pRowValues;

    // Get the description of the rowset for use in binding structure
    // creation.
    if (FAILED(myGetColumnsInfo(pIRowset, &nCols, &pColumnsInfo,
        &pColumnStrings)))
        {
        return;
        }

    // Create the binding structures.
    myCreateDBBindings(nCols, pColumnsInfo, &pDBBindings, 
        &pRowValues);
    pDBBindStatus = new DBBINDSTATUS[nCols];

    // Create the accessor.
    pIRowset->QueryInterface(IID_IAccessor, (void**) &pIAccessor);
    pIAccessor->CreateAccessor(
        DBACCESSOR_ROWDATA, // Accessor will be used to retrieve row 
                            // data.
        nCols,              // Number of columns being bound
        pDBBindings,        // Structure containing bind info
        0,                  // Not used for row accessors 
        &hAccessor,         // Returned accessor handle
        pDBBindStatus       // Information about binding validity
        );

    // Process all the rows, NUMROWS_CHUNK rows at a time
    while (TRUE)
        {
        pIRowset->GetNextRows(
            0,                  // Reserved
            0,                  // cRowsToSkip
            NUMROWS_CHUNK,      // cRowsDesired
            &cRowsObtained,     // cRowsObtained
            &pRows );           // Filled in w/ row handles.


        // All done; there is no more rows left to get.
        if (cRowsObtained == 0)
            break;

        // Loop over rows obtained, getting data for each.
        for (iRow=0; iRow < cRowsObtained; iRow++)
            {
            pIRowset->GetData(rghRows[iRow], hAccessor, pRowValues);
            for (nCol = 0; nCol < nCols; nCol++)
                {
                wprintf(OLESTR("%s%s:"), pColumnsInfo[nCol].pwszName,
                    wcslen(pColumnsInfo[nCol].pwszName) > 10 ? 
                        OLESTR("\t") :
                        OLESTR("\t\t"));
                printf("\t%s\n", 
                        &pRowValues[pDBBindings[nCol].obValue]);
                }
            printf("\n");
            }

        // Release row handles.
        pIRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL, 
           NULL);
        }  // End while

    // Release the accessor.
    pIAccessor->ReleaseAccessor(hAccessor, NULL);
    pIAccessor->Release();

    delete [] pDBBindings;
    delete [] pDBBindStatus;

    g_pIMalloc->Free( pColumnsInfo );
    g_pIMalloc->Free( pColumnStrings );

    return;
    }

/********************************************************************
* Dump an error to the console.
********************************************************************/
void DumpError(LPSTR lpStr)
    {
    printf(lpStr);
    printf("\n");
    }