【发布时间】:2017-01-11 13:22:10
【问题描述】:
我正在研究几个使用 ADO 访问 SQL Server 数据库的 Visual Studio 2015 C++ 项目类型。简单示例对表执行选择、读取行、更新每一行并更新表。
MFC 版本运行良好。 Windows 控制台版本是我在更新记录集中的行时遇到问题的地方。记录集的update() 方法抛出了一个COM 异常,错误文本为:
L"Item cannot be found in the collection corresponding to the requested name or ordinal."
具有HRESULT 的0x800a0cc1。
在这两种情况下,我都使用定义为的标准 ADO 记录集对象;
_RecordsetPtr m_pRecordSet; // recordset object
在MFC版本中,更新记录集中当前行的函数是:
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\n", e.ErrorMessage(), description);
m_hr = e.Error();
}
}
if (FAILED(m_hr))
TRACE3(" %S(%d): CDBrecordset::UpdateRow() m_hr = 0x%x\n", __FILE__, __LINE__, m_hr);
return m_hr;
}
这个函数是通过使用两个COleSafeArray对象组合成一个辅助类来调用的,这样可以更容易地指定要更新的列名和值。
// 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 是一个模板。
在 MFC 源中,COleSafeArray 是派生自 tagVARIANT 的类,union 是 VARIANT 的数据结构。但是在 ATL COM 中,CComSafeArray 是一个模板,我将其用作 CComSafeArray<VARIANT>,这似乎是合理的。
但是,当我尝试使用使用此模板定义的变量时,即派生自 CComSafeArray<VARIANT> 的类 CDBsafeArray,我在调用 m_pRecordSet->Update() 时收到以下编译错误:
no suitable user-defined conversion from "const CDBsafeArray" to "const _variant_t" exists
_variant_t 似乎是VARIANT 的包装类,CComSafeArray<VARIANT> 和 _variant_t 之间似乎没有转换路径,但COleSafeArray 和 _variant_t 之间存在转换路径。
我尝试的是指定类的m_psa 成员,该成员是VARIANT 类型的SAFEARRAY,并且可以编译但是我在测试应用程序时看到了上面的COM 异常。使用调试器查看对象,指定要更新的字段的对象似乎是正确的。
看来我正在混合不兼容的类。与_variant_t 一起使用的SAFEARRAY 包装类是什么?
【问题讨论】:
-
这样的东西应该可以工作:
_variant_t var; var.parray = myarray.Detach(); var.vt = VT_ARRAY | VT_VARIANT;。其中myarray是CComSafeArray<VARIANT>。看起来没有与_variant_t自然互操作的安全数组包装器,因此您需要下拉到原始VARIANT级别。 -
@IgorTandetnik,感谢您的帮助。我会看看这条路,看看它通向何方。我想知道我是否需要做类似的事情。在使用 MFC 示例时,我有自己的
VARIANT包装器,直到我意识到 MFC 有一些类比我的工作更好,并且希望更安全。 -
@IgorTandetnik,我正在研究的是使
CDBsafeArray类派生自tagVARIANT,然后使用CComSafeArray<VARIANT>在CDBsafeArray类中创建一个成员m_SafeArray。然后我修改CDBsafeArray类的tagVARIANT部分,使其看起来像一个带有vt = VT_ARRAY | VT_VARIANT;和parray = m_SafeArray.m_psa;的VARIANT 数组,这导致CDBsafeArray对象看起来像一个SAFEARRAY 的VARIANTS。这在我的有限示例中运行良好,但是我怀疑会出现比我正在做的更复杂的问题。 -
@IgorTandetnik 我在想我真正需要的是有一个转换运算符,它会做一个惰性求值,并在调用转换运算符时使用
CComSafeArray<VARIANT>数据创建tagVARIANT表示.这样,如果CComSafeArray<VARIANT>成员发生变化,转换操作员只会显示最新版本。 -
@IgorTandetnik 我已经发布了答案。您能否查看一下,如果有任何严重错误,请告诉我?