C++ COM:将引用传递到安全数组
导言 我想用Qt查询一个COM函数。我有一个关于VB的文档。然而,这份文件说: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轴条目的
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, ¶ms, 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);
}