Поддержка закладок поставщиками
Пример в этом разделе добавляет IRowsetLocate
интерфейс в CCustomRowset
класс. В почти всех случаях сначала добавляется интерфейс в существующий COM-объект. Затем его можно протестировать, добавив дополнительные вызовы из шаблонов потребителей. В примере показано, как:
Добавьте интерфейс в поставщик.
Динамически определите столбцы, возвращаемые потребителю.
Добавьте поддержку закладок.
Интерфейс IRowsetLocate
наследует от интерфейса IRowset
. Чтобы добавить IRowsetLocate
интерфейс, наследуется CCustomRowset
от IRowsetLocateImpl.
IRowsetLocate
Добавление интерфейса немного отличается от большинства интерфейсов. Для создания очереди VTABLEs шаблоны поставщиков 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
вызову QueryInterface
IRowsetLocate
интерфейса. Добавьте строку 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
. Добавьте макрос COM_INTERFACE_ENTRY_CHAIN для перехватчика на CRowsetImpl
карте. Кроме того, создайте имя RowsetBaseClass
типа, состоящее из сведений о наследовании. Этот типдф является произвольным и может быть проигнорирован.
Наконец, обработайте IColumnsInfo::GetColumnsInfo
вызов. Обычно для этого используются макросы PROVIDER_COLUMN_ENTRY. Однако потребитель может использовать закладки. Вы должны иметь возможность изменять столбцы, возвращаемые поставщиком в зависимости от того, запрашивает ли потребитель закладку.
Чтобы обработать IColumnsInfo::GetColumnsInfo
вызов, удалите карту PROVIDER_COLUMN в CTextData
классе. Макрос 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);
...
};
Затем реализуйте функцию GetColumnInfo
в файле customRS.cpp следующим образом:
////////////////////////////////////////////////////////////////////
// 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 для вставки сведений в массив. Макросы можно добавить в настраиваемый RS-сервер. 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
содержит код для вызова Compare
метода в интерфейсе IRowsetLocate
. Код, который вы всегда должны передавать, так как сравниваете те же закладки. Кроме того, сохраните одну закладку во временной переменной, чтобы ее можно было использовать после while
завершения цикла для вызова MoveToBookmark
функции в шаблонах потребителей. Функция MoveToBookmark
вызывает GetRowsAt
метод в IRowsetLocate
.
Кроме того, необходимо обновить запись пользователя в потребителе. Добавьте запись в класс для обработки закладки и записи в 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
помощью интерфейса.