プロバイダーのブックマーク サポート
このトピックの例では、IRowsetLocate
インターフェイスを CCustomRowset
クラスに追加します。 ほとんどの場合、最初に既存の COM オブジェクトにインターフェイスを追加します。 その後、コンシューマー テンプレートからの呼び出しをさらに追加してテストできます。 この例では、次の方法を示します。
プロバイダーにインターフェイスを追加する。
コンシューマーに返す列を動的に判断する。
ブックマーク サポートを追加する。
IRowsetLocate
インターフェイスは IRowset
インターフェイスを継承します。 IRowsetLocate
インターフェイスを追加するには、CCustomRowset
を IRowsetLocateImpl から継承します。
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
関数は 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
インターフェイスを使用してプロバイダーをビルドして実行できるようになります。