Prise en charge des signets par le fournisseur
L’exemple de cette rubrique ajoute l’interface IRowsetLocate
à la CCustomRowset
classe. Dans presque tous les cas, vous commencez par ajouter une interface à un objet COM existant. Vous pouvez ensuite le tester en ajoutant d’autres appels à partir des modèles consommateur. L’exemple montre comment :
Ajoutez une interface à un fournisseur.
Déterminez dynamiquement les colonnes à retourner au consommateur.
Ajoutez la prise en charge des signets.
L'interface IRowsetLocate
hérite de l'interface IRowset
. Pour ajouter l’interface IRowsetLocate
, héritez CCustomRowset
de IRowsetLocateImpl.
L’ajout de l’interface IRowsetLocate
est un peu différent de la plupart des interfaces. Pour aligner les VTABLEs, les modèles de fournisseur OLE DB ont un paramètre de modèle pour gérer l’interface dérivée. Le code suivant montre la nouvelle liste d’héritage :
////////////////////////////////////////////////////////////////////////
// CustomRS.h
// CCustomRowset
class CCustomRowset : public CRowsetImpl< CCustomRowset,
CTextData, CCustomCommand, CAtlArray<CTextData>,
CSimpleRow,
IRowsetLocateImpl<CCustomRowset, IRowsetLocate>>
Les quatrième, cinquième et sixième paramètres sont tous ajoutés. Cet exemple utilise les valeurs par défaut pour les quatrième et cinquième paramètres, mais spécifiez IRowsetLocateImpl
comme sixième paramètre. IRowsetLocateImpl
est une classe de modèle OLE DB qui prend deux paramètres de modèle : celles-ci raccordent l’interface IRowsetLocate
à la CCustomRowset
classe. Pour ajouter la plupart des interfaces, vous pouvez ignorer cette étape et passer à la suivante. Seules les IRowsetLocate
interfaces doivent IRowsetScroll
être gérées de cette façon.
Vous devez ensuite indiquer à l’utilisateur CCustomRowset
d’appeler QueryInterface
l’interface IRowsetLocate
. Ajoutez la ligne COM_INTERFACE_ENTRY(IRowsetLocate)
à la carte. Le mappage CCustomRowset
d’interface doit apparaître comme indiqué dans le code suivant :
////////////////////////////////////////////////////////////////////////
// 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()
Vous devez également raccorder votre carte à la CRowsetImpl
classe. Ajoutez la macro COM_INTERFACE_ENTRY_CHAIN pour se connecter à la CRowsetImpl
carte. Créez également un typedef appelé RowsetBaseClass
qui se compose des informations d’héritage. Ce typedef est arbitraire et peut être ignoré.
Enfin, gérez l’appel IColumnsInfo::GetColumnsInfo
. Vous utilisez normalement les macros PROVIDER_COLUMN_ENTRY pour effectuer cette opération. Toutefois, un consommateur peut vouloir utiliser des signets. Vous devez être en mesure de modifier les colonnes retournées par le fournisseur selon que le consommateur demande un signet.
Pour gérer l’appel IColumnsInfo::GetColumnsInfo
, supprimez le mappage PROVIDER_COLUMN dans la CTextData
classe. La macro PROVIDER_COLUMN_MAP définit une fonction GetColumnInfo
. Définissez votre propre GetColumnInfo
fonction. La déclaration de fonction doit ressembler à ceci :
////////////////////////////////////////////////////////////////////////
// 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);
...
};
Ensuite, implémentez la GetColumnInfo
fonction dans le fichier de RS.cpp personnalisécomme suit :
////////////////////////////////////////////////////////////////////
// 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
vérifie d’abord si une propriété appelée DBPROP_IRowsetLocate
est définie. OLE DB a des propriétés pour chacune des interfaces facultatives de l’objet d’ensemble de lignes. Si le consommateur souhaite utiliser l’une de ces interfaces facultatives, il définit une propriété sur true. Le fournisseur peut ensuite vérifier cette propriété et effectuer une action spéciale en fonction de celui-ci.
Dans votre implémentation, vous obtenez la propriété à l’aide du pointeur vers l’objet de commande. Le pThis
pointeur représente l’ensemble de lignes ou la classe de commandes. Étant donné que vous utilisez des modèles ici, vous devez le transmettre en tant que void
pointeur ou que le code ne se compile pas.
Spécifiez un tableau statique pour contenir les informations de colonne. Si le consommateur ne souhaite pas la colonne signet, une entrée dans le tableau est perdue. Vous pouvez allouer dynamiquement ce tableau, mais vous devez vous assurer de le détruire correctement. Cet exemple définit et utilise les macros ADD_COLUMN_ENTRY et ADD_COLUMN_ENTRY_EX pour insérer les informations dans le tableau. Vous pouvez ajouter les macros au RS personnalisé. Fichier H, comme indiqué dans le code suivant :
////////////////////////////////////////////////////////////////////////
// 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;
Pour tester le code dans le consommateur, vous devez apporter quelques modifications au OnRun
gestionnaire. La première modification apportée à la fonction est que vous ajoutez du code pour ajouter une propriété au jeu de propriétés. Le code définit la DBPROP_IRowsetLocate
propriété sur true, indiquant ainsi au fournisseur que vous souhaitez la colonne signet. Le code du OnRun
gestionnaire doit apparaître comme suit :
//////////////////////////////////////////////////////////////////////
// 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);
}
La boucle contient du while
code pour appeler la Compare
méthode dans l’interface IRowsetLocate
. Le code que vous avez toujours passé doit toujours passer, car vous comparez exactement les mêmes signets. En outre, stockez un signet dans une variable temporaire afin de pouvoir l’utiliser une fois la while
boucle terminée pour appeler la MoveToBookmark
fonction dans les modèles consommateur. La MoveToBookmark
fonction appelle la GetRowsAt
méthode dans IRowsetLocate
.
Vous devez également mettre à jour l’enregistrement utilisateur dans le consommateur. Ajoutez une entrée dans la classe pour gérer un signet et une entrée dans le 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()
};
Lorsque vous avez mis à jour le code, vous devez être en mesure de générer et d’exécuter le fournisseur avec l’interface IRowsetLocate
.