次の方法で共有


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

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

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

  • コンシューマーに返す列を動的に決める方法

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

IRowsetLocate インターフェイスは IRowset インターフェイスを継承します。IRowsetLocate インターフェイスを追加するには、CMyProviderRowset を IRowsetLocateImpl から派生させます。

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

////////////////////////////////////////////////////////////////////////
// MyProviderRS.h

// CMyProviderRowset
class CMyProviderRowset : public CRowsetImpl< CMyProviderRowset, 
      CTextData, CMyProviderCommand, CAtlArray<CTextData>, 
      CSimpleRow, 
          IRowsetLocateImpl<CMyProviderRowset, IRowsetLocate> >

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

次に、CMyProviderRowset に IRowsetLocate インターフェイスの QueryInterface を呼び出させる必要があります。COM_INTERFACE_ENTRY(IRowsetLocate) 行をマップに追加します。CMyProviderRowset のインターフェイス マップは、次のコードのようになります。

////////////////////////////////////////////////////////////////////////
// MyProviderRS.h

typedef CRowsetImpl< CMyProviderRowset, CTextData, CMyProviderCommand, CAtlArray<CTextData>, CSimpleRow, IRowsetLocateImpl<CMyProviderRowset, IRowsetLocate> > _RowsetBaseClass;

BEGIN_COM_MAP(CMyProviderRowset)
   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 関数を定義する必要があります。関数宣言は次のようになります。

////////////////////////////////////////////////////////////////////////
// MyProviderRS.H

class CTextData
{
   ...
     // NOTE: Be sure you removed the PROVIDER_COLUMN_MAP!
   static ATLCOLUMNINFO* GetColumnInfo(CMyProviderRowset* pThis, 
        ULONG* pcCols);
   static ATLCOLUMNINFO* GetColumnInfo(CMyProviderCommand* pThis, 
        ULONG* pcCols);
...
};

その後、次のように GetColumnInfo 関数を MyProviderRS.cpp ファイルに実装します。

////////////////////////////////////////////////////////////////////
// MyProviderRS.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(CMyProviderCommand* 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 型のポインターとして渡す必要があります。そうしないとコードがコンパイルされません。

次に、列情報を格納するための静的配列を指定します。コンシューマーがブックマーク列を要求しない場合は、配列のエントリを 1 つ余分に使用することになります。この配列を動的に割り当てることができますが、割り当てた配列が適切に破棄されるようにする必要があります。ここでは ADD_COLUMN_ENTRY マクロと ADD_COLUMN_ENTRY_EX マクロを定義および使用して、情報を配列に挿入します。このマクロを以下のように MyProviderRS.H ファイルに追加できます。

////////////////////////////////////////////////////////////////////////
// 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;

コンシューマー内のコードをテストするには、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("MyProvider.MyProvider.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 関数は、IRowsetLocateGetRowsAt メソッドを呼び出します。

コンシューマーのユーザー レコードも更新する必要があります。ブックマークを処理するクラスと COLUMN_MAP にそれぞれエントリを 1 つ追加します。

///////////////////////////////////////////////////////////////////////
// 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 インターフェイスを使用してプロバイダーをビルドし、実行できるようになります。

参照

概念

高度なプロバイダー手法