大致程序如下,纯手工原创,需要的话再电邮咨询 fuelone@gmail.com
IOPCItemMgt* ipMgt; // ipServer->AddGroup 时获得 ipMgt的实际指针
IOPCSyncIO *pIOPCSyncIO = NULL;
HRESULT hResult = ipMgt->QueryInterface(IID_IOPCSyncIO, (void**)&pIOPCSyncIO);
OPCITEMRESULT* pResults = (OPCITEMRESULT*)CoTaskMemAlloc(dwCount * sizeof(OPCITEMRESULT));
DWORD dwCount = 10; // 点数,多少个item你想读或写?
OPCHANDLE * hServerItem = (OPCHANDLE *)CoTaskMemAlloc(dwCount * sizeof(OPCHANDLE));
HRESULT * pErrors = (HRESULT *)CoTaskMemAlloc(dwCount * sizeof(HRESULT));
OPCITEMDEF* pItems = (OPCITEMDEF*)CoTaskMemAlloc(dwCount * sizeof(OPCITEMDEF));
memset(pItems, NULL, dwCount * sizeof(OPCITEMDEF));
wchar_t wtext[MAXCHAR] = { 0 };
size_t size, numOfCharConverted;
for (size_t i = 0; i < dwCount; i++)
{
size = strlen(*(items + i)) + 1;
pItems[i].szItemID = (LPWSTR)malloc(sizeof(wchar_t)*size);
mbstowcs_s(&numOfCharConverted, pItems[i].szItemID, size, *(items + i), size);//Plus null
pItems[i].szAccessPath = NULL;
pItems[i].bActive = TRUE;
pItems[i].hClient = (OPCHANDLE)pItems[i].szItemID; // use unique point as client handle
pItems[i].vtRequestedDataType = VT_EMPTY;
pItems[i].dwBlobSize = 0;
pItems[i].pBlob = NULL;
}
hResult = ipMgt->AddItems(dwCount, pItems, pResults, pErrors);
// 这里需要对返回的值进行有效性检查,并释放一些刚分配以后不再用的内存参数
for (DWORD ii = 0; ii < dwCount; ii++) {
if (!pErrors[ii])
hServerItem[ii] = pResults[ii].hServer;
}
1. 如果要进行同步读
hResult = pIOPCSyncIO->Read(OPC_DS_DEVICE, dwCount, hServerItem, &pValues, &pErrors);
for (DWORD ii = 0; ii < dwCount; ii++)
{
VARIANT vValue;
VariantInit(&vValue);
if (SUCCEEDED(VariantChangeType(&vValue, &pValues[ii].vDataValue, NULL, VT_BSTR))) {
//获得你的值vValue.bstrVal
}
VariantClear(&vValue);
}
// 释放一些刚分配以后不再用的内存参数
2. 如果要进行同步写
char** values = NULL; //你需要建一个要写入的数组
VARIANT *varValue = (VARIANT *)CoTaskMemAlloc(dwCount * sizeof(VARIANT));
for (DWORD ii = 0; ii < dwCount; ii++)
{
if (!pErrors[ii])
{
varValue[ii].vt = VT_R4;
varValue[ii].fltVal = strtof(values[ii], NULL);
}
}
hResult = pIOPCSyncIO->Write(dwCount, hServerItem, varValue, &pErrors);
// 释放一些刚分配以后不再用的内存参数
COM是programming against interface,面向接口编程,OPC客户端编程也不例外。根据接口的定义,需要什么数据类型就给什么类型,直接了当,要注意把不要的数据清理干净,尤其是通过客户端在服务端创建的内存占用,否则在服务端造成内存泄漏而不自知。需要有清楚的栈和堆的概念,知道内存是如何在它们上分配的,这是最基本的功力。读写问题涉及的接口及过程如下,
1. 列出所有的OPC服务端,用到的实例接口是CLSID_OpcServerList,MultiQI含有二个子接口IID_IOPCServerList和IID_IOPCServerList2,
MULTI_QI MultiQI[2] = { NULL };
MultiQI[0].pIID = &IID_IOPCServerList;
MultiQI[1].pIID = &IID_IOPCServerList2;
hr = CoCreateInstanceEx(CLSID_OpcServerList, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER, &ServerInfo, count, MultiQI); 然后可以遍历获得的子接口中的任何一个,这里是用的支持IID_IOPCServerList2的实例m_spServerList2,
m_spServerList2->EnumClassesOfCategories(sizeof(arrcatid), arrcatid, 0, NULL, &pEnum);
2. 根据你要的OPC服务器实例接口cClsid获得实例,hResult = CoCreateInstanceEx(
cClsid,
NULL,
CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER,
&cInfo,
1,
&cResults);
3. 获取该服务器相应的管理接口,这里用到的接口是IID_IOPCItemMgt,hResult = ipServer->AddGroup(
L"",
TRUE,
1000,
NULL,
NULL,
NULL,
LOCALE_SYSTEM_DEFAULT,
&hGroup,
&dwRevisedUpdateRate,
IID_IOPCItemMgt,
(IUnknown**)&ipMgt
);
4. 从管理接口获得读写接口,这里用到的接口是IID_IOPCSyncIO,hResult = ipMgt->QueryInterface(IID_IOPCSyncIO, (void**)&pIOPCSyncIO);
5. 从读写接口开始读,hResult = pIOPCSyncIO->Read(OPC_DS_DEVICE, dwCount, hServerItem, &pValues, &pErrors);
6. 从读写接口开始写,hResult = pIOPCSyncIO->Write(dwCount, hServerItem, varValue, &pErrors);