The PROVIDER_COLUMN_ENTRY macros normally handle the IColumnsInfo::GetColumnsInfo call. Since a consumer may or may not choose to use bookmarks, however, the provider must be able to change the columns returned depending upon whether or not the consumer asks for a bookmark.
To handle the IColumnsInfo::GetColumnsInfo call, delete the PROVIDER_COLUMN_MAP (which defines a function _GetColumnInfo
) from the CAgentMan
user record in MyProviderRS.h and replace it with the definition for your own _GetColumnInfo
function:
////////////////////////////////////////////////////////////////////////
// MyProviderRS.H
class CAgentMan
{
public:
DWORD dwBookmark;
TCHAR szCommand[256];
TCHAR szText[256];
TCHAR szCommand2[256];
TCHAR szText2[256];
static ATLCOLUMNINFO* _GetColumnInfo(void* pThis, ULONG* pcCols);
bool operator==(const CAgentMan& am)
{
return (lstrcmpi(szCommand, am.szCommand) == 0);
}
};
Next, implement the _GetColumnInfo
function in MyProviderRS.cpp, as shown in the following code.
_GetColumnInfo
checks first to see if the OLE DB property DBPROP_IRowsetLocate is set. To get the property, _GetColumnInfo
uses a pointer (pRowset
) to the rowset object. The pThis
pointer represents the class that created the rowset (which is the class where the property map is stored). _GetColumnInfo
typecasts the pThis
pointer to a RMyProviderRowset
pointer.
To check for the DBPROP_IRowsetLocate property, _GetColumnInfo
uses the IRowsetInfo interface, which you can obtain by calling QueryInterface on the pRowset
interface. (As an alternative, you can use an ATL CComQIPtr instead.)
////////////////////////////////////////////////////////////////////
// MyProviderRS.cpp
ATLCOLUMNINFO* CAgentMan::_GetColumnInfo(void* pThis, ULONG* pcCols)
{
static ATLCOLUMNINFO _rgColumns[5];
ULONG ulCols = 0;
// Check the property flag for bookmarks, if it is set, set the zero ordinal
// entry in the column map with the bookmark information.
CAgentRowset* pRowset = (CAgentRowset*) pThis;
CComQIPtr<IRowsetInfo, &IID_IRowsetInfo> spRowsetProps = pRowset;
CDBPropIDSet set(DBPROPSET_ROWSET);
set.AddPropertyID(DBPROP_BOOKMARKS);
DBPROPSET* pPropSet = NULL;
ULONG ulPropSet = 0;
HRESULT hr;
if (spRowsetProps)
hr = spRowsetProps->GetProperties(1, &set, &ulPropSet, &pPropSet);
if (pPropSet)
{
CComVariant var = pPropSet->rgProperties[0].vValue;
CoTaskMemFree(pPropSet->rgProperties);
CoTaskMemFree(pPropSet);
if (SUCCEEDED(hr) && (var.boolVal == VARIANT_TRUE))
{
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Bookmark"), 0, sizeof(DWORD), DBTYPE_BYTES,
0, 0, GUID_NULL, CAgentMan, dwBookmark, DBCOLUMNFLAGS_ISBOOKMARK)
ulCols++;
}
}
// Next set the other columns up.
ADD_COLUMN_ENTRY(ulCols, OLESTR("Command"), 1, 256, DBTYPE_STR, 0xFF, 0xFF,
GUID_NULL, CAgentMan, szCommand)
ulCols++;
ADD_COLUMN_ENTRY(ulCols, OLESTR("Text"), 2, 256, DBTYPE_STR, 0xFF, 0xFF,
GUID_NULL, CAgentMan, szText)
ulCols++;
ADD_COLUMN_ENTRY(ulCols, OLESTR("Command2"), 3, 256, DBTYPE_STR, 0xFF, 0xFF,
GUID_NULL, CAgentMan, szCommand2)
ulCols++;
ADD_COLUMN_ENTRY(ulCols, OLESTR("Text2"), 4, 256, DBTYPE_STR, 0xFF, 0xFF,
GUID_NULL, CAgentMan, szText2)
ulCols++;
if (pcCols != NULL)
*pcCols = ulCols;
return _rgColumns;
}
This example uses a static array to contain the column information. If the consumer does not want the bookmark column, one entry in the array is unused. To handle the information, you create two array macros, ADD_COLUMN_ENTRY and ADD_COLUMN_ENTRY_EX. ADD_COLUMN_ENTRY_EX takes an extra parameter, flags
, that is needed if you designate a bookmark column.
////////////////////////////////////////////////////////////////////////
// MyProviderRS.h
#define ADD_COLUMN_ENTRY(ulCols, name, ordinal, colSize, type, precision, scale, guid, dataClass, member) \
_rgColumns[ulCols].pwszName = (LPOLESTR)name; \
_rgColumns[ulCols].pTypeInfo = (ITypeInfo*)NULL; \
_rgColumns[ulCols].iOrdinal = (ULONG)ordinal; \
_rgColumns[ulCols].dwFlags = 0; \
_rgColumns[ulCols].ulColumnSize = (ULONG)colSize; \
_rgColumns[ulCols].wType = (DBTYPE)type; \
_rgColumns[ulCols].bPrecision = (BYTE)precision; \
_rgColumns[ulCols].bScale = (BYTE)scale; \
_rgColumns[ulCols].cbOffset = offsetof(dataClass, member);
#define ADD_COLUMN_ENTRY_EX(ulCols, name, ordinal, colSize, type, precision, scale, guid, dataClass, member, flags) \
_rgColumns[ulCols].pwszName = (LPOLESTR)name; \
_rgColumns[ulCols].pTypeInfo = (ITypeInfo*)NULL; \
_rgColumns[ulCols].iOrdinal = (ULONG)ordinal; \
_rgColumns[ulCols].dwFlags = flags; \
_rgColumns[ulCols].ulColumnSize = (ULONG)colSize; \
_rgColumns[ulCols].wType = (DBTYPE)type; \
_rgColumns[ulCols].bPrecision = (BYTE)precision; \
_rgColumns[ulCols].bScale = (BYTE)scale; \
_rgColumns[ulCols].cbOffset = offsetof(dataClass, member); \
memset(&(_rgColumns[ulCols].columnid), 0, sizeof(DBID)); \
_rgColumns[ulCols].columnid.uName.pwszName = (LPOLESTR)name;
In the _GetColumnInfo
function, the bookmark macro is used like this:
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Bookmark"), 0, sizeof(DWORD),
DBTYPE_BYTES, 0, 0, GUID_NULL, CAgentMan, dwBookmark,
DBCOLUMNFLAGS_ISBOOKMARK)
You can now compile and run the enhanced provider. To test the provider, modify the test consumer, as described in Modifying the Consumer for Use with the Enhanced Provider. Run the test consumer with the provider. Verify that the test consumer retrieves the proper strings from the provider when you click the Run button on the test consumer dialog box.