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:
-
This code example is for illustrative purposes; the sample provider has not been rigorously tested.
-
IDBInitialize::Initialize requires the path to the .csv file. If the path is not supplied by the calling application, SAMPPROV prompts the user for the path.
-
The data source object can create only one session; the session can create only one rowset. This is because the underlying Cfileio object opens the .csv text files in exclusive mode, and has no mechanism to share access to the file.
-
The use of properties is very simple. Only a small set of properties are illustrated, and properties are read-only.
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:
-
OLEDB.h
-
OLEDBERR.h
-
OLEDB.LIB
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.
-
The data source object, which enables consumers to connect and initialize the interaction with the data file.
-
The session, which enables consumers to create a rowset for the data set in the data file.
-
The rowset, which exposes a data set to the consumer.
These objects, along with the interfaces implemented in the sample, are described in detail in the following topics, which also contain some of the implementation code for specific interfaces and methods.
The following table lists the interfaces and methods implemented in the sample provider along with the name of the source code file in which each can be found.
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:
-
GetProperties returns the list of properties currently set on the data source object.
-
GetPropertyInfo returns information about supported rowset and data source properties.
-
SetProperties sets the properties on the data source object.
The IDBInfo interface contains two methods:
-
GetKeywords returns a list of supported keywords.
-
GetLiteralInfo returns information about literals used in text commands.
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.