Поделиться через


Поддержка закладок поставщиками

Пример в этом разделе добавляет 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 помощью интерфейса.

См. также

Дополнительные способы использования поставщика