_variant_t、COleVariant、CComVariant 和 VARIANT 之间的用法差异以及使用 SAFEARRAY 变体 [英] usage differences between _variant_t, COleVariant, CComVariant, and VARIANT and using SAFEARRAY variations
问题描述
我正在研究几种使用 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."
HRESULT
为 0x800a0cc1
.
在这两种情况下,我都使用定义为的标准 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 VARIANT
和 SAFEARRAY
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++ 源代码如下所示(所有组件 struct
和 union
成员似乎都是匿名,例如 __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
类型的类是 COleVariant
和 COleSafeArray
.如果我们查看这两个类的声明,我们会看到以下内容:
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 版本,我们会发现 CComVariant
和 CComSafeArray
但是 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++)在执行 VARIANT
和 SAFEARRAY
的方式之间的细微差别.
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
对象,将 CComSafeArray
的 SAFEARRAY
对象包装在 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屋!