Le type VARIANT
est utilisé pour créer une variable pouvant contenir une valeur de nombreux types différents. Une telle variable peut recevoir une valeur entière à un point et une valeur de chaîne à un autre.ADO utilise VARIANT
avec un certain nombre de méthodes différentes pour que les valeurs lues à partir d'une base de données ou écrites dans une base de données puissent être fournies à l'appelant via une interface standard plutôt que d'essayer différentes interfaces spécifiques.
Microsoft spécifie le type VARIANT
qui est représenté en tant que C/C++ struct
qui contient un certain nombre de champs. Les deux parties principales de ce struct
sont un champ qui contient une valeur représentant le type de la valeur actuelle stockée dans le VARIANT
et une union des différents types de valeur pris en charge par un VARIANT
. En plus de VARIANT
, un autre type utile est SAFEARRAY
. Un SAFEARRAY
est un tableau qui contient des données de gestion de tableau, des données sur le tableau telles que le nombre d'éléments qu'il contient, ses dimensions et les limites supérieures et inférieures (les données de limites vous permettent d'avoir des plages d'index arbitraires).
Le C/C code source de un VARIANT
ressemble à quelque chose comme ce qui suit (tous les composants struct
et union
membres semblent être anonymes, par exemple __VARIANT_NAME_2
est #defined
être vide):
typedef struct tagVARIANT VARIANT;
struct tagVARIANT
{
union
{
struct __tagVARIANT
{
VARTYPE vt;
WORD wReserved1;
WORD wReserved2;
WORD wReserved3;
union
{
LONGLONG llVal;
LONG lVal;
BYTE bVal;
SHORT iVal;
// ... lots of other fields in the union
} __VARIANT_NAME_2;
DECIMAL decVal;
} __VARIANT_NAME_1;
} ;
COM utilise le VARIANT
tapez dans les interfaces d'objet COM pour fournir la possibilité de transmettre des données à travers l'interface et de faire tout type de transformation de données nécessaire (marshaling).
Le type VARIANT
prend en charge une grande variété de types de données dont l'un est SAFEARAY
. Vous pouvez donc utiliser un VARIANT
pour passer un SAFEARRAY
sur une interface. Plutôt que d'avoir une interface explicite SAFEARRAY
, vous pouvez spécifier à la place une interface VARIANT
qui reconnaîtra et traitera un VARIANT
qui contient un SAFEARRAY
.
Il existe plusieurs fonctions qui permettent de gérer le type VARIANT
dont certains sont:
VariantInit()
VariantClear()
VariantCopy()
Et il y a plusieurs fonctions qui permettent de gérer le type SAFEARRAY
dont certains sont:
SafeArrayCreate()
SafeArrayCreateEx()
SafeArrayCopyData();
Microsoft a fourni plusieurs cadres différents au cours des années et l'un des objectifs de ces cadres et bibliothèques a été la capacité de travailler facilement avec des objets COM.
Nous examinerons trois versions différentes de classes VARIANT pour C++ dans les éléments suivants: (1) MFC, (2) ATL et (3) ce que Microsoft appelle C++ natif. MFC est un framework complexe développé au début de la vie C++ pour fournir une bibliothèque très complète pour les programmeurs Windows C++. ATL est un framework plus simple développé pour aider les gens à créer des composants logiciels basés sur COM. Le _variant_t
semble être un wrapper de classe C++ standard pour VARIANT
.
La classe ADO _RecordsetPtr
a la méthode Update()
qui accepte un objet _variant_t
qui ressemble à:
inline HRESULT Recordset15::Update (const _variant_t & Fields, const _variant_t & Values) {
HRESULT _hr = raw_Update(Fields, Values);
if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
return _hr;
}
MFC fournit un ensemble de classes pour travailler avec des objets COM avec les classes pour le type VARIANT
étant COleVariant
et COleSafeArray
. Si nous regardons la déclaration de ces deux classes, nous voyons les suivantes:
class COleVariant : public tagVARIANT
{
// Constructors
public:
COleVariant();
COleVariant(const VARIANT& varSrc);
// .. the rest of the class declaration
};
class COleSafeArray : public tagVARIANT
{
//Constructors
public:
COleSafeArray();
COleSafeArray(const SAFEARRAY& saSrc, VARTYPE vtSrc);
// .. the rest of the class declaration
};
Si nous regardons les versions ATL de ces classes ce que nous trouvons est CComVariant
et CComSafeArray
cependant CComSafeArray
est un modèle de C++. Lorsque vous déclarez une variable avec CComSafeArray
, vous spécifiez le type des valeurs à contenir dans la structure SAFEARRAY
sous-jacente. Les déclarations ressemblent:
class CComVariant : public tagVARIANT
{
// Constructors
public:
CComVariant() throw()
{
// Make sure that variant data are initialized to 0
memset(this, 0, sizeof(tagVARIANT));
::VariantInit(this);
}
// .. other CComVariant class stuff
};
// wrapper for SAFEARRAY. T is type stored (e.g. BSTR, VARIANT, etc.)
template <typename T, VARTYPE _vartype = _ATL_AutomationType<T>::type>
class CComSafeArray
{
public:
// Constructors
CComSafeArray() throw() : m_psa(NULL)
{
}
// create SAFEARRAY where number of elements = ulCount
explicit CComSafeArray(
_In_ ULONG ulCount,
_In_ LONG lLBound = 0) : m_psa(NULL)
{
// .... other CComSafeArray class declaration/definition
};
La classe _variant_t est déclarée comme suit:
class _variant_t : public ::tagVARIANT {
public:
// Constructors
//
_variant_t() throw();
_variant_t(const VARIANT& varSrc) ;
_variant_t(const VARIANT* pSrc) ;
// .. other _variant_t class declarations/definition
};
donc ce que nous voyons est une petite différence entre la façon dont les trois cadres différents (MFC, ATL et C++ natif) faites VARIANT
et SAFEARRAY
.
Tous les trois ont une classe pour représenter un VARIANT
qui est dérivé du struct tagVARIANT
qui permet aux trois d'être utilisés interchangeables entre les interfaces. La différence est la façon dont chacun gère un SAFEARRAY
. Le cadre MFC fournit COleSafeArray
qui dérive de struct tagVARIANT
et enveloppe la bibliothèque SAFEARRAY
. Le framework ATL fournit CComSafeArray
qui ne dérive pas de struct tagVARIANT
mais utilise plutôt la composition plutôt que l'héritage.
La classe _variant_t
a un ensemble de constructeurs qui acceptent un VARIANT
ou un pointeur vers une VARIANT
ainsi que des méthodes de l'opérateur d'affectation et de conversion qui acceptent un VARIANT
ou pointeur vers un VARIANT
.
Ces _variant_t
méthodes pour VARIANT
travail avec l'ATL CComVariant
classe et avec le MFC COleVariant
et COleSafeArray
les classes parce que ceux-ci sont tous dérivés de struct tagVARIANT
qui est VARIANT
. Toutefois, la classe de modèle ATL CComSafeArray
ne fonctionne pas correctement avec _variant_t
car elle n'hérite pas de struct tagVARIANT
.
Pour C++, cela signifie qu'une fonction qui prend un argument de _variant_t
peut être utilisé avec l'ATL CComVariant
ou avec le MFC COleVariant
et COleSafeArray
mais ne peut pas être utilisé avec un ATL CComSafeArray
. Cela génère une erreur de compilation telles que:
no suitable user-defined conversion from "const ATL::CComSafeArray<VARIANT, (VARTYPE)12U>" to "const _variant_t" exists
Voir User-Defined Type Conversions (C++) dans la documentation Microsoft Developer Network pour une explication.
Le travail le plus simple autour d'un CComSafeArray
semble définir une classe qui dérive de CComSafeArray
et ensuite fournir une méthode qui fournira un objet VARIANT
qui enveloppe l'objet SAFEARRAY
de la CComSafeArray
à l'intérieur d'un VARIANT
.
struct CDBsafeArray: public CComSafeArray<VARIANT>
{
int m_size;
HRESULT m_hr;
CDBsafeArray(int nSize = 0) : m_size(nSize), m_hr(0)
{
// if a size of number of elements greater than zero specified then
// create the SafeArray which will start out empty.
if (nSize > 0) m_hr = this->Create(nSize);
}
HRESULT CreateOneDim(int nSize)
{
// remember the size specified and create the SAFEARRAY
m_size = nSize;
m_hr = this->Create(nSize);
return m_hr;
}
// create a VARIANT representation of the SAFEARRAY for those
// functions which require a VARIANT rather than a CComSafeArray<VARIANT>.
// this is to provide a copy in a different format and is not a transfer
// of ownership.
VARIANT CreateVariant() const {
VARIANT m_variant = { 0 }; // the function VariantInit() zeros out so just do it.
m_variant.vt = VT_ARRAY | VT_VARIANT; // indicate we are a SAFEARRAY containing VARIANTs
m_variant.parray = this->m_psa; // provide the address of the SAFEARRAY data structure.
return m_variant; // return the created VARIANT containing a SAFEARRAY.
}
};
Cette classe serait alors utilisée pour contenir les noms de champs et les valeurs de ces champs et la méthode ADO _RecordsetPtr
de Update()
serait appelé comme:
m_hr = m_pRecordSet->Update(saFields.CreateVariant(), saValues.CreateVariant());
Quelque chose comme cela devrait fonctionner: '_variant_t var; var.parray = myarray.Detach(); var.vt = VT_ARRAY | VT_VARIANT; '. Où 'myarray' est un' CComSafeArray '. Il ne semble pas qu'il y ait un wrapper safearray qui interagisse naturellement avec '_variant_t', donc vous devez descendre au niveau brut de' VARIANT'. –
@IgorTandetnik, merci pour l'aide. Je vais regarder ce chemin et voir où cela mène. Je me demandais si j'aurais besoin de faire quelque chose comme ça. Tout en travaillant avec l'exemple MFC à un moment donné, j'avais ma propre wrapper pour 'VARIANT' jusqu'à ce que je compris que MFC a des classes qui étaient beaucoup plus agréable et je l'espère plus que l'épreuve des balles mon travail. –
@IgorTandetnik, ce que je cherche en fait la classe la '' CDBsafeArray' dérivent de tagVARIANT' puis en utilisant le '' CComSafeArray pour créer un membre dans la classe 'CDBsafeArray',' m_SafeArray'. Ensuite, je modifie la partie 'tagVARIANT' de la classe' CDBsafeArray' pour ressembler à un tableau de VARIANT avec 'vt = VT_ARRAY | VT_VARIANT, 'et' parray = m_SafeArray.m_psa, 'qui est à l'origine l'objet' CDBsafeArray' pour ressembler à un SAFEARRAY de Variant. Cela fonctionne bien avec mon exemple limité mais je soupçonne qu'il y aurait des problèmes avec quelque chose de plus compliqué que ce que je fais. –