无法将 ByRef VARIANT 数组转换为 SAFEARRAY

Posted

技术标签:

【中文标题】无法将 ByRef VARIANT 数组转换为 SAFEARRAY【英文标题】:Cannot convert ByRef VARIANT array to SAFEARRAY 【发布时间】:2013-07-26 13:32:09 【问题描述】:

我正在编写一个可以从 Excel VBA 访问的 C++ DLL(它只是完成数学方程式,不需要从工作表中访问)。该 DLL 旨在替换当前的 VBA 代码,我想用 C/C++ 编写它以提高性能。

由于我正在使用现有代码并且只是想用新的 DLL 替换当前的 VBA 函数,因此我必须使用包含单个向量数组的 VBA 变体。以下是VBA调用代码的简略示例:

Sub VBACaller()
    Dim InputR0(1 To 5) As Variant, vResult As Variant

    InputR0(1) = 26.8
    InputR0(2) = 27.8
    InputR0(3) = 28.8
    InputR0(4) = 29.8

    vResult = ReadArrayVBA(InputR0)
End Sub

我可以将此 Variant 数组作为 VARIANT 数据类型传递给我的 C++ 函数 ByValByRef,并且它似乎被正确接收。当我使用SafeArrayAccessData 将数组转换为SAFEARRAY 后尝试读取数组的内容时,就会出现问题。 SAFEARRAY 转换似乎有效,但数组的内容不正确。以下是我的 C++ 代码:

VARIANT __stdcall ReadArrayByRef(VARIANT *input0)

//Variable to assign value from Array
double d_m = 0;

//Check if this is a variant type or not
if (V_VT(input0) & VT_ARRAY)

    SAFEARRAY* pSafeArrayInput0 = NULL;
    pSafeArrayInput0 = V_ARRAY(input0);

    double* pVals;

    HRESULT hr = SafeArrayAccessData(pSafeArrayInput0, (void **)&pVals); // direct access to SA memory
    if (SUCCEEDED(hr))
    
        long lLBound = -1, lUBound = 1;  // get array bounds
        SafeArrayGetLBound(pSafeArrayInput0, 1, &lLBound);
        SafeArrayGetUBound(pSafeArrayInput0, 1, &lUBound);

        if (lLBound > -1 && lUBound > -1)
        
            d_m =  pVals[1];
        
        SafeArrayUnaccessData(pSafeArrayInput0);
    


//Output
VARIANT v;
VariantInit(&v);
v.vt = VT_R8;
v.dblVal = d_m;

return v;
    

我发现的示例似乎表明.parray 应该保存数据,但是检查input0 表明它在.pparray 中。如果我尝试分配 pSafeArrayInput0 = input0.pparray 它会出错。 d_m 返回的值与 1.05319234616515E-307 类似。

如果我将输入更改为 ByVal,那么我可以使用以下 C++ 代码正确访问数组的元素(唯一的区别是 SAFEARRAY 正在访问 input0 的地址。

VARIANT __stdcall ReadArrayByVal(VARIANT input0)

//Variable to assign value from Array
double d_m = 0;

//Check if this is a variant type or not
if (V_VT(&input0) & VT_ARRAY)

    SAFEARRAY* pSafeArrayInput0 = NULL;
    pSafeArrayInput0 = V_ARRAY(&input0);

    double* pVals;

    HRESULT hr = SafeArrayAccessData(pSafeArrayInput0, (void **)&pVals); // direct access to SA memory
    if (SUCCEEDED(hr))
    
        long lLBound = -1, lUBound = 1;  // get array bounds
        SafeArrayGetLBound(pSafeArrayInput0, 1, &lLBound);
        SafeArrayGetUBound(pSafeArrayInput0, 1, &lUBound);

        if (lLBound > -1 && lUBound > -1)
        
            d_m =  pVals[1];
        
        SafeArrayUnaccessData(pSafeArrayInput0);
    


VARIANT v; 
VariantInit(&v);
v.vt = VT_R8;
v.dblVal = d_m;

return v;

我发现的所有示例都表明,通过指针传递 VARIANT 输入应该适用于我的 ReadArrayByRef 函数(即 http://support.microsoft.com/kb/167668,它略有不同,但在我所追求的点上相同)。

我在 ByRef 函数中做错了什么?

【问题讨论】:

您实际上并没有检查数组的类型,只是检查了它是一个数组(例如,您正在查看 VT_ARRAY 标志,但没有别的)。我对Excel VBA一无所知,但例如VBScript只支持VT_VARIANT的数组,你似乎假设你有一个VT_R8的数组。 是的,最终代码应该检查双精度数据类型,但是我没有打扰这里的示例,因为我通过调试验证了每个元素都是VT_R8。将每个元素更改为不同的数据类型对应于VARIANT 数组中的正确数据类型(即整数将作为VT_I2 出现。 我猜您还应该检查VT_BYREF,因为看起来该数组是作为参考出现的 - 正如您所发现的那样。要访问 pparray 中的数组,您需要取消引用它 - 即 pSafeArrayInput0 = *input0.pparray; 感谢您的建议,我认为从.pparray 获取 SafeArray 是正确的。我可以检查VT_BYREF ok,但是当我尝试pSafeArrayInput0 = *input0.pparray 时,我收到“表达式必须有类”的错误。在ByVal 的情况下,pSafeArrayInput0 = V_ARRAY(&input0); 等同于pSafeArrayInput0 = input0.parray,但不适用于.pparray pSafeArrayInput0 = *(input0.pparray); 工作吗? 【参考方案1】:

您应该检查VT_BYREF 标志,如果已设置,请取消引用指针以访问数组,如下所示:

pSafeArrayInput0 = *V_ARRAYREF(input0);

【讨论】:

以上是关于无法将 ByRef VARIANT 数组转换为 SAFEARRAY的主要内容,如果未能解决你的问题,请参考以下文章

将 VARIANT 转换为字节,反之亦然? [复制]

将double [,]转换为Variant *

将字节数组转换为 tagVARIANT 数组

在 python 中的 byref 向量参数上使用 ctypes 进行 DLL 转换

在 VBA Excel 2007 中传递变体 ByRef

如何将 boost::hana::tuple 转换为 std::variant