我专门为Excel调用制作的DLL中有许多C++函数。

我经常将这些函数作为包含SafeArrays的OLE VARIANTs参数传递给它们。

我编写了一个函数来充当警戒,以确保传递的VARIANT实际上包含一个SafeArray,并且就数据类型和维数而言,确保该数组具有适合该场合的类型。

如果满足这三个条件,则该函数将返回指向第一个元素的指针,并且还具有两个out参数以返回每个维度中的元素数量(注意:我只关心1d和2d SafeArrays)。

这是函数:

PVOID SafeArrayDataPointer(VARIANT &v, const long &ArrayType, const long &ArrayDims, long &Elems1D, long &Elems2D) {

    SAFEARRAY* pSafeArray = NULL;

    if ( V_VT(&v) & VT_ARRAY ) {

        if ( ArrayType != (V_VT(&v) & VT_TYPEMASK) ) return NULL;

        pSafeArray = V_ARRAY(&v);
        if ( ArrayDims != pSafeArray->cDims ) return NULL;

        switch (ArrayDims) {
        case 2:
            Elems1D = (pSafeArray->rgsabound)[1].cElements;
            Elems2D = (pSafeArray->rgsabound)[0].cElements;
            break;
        case 1:
            Elems1D = (pSafeArray->rgsabound)[0].cElements;
            Elems2D = 0;
            break;
        default:
            Elems1D = 0;
            Elems2D = 0;
            break;
        }

        return pSafeArray->pvData;

    } else return NULL;

}

此函数运行良好,并允许我通过这样的调用方便地获取数据指针并获取每个维度中的元素数(假设vData是从Excel传递的VARIANT:
pDataArray = (VARIANT*) SafeArrayDataPointer(vData, VT_VARIANT, 2, Elems1D, Elems2D); if (pDataArray == NULL) goto error1;

但是,我对此有两点不满意:

1.)由于函数返回了PVOID,因此我必须将其转换为相关的指针类型...但这是多余的,因为我已经在第二个参数中指定了VARIANT中必须包含哪种类型的数组。例如,在另一种情况下,我可能需要确保数组具有长值:
pDataArray = (long*) SafeArrayDataPointer(vData, VT_I4, 1, Elems1D, Junk); if (pDataArray == NULL) goto error1;

再次,这可以正常工作,但这是多余的。我更喜欢某种允许该函数返回正确类型的指针的机制。我希望无需模板即可完成此操作。

2)我无法弄清楚在第二个示例中如何不指定Junk参数,在该示例中,数组必须为1d。显然,在这种情况下,第二维中没有元素。

最佳答案

如果您不想使用模板,也不想使用专门的功能,则可以使用 Gross宏解决第一次调用PVOID时必须从SafeArrayVariantPointer强制转换的问题:

像这样:

#define CAST_TYPE_VT_VARIANT (VARIANT *)
#define CAST_TYPE_VT_LONG (long *)

#define SAFE_ARRAY_DATA_POINTER(vData, vType, dimSize, elems1D, elems2D) \
    CAST_TYPE_##vType SafeArrayDataPointer((vData), (vType), (dimSize), (elems1D), (elems2D))

然后您可以致电:
VARIANT *pDataArray = SAFE_ARRAY_DATA_POINTER(vData, VT_VARIANT, 2, Elems1D, Elems2D);

但是首先,您需要更改方法签名,以便ArrayType参数被视为long而非const long &

请注意,这假定SAFE_ARRAY_DATA_POINTER宏的第二个参数必须是与您定义的CAST_TYPE_ *宏之一相对应的文字。它不能是变量。

对于关于冗余Junk参数的第二个问题,您可以创建一个重载的SafeArrayDataPointer函数,该函数仅返回第一维的大小。它可以调用SafeArrayDataPointer的第一个版本,并丢弃第二个维度的大小。

就像是:
PVOID SafeArrayDataPointer(VARIANT &v, long ArrayType, const long &ArrayDims, long &Elems1D)
{
    long Elems2D;
    PVOID *toReturn = SafeArrayDataPointer(v, ArrayType, ArrayDims, Elems1D, Elems2D);
    if (Elems2D != 0) toReturn = NULL;
    return toReturn;
}

但是,要解决此问题,我可能会使用模板。

首先,创建一组array_type_traits类,这些类在给定较长表示typedefVT_LONG等的情况下为您的转换类型公开VT_VARIANT
//Generic array_type_traits class
template<long array_type>
class array_type_traits
{
public:
    typedef PVOID cast_type;
};

//Specialized for VT_LONG
template<>
class array_type_traits<VT_LONG>
{
public:
    typedef long * cast_type;
};

//Specialized for VT_VARIANT
template<>
class array_type_traits<VT_VARIANT>
{
public:
    typedef VARIANT * cast_type;
};

继续针对您拥有的每种VT_ *类型专门化这些。

接下来,将SafeArrayDataPointer函数封装在SafeArrayDataPointerBase类中。
//Base class which has one static function Get() that returns a PVOID
class SafeArrayDataPointerBase
{
protected:
    static PVOID Get(VARIANT& vData, long vType, long dimSize, long& elems1D, long& elems2D)
    {
        // Place your SafeArrayDataPointer function code here
    }
};

现在创建您的类,该类将调用`SafeArrayDataPointerBase::Get(),然后将结果转换为正确的类型。
template<long ArrayType>
class SafeArrayDataPointer : public SafeArrayDataPointerBase
{
public:
    typedef typename array_type_traits<ArrayType>::cast_type cast_type;

    static cast_type Get(VARIANT& v, long ArrayDims, long& Elems1D, long& Elems2D)
    {
        return (cast_type) SafeArrayDataPointerBase::Get(v, ArrayDims, ArrayType, Elems1D, Elems2D);
    }
};

最后,您可以这样调用模板类:
VARIANT *vp = SafeArrayDataPointer<VT_VARIANT>::Get(v, ArrayDims, Elems1D, Elems2D);
long *vl = SafeArrayDataPointer<VT_LONG>::Get(v, ArrayDims, Elems1D, Elems2D);

08-16 00:40