C++ COM:将引用传递到安全数组

C++ COM:将引用传递到安全数组,c++,qt,com,ole,safearray,C++,Qt,Com,Ole,Safearray,导言 我想用Qt查询一个COM函数。我有一个关于VB的文档。然而,这份文件说: Object.Frequencies DataArray Object -> An object expression that evaluates to a BKDataSet object. DataArray -> An array of strings or an array of values 必须在控制脉冲的应用程序中声明DataArray结构,并且数组的大小必须等于x轴条目的

导言

我想用Qt查询一个COM函数。我有一个关于VB的文档。然而,这份文件说:

Object.Frequencies DataArray

Object     -> An object expression that evaluates to a BKDataSet object.
DataArray  -> An array of strings or an array of values 
必须在控制脉冲的应用程序中声明DataArray结构,并且数组的大小必须等于x轴条目的数量。在Visual Basic中,使用DIM或相应语句声明变量并分配存储空间。在其他语言中,使用字符串的安全数组VT_BSTR或reals VT_R8表示double或VT_R4表示float/single

第一步

首先,我习惯于看到函数原型的真实外观

我得到:void FrequenciesVARIANT*FrequencyArray

所以我试着用Qt来做:

IBKDataSet *data = function->FunctionData();
int nbFrequencies = data->dynamicCall("GetNumberOfXAxisEntries()").toInt();
QList<QVariant> frequencies;
for(int i=0; i<nbFrequencies; j++) {
    frequencies << 0.0;
}
QList<QVariant> parameters;
parameters << QVariant(frequencies);
data->dynamicCall("Frequencies(QVariant&)", parameters);
for(int i=0; i<frequencies.size(); i++) {
    qDebug() << frequencies.at(i);
}
没有更多的错误,但返回的值是初始化期间设置的值,我尝试了几个值,而不仅仅是0.0

步骤3

之后,我尝试了许多组合,但都没有成功。因此,我要求难以联系的开发人员提供一个示例。他们向我发送了以下代码Visual C++:

IBKDataSetPtr bkDataSet(function->FunctionData);
int nEntries = bkDataSet->GetNumberOfXAxisEntries();
COleSafeArray safeArray;

safeArray.CreateOneDim( VT_R8, nEntries);
COleVariant vDontCare;
bkDataSet->Frequencies(&safeArray);
double* pFrequencyData;
safeArray.AccessData((void**)&pFrequencyData);
CString sMessage;
sMessage.Format("Y[1] = %1.4e ", pFrequencyData[1]);
MessageBox(sMessage);
safeArray.UnaccessData();
问题:

如何在QtCreator中使用此代码

我意识到COleSafeArray是VisualStudio特有的

我为文章的篇幅感到抱歉,但这是一段时间,我试图回避这个问题,但没有成功

编辑

以下是有关如何使用WIN32 API执行此操作的提示:

QList<double> getCOMValues(IDispatch *dispatch, const QString &method, int arraySize) const {
    QList<double> result;
    HRESULT hr;
    // I don't know how to convert a QString into an OLECHAR*
    OLECHAR * szMember = L"Frequencies";
    DISPID dispid;

    // Retrieve the dispatch identifier for the method.
    // Use defaults where possible.
    hr = dispatch->GetIDsOfNames(IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid);

    // Test return is OK
    if(FAILED(hr)) {
        qDebug() << "Error while trying to read the " << method;
        return result;
    }

    // Defines a SAFEARRAY.
    SAFEARRAY *psa;
    // Represents the bounds of one dimension of the array.
    SAFEARRAYBOUND freqBound[1];
    // The lower bound of the dimension starts at 0.
    freqBound[0].lLbound = 0;
    // The number of elements in the dimension is the number of frequencies.
    freqBound[0].cElements = arraySize;

    // Creates a new array descriptor, allocates and initializes the data for
    // the array, and returns a pointer to the new array descriptor.
    // VT_R8 represents double.
    // 1 is the number of dimensions in the array.
    psa = SafeArrayCreate(VT_R8, 1, freqBound);

    // Check the array is not NULL.
    if(psa == NULL) {
        qDebug() << "Error while trying to read the " << method;
        return result;
    }

    // This is the default value to put into the array.
    double init[] = {0.0};
    // Populates the array with 0.0.
    for(long index=0;index<arraySize;index++)
    {
        // Stores the data element at the specified location in the array.
        hr = SafeArrayPutElement(psa,&index,init);
        if( FAILED(hr)) {
            qDebug() << "Error while initialising the array for " << method;
        }
    }

    // Describes arguments passed within DISPPARAMS.
    VARIANTARG v[1];
    // A safe array descriptor, which describes the dimensions, size, and in-memory location of the array.
    v[0].parray = psa;
    // The type of data in the union.
    v[0].vt = VT_ARRAY|VT_R8;

    // Contains the arguments passed to a method or property.
    DISPPARAMS params = {v, NULL, 1, 0};

    // To catch errors
    EXCEPINFO pExcepInfo;

    // Provides access to properties and methods exposed by the object.
    hr = dispatch->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, &pExcepInfo, NULL);

    if(FAILED(hr)) {
        if(hr == DISP_E_EXCEPTION) {
            printf("%S\n", pExcepInfo.bstrSource);
            printf("%S\n", pExcepInfo.bstrDescription);
        }

        qDebug() << "Error while trying to read the " << method;
        return result;
    }

    double res = 0;
    for(long idx=0; idx<arraySize; idx++) {
        hr = SafeArrayGetElement(psa, &idx, &res);
        if(hr == S_OK) {
            result.push_back(res);
        } else {
            result.push_back(0.0);
            qDebug() << "Error while trying to read the " << method;
        }
    }

    // Clears the variant.
    hr = VariantClear(v);

    return result;
}

但是Invoke方法返回DISP_E_异常:参数中的类型不匹配。传递字符串或实数类型的数组。

发现问题。非常简单

IBKDataSet *data = function->FunctionData();
int nbFrequencies = data->dynamicCall("GetNumberOfXAxisEntries()").toInt();
QList<QString> frequencies;
for(int i=0; i<nbFrequencies; i++) {
    frequencies << "0.0";
}
QList<QVariant> parameters;
parameters << QVariant(frequencies);
data->dynamicCall("Frequencies(QList<QString>&)", parameters);
frequencies = parameters.first().toStringList();
for(int j=0; j<frequencies.size(); j++) {
    qDebug() << frequencies.at(j);
}

我必须读取参数的第一个元素并将其转换为QStringList…

FWIW,COleSafeArray和CString都不是VisualStudio特有的;它们特定于MFC/ATL。您可以使用原始WIN32 API执行此操作,并避免MFC/ATL依赖关系。但是,我发现您不太可能找到只编写跨平台Qt的解决方案,特别是因为您实际上是在调用需要平台相关代码的第三方库接口,即用于SAFEARRAY和变体管理的OLE LIB。如果您想知道如何创建任何数据类型的SAFEARRAY,并将其固定到不带MFC/ATL的变体中,这可能已经得到了回答。首先感谢您的评论。我想知道为什么Qt没有提供一种方法来实现这一点。我希望避免使用MFC/ATL,但我从未使用WIN32 API这样做。你能告诉我怎么做吗?解释BSTR、VARIANT和SAFEARRAY管理远远超出了单个StackOverflow问答的范围。我可以编写代码,但我怀疑你要找的不是一团不可思议的黏液。搜索SO和MSDN以创建变体SAFEARRAY。如果你真的想看一段代码的话,网上有很多,但是如果我有时间从我可笑的工作日程中解放出来,我可以写一段。好的,谢谢。我在答案的末尾添加了一些代码。你能告诉我我是否走上了正轨吗?你看到代码有什么问题吗当然dataDisp是初始化的,但我忘了这一行了?实际上不是最优秀的代码。a没有正确检查HRESULT返回值,这是正确运行OLE/COM代码的命脉,b没有正确初始化IDispatch*dataDisp;使用前的接口指针,c在使用psa var之前不检查SafeArrayCreate的有效返回值,d在完成时不销毁安全数组,调用完成后,可以/应该使用VariantClear v来完成。代码有很多错误,但至少您可以看到其中一些代码是如何工作的,这比您以前的代码要好。
IBKDataSet *data = function->FunctionData();
int nbFrequencies = data->dynamicCall("GetNumberOfXAxisEntries()").toInt();
QList<QString> frequencies;
for(int i=0; i<nbFrequencies; i++) {
    frequencies << "0.0";
}
QList<QVariant> parameters;
parameters << QVariant(frequencies);
data->dynamicCall("Frequencies(QList<QString>&)", parameters);
frequencies = parameters.first().toStringList();
for(int j=0; j<frequencies.size(); j++) {
    qDebug() << frequencies.at(j);
}