_variant_t、COleVariant、CComVariant 和 VARIANT 之间的用法差异以及使用 SAFEARRAY 变体 [英] usage differences between _variant_t, COleVariant, CComVariant, and VARIANT and using SAFEARRAY variations

查看:56
本文介绍了_variant_t、COleVariant、CComVariant 和 VARIANT 之间的用法差异以及使用 SAFEARRAY 变体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在研究几种使用 ADO 访问 SQL Server 数据库的 Visual Studio 2015 C++ 项目类型.简单示例对表执行选择、读入行、更新每一行并更新表.

I am investigating several Visual Studio 2015 C++ project types which use ADO to access an SQL Server database. The simple example performs a select against a table, reads in the rows, updates each row, and updates the table.

MFC 版本运行良好.Windows 控制台版本是我在更新记录集中的行时遇到问题的地方.记录集的 update() 方法抛出 COM 异常,错误文本为:

The MFC version works fine. The Windows console version is where I am having a problem updating the rows in the recordset. The update() method of the recordset is throwing a COM exception with the error text of:

L"Item cannot be found in the collection corresponding to the requested name or ordinal."

HRESULT0x800a0cc1.

在这两种情况下,我都使用定义为的标准 ADO 记录集对象;

In both cases I am using a standard ADO recordset object defined as;

_RecordsetPtr       m_pRecordSet;   // recordset object

在MFC版本中,更新记录集中当前行的函数是:

In the MFC version, the function to update the current row in the recordset is:

HRESULT CDBrecordset::UpdateRow(const COleVariant vPutFields, COleVariant vValues)
{
    m_hr = 0;
    if (IsOpened()) {
        try {
            m_hr = m_pRecordSet->Update(vPutFields, vValues);
        }
        catch (_com_error &e) {
            _bstr_t bstrSource(e.Description());
            TCHAR *description;
            description = bstrSource;
            TRACE2("  _com_error CDBrecordset::UpdateRow %s  %s
", e.ErrorMessage(), description);
            m_hr = e.Error();
        }
    }

    if (FAILED(m_hr))
        TRACE3(" %S(%d): CDBrecordset::UpdateRow()  m_hr = 0x%x
", __FILE__, __LINE__, m_hr);
    return  m_hr;
}

通过使用组成一个帮助类的两个 COleSafeArray 对象来调用此函数,以便更轻松地指定要更新的列名和值.

This function is called by using two COleSafeArray objects composed into a helper class to make it easier to specify the column names and values to be updated.

// Create my connection string and specify the target database in it.
// Then connect to SQL Server and get access to the database for the following actions.
CString  ConnectionString;
ConnectionString.Format(pConnectionStringTemp, csDatabaseName);

CDBconnector x;
x.Open(_bstr_t(ConnectionString));

// Create a recordset object so that we can pull the table data that we want.
CDBrecordset y(x);

//  ....... open and reading of record set deleted.

MyPluOleVariant thing(2);

thing.PushNameValue (SQLVAR_TOTAL, prRec.lTotal);
thing.PushNameValue (SQLVAR_COUNTER, prRec.lCounter);

hr = y.UpdateRow(thing.saFields, thing.saValues);

因为 Windows 控制台版本没有使用 MFC,我遇到了一些定义问题,这似乎是由于 ATL COM 类 CComSafeArray 是一个模板.

Because the Windows Console version is not using MFC, I am running into some definition issues which appear to be due to ATL COM class CComSafeArray being a template.

在 MFC 源代码中,COleSafeArray 是从 tagVARIANT 派生的类,它是一个 union,它是 的数据结构变体.但是在 ATL COM 中,CComSafeArray 是我用作 CComSafeArray 的模板,这看起来很合理.

In the MFC source, COleSafeArray is a class derived from tagVARIANT which is a union that is the data structure for a VARIANT. However in ATL COM, CComSafeArray is a template that I am using as CComSafeArray<VARIANT> which seems reasonable.

但是,当我尝试使用用此模板定义的变量(从 CComSafeArray<VARIANT> 派生的类 CDBsafeArray)时,我在以下位置收到编译错误我调用 m_pRecordSet->Update():

However when I try to use a variable defined with this template, a class CDBsafeArray derived from CComSafeArray<VARIANT>, I get the following compilation error at the point where I call m_pRecordSet->Update():

no suitable user-defined conversion from "const CDBsafeArray" to "const _variant_t" exists

_variant_t 似乎是 VARIANT 的包装类,并且 CComSafeArray 之间似乎没有转换路径code>_variant_t 但是 COleSafeArray_variant_t 之间有一个转换路径.

_variant_t seems to be a wrapper class for VARIANT and there does not seem to be a conversion path between CComSafeArray<VARIANT> and _variant_t however there is a conversion path between COleSafeArray and _variant_t.

我尝试的是指定类的 m_psa 成员,它是 VARIANT 类型的 SAFEARRAY 并且编译但是我看到测试应用程序时上面的 COM 异常.使用调试器查看对象,指定要更新的字段的对象似乎是正确的.

What I have tried is to specify the m_psa member of the class which is a SAFEARRAY of type VARIANT and this compiles however I see the COM exception above when testing the application. Looking in the object with the debugger, the object specifying the fields to be updated appears to be correct.

所以看起来我混合了不兼容的类.什么是可以与 _variant_t 一起使用的 SAFEARRAY 包装器类?

So it appears I am mixing incompatible classes. What would be a SAFEARRAY wrapper class that will work with _variant_t?

推荐答案

Microsoft VARIANTSAFEARRAY

A Brief Overview of Microsoft VARIANT and SAFEARRAY

VARIANT 类型用于创建可能包含多种不同类型值的变量.这样的变量可以在一个点被分配一个整数值,在另一个点被分配一个字符串值.ADO 将 VARIANT 与许多不同的方法一起使用,以便可以通过标准接口将从数据库读取或写入数据库的值提供给调用者,而不是试图拥有许多不同的数据类型特定接口.

The VARIANT type is used to create a variable that may contain a value of many different types. Such a variable may be assigned an integer value at one point and a string value at another. ADO uses VARIANT with a number of different methods so that the values read from a database or written to a database can be provided to the caller through a standard interface rather than trying to have lots of different, data type specific interfaces.

Microsoft 指定了 VARIANT 类型,该类型表示为包含多个字段的 C/C++ struct.此 struct 的两个主要部分是一个字段,该字段包含一个值,该值表示存储在 VARIANT 中的当前值的类型,以及一个由VARIANT.

Microsoft specifies the VARIANT type which is represented as a C/C++ struct which contains a number of fields. The two primary parts of this struct are a field that contains a value representing the type of the current value stored in the VARIANT and a union of the various value types supported by a VARIANT.

除了 VARIANT 之外,另一个有用的类型是 SAFEARRAY.SAFEARRAY 是一个数组,它包含数组管理数据、关于数组的数据,例如它包含的元素数量、维度和上下限(边界数据允许您具有任意索引范围).

In addition to VARIANT another useful type is SAFEARRAY. A SAFEARRAY is an array which contains array management data, data about the array such as how many elements it contains, its dimensions, and the upper and lower bounds (the bounds data allows you to have arbitrary index ranges).

VARIANT 的 C/C++ 源代码如下所示(所有组件 structunion 成员似乎都是匿名,例如 __VARIANT_NAME_2#defined 为空):

The C/C++ source code for a VARIANT looks something like the following (all of the component struct and union members seem to be anonymous, e.g. __VARIANT_NAME_2 is #defined to be empty):

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 使用 COM 对象接口中的 VARIANT 类型来提供通过接口来回传递数据和执行所需的任何类型的数据转换(编组)的能力.

COM uses the VARIANT type in COM object interfaces to provide the ability to pass data through the interface back and forth and doing any kind of data transformation needed (marshaling).

VARIANT 类型支持多种数据类型,其中之一是 SAFERAY.因此,您可以使用 VARIANT 在接口上传递 SAFEARRAY.与其使用显式的 SAFEARRAY 接口,不如指定一个 VARIANT 接口来识别和处理包含 SAFEARRAY<的 VARIANT/代码>.

The VARIANT type supports a large variety of data types one of which is SAFEARAY. So you can use a VARIANT to pass a SAFEARRAY over an interface. Rather than having an explicit SAFEARRAY interface you can instead specify a VARIANT interface that will recognize and process a VARIANT that contains a SAFEARRAY.

提供了几个函数来管理 VARIANT 类型,其中一些是:

There are several functions provided to manage the VARIANT type some of which are:

VariantInit()
VariantClear()
VariantCopy()

并且提供了几个函数来管理 SAFEARRAY 类型,其中一些是:

And there are several functions provided to manage the SAFEARRAY type some of which are:

SafeArrayCreate()
SafeArrayCreateEx()
SafeArrayCopyData();

三个不同的 Microsoft VARIANT 类:MFC、ATL、Native C++

Three different Microsoft VARIANT classes: MFC, ATL, Native C++

多年来,Microsoft 提供了几种不同的框架,这些框架和库的目标之一是能够轻松处理 COM 对象.

Microsoft has provided several different frameworks over the years and one of the goals of these frameworks and libraries has been the ability to work easily with COM objects.

我们将在以下内容中查看 C++ 的三个不同版本的 VARIANT 类:(1) MFC,(2) ATL,以及 (3) Microsoft 所谓的原生 C++.

We will look at three different versions of VARIANT classes for C++ in the following: (1) MFC, (2) ATL, and (3) what Microsoft calls native C++.

MFC 是在 C++ 生命早期开发的复杂框架,为 Windows C++ 程序员提供非常全面的库.

MFC is a complex framework developed early in the C++ life to provide a very comprehensive library for Windows C++ programmers.

ATL 是一个更简单的框架,旨在帮助人们创建基于 COM 的软件组件.

ATL is a simpler framework developed to assist people creating COM based software components.

_variant_t 似乎是 VARIANT 的标准 C++ 类包装器.

The _variant_t seems to be a standard C++ class wrapper for VARIANT.

ADO _RecordsetPtr 类具有 Update() 方法,该方法接受 _variant_t 对象,如下所示:

The ADO _RecordsetPtr class has the Update() method that accepts a _variant_t object which looks like:

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 提供了一组用于处理 COM 对象的类,其中 VARIANT 类型的类是 COleVariantCOleSafeArray.如果我们查看这两个类的声明,我们会看到以下内容:

MFC provides a set of classes for working with COM objects with the classes for the VARIANT type being COleVariant and COleSafeArray. If we look at the declaration for these two classes we see the following:

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

如果我们查看这些类的 ATL 版本,我们会发现 CComVariantCComSafeArray 但是 CComSafeArray 是一个 C++ 模板.当您使用 CComSafeArray 声明变量时,您指定要包含在基础 SAFEARRAY 结构中的值的类型.声明如下:

If we look at the ATL versions of these classes what we find is CComVariant and CComSafeArray however CComSafeArray is a C++ template. When you declare a variable with CComSafeArray you specify the type of the values to be contained in the underlying SAFEARRAY structure. The declarations look like:

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

_variant_t 类声明如下:

The _variant_t class is declared as follows:

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

所以我们看到的是三种不同的框架(MFC、ATL 和原生 C++)在执行 VARIANTSAFEARRAY 的方式之间的细微差别.

So what we see is a small difference between how the three different frameworks (MFC, ATL, and native C++) do VARIANT and SAFEARRAY.

同时使用三个 VARIANT

Using the Three VARIANT Classes Together

所有三个都有一个表示 VARIANT 的类,该类派生自 struct tagVARIANT,允许所有三个在接口之间互换使用.不同之处在于它们如何处理 SAFEARRAY. MFC 框架提供了 COleSafeArray,它派生自 struct tagVARIANT 并包装了SAFEARRAY 库.ATL 框架提供了 CComSafeArray,它不是从 struct tagVARIANT 派生的,而是使用组合而不是继承.

All three have a class to represent a VARIANT which is derived from the struct tagVARIANT which allows all three to be used interchangeable across interfaces. The difference is how each handles a SAFEARRAY. The MFC framework provides COleSafeArray which derives from struct tagVARIANT and wraps the SAFEARRAY library. The ATL framework provides CComSafeArray which does not derive from struct tagVARIANT but instead uses composition rather than inheritance.

_variant_t 类具有一组构造函数,它们将接受 VARIANT 或指向 VARIANT 的指针以及用于赋值的运算符方法以及接受 VARIANT 或指向 VARIANT 的指针的转换.

The _variant_t class has a set of constructors which will accept a VARIANT or a pointer to a VARIANT as well as operator methods for assignment and conversion that will accept a VARIANT or pointer to a VARIANT.

VARIANT 的这些 _variant_t 方法与 ATL CComVariant 类和 MFC COleVariant 一起使用>COleSafeArray 类,因为它们都是从 struct tagVARIANT 派生的,也就是 VARIANT.然而,ATL CComSafeArray 模板类不能很好地与 _variant_t 一起使用,因为它不是从 struct tagVARIANT 继承的.

These _variant_t methods for VARIANT work with the ATL CComVariant class and with the MFC COleVariant and COleSafeArray classes because these are all derived from struct tagVARIANT which is VARIANT. However the ATL CComSafeArray template class does not work well with _variant_t because it does not inherit from struct tagVARIANT.

对于 C++,这意味着带有 _variant_t 参数的函数可以与 ATL CComVariant 或 MFC COleVariant 一起使用,并且COleSafeArray 但不能与 ATL CComSafeArray 一起使用.这样做会产生编译器错误,例如:

For C++ this means that a function that takes an argument of _variant_t can be used with the ATL CComVariant or with the MFC COleVariant and COleSafeArray but can not be used with an ATL CComSafeArray. Doing so will generate a compiler error such as:

no suitable user-defined conversion from "const ATL::CComSafeArray<VARIANT, (VARTYPE)12U>" to "const _variant_t" exists

请参阅 Microsoft 中的用户定义的类型转换 (C++)用于说明的开发者网络文档.

See User-Defined Type Conversions (C++) in the Microsoft Developer Network documentation for an explanation.

CComSafeArray 最简单的解决方法似乎是定义一个派生自 CComSafeArray 的类,然后提供一个方法来提供 VARIANT 对象,将 CComSafeArraySAFEARRAY 对象包装在 VARIANT 内.

The simplest work around for a CComSafeArray seems to be to define a class that derives from CComSafeArray and to then provide a method that will provide a VARIANT object that wraps the SAFEARRAY object of the CComSafeArray inside of a 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.
    }
};

这个类将用于包含字段名称和这些字段的值,并且 Update() 的 ADO _RecordsetPtr 方法将被调用如下:

This class would then be used to contain the field names and the values for those fields and the ADO _RecordsetPtr method of Update() would be called like:

m_hr = m_pRecordSet->Update(saFields.CreateVariant(), saValues.CreateVariant());

这篇关于_variant_t、COleVariant、CComVariant 和 VARIANT 之间的用法差异以及使用 SAFEARRAY 变体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆