共用方式為


提供者書籤支援

本主題中的範例會將 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>>

第四個、第五個和第六個參數全部新增。 此範例使用第四個和第五個參數的預設值,但指定 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 的 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);
...
};

然後,在自定義RS.cpp檔案中實作 函GetColumnInfo式,如下所示:

////////////////////////////////////////////////////////////////////
// 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迴圈包含在介面中IRowsetLocate呼叫 Compare 方法的程序代碼。 您一律應該傳遞的程式代碼,因為您正在比較完全相同的書籤。 此外,將一個書簽儲存在暫存變數中,以便在迴圈完成之後 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 介面來建置和執行提供者。

另請參閱

進階的提供者技術