用于书签的提供程序支持
本主题中的示例将 IRowsetLocate
接口添加到 CCustomRowset
类。 几乎所有情况都需要首先将接口添加到现有 COM 对象。 然后,可以通过添加来自使用者模板的更多调用来测试它。 示例演示如何执行以下操作:
向提供程序添加接口。
动态确定要返回到使用者的列。
添加书签支持。
IRowsetLocate
接口继承自 IRowset
接口。 若要添加 IRowsetLocate
接口,请从 IRowsetLocateImpl 继承 CCustomRowset
。
添加 IRowsetLocate
接口的操作与添加大多数接口的操作略有不同。 若要将 VTABLE 排队,可以使用 OLE DB 提供程序模板的用于处理派生接口的模板参数。 以下代码显示了新的继承列表:
////////////////////////////////////////////////////////////////////////
// CustomRS.h
// CCustomRowset
class CCustomRowset : public CRowsetImpl< CCustomRowset,
CTextData, CCustomCommand, CAtlArray<CTextData>,
CSimpleRow,
IRowsetLocateImpl<CCustomRowset, IRowsetLocate>>
将添加第四个、第五个和第六个参数。 此示例使用第四个和第五个参数的默认值,但指定 IRowsetLocateImpl
作为第六个参数。 IRowsetLocateImpl
是一个 OLE DB 模板类,它采用两个模板参数:这些参数将 IRowsetLocate
接口挂接到 CCustomRowset
类。 若要添加大多数接口,可以跳过此步骤,转到下一个步骤。 仅 IRowsetLocate
和 IRowsetScroll
接口需以这种方式处理。
然后,需告知 CCustomRowset
为 IRowsetLocate
接口调用 QueryInterface
。 向映射添加 COM_INTERFACE_ENTRY(IRowsetLocate)
行。 应会显示 CCustomRowset
的接口映射,如以下代码所示:
////////////////////////////////////////////////////////////////////////
// CustomRS.h
typedef CRowsetImpl< CCustomRowset, CTextData, CCustomCommand, CAtlArray<CTextData>, CSimpleRow, IRowsetLocateImpl<CCustomRowset, IRowsetLocate>> _RowsetBaseClass;
BEGIN_COM_MAP(CCustomRowset)
COM_INTERFACE_ENTRY(IRowsetLocate)
COM_INTERFACE_ENTRY_CHAIN(_RowsetBaseClass)
END_COM_MAP()
还需将映射挂接到 CRowsetImpl
类中。 添加要在 CRowsetImpl
映射中挂接的 COM_INTERFACE_ENTRY_CHAIN 宏。 另请创建一个名为 RowsetBaseClass
的 typedef,其中包含继承信息。 此 typedef 是任意的,可以忽略。
最后,处理 IColumnsInfo::GetColumnsInfo
调用。 通常可以使用 PROVIDER_COLUMN_ENTRY 宏执行此操作。 但是,使用者可能想要使用书签。 必须能够更改提供程序返回的列,具体取决于使用者是否要求使用书签。
若要处理 IColumnsInfo::GetColumnsInfo
调用,请删除 CTextData
类中的 PROVIDER_COLUMN 映射。 PROVIDER_COLUMN_MAP 宏定义函数 GetColumnInfo
。 定义自己的 GetColumnInfo
函数。 函数声明应如下所示:
////////////////////////////////////////////////////////////////////////
// CustomRS.H
class CTextData
{
...
// NOTE: Be sure you removed the PROVIDER_COLUMN_MAP!
static ATLCOLUMNINFO* GetColumnInfo(CCustomRowset* pThis,
ULONG* pcCols);
static ATLCOLUMNINFO* GetColumnInfo(CCustomCommand* pThis,
ULONG* pcCols);
...
};
然后,在 CustomRS.cpp 文件中实现 GetColumnInfo
函数,如下所示:
////////////////////////////////////////////////////////////////////
// CustomRS.cpp
template <class TInterface>
ATLCOLUMNINFO* CommonGetColInfo(IUnknown* pPropsUnk, ULONG* pcCols)
{
static ATLCOLUMNINFO _rgColumns[5];
ULONG ulCols = 0;
CComQIPtr<TInterface> spProps = pPropsUnk;
CDBPropIDSet set(DBPROPSET_ROWSET);
set.AddPropertyID(DBPROP_BOOKMARKS);
DBPROPSET* pPropSet = NULL;
ULONG ulPropSet = 0;
HRESULT hr;
if (spProps)
hr = spProps->GetProperties(1, &set, &ulPropSet, &pPropSet);
// Check the property flag for bookmarks, if it is set, set the
// zero ordinal entry in the column map with the bookmark
// information.
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_EX(ulCols, OLESTR("Field1"), 1, 16, DBTYPE_STR,
0xFF, 0xFF, GUID_NULL, CTextData, szField1)
ulCols++;
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Field2"), 2, 16, DBTYPE_STR,
0xFF, 0xFF, GUID_NULL, CTextData, szField2)
ulCols++;
if (pcCols != NULL)
*pcCols = ulCols;
return _rgColumns;
}
ATLCOLUMNINFO* CTextData::GetColumnInfo(CCustomCommand* pThis,
ULONG* pcCols)
{
return CommonGetColInfo<ICommandProperties>(pThis->GetUnknown(),
pcCols);
}
ATLCOLUMNINFO* CAgentMan::GetColumnInfo(RUpdateRowset* pThis, ULONG* pcCols)
{
return CommonGetColInfo<IRowsetInfo>(pThis->GetUnknown(), pcCols);
}
GetColumnInfo
首先查看是否设置了名为 DBPROP_IRowsetLocate
的属性。 OLE DB 具有行集对象的每个可选接口的属性。 如果使用者想要使用这些可选接口之一,可将某个属性设置为 true。 然后,提供程序就可以检查此属性,并根据它执行特殊操作。
在实现中,请使用指向命令对象的指针获取属性。 pThis
指针表示行集或命令类。 由于你在此处使用模板,因此必须将其作为 void
指针传入,否则代码不会进行编译。
请指定用于保存列信息的静态数组。 如果使用者不想要使用书签列,则会浪费数组中的条目。 可以动态分配此数组,但需要确保将其正确销毁。 此示例定义并使用 ADD_COLUMN_ENTRY 和 ADD_COLUMN_ENTRY_EX 宏将信息插入数组中。 可以将宏添加到 CustomRS.H 文件,如以下代码所示:
////////////////////////////////////////////////////////////////////////
// CustomRS.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;
若要在使用者中测试代码,需要对 OnRun
处理程序进行一些更改。 对函数的第一个更改是添加代码以将属性添加到属性集。 代码将 DBPROP_IRowsetLocate
属性设置为 true,从而告知提供程序:你需要书签列。 OnRun
处理程序代码应如下所示:
//////////////////////////////////////////////////////////////////////
// TestProv Consumer Application in TestProvDlg.cpp
void CTestProvDlg::OnRun()
{
CCommand<CAccessor<CProvider>> table;
CDataSource source;
CSession session;
if (source.Open("Custom.Custom.1", NULL, NULL, NULL,
NULL) != S_OK)
return;
if (session.Open(source) != S_OK)
return;
CDBPropSet propset(DBPROPSET_ROWSET);
propset.AddProperty(DBPROP_IRowsetLocate, true);
if (table.Open(session, _T("c:\\public\\testprf2\\myData.txt"),
&propset) != S_OK)
return;
CBookmark<4> tempBookmark;
ULONG ulCount=0;
while (table.MoveNext() == S_OK)
{
DBCOMPARE compare;
if (ulCount == 2)
tempBookmark = table.bookmark;
HRESULT hr = table.Compare(table.dwBookmark, table.dwBookmark,
&compare);
if (FAILED(hr))
ATLTRACE(_T("Compare failed: 0x%X\n"), hr);
else
_ASSERTE(compare == DBCOMPARE_EQ);
m_ctlString1.AddString(table.szField1);
m_ctlString2.AddString(table.szField2);
ulCount++;
}
table.MoveToBookmark(tempBookmark);
m_ctlString1.AddString(table.szField1);
m_ctlString2.AddString(table.szField2);
}
while
循环包含用于在 IRowsetLocate
接口中调用 Compare
方法的代码。 你的代码应始终通过,因为你在比较完全相同的书签。 另请在临时变量中存储一个书签,这样就可以在 while
循环完成后使用它来调用使用者模板中的 MoveToBookmark
函数。 MoveToBookmark
函数在 IRowsetLocate
中调用 GetRowsAt
方法。
还需要更新使用者中的用户记录。 在类中添加一个条目以处理书签和 COLUMN_MAP 中的条目:
///////////////////////////////////////////////////////////////////////
// TestProvDlg.cpp
class CProvider
{
// Attributes
public:
CBookmark<4> bookmark; // Add this line
char szCommand[16];
char szText[256];
// Binding Maps
BEGIN_ACCESSOR_MAP(CProvider, 1)
BEGIN_ACCESSOR(0, true) // auto accessor
BOOKMARK_ENTRY(bookmark) // Add this line
COLUMN_ENTRY(1, szField1)
COLUMN_ENTRY(2, szField2)
END_ACCESSOR()
END_ACCESSOR_MAP()
};
更新代码后,应能够使用 IRowsetLocate
接口生成并执行提供程序。