【问题标题】:COM: pass reference to a SAFEARRAYCOM:传递对 SAFEARRAY 的引用
【发布时间】:2013-06-16 05:28:50
【问题描述】:

简介

我想用 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 

The DataArray structure must be declared in the application that is controlling PULSE and the size of the array must be equal to the number of x-axis entries. In Visual Basic, use the DIM (or corresponding) statement to declare the variable and allocate storage space. In other languages use a safearray of strings (VT_BSTR) or reals (VT_R8 for double or VT_R4 for float/single).


第 1 步

首先我使用OLEVIEW 来查看函数原型的真实样子。

我得到了:void Frequencies(VARIANT* 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);
}

注意:我使用了QList&lt;QVariant&gt;,因为它显示为here

但我收到了以下消息Type Mismatch in Parameter. Pass an array of type string or real.


第 2 步

所以我尝试了一个字符串数组:

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);
for(int j=0; j<frequencies.size(); j++) {
    qDebug() << frequencies.at(j);
}

没有更多错误,但返回值是初始化期间设置的值(我尝试了几个,不仅仅是 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_EXCEPTION: Type Mismatch in Parameter. Pass an array of type string or real.

【问题讨论】:

  • FWIW,COleSafeArray 和 CString 都不是 VisualStudio 特有的;它们特定于 MFC/ATL。您可以使用原始 WIN32 API 来执行此操作,并避免 MFC/ATL 依赖性。但是,我发现您不太可能找到编写跨平台的仅 Qt 解决方案,特别是因为您实际上是在调用需要平台相关代码的第 3 方库接口(即用于 SAFEARRAY 和 VARIANT 管理的 OLE 库)。如果您想知道如何创建任何数据类型的 SAFEARRAY 并将其固定到没有 MFC/ATL 的 VARIANT 中,可以(并且可能以前已经)回答。
  • 首先感谢您的评论。我想知道为什么 Qt 不提供这样做的方法。我想避免使用 MFC/ATL,但我从未使用过 WIN32 API 来这样做。你能告诉我怎么做吗?
  • 解释 BSTR、VARIANT 和 SAFEARRAY 管理远远超出了单个 StackOverflow 问答的范围。我可以编写代码,但我怀疑一团难以理解的粘性物质不是您要找的。在 SO 和 MSDN 中搜索“创建 VARIANT SAFEARRAY”。如果你真的想看一段示例代码,网上有很多,但如果我有时间从可笑的工作日程中解放出来,我可以写一个。
  • 好的,谢谢。我在答案的末尾添加了一些代码。你能告诉我我是否走上正轨吗?您是否发现该代码有任何问题(当然 dataDisp 已初始化但我忘记了此处的行)?
  • 其实不是最优秀的代码。 (a) 没有正确检查 HRESULT 返回值(这是正确运行的 OLE/COM 代码的命脉),(b) 在使用之前没有正确初始化 IDispatch* dataDisp; 接口指针,(c) 没有检查在使用 psa var 之前来自 SafeArrayCreate 的有效返回值,以及 (d) 调用完成后完成时不破坏安全数组(可以/应该使用 vVariantClear 来完成) .代码有很多错误,但至少你可以看到其中一些是如何工作的,这比你以前的要好。

标签: c++ qt com ole safearray


【解决方案1】:

发现问题。很简单!

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);
}

我必须阅读parameters 的第一个元素并将其转换为QStringList...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-08-03
    • 2018-04-27
    • 1970-01-01
    • 2023-01-04
    • 2012-03-12
    • 1970-01-01
    • 2016-06-06
    • 2011-11-06
    相关资源
    最近更新 更多