OPC DA客户端ItemMgt功能错误



我在Qt5中为C++COM API创建了一个简单的OPC DA客户端。客户端连接到远程服务器,获取一个OPCServer指针,使用ItemMgt接口创建一个新的OPC组,当我尝试向该组添加项目时失败。

错误消息为:功能不正确。据我所见,IUnknown::QueryInterface适用于此pItemMgt,但ValidateItems、CreateEnumerator和AddItems调用会导致相同的错误函数。OPC服务器是一个QMS220模拟器(Quadera(。你知道问题出在哪里吗?这是我第一次尝试编写DCOM客户端,这段代码可能有很多错误。qms220.h文件包含QMS220Simulator的CLSID。重现问题的最短代码是:

#include "opcda.h"
#include "qms220.h"
#include <QApplication>
#include <QDebug>
#include <comdef.h>
static void showStatus(const QString &message,HRESULT code);
IOPCServer *pOPCServer = nullptr;
IOPCItemMgt *pItemMgt = nullptr;
OPCHANDLE serverGroupHandle;
bool initializeCOM()
{
HRESULT hr = CoInitializeEx(nullptr,COINIT_APARTMENTTHREADED);
if (FAILED(hr)) {
showStatus("COM initialization failed!",hr);
return false;
}
hr = CoInitializeSecurity(
NULL, //security descriptor
-1, //COM authentication
NULL, //authentication services
NULL, //reserved
RPC_C_AUTHN_LEVEL_DEFAULT, //default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, //default impersonation
NULL, //authentication info
EOAC_NONE, //additional capabilities
NULL //reserved
);
if (hr == RPC_E_TOO_LATE) {
showStatus("RPC initalization is too late, ignoring...",hr);
} else {
if (FAILED(hr)) {
showStatus("CoInitializeSecurity",hr);
return false;
}
}
return true;
}

void deinitializeCOM()
{
CoUninitialize();
}

static const int INTERFACE_COUNT = 1;

bool connectToServer(const QString &address)
{
_bstr_t serverName = address.toStdString().c_str();
COSERVERINFO cs;
memset(&cs,0,sizeof(cs));
cs.pwszName = serverName;
MULTI_QI qi[INTERFACE_COUNT];
memset(qi,0,sizeof(qi));
qi[0].pIID = &IID_IOPCServer;
HRESULT hr = CoCreateInstanceEx(
CLSID_QMG220SIMDA,
NULL,
CLSCTX_SERVER,
&cs,
INTERFACE_COUNT,
qi
);
if (FAILED(hr)) {
showStatus("CoCreateInstanceEx",hr);
return false;
}
pOPCServer = (IOPCServer*)(qi[0].pItf);
return true;
}
void disconnectFromServer()
{
if (pOPCServer != nullptr) {
pOPCServer->Release();
pOPCServer = nullptr;
}
}
void showOPCStatus(const QString &message,HRESULT hr)
{
if (pOPCServer != nullptr) {
LPWSTR buffer = nullptr;
HRESULT hr2 = pOPCServer->GetErrorString(hr,LOCALE_SYSTEM_DEFAULT,&buffer);
if (hr2 != S_OK) {
qDebug() << message << QString(": HRESULT: 0x%1").arg(hr,8,16,QChar('0'));
} else {
qDebug() << message << QString(": ") << QString::fromWCharArray(buffer);
CoTaskMemFree(buffer);
}
} else {
qDebug() << message << QString(": HRESULT: 0x%1").arg(hr,8,16,QChar('0'));
}
}
static const LPCWSTR MIDGROUPNAME = L"mid";
bool createMIDGroup()
{
if (pOPCServer == nullptr) return false;
OPCHANDLE clientGroupHandle = 1;
DWORD revisedUpdateRate;
HRESULT hr = pOPCServer->AddGroup(
MIDGROUPNAME,
FALSE, //active
0, // requestedUpdateRate
clientGroupHandle,
NULL, //timebias
NULL, //percentDeadBand,
LOCALE_SYSTEM_DEFAULT, //lcid
&serverGroupHandle,
&revisedUpdateRate,
IID_IOPCItemMgt,
(LPUNKNOWN *)(&pItemMgt)
);
showOPCStatus("OPCServer::AddGroup",hr);
if (hr != S_OK) return false;
qDebug() << "The server group handle is: " << QString("0x%1").arg(serverGroupHandle,4,16);
qDebug() << "The revised update rate is: " << revisedUpdateRate;

#define ITEM_ID L"Hardware.Modules.Analyser.SI220.SimulationMode"
QString accessPath("");
QString itemId("Hardware.Modules.Analyser.SI220.SimulationMode");
wchar_t accessPathBuffer[1024];
wchar_t itemIdBuffer[1024];
accessPath.toWCharArray(accessPathBuffer);
itemId.toWCharArray(itemIdBuffer);
static const int ITEM_COUNT = 1;
OPCITEMDEF ItemArray[ITEM_COUNT] =
{{
/*szAccessPath*/ accessPathBuffer,
/*szItemID*/ itemIdBuffer,
/*bActive*/ FALSE,
/*hClient*/ 1,
/*dwBlobSize*/ 0,
/*pBlob*/ NULL,
/*vtRequestedDataType*/ VT_UI1,
/*wReserved*/0
}};
OPCITEMRESULT *itemResults = nullptr;
HRESULT *errors = nullptr;
hr = pItemMgt->AddItems(ITEM_COUNT,ItemArray,&itemResults,&errors);
bool failed = false;
if (hr != S_OK) {
failed = true;
}
showOPCStatus("createMidGroup/AddItems ",hr);
for(DWORD k=0;k<ITEM_COUNT;k++) {
showOPCStatus(QString("createMidGroup/AddItems[%1]").arg(k),errors[k]);
if (errors[k] != S_OK) {
failed = true;
}
CoTaskMemFree(itemResults[k].pBlob);
}
CoTaskMemFree(itemResults);
CoTaskMemFree(errors);
return !failed;
}
void removeMIDGroup()
{
if (pOPCServer != nullptr) {
if (pItemMgt != nullptr) {
pItemMgt->Release();
pItemMgt = nullptr;
}
HRESULT hr = pOPCServer->RemoveGroup(serverGroupHandle,false);
if (hr != S_OK) {
showStatus("deleteMIDGroup",hr);
}
}
}

int main(int argc, char *argv[])
{
Q_UNUSED(argc)
Q_UNUSED(argv)
if (!initializeCOM()) return -1;
if (connectToServer(QString("192.168.12.106"))) {
if (createMIDGroup()) {
removeMIDGroup();
}
disconnectFromServer();
}

deinitializeCOM();
return 0;
}
static void showStatus(const QString &message,HRESULT code)
{
_com_error error(code);
qDebug() << message + QString(": " ) +  QString::fromWCharArray(error.ErrorMessage());
}

因此,根据Qt文档:https://doc.qt.io/qt-5/qstring.html#toWCharArraytoWCharArray创建一个NOT NULL CHAR TERMINATED unicode字符串。因此,更好的用法是:int size=itemId.toWCharArray(itemIdBuffer(;itemIdBuffer[size]=L'\0';accessPathBuffer也是如此。将未终止的组名发送到OPC服务器可能不是一个好主意。好的方面是,CreateGroupEnumerator发送回与接收到的相同的未终止的组名。因此,问题既与COM无关,也与OPC无关,只是没有阅读好的文档。

最新更新