Supporto dei bookmark nel provider

L'esempio in questo argomento aggiunge l'interfaccia IRowsetLocate alla CCustomRowset classe . In quasi tutti i casi, si inizia aggiungendo un'interfaccia a un oggetto COM esistente. È quindi possibile testarlo aggiungendo altre chiamate dai modelli di consumer. L'esempio illustra come:

  • Aggiungere un'interfaccia a un provider.

  • Determinare dinamicamente le colonne da restituire al consumer.

  • Aggiungere il supporto dei segnalibri.

L'interfaccia IRowsetLocate eredita dall'interfaccia IRowset . Per aggiungere l'interfaccia IRowsetLocate , ereditare CCustomRowset da IRowsetLocateImpl.

L'aggiunta dell'interfaccia IRowsetLocate è leggermente diversa dalla maggior parte delle interfacce. Per far sì che le VTABLEs siano allineate, i modelli di provider OLE DB hanno un parametro di modello per gestire l'interfaccia derivata. Il codice seguente mostra il nuovo elenco di ereditarietà:

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

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

Vengono aggiunti tutti i parametri quarto, quinto e sesto. In questo esempio vengono utilizzate le impostazioni predefinite per il quarto e il quinto parametro, ma viene specificato IRowsetLocateImpl come sesto parametro. IRowsetLocateImpl è una classe modello OLE DB che accetta due parametri di modello: questi associano l'interfaccia IRowsetLocate alla CCustomRowset classe . Per aggiungere la maggior parte delle interfacce, è possibile ignorare questo passaggio e passare a quello successivo. Solo le IRowsetLocate interfacce e IRowsetScroll devono essere gestite in questo modo.

È quindi necessario indicare all'oggetto CCustomRowset di chiamare QueryInterface per l'interfaccia IRowsetLocate . Aggiungere la linea COM_INTERFACE_ENTRY(IRowsetLocate) alla mappa. La mappa dell'interfaccia per CCustomRowset dovrebbe essere visualizzata come illustrato nel codice seguente:

////////////////////////////////////////////////////////////////////////
// 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()

È anche necessario associare la mappa alla CRowsetImpl classe . Aggiungere nella macro COM_INTERFACE_ENTRY_CHAIN da associare alla CRowsetImpl mappa. Creare anche un typedef denominato RowsetBaseClass costituito dalle informazioni sull'ereditarietà. Questo typedef è arbitrario e può essere ignorato.

Infine, gestire la IColumnsInfo::GetColumnsInfo chiamata. Per eseguire questa operazione, in genere si userebbero le macro PROVIDER_COLUMN_ENTRY. Tuttavia, un consumer potrebbe voler usare segnalibri. È necessario essere in grado di modificare le colonne restituite dal provider a seconda che il consumer richieda un segnalibro.

Per gestire la IColumnsInfo::GetColumnsInfo chiamata, eliminare la mappa PROVIDER_COLUMN nella CTextData classe . La macro PROVIDER_COLUMN_MAP definisce una funzione GetColumnInfo. Definire una funzione personalizzata GetColumnInfo . La dichiarazione di funzione dovrebbe essere simile alla seguente:

////////////////////////////////////////////////////////////////////////
// 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);
...
};

Implementare quindi la GetColumnInfo funzione nel file di RS.cpp personalizzatocome indicato di seguito:

////////////////////////////////////////////////////////////////////
// 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 controlla prima di tutto se è impostata una proprietà denominata DBPROP_IRowsetLocate . OLE DB dispone di proprietà per ognuna delle interfacce facoltative all'esterno dell'oggetto set di righe. Se il consumer vuole usare una di queste interfacce facoltative, imposta una proprietà su true. Il provider può quindi controllare questa proprietà ed eseguire un'azione speciale in base a essa.

Nell'implementazione si ottiene la proprietà usando il puntatore all'oggetto comando. Il pThis puntatore rappresenta il set di righe o la classe di comando. Poiché si usano i modelli qui, è necessario passarlo come void puntatore o il codice non viene compilato.

Specificare una matrice statica per contenere le informazioni sulla colonna. Se il consumer non vuole che la colonna segnalibro, una voce nella matrice viene sprecato. È possibile allocare dinamicamente questa matrice, ma è necessario assicurarsi di eliminarla correttamente. In questo esempio vengono definite e utilizzate le macro ADD_COLUMN_ENTRY e ADD_COLUMN_ENTRY_EX per inserire le informazioni nella matrice. È possibile aggiungere le macro a CustomRS. File H come illustrato nel codice seguente:

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

Per testare il codice nel consumer, è necessario apportare alcune modifiche al OnRun gestore. La prima modifica alla funzione consiste nell'aggiungere codice per aggiungere una proprietà al set di proprietà. Il codice imposta la DBPROP_IRowsetLocate proprietà su true, in modo da indicare al provider che si desidera la colonna del segnalibro. Il OnRun codice del gestore dovrebbe essere visualizzato nel modo seguente:

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

Il while ciclo contiene codice per chiamare il Compare metodo nell'interfaccia IRowsetLocate . Il codice da passare deve essere sempre passato perché si confrontano esattamente gli stessi segnalibri. Archiviare anche un segnalibro in una variabile temporanea in modo da poterlo usare al termine del while ciclo per chiamare la MoveToBookmark funzione nei modelli di consumer. La MoveToBookmark funzione chiama il GetRowsAt metodo in IRowsetLocate.

È anche necessario aggiornare il record utente nel consumer. Aggiungere una voce nella classe per gestire un segnalibro e una voce nel 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()
};

Dopo aver aggiornato il codice, dovrebbe essere possibile compilare ed eseguire il provider con l'interfaccia IRowsetLocate .

Vedi anche

Tecniche avanzate del provider