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
.