Sample Provider Overview

OLE DB

Universal Data Access for Microsoft® Windows® and Windows NT® Operating Systems

White Paper

Abstract

This White Paper describes the Microsoft® OLE DB sample provider (SAMPPROV.DLL) which developers can use to learn about building OLE DB providers. In addition to general guidelines on how to construct an OLE DB data provider, there is a section which takes users step-by-step through the process of writing an OLE DB provider.

© 1997 Microsoft Corporation. All rights reserved.

The information contained in this document represents the current view of Microsoft Corporation on the issues discussed as of the date of publication. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information presented after the date of publication.

This White Paper is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS OR IMPLIED, IN THIS DOCUMENT.

Microsoft, Visual Basic, Windows, and Windows NT are registered trademarks of Microsoft Corporation.

Other product or company names mentioned herein may be the trademarks of their respective owners.

Microsoft Corporation · One Microsoft Way · Redmond, WA 98052-6399 · USA

0997

Introduction

The Microsoft® OLE DB sample provider, SAMPPROV.DLL, is a learning tool for developers new to OLE DB. Users can displaying the source code in the debugger to learn about building OLE DB providers. Although this provider uses a simple interface to access data in the underlying data source, it demonstrates the general procedures that developers need to follow to expose rowsets over a file-based data source using IOpenRowset.

Sample Provider Features

The sample provider exposes data in a single, comma-separated value file named CUSTOMER.CSV. If you alter the code to use a different file, you must follow the formatting conventions of the sample file, which follow.

The first line of a .csv file that can be read by SAMPPROV must contain the column names in quotation marks. The second line must list the column types and lengths. All additional lines contain data, in comma-separated format. Every line of data in the file must contain the correct number of values. The following example illustrates the file format.

"Column1","Column2","Column3"
Char(20),SLONG,SLONG
"Name1",10,-10
@@@@@@@@@@@@@@@@@@@@@           // Deleted Row
"Name2",110,-110

In addition, the sample provider replaces deleted rows in the file with the at sign (@). These rows are ignored by the sample provider when the file is initialized. If a user updates a row, the new data is appended to the end of the file and the original row is overwritten with at signs, indicating that it is deleted.

Sample Provider Limitations

The following are limitations in SAMPPROV.DLL:

Sample Provider Files

The following source files are included with the sample provider. The source files are located in the <install directory>\SAMPLES\SAMPPROV directory. The sample provider executable file SAMPPROV.DLL is located in the <install directory>\BIN directory.

File name Description
ACCESSOR.cpp CImpIAccessor object implementation.
ASSERTS.cpp Simple assertion routines.
ASSERTS.h Simple assertion routine header file.
BITARRAY.cpp An implementation of a bit array class used by the internal buffer to mark released or unreleased rows.
BITARRAY.h Class definitions for the bit array class.
CLASSFAC.cpp DLL entry and exit points and the OLE ClassFactory class.
CLASSFAC.h Class definitions for CClassFactory and DLL entry points.
COLINFO.cpp IColumnsInfo interface implementation.
CRTSESS.cpp IDBCreateSession interface implementation.
CVTTYPE.cpp IConvertType interface implementation.
DATASRC.cpp CDataSource object implementation.
DATASRC.h CDataSource base object and contained interface definitions.
DBINIT.cpp IDBInitialize interface implementation.
DBPROP.cpp IDBProperties and IDBInfo interface implementations.
DBSESS.cpp CDBSession object implementation.
DBSESS.h CDBSession base object and contained interface definitions.
EXTBUFF.cpp The class that provides array-based access to a big block of memory.
EXTBUFF.h Class definitions for CExtBuffer class.
FILEIDX.cpp The index array code for a comma-separated value (.csv) provider.
FILEIDX.h Class definitions for CFileIO class.
FILEIO.cpp The file manipulation code for a comma-separated value (.csv) provider.
FILEIO.h Class definitions for CFileIO class.
GLOBALS.cpp Global initialization object.
GUIDS.h Internal GUIDs.
HASHTBL.cpp Hashing routines for row manipulation.
HASHTBL.h Class definitions for CHashTbl class and miscellaneous bookmark functions.
HEADERS.cpp Precompiled headers module.
File name Description
HEADERS.h Precompiled headers.
IROWSET.cpp IRowset interface implementation.
LOCAL.RC Localizable resource.
OPNROWST.cpp IOpenRowset interface implementation.
PERSIST.cpp IPersist interface implementation.
RC.h Resource IDs.
ROWCHNG.cpp IRowsetChange interface implementation.
ROWINFO.cpp IRowsetInfo interface implementation.
ROWSET.cpp CRowset object implementation.
ROWSET.h CRowset base object and contained interface definitions.
SAMPPROV.DEF Project definitions.
SAMPPROV.h Main include file.
SAMPPROV.MAK Microsoft Developer Studio–generated NMAKE File.
SAMPPROV.RC Main resource file.
SAMPVER.h Version include file.
UTILPROP.cpp Properties utility object implementation.
UTILPROP.h CUtilProp object definitions.

Building the Sample Provider

The file <install directory>\SAMPLES\SAMPPROV\SAMPPROV.MAK is set up for compilation with NMAKE, the Microsoft Program Maintenance Utility provided with Microsoft Developer Studio. SAMPPROV must have access to the following OLE DB files:

Verify that these files are on the path referred to by the INCLUDE environment variable.

The following example syntax builds a debug version of SAMPPROV on the Intel x86 platform based on the Win32 (x86) Dynamic-Link Library configuration.

NMAKE /f "sampprov.mak" CFG="sampprov - Win32 x86 Debug"

To build SAMPPROV on another platform or with a different configuration, use one of the following configuration options.

Configuration option Type of file built
"sampprov - Win32 x86 Debug" Debug version of SAMPPROV based on Win32 (x86) Dynamic-Link Library.
"sampprov - Win32 x86 Release" Retail version of SAMPPROV based on Win32 (x86) Dynamic-Link Library.
"sampprov - Win32 (ALPHA) axp Debug" Debug version of SAMPPROV based on Win32 (ALPHA) Dynamic-Link Library.
"sampprov - Win32 (ALPHA) axp Release" Retail version of SAMPPROV based on Win32 (ALPHA) Dynamic-Link Library.
"sampprov - Win32 (PPC) ppc Debug" Debug version of SAMPPROV based on Win32 (PPC) Dynamic-Link Library.
"sampprov - Win32 (PPC) ppc Release" Retail version of SAMPPROV based on Win32 (PPC) Dynamic-Link Library.

For additional information on using NMAKE, see the Developer Studio documentation.

Writing an OLE DB Provider: An Introduction

The purpose of this section is to offer some general guidelines on how to construct an OLE DB data provider.

Information Needed to Write an OLE DB Provider

OLE DB is based on the Component Object Model. Developers must be familiar with this model as well as with basic object-oriented coding practices to create an OLE DB provider. Although many different implementations are possible, the sample provider implements the TDataSource, TDBSession, and TRowset CoTypes. The sample provider uses the IOpenRowset interface, which is a required interface on the session for all providers. IOpenRowset is used by consumers that need access to the full data set and do not need to specify selection criteria.

Developers should become familiar with data source objects, sessions, and rowsets by reading the OLE DB Programmer’s Reference. In addition, developers may want to read the “Overview” in the OLE DB Programmer’s Reference to get a general introduction to OLE DB conventions, design considerations, and a complete introduction to the objects that make up OLE DB.

The Sample Provider

The sample provider offers access to a fixed-length text data file using OLE DB interfaces. The purpose of this sample provider is purely illustrative. The capabilities of the sample provider are limited; these limitations are discussed in “Sample Provider Overview,” previously in this paper.

The sample provider opens the sample data file and enables the consumer to read the data, using the IRowset interface, in a forward-only fashion. The consumer can update or delete the current row but cannot add new rows. The format of the data file is discussed in “Sample Provider Overview,” previously in this paper.

Objects and Interfaces Implemented by the Sample Provider

The sample provider implements three OLE DB objects.

Interface name Method name Source code file
IDBInitialize Initialize DBInit.cpp
Uninitialize DBInit.cpp
IDBCreateSession CreateSession CrtSess.cpp
IDBProperties GetProperties UtilProp.cpp
GetPropertyInfo UtilProp.cpp
SetProperties UtilProp.cpp
IPersist GetClassID Persist.cpp
IGetDataSource GetDataSource DBSess.cpp
IOpenRowset OpenRowset OpnRowst.cpp
IColumnsInfo GetColumnInfo ColInfo.cpp
MapColumnIDs ColInfo.cpp
IConvertType CanConvert CvtType.cpp
IAccessor AddRefAccessor Accessor.cpp
CreateAccessor Accessor.cpp
GetBindings Accessor.cpp
ReleaseAccessor Accessor.cpp
IRowset AddRefRows IRowset.cpp
GetData IRowset.cpp
GetNextRows IRowset.cpp
ReleaseRows IRowset.cpp
RestartPosition IRowset.cpp
IRowsetInfo GetProperties RowInfo.cpp
GetReferencedRowset RowInfo.cpp
GetSpecification RowInfo.cpp
IRowsetChange DeleteRows RowChng.cpp
InsertRow RowChng.cpp
SetData RowChng.cpp
ISessionProperties GetProperties DBSess.cpp
SetProperties DBSess.cpp

Setting and Getting Provider Properties

The data source object is the first object created when a consumer instantiates an OLE DB data provider by calling CoCreateInstance or through some other technique.

The data source object provides the starting point for communications between the provider and consumer. For example, a consumer can call CoCreateInstance and request an IDBInitialize interface pointer to instantiate a data source object. The provider’s developer must generate a CLSID (class ID) and store the ID in the Windows Registry. The consumer can use this CLSID with CoCreateInstance to instantiate the data source object. To facilitate this operation, providers should add their application or library information to the Windows Registry upon installation on a new system. The sample provider setup program registers the sample provider in the Windows Registry.

The data source object is also responsible for setting and returning information about the properties supported by the provider and exposing the list of supported keywords and literals. This functionality is supported through the mandatory IDBProperties interface and the optional IDBInfo interface. The IDBProperties interface contains three methods:

The IDBInfo interface contains two methods:

In the sample provider implementation, no command interfaces are implemented, so GetKeywords returns NULL. GetLiteralInfo is not implemented and returns E_NOTIMPL. GetProperties, GetPropertyInfo, and SetProperties are handled by passing the calls to the utility object that manages properties, which is found in UtilProp.cpp. The structure containing the properties known to the sample provider, which is also found in UtilProp.cpp, follows.

// Struct containing the properties we know about. The GUID and string fields are
// initialized in the constructor, because C++ makes it awkward to do so at declaration
// time. So, if you change this table, be sure to make parallel changes in CUtilProp::CUtilProp.
PROPSTRUCT s_rgprop[] =
   {
/* 0 */ {DBPROP_IAccessor,       FLAGS_ROWSETRO,   VT_BOOL, TRUE,  0, NULL,         L"IAccessor"},
/* 1 */ {DBPROP_IColumnsInfo,    FLAGS_ROWSETRO,   VT_BOOL, TRUE,  0, NULL,         L"IColumnsInfo"},
/* 2 */ {DBPROP_IConvertType,    FLAGS_ROWSETRO,   VT_BOOL, TRUE,  0, NULL,         L"IConvertType"},
/* 3 */ {DBPROP_IRowset,         FLAGS_ROWSETRO,   VT_BOOL, TRUE,  0, NULL,         L"IRowset"},
/* 4 */ {DBPROP_IRowsetChange,   FLAGS_ROWSETRW,   VT_BOOL, TRUE,  0, NULL,         L"IRowsetChange"},
/* 5 */ {DBPROP_IRowsetInfo,     FLAGS_ROWSETRO,   VT_BOOL, TRUE,  0, NULL,         L"IRowsetInfo"},
/* 6 */ {DBPROP_ACTIVESESSIONS,  FLAGS_DATASOURCE, VT_I4,   FALSE, 1, NULL,         L"Active Sessions"},
/* 7 */ {DBPROP_PROVIDERNAME,    FLAGS_DATASOURCE, VT_BSTR, FALSE, 0, L"SAMPPROV.DLL",L"Provider Name"},
/* 8 */ {DBPROP_PROVIDEROLEDBVER,FLAGS_DATASOURCE, VT_BSTR, FALSE, 0, L"01.10",     L"OLE DB Version"},
/* 9 */ {DBPROP_PROVIDERVER,     FLAGS_DATASOURCE, VT_BSTR, FALSE, 0, _TEXT(VER_PRODUCTVERSION_STR), L"Provider Version"},
/* 10*/ {DBPROP_INIT_DATASOURCE, FLAGS_DBINITRW,   VT_BSTR, FALSE, 0, L"",          L"Data Source"},
/* 11*/ {DBPROP_INIT_HWND,       FLAGS_DBINITRW,   VT_I4,   FALSE, 0, NULL,         L"Window Handle"},
/* 12*/ {DBPROP_INIT_PROMPT,     FLAGS_DBINITRW,   VT_I2,   FALSE, 4, NULL,         L"Prompt"}
    };

The source code for GetPropertyInfo, which passes property information back to the provider, follows.

// CUtilProp::GetPropertyInfo  -----------------------------------------
//
// @mfunc   Returns information about rowset and data source properties 
//         supported by the provider
//
// @rdesc HRESULT
//      @flag S_OK          | The method succeeded
//      @flag E_INVALIDARG  | pcPropertyIDSets or prgPropertyInfo was NULL
//      @flag E_OUTOFMEMORY | Out of memory
//

STDMETHODIMP CUtilProp::GetPropertyInfo
    (
    BOOL              fDSOInitialized,     //@parm IN  | if Initialized
    ULONG             cPropertyIDSets,     //@parm IN  | # properties
    const DBPROPIDSET rgPropertyIDSets[],  //@parm IN  | Array of property sets
    ULONG*            pcPropertyInfoSets,  //@parm OUT | # DBPROPSET structures
    DBPROPINFOSET**   prgPropertyInfoSets, //@parm OUT | DBPROPSET structures property 
                                           //          | information returned
    WCHAR**           ppDescBuffer         //@parm OUT | Property descriptions
    )
{
   BOOL           fRet               = TRUE;
   BOOL           fPropsinError      = FALSE;
   BOOL           fPropsSucceed      = FALSE;
   BOOL           fIsSpecialGUID     = FALSE;
   BOOL           fIsNotSpecialGUID  = FALSE;
   ULONG          cProps             = 0;
   ULONG          cCount             = 0;
   ULONG          ulPropertySets     = 0;
   WCHAR*         pDescBuffer        = NULL;
   DBPROPINFO*    pPropInfo;
   DBPROPINFOSET* pPropInfoSet;

   // init out params
   if (pcPropertyInfoSets)
      *pcPropertyInfoSets    = 0;
   if (prgPropertyInfoSets)
      *prgPropertyInfoSets   = NULL;
   if (ppDescBuffer)
      *ppDescBuffer = NULL;

   // Check Arguments, on failure post HRESULT to error queue
   if( ((cPropertyIDSets > 0) && !rgPropertyIDSets) ||
      !pcPropertyInfoSets || !prgPropertyInfoSets )
      return ResultFromScode( E_INVALIDARG );

   // New argument check for > 1 cPropertyIDs and NULL pointer for 
   // array of property ids.
   for(ULONG ul=0; ul<cPropertyIDSets; ul++)
   {
      if( rgPropertyIDSets[ul].cPropertyIDs && !(rgPropertyIDSets[ul].rgPropertyIDs) )
         return ResultFromScode( E_INVALIDARG );
      
      if (DBPROPSET_ROWSETALL == rgPropertyIDSets[ul].guidPropertySet          ||
         DBPROPSET_DATASOURCEALL == rgPropertyIDSets[ul].guidPropertySet       ||
         DBPROPSET_DATASOURCEINFOALL == rgPropertyIDSets[ul].guidPropertySet   ||
         DBPROPSET_SESSIONALL == rgPropertyIDSets[ul].guidPropertySet          ||
         DBPROPSET_DBINITALL == rgPropertyIDSets[ul].guidPropertySet)
         fIsSpecialGUID = TRUE;
      else
         fIsNotSpecialGUID = TRUE;

      if(fIsSpecialGUID && fIsNotSpecialGUID)
         return ResultFromScode( E_INVALIDARG );
   }

   // save the count of PropertyIDSets
   cProps = cPropertyIDSets;

   // If the consumer does not restrict the property sets
   // by specify an array of property sets and a cPropertySets
   // greater than 0, then we need to make sure we 
   // have some to return
   if ( (cPropertyIDSets == 0) )
   {
      if( fDSOInitialized )
         cProps = NUMBER_OF_SUPPORTED_PROPERTY_SETS;
      else
         cProps = 1;
   }   

   // use task memory allocater to alloc a DBPROPINFOSET struct
   pPropInfoSet = (DBPROPINFOSET*) g_pIMalloc->Alloc(cProps *
                                         sizeof( DBPROPINFOSET ));
   if ( !pPropInfoSet )
      return ResultFromScode( E_OUTOFMEMORY );

   memset( pPropInfoSet, 0, (cProps * sizeof( DBPROPINFOSET )));

   // Alloc memory for ppDescBuffer
   if ( ppDescBuffer )
   {
      pDescBuffer = (WCHAR*)g_pIMalloc->Alloc(NUMBER_OF_SUPPORTED_PROPERTIES * 
         CCH_GETPROPERTYINFO_DESCRIP_BUFFER_SIZE * sizeof(WCHAR) );
      
      if( pDescBuffer )
      {
         memset(pDescBuffer, 0, (NUMBER_OF_SUPPORTED_PROPERTIES * 
            CCH_GETPROPERTYINFO_DESCRIP_BUFFER_SIZE * sizeof(WCHAR)));
      
         *ppDescBuffer = pDescBuffer;
      }
      else
      {
         g_pIMalloc->Free( pPropInfoSet );
         return ResultFromScode( E_OUTOFMEMORY );
      }
   }

   // For each supported Property Set
   for (ulPropertySets=0; ulPropertySets < cProps; ulPropertySets++)
   {
      BOOL fGetAllProps = FALSE;

      // If no restrictions return all properties from the three supported property sets
      if( cPropertyIDSets == 0 )
      {
         fGetAllProps = TRUE;

         // only do this once
         if (ulPropertySets == 0)
         {
            pPropInfoSet[0].guidPropertySet = DBPROPSET_DBINIT;
            pPropInfoSet[0].cPropertyInfos  = NUMBER_OF_SUPPORTED_DBINIT_PROPERTIES;

            if ( fDSOInitialized )
            {
               pPropInfoSet[1].guidPropertySet = DBPROPSET_DATASOURCEINFO;
               pPropInfoSet[1].cPropertyInfos  = NUMBER_OF_SUPPORTED_DATASOURCEINFO_PROPERTIES;
               pPropInfoSet[2].guidPropertySet = DBPROPSET_ROWSET;
               pPropInfoSet[2].cPropertyInfos  = NUMBER_OF_SUPPORTED_ROWSET_PROPERTIES;
            }
         }
      }
      else
      {
         pPropInfoSet[ulPropertySets].guidPropertySet = rgPropertyIDSets[ulPropertySets].guidPropertySet;
         pPropInfoSet[ulPropertySets].cPropertyInfos  = rgPropertyIDSets[ulPropertySets].cPropertyIDs;

         if ((rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DBINITALL) ||
            (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DBINIT     &&
             rgPropertyIDSets[ulPropertySets].cPropertyIDs == 0))
         {
            fGetAllProps = TRUE;
            pPropInfoSet[ulPropertySets].guidPropertySet = DBPROPSET_DBINIT;
            pPropInfoSet[ulPropertySets].cPropertyInfos  = NUMBER_OF_SUPPORTED_DBINIT_PROPERTIES;
         }
         else if (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DATASOURCEALL)
         {
            // Since we do not support it should return DB_E_ERRORSOCCURRED with 0 & NULL
            fPropsinError = TRUE;
            pPropInfoSet[ulPropertySets].guidPropertySet = DBPROPSET_DATASOURCE;
            pPropInfoSet[ulPropertySets].cPropertyInfos  = 0;
         }
         else if( fDSOInitialized )
         {
            if( (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DATASOURCEINFOALL) ||
               ((rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DATASOURCEINFO)   &&
                (rgPropertyIDSets[ulPropertySets].cPropertyIDs == 0)) )
            {
               fGetAllProps = TRUE;
               pPropInfoSet[ulPropertySets].guidPropertySet = DBPROPSET_DATASOURCEINFO;
               pPropInfoSet[ulPropertySets].cPropertyInfos  = NUMBER_OF_SUPPORTED_DATASOURCEINFO_PROPERTIES;
            }
            else if (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_SESSIONALL)
            {
               // Since we do not support it should return a error with 0 & NULL
               fPropsinError = TRUE;
               pPropInfoSet[ulPropertySets].guidPropertySet = DBPROPSET_SESSION;
               pPropInfoSet[ulPropertySets].cPropertyInfos  = 0;
            }
            else if( (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_ROWSETALL) ||
                   ((rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_ROWSET) &&
                    (rgPropertyIDSets[ulPropertySets].cPropertyIDs == 0)) )
            {
               fGetAllProps = TRUE;
               pPropInfoSet[ulPropertySets].guidPropertySet = DBPROPSET_ROWSET;
               pPropInfoSet[ulPropertySets].cPropertyInfos  = NUMBER_OF_SUPPORTED_ROWSET_PROPERTIES;
            }
            else if (rgPropertyIDSets[ulPropertySets].cPropertyIDs == 0)
               fPropsinError = TRUE;
         }
         else if (rgPropertyIDSets[ulPropertySets].cPropertyIDs == 0)
         {
            // Since we do not support it should return a error with 0 & NULL
            fPropsinError = TRUE;
         }
      }
      
      if (pPropInfoSet[ulPropertySets].cPropertyInfos)
      {
          // use task memory allocater to alloc array of DBPROPINFO structs
         pPropInfo = (DBPROPINFO*) g_pIMalloc->Alloc(sizeof( DBPROPINFO ) *
                           pPropInfoSet[ulPropertySets].cPropertyInfos);

         if (!pPropInfo)
         {
            if ( ppDescBuffer ) 
               *ppDescBuffer = NULL;
            g_pIMalloc->Free( pPropInfoSet );
            g_pIMalloc->Free( pDescBuffer );
            
            return ResultFromScode( E_OUTOFMEMORY );
         }
      
         pPropInfoSet[ulPropertySets].rgPropertyInfos = &pPropInfo[0];

         memset( pPropInfo, 0, 
            (pPropInfoSet[ulPropertySets].cPropertyInfos * sizeof( DBPROPINFO )));
      }

       // for each prop in our table..
      for (cCount=0; cCount < pPropInfoSet[ulPropertySets].cPropertyInfos; cCount++)
      {
         // init the Variant right up front
         // that way we can VariantClear with no worried (if we need to)
         VariantInit( &pPropInfo[cCount].vValues );

         // set the description pointer
         pPropInfo[cCount].pwszDescription = pDescBuffer;

         // Check supported property sets
         if ( (pPropInfoSet[ulPropertySets].guidPropertySet == DBPROPSET_DBINIT) && 
             (fGetAllProps) )
         {
            // load up their DBPROPINFO from our table
            fPropsSucceed = TRUE;
            fRet = LoadDBPROPINFO( &m_rgproperties[START_OF_SUPPORTED_DBINIT_PROPERTIES + cCount], 
                     &pPropInfo[cCount] );
         }
         else if ( (pPropInfoSet[ulPropertySets].guidPropertySet == DBPROPSET_DATASOURCEINFO) && 
              fGetAllProps && fDSOInitialized)
         {
            // load up their DBPROPINFO from our table
            fPropsSucceed = TRUE;
            fRet = LoadDBPROPINFO( &m_rgproperties[START_OF_SUPPORTED_DATASOURCEINFO_PROPERTIES + cCount], 
                     &pPropInfo[cCount] );
         }

         else if ( (pPropInfoSet[ulPropertySets].guidPropertySet == DBPROPSET_ROWSET) &&
                 fGetAllProps && fDSOInitialized)
         {
            // load up their DBPROPINFO from our table
            fPropsSucceed = TRUE;
            fRet = LoadDBPROPINFO( &m_rgproperties[START_OF_SUPPORTED_ROWSET_PROPERTIES + cCount], 
                     &pPropInfo[cCount] );
         }
         else
         {
            ULONG ulIndex;

            pPropInfo[cCount].dwPropertyID   = rgPropertyIDSets[ulPropertySets].rgPropertyIDs[cCount];
            pPropInfo[cCount].dwFlags      = DBPROPFLAGS_NOTSUPPORTED;

            if ( (GetPropIndex(rgPropertyIDSets[ulPropertySets].rgPropertyIDs[cCount], &ulIndex)) &&
                (fDSOInitialized || (pPropInfoSet[ulPropertySets].guidPropertySet == DBPROPSET_DBINIT)) )
            {
               fPropsSucceed = TRUE;
               fRet = LoadDBPROPINFO( &m_rgproperties[ulIndex], &pPropInfo[cCount] );
            }
            else
            {
               fPropsinError = TRUE;
               pPropInfo[cCount].pwszDescription = NULL;
            }
         }

         if (!fRet)
         {
            ULONG ulFor;

            // something went wrong
            // clear all variants used so far..
            for (ulFor = 0; ulFor < cCount; ulFor++)
               VariantClear( &pPropInfo[ulFor].vValues );

            // .. delete the pPropInfo array, return failure
            if ( ppDescBuffer ) *ppDescBuffer = NULL;
            g_pIMalloc->Free( pPropInfo );
            g_pIMalloc->Free( pPropInfoSet );
            g_pIMalloc->Free( pDescBuffer );
            return ResultFromScode( E_FAIL );
         }

         // move the description pointer to the next
         if ( pPropInfo[cCount].pwszDescription )
            pDescBuffer += (wcslen(pPropInfo[cCount].pwszDescription) + sizeof(CHAR));
      }
      // Set local back to FALSE
      fGetAllProps = FALSE;
   }

   // set count of properties and property information
   *pcPropertyInfoSets    = cProps;
   *prgPropertyInfoSets = pPropInfoSet;

   if ( !fPropsSucceed && fPropsinError )
   {
      if ( ppDescBuffer ) *ppDescBuffer = NULL;
      g_pIMalloc->Free( pDescBuffer );
      return ResultFromScode( DB_E_ERRORSOCCURRED );
   }
   else if ( fPropsSucceed && fPropsinError )
      return ResultFromScode( DB_S_ERRORSOCCURRED );
   else
      return ResultFromScode( S_OK );
}

The source code for GetProperties, which returns the current settings of all supported properties, follows. Immediately following the creation of the data source object, none of the properties are set. Properties are set by the SetProperties method.

// CUtilProp::GetProperties ----------------------------------------------------
//
// @mfunc Returns current settings of all properties supported by the DSO/rowset
//
// @rdesc HRESULT
//      @flag S_OK          | The method succeeded
//      @flag E_INVALIDARG  | pcProperties or prgPropertyInfo was NULL
//      @flag E_OUTOFMEMORY | Out of memory
//
STDMETHODIMP CUtilProp::GetProperties
    (
    DWORD               dwBitMask,           //@parm IN  | Mask if Initialized
    ULONG               cPropertyIDSets,     //@parm IN  | # of restiction property IDs
    const DBPROPIDSET   rgPropertyIDSets[],  //@parm IN  | restriction guids
    ULONG*              pcPropertySets,      //@parm OUT | count of properties returned
    DBPROPSET**         prgPropertySets      //@parm OUT | property information returned
    )
{
   BOOL         fRet           = TRUE;
   BOOL         fPropsinError  = FALSE;
   BOOL         fPropsSucceed  = FALSE;
   ULONG        cProps         = 0;
   ULONG        cCount         = 0;
   ULONG        ulPropertySets = 0;
   DBPROP*      pProp;
   DBPROPSET*   pPropSet;

   // save the count of PropertyIDSets
   cProps = cPropertyIDSets;

   // If the consumer does not restrict the property sets
   // by specify an array of property sets and a cPropertySets
   // greater than 0, then we need to make sure we 
   // have some to return
   if( cPropertyIDSets == 0 )
   {
      // only allow the DBINIT and DATASOURCE if Initialized
      if ( (dwBitMask & PROPSET_DSOINIT) == PROPSET_DSOINIT )
         cProps = 2;
      else
         cProps = 1;
   }

   // use task memory allocater to alloc a DBPROPINFOSET struct
   pPropSet = (DBPROPSET*) g_pIMalloc->Alloc(cProps *
                                             sizeof( DBPROPSET ));
   if ( !pPropSet )
      return ResultFromScode( E_OUTOFMEMORY );

   memset( pPropSet, 0, (cProps * sizeof( DBPROPSET )));

   // For each supported Property Set
   for (ulPropertySets=0; ulPropertySets < cProps; ulPropertySets++)
   {
      BOOL fGetAllProps = FALSE;

      // If no restrictions return all properties from the three supported property sets
      if ( cPropertyIDSets == 0 )
      {
            fGetAllProps = TRUE;
            
            // only do this once
            if ( ulPropertySets == 0 )
            {
               if( !(dwBitMask & PROPSET_SESSION) )
               {                  
                  if ( !(dwBitMask & PROPSET_ROWSET) )
                  {
                        pPropSet[0].guidPropertySet = DBPROPSET_DBINIT;
                        pPropSet[0].cProperties  = NUMBER_OF_SUPPORTED_DBINIT_PROPERTIES;

                     if( dwBitMask & PROPSET_INIT )
                     {
                        pPropSet[1].guidPropertySet = DBPROPSET_DATASOURCEINFO;
                        pPropSet[1].cProperties  = NUMBER_OF_SUPPORTED_DATASOURCEINFO_PROPERTIES;
                     }
                  }
                  else
                  {
                     pPropSet[0].guidPropertySet = DBPROPSET_ROWSET;
                     pPropSet[0].cProperties  = NUMBER_OF_SUPPORTED_ROWSET_PROPERTIES;
                  }
               }
            }
      }
      else
      {
         pPropSet[ulPropertySets].guidPropertySet = rgPropertyIDSets[ulPropertySets].guidPropertySet;
         pPropSet[ulPropertySets].cProperties  = rgPropertyIDSets[ulPropertySets].cPropertyIDs;

         if( rgPropertyIDSets[ulPropertySets].cPropertyIDs == 0 )
         {
            fGetAllProps = TRUE;

            if( rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DBINIT )
            {
               pPropSet[ulPropertySets].cProperties  = NUMBER_OF_SUPPORTED_DBINIT_PROPERTIES;
            }
            else if( (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DATASOURCEINFO) &&
                   ((dwBitMask & PROPSET_DSOINIT) == PROPSET_DSOINIT) )
            {
               pPropSet[ulPropertySets].cProperties  = NUMBER_OF_SUPPORTED_DATASOURCEINFO_PROPERTIES;
            }
            else if( (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_ROWSET) &&
                   (dwBitMask & PROPSET_ROWSET))
            {
               pPropSet[ulPropertySets].cProperties  = NUMBER_OF_SUPPORTED_ROWSET_PROPERTIES;
            }
            else
            {
               fGetAllProps = FALSE;
            }
         }
      }
      
      if( pPropSet[ulPropertySets].cProperties )
      {
          // use task memory allocater to alloc array of DBPROPINFO structs
         pProp = (DBPROP*) g_pIMalloc->Alloc(sizeof( DBPROP ) *
                               pPropSet[ulPropertySets].cProperties);

         if (!pProp)
         {
            for(ULONG ul=0; ul<ulPropertySets; ul++)
            {
               for(ULONG ul2=0; ul2<pPropSet[ul].cProperties; ul2++)
                  VariantClear( &pPropSet[ul].rgProperties[ul2].vValue );

               g_pIMalloc->Free( pPropSet[ul].rgProperties );
            }
            g_pIMalloc->Free( pPropSet );

            return ResultFromScode( E_OUTOFMEMORY );
         }
      
         pPropSet[ulPropertySets].rgProperties = &pProp[0];

         memset( pProp, 0, 
            (pPropSet[ulPropertySets].cProperties * sizeof( DBPROP )));
      }

       // for each prop in our table..
      for (cCount=0; cCount < pPropSet[ulPropertySets].cProperties; cCount++)
      {
         // init the Variant right up front
         // that way we can VariantClear with no worried (if we need to)
         VariantInit( &pProp[cCount].vValue );

         // Check supported property sets
         if ( pPropSet[ulPropertySets].guidPropertySet == DBPROPSET_DBINIT &&
             fGetAllProps )
         {
            fPropsSucceed = TRUE;
            // load up their DBPROP from our table
            fRet = LoadDBPROP( &m_rgproperties[START_OF_SUPPORTED_DBINIT_PROPERTIES + cCount], 
                              &pProp[cCount] );
         }
         else if ( pPropSet[ulPropertySets].guidPropertySet == DBPROPSET_DATASOURCEINFO &&
                  fGetAllProps )
         {
            fPropsSucceed = TRUE;
            // load up their DBPROPINFO from our table
            fRet = LoadDBPROP( &m_rgproperties[START_OF_SUPPORTED_DATASOURCEINFO_PROPERTIES + cCount], 
                              &pProp[cCount] );
         }
         else if ( pPropSet[ulPropertySets].guidPropertySet == DBPROPSET_ROWSET &&
                  fGetAllProps )
         {
            fPropsSucceed = TRUE;
            // load up their DBPROPINFO from our table
            fRet = LoadDBPROP( &m_rgproperties[START_OF_SUPPORTED_ROWSET_PROPERTIES + cCount], 
                              &pProp[cCount] );
         }
         else
         {
            ULONG ulIndex;

            pProp[cCount].dwPropertyID   = rgPropertyIDSets[ulPropertySets].rgPropertyIDs[cCount];
            pProp[cCount].dwStatus      = DBPROPSTATUS_NOTSUPPORTED;

            if(  (GetPropIndex(rgPropertyIDSets[ulPropertySets].rgPropertyIDs[cCount], &ulIndex)) &&
                (((dwBitMask & PROPSET_DSO) && 
                  (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DBINIT)) ||
                 (((dwBitMask & PROPSET_DSOINIT) == PROPSET_DSOINIT) &&
                  ((rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DATASOURCE) ||
                  (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DATASOURCEINFO))) ||
                 ((dwBitMask & PROPSET_SESSION) && 
                  (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_SESSION)) ||
                 ((dwBitMask & PROPSET_ROWSET) && 
                  (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_ROWSET))) )
            {
               fPropsSucceed = TRUE;
               fRet = LoadDBPROP( &m_rgproperties[ulIndex], &pProp[cCount] );
            }
            else
               fPropsinError = TRUE;
         }

         if (!fRet)
         {
            ULONG ulFor;

            // something went wrong
            // clear all variants used so far..
            for (ulFor = 0; ulFor < cCount; ulFor++)
               VariantClear( &pProp[ulFor].vValue );

            // .. delete the pPropInfo array, return failure
            g_pIMalloc->Free( pProp );
            g_pIMalloc->Free( pPropSet );
            return ResultFromScode( E_FAIL );
         }
      }
      // Set local back to FALSE
      fGetAllProps = FALSE;
   }

   // set count of properties and property information
   *pcPropertySets    = cProps;
   *prgPropertySets = pPropSet;

   if ( !fPropsSucceed && fPropsinError )
      return ResultFromScode( DB_E_ERRORSOCCURRED );
   else if ( fPropsSucceed && fPropsinError )
      return ResultFromScode( DB_S_ERRORSOCCURRED );
   else
      return ResultFromScode( S_OK );
}

The source code for SetProperties, which sets the values of all supported properties, follows. The consumer must set properties on the provider before initializing the data source object. In most providers, these properties provide information such as a database location, database name, user ID, and password. The sample provider requires only one value: a valid directory path to the data file.