次の方法で共有


プロバイダーのブックマーク サポート

このトピックの例では、IRowsetLocate インターフェイスを CCustomRowset クラスに追加します。 ほとんどの場合、最初に既存の COM オブジェクトにインターフェイスを追加します。 その後、コンシューマー テンプレートからの呼び出しをさらに追加してテストできます。 この例では、次の方法を示します。

  • プロバイダーにインターフェイスを追加する。

  • コンシューマーに返す列を動的に判断する。

  • ブックマーク サポートを追加する。

IRowsetLocate インターフェイスは IRowset インターフェイスを継承します。 IRowsetLocate インターフェイスを追加するには、CCustomRowsetIRowsetLocateImpl から継承します。

IRowsetLocate インターフェイスの追加は、ほとんどのインターフェイスとは少し異なります。 VTABLE を準備するために、OLE DB プロバイダー テンプレートには、派生インターフェイスを処理するテンプレート パラメーターがあります。 次のコードは、新しい継承リストを示しています。

////////////////////////////////////////////////////////////////////////
// CustomRS.h

// CCustomRowset
class CCustomRowset : public CRowsetImpl< CCustomRowset,
      CTextData, CCustomCommand, CAtlArray<CTextData>,
      CSimpleRow,
          IRowsetLocateImpl<CCustomRowset, IRowsetLocate>>

4 番目、5 番目、および 6 番目のパラメーターがすべて追加されます。 この例では、4 番目と 5 番目のパラメーターに既定値を使用しますが、6 番目のパラメーターとして IRowsetLocateImpl を指定します。 IRowsetLocateImpl は、2 つのテンプレート パラメーターを受け取る OLE DB テンプレート クラスです。これらは、IRowsetLocate インターフェイスを CCustomRowset クラスにフックします。 ほとんどのインターフェイスを追加するには、この手順をスキップし、次の手順に移動します。 IRowsetLocate インターフェイスと IRowsetScroll インターフェイスのみは、この方法で処理する必要があります。

次に、IRowsetLocate インターフェイスのために QueryInterface を呼び出すように CCustomRowset に指示する必要があります。 行 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 という名前の 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);
...
};

その後、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 を定義して使用し、情報を配列に挿入します。 次のコードに示すようにマクロを 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 メソッドを呼び出すコードが含まれています。 まったく同じブックマークを比較しているため、使用しているコードは常に合格するはずです。 また、1 つのブックマークを一時変数に格納します。それを while ループが終わってから使用すると、コンシューマー テンプレートの MoveToBookmark 関数を呼び出すことができます。 MoveToBookmark 関数は IRowsetLocateGetRowsAt メソッドを呼び出します。

また、コンシューマーのユーザー レコードを更新することも必要です。 エントリをクラスに追加して、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 インターフェイスを使用してプロバイダーをビルドして実行できるようになります。

関連項目

高度なプロバイダー手法