数据订阅主要用到了一个接口——IOPCDataCallback,该接口是在opcda.h中定义的,因为它是个抽像类,所以需要实现OnDataChange、OnReadComplete、OnWriteComplete、OnCancelComplete以及QueryInterface、AddRef、Release七个方法,为了避免重写后面三种方法,笔者才用了COM组件的模板类,就只需要解决上面四种方法即可(其实笔者也不太清楚为什么要这样做,但是仿佛使用了模板类以后,程序能够编译通过,要是采用IConnectionPointContainer这种方式,需要把七种方法全部都实现,所以果断就这样写了)。
数据订阅主要用到了一个接口——IOPCDataCallback,该接口是在opcda.h中定义的,因为它是个抽像类,所以需要实现OnDataChange、OnReadComplete、OnWriteComplete、OnCancelComplete以及QueryInterface、AddRef、Release七个方法,为了避免重写后面三种方法,笔者才用了COM组件的模板类,就只需要解决上面四种方法即可(其实笔者也不太清楚为什么要这样做,但是仿佛使用了模板类以后,程序能够编译通过,要是采用IConnectionPointContainer这种方式,需要把七种方法全部都实现,所以果断就这样写了)。
笔者重写了一个类,继承自IOPCDataCallback,命名为COPCDataCallback,其头文件为:
- #include <atlbase.h>
- #include <atlcom.h>
- #include "opcda.h"
- //
- // Callback.h : Declaration of callback class and definition of minor methods
- //
- //---------------------------------------------------------
- // (c) COPYRIGHT 2003,2004 http://www.opc-china.com INC.
- // ALL RIGHTS RESERVED
- // Original Author:geekCarnegie
- // Original Author Email:geekcarnegie@gmail.com
- //---------------------------------------------------------
- class COPCDataCallback : public IOPCDataCallback,
- public CComObjectRootEx<CComSingleThreadModel>
- {
- public:
- COPCDataCallback() {};
- virtual ~COPCDataCallback() { ; };
- BEGIN_COM_MAP(COPCDataCallback)
- COM_INTERFACE_ENTRY(IOPCDataCallback)
- END_COM_MAP()
- // IOPCDataCallback
- STDMETHODIMP OnDataChange(
- /* [in] */ DWORD dwTransid,
- /* [in] */ OPCHANDLE hGroup,
- /* [in] */ HRESULT hrMasterquality,
- /* [in] */ HRESULT hrMastererror,
- /* [in] */ DWORD dwCount,
- /* [size_is][in] */ OPCHANDLE __RPC_FAR *phClientItems,
- /* [size_is][in] */ VARIANT __RPC_FAR *pvValues,
- /* [size_is][in] */ WORD __RPC_FAR *pwQualities,
- /* [size_is][in] */ FILETIME __RPC_FAR *pftTimeStamps,
- /* [size_is][in] */ HRESULT __RPC_FAR *pErrors);
- STDMETHODIMP OnReadComplete(
- /* [in] */ DWORD dwTransid,
- /* [in] */ OPCHANDLE hGroup,
- /* [in] */ HRESULT hrMasterquality,
- /* [in] */ HRESULT hrMastererror,
- /* [in] */ DWORD dwCount,
- /* [size_is][in] */ OPCHANDLE __RPC_FAR *phClientItems,
- /* [size_is][in] */ VARIANT __RPC_FAR *pvValues,
- /* [size_is][in] */ WORD __RPC_FAR *pwQualities,
- /* [size_is][in] */ FILETIME __RPC_FAR *pftTimeStamps,
- /* [size_is][in] */ HRESULT __RPC_FAR *pErrors);
- STDMETHODIMP OnWriteComplete(
- /* [in] */ DWORD dwTransid,
- /* [in] */ OPCHANDLE hGroup,
- /* [in] */ HRESULT hrMastererr,
- /* [in] */ DWORD dwCount,
- /* [size_is][in] */ OPCHANDLE __RPC_FAR *pClienthandles,
- /* [size_is][in] */ HRESULT __RPC_FAR *pErrors);
- STDMETHODIMP OnCancelComplete(
- /* [in] */ DWORD dwTransid,
- /* [in] */ OPCHANDLE hGroup)
- {
- return S_OK;
- };
- };
- STDMETHODIMP COPCDataCallback::OnDataChange( // OnDataChange notifications
- DWORD dwTransID, // 0 for normal OnDataChange events, non-zero for Refreshes
- OPCHANDLE hGroup, // client group handle
- HRESULT hrMasterQuality, // S_OK if all qualities are GOOD, otherwise S_FALSE
- HRESULT hrMasterError, // S_OK if all errors are S_OK, otherwise S_FALSE
- DWORD dwCount, // number of items in the lists that follow
- OPCHANDLE *phClientItems, // item client handles
- VARIANT *pvValues, // item data
- WORD *pwQualities, // item qualities
- FILETIME *pftTimeStamps, // item timestamps
- HRESULT *pErrors) // item errors
- {
- std::cout << std::endl;
- std::cout << "数据第" << changeFlag << "次变更:" << std::endl;
- DWORD i;
- for (i = 0; i<dwCount; i++)
- {
- value[i] = pvValues[i].fltVal;
- quility[i] = GetQualityText(pwQualities[i]);
- timestamp[i] = COleDateTime(pftTimeStamps[i]).Format();
- }
- std::cout << "数据变更完毕..." << std::endl;
- changeFlag++;
- return S_OK;
- };</span>
#include <atlbase.h>
#include <atlcom.h>
#include "opcda.h"
//
// Callback.h : Declaration of callback class and definition of minor methods
//
//---------------------------------------------------------
// (c) COPYRIGHT 2003,2004 http://www.opc-china.com INC.
// ALL RIGHTS RESERVED
// Original Author:geekCarnegie
// Original Author Email:geekcarnegie@gmail.com
//---------------------------------------------------------
class COPCDataCallback : public IOPCDataCallback,
public CComObjectRootEx<CComSingleThreadModel>
{
public:
COPCDataCallback() {};
virtual ~COPCDataCallback() { ; };
BEGIN_COM_MAP(COPCDataCallback)
COM_INTERFACE_ENTRY(IOPCDataCallback)
END_COM_MAP()
// IOPCDataCallback
STDMETHODIMP OnDataChange(
/* [in] */ DWORD dwTransid,
/* [in] */ OPCHANDLE hGroup,
/* [in] */ HRESULT hrMasterquality,
/* [in] */ HRESULT hrMastererror,
/* [in] */ DWORD dwCount,
/* [size_is][in] */ OPCHANDLE __RPC_FAR *phClientItems,
/* [size_is][in] */ VARIANT __RPC_FAR *pvValues,
/* [size_is][in] */ WORD __RPC_FAR *pwQualities,
/* [size_is][in] */ FILETIME __RPC_FAR *pftTimeStamps,
/* [size_is][in] */ HRESULT __RPC_FAR *pErrors);
STDMETHODIMP OnReadComplete(
/* [in] */ DWORD dwTransid,
/* [in] */ OPCHANDLE hGroup,
/* [in] */ HRESULT hrMasterquality,
/* [in] */ HRESULT hrMastererror,
/* [in] */ DWORD dwCount,
/* [size_is][in] */ OPCHANDLE __RPC_FAR *phClientItems,
/* [size_is][in] */ VARIANT __RPC_FAR *pvValues,
/* [size_is][in] */ WORD __RPC_FAR *pwQualities,
/* [size_is][in] */ FILETIME __RPC_FAR *pftTimeStamps,
/* [size_is][in] */ HRESULT __RPC_FAR *pErrors);
STDMETHODIMP OnWriteComplete(
/* [in] */ DWORD dwTransid,
/* [in] */ OPCHANDLE hGroup,
/* [in] */ HRESULT hrMastererr,
/* [in] */ DWORD dwCount,
/* [size_is][in] */ OPCHANDLE __RPC_FAR *pClienthandles,
/* [size_is][in] */ HRESULT __RPC_FAR *pErrors);
STDMETHODIMP OnCancelComplete(
/* [in] */ DWORD dwTransid,
/* [in] */ OPCHANDLE hGroup)
{
return S_OK;
};
};
STDMETHODIMP COPCDataCallback::OnDataChange( // OnDataChange notifications
DWORD dwTransID, // 0 for normal OnDataChange events, non-zero for Refreshes
OPCHANDLE hGroup, // client group handle
HRESULT hrMasterQuality, // S_OK if all qualities are GOOD, otherwise S_FALSE
HRESULT hrMasterError, // S_OK if all errors are S_OK, otherwise S_FALSE
DWORD dwCount, // number of items in the lists that follow
OPCHANDLE *phClientItems, // item client handles
VARIANT *pvValues, // item data
WORD *pwQualities, // item qualities
FILETIME *pftTimeStamps, // item timestamps
HRESULT *pErrors) // item errors
{
std::cout << std::endl;
std::cout << "数据第" << changeFlag << "次变更:" << std::endl;
DWORD i;
for (i = 0; i<dwCount; i++)
{
value[i] = pvValues[i].fltVal;
quility[i] = GetQualityText(pwQualities[i]);
timestamp[i] = COleDateTime(pftTimeStamps[i]).Format();
}
std::cout << "数据变更完毕..." << std::endl;
changeFlag++;
return S_OK;
};</span>
其中OnDataChange方法的实现代码为:
- STDMETHODIMP COPCDataCallback::OnDataChange( // OnDataChange notifications
- DWORD dwTransID, // 0 for normal OnDataChange events, non-zero for Refreshes
- OPCHANDLE hGroup, // client group handle
- HRESULT hrMasterQuality, // S_OK if all qualities are GOOD, otherwise S_FALSE
- HRESULT hrMasterError, // S_OK if all errors are S_OK, otherwise S_FALSE
- DWORD dwCount, // number of items in the lists that follow
- OPCHANDLE *phClientItems, // item client handles
- VARIANT *pvValues, // item data
- WORD *pwQualities, // item qualities
- FILETIME *pftTimeStamps, // item timestamps
- HRESULT *pErrors) // item errors
- {
- std::cout << std::endl;
- std::cout << "数据第" << changeFlag << "次变更:" << std::endl;
- DWORD i;
- for (i = 0; i<dwCount; i++)
- {
- value[i] = pvValues[i].fltVal;
- quility[i] = GetQualityText(pwQualities[i]);
- timestamp[i] = COleDateTime(pftTimeStamps[i]).Format();
- }
- std::cout << "数据变更完毕..." << std::endl;
- changeFlag++;
- return S_OK;
- };
STDMETHODIMP COPCDataCallback::OnDataChange( // OnDataChange notifications
DWORD dwTransID, // 0 for normal OnDataChange events, non-zero for Refreshes
OPCHANDLE hGroup, // client group handle
HRESULT hrMasterQuality, // S_OK if all qualities are GOOD, otherwise S_FALSE
HRESULT hrMasterError, // S_OK if all errors are S_OK, otherwise S_FALSE
DWORD dwCount, // number of items in the lists that follow
OPCHANDLE *phClientItems, // item client handles
VARIANT *pvValues, // item data
WORD *pwQualities, // item qualities
FILETIME *pftTimeStamps, // item timestamps
HRESULT *pErrors) // item errors
{
std::cout << std::endl;
std::cout << "数据第" << changeFlag << "次变更:" << std::endl;
DWORD i;
for (i = 0; i<dwCount; i++)
{
value[i] = pvValues[i].fltVal;
quility[i] = GetQualityText(pwQualities[i]);
timestamp[i] = COleDateTime(pftTimeStamps[i]).Format();
}
std::cout << "数据变更完毕..." << std::endl;
changeFlag++;
return S_OK;
};
OnReadComplete的代码为:
- STDMETHODIMP COPCDataCallback::OnReadComplete( // OnReadComplete notifications
- DWORD dwTransID, // Transaction ID returned by the server when the read was initiated
- OPCHANDLE hGroup, // client group handle
- HRESULT hrMasterQuality, // S_OK if all qualities are GOOD, otherwise S_FALSE
- HRESULT hrMasterError, // S_OK if all errors are S_OK, otherwise S_FALSE
- DWORD dwCount, // number of items in the lists that follow
- OPCHANDLE *phClientItems, // item client handles
- VARIANT *pvValues, // item data
- WORD *pwQualities, // item qualities
- FILETIME *pftTimeStamps, // item timestamps
- HRESULT *pErrors) // item errors
- {
- std::cout << std::endl;
- std::cout << "数据获取中..." << std::endl;
- if (pErrors[0] == S_OK)
- {
- DWORD i;
- for (i = 0; i<dwCount; i++)
- {
- readValue[i] = pvValues[i].fltVal;
- readQulity[i] = GetQualityText(pwQualities[i]);
- readTS[i] = COleDateTime(pftTimeStamps[i]).Format();
- }
- }
- else
- {
- CString readQuality = GetQualityText(pErrors[0]);
- }
- std::cout << "数据已全部读完..." << std::endl;
- return S_OK;
- };
STDMETHODIMP COPCDataCallback::OnReadComplete( // OnReadComplete notifications
DWORD dwTransID, // Transaction ID returned by the server when the read was initiated
OPCHANDLE hGroup, // client group handle
HRESULT hrMasterQuality, // S_OK if all qualities are GOOD, otherwise S_FALSE
HRESULT hrMasterError, // S_OK if all errors are S_OK, otherwise S_FALSE
DWORD dwCount, // number of items in the lists that follow
OPCHANDLE *phClientItems, // item client handles
VARIANT *pvValues, // item data
WORD *pwQualities, // item qualities
FILETIME *pftTimeStamps, // item timestamps
HRESULT *pErrors) // item errors
{
std::cout << std::endl;
std::cout << "数据获取中..." << std::endl;
if (pErrors[0] == S_OK)
{
DWORD i;
for (i = 0; i<dwCount; i++)
{
readValue[i] = pvValues[i].fltVal;
readQulity[i] = GetQualityText(pwQualities[i]);
readTS[i] = COleDateTime(pftTimeStamps[i]).Format();
}
}
else
{
CString readQuality = GetQualityText(pErrors[0]);
}
std::cout << "数据已全部读完..." << std::endl;
return S_OK;
};
在主程序中先获取组状态:
- /*组更新状态*/
- hr = pIOPCItemMgt->QueryInterface(IID_IOPCGroupStateMgt, /*OUT*/(void**)&pIOPCGroupStateMgt); //得到第十个指针
- ASSERT(pIOPCGroupStateMgt);
- if (FAILED(hr))
- {
- cout << "获取IOPCGroupStateMgt接口失败..." << endl;
- if (pItemResult) CoTaskMemFree(pItemResult);
- if (pErrors) CoTaskMemFree(pErrors);
- CoTaskMemFree(&hOPCServer1);
- CoTaskMemFree(&hOPCServer2); //第六个内存释放
- CoTaskMemFree(&itemArray); //第五个内存释放
- CoTaskMemFree(&clsid); //第四个内存释放
- CoTaskMemFree(&catID); //第三个内存释放
- CoTaskMemFree(&mqi); //第二个内存释放
- CoTaskMemFree(&si); //第一个内存释放
- if (pIOPCGroupStateMgt) pIOPCGroupStateMgt->Release(); //第十个指针释放
- pIOPCGroupStateMgt = NULL;
- if (pIOPCItemMgt) pIOPCItemMgt->Release(); //第五个指针释放
- pIOPCItemMgt = NULL;
- if (pIServer) pIServer->Release(); //第四个指针释放
- pIServer = NULL;
- if (pIUnknown) pIUnknown->Release(); //第三个指针释放
- pIUnknown = NULL;
- if (pIEnumGUID) pIEnumGUID->Release(); //第二个指针释放
- pIEnumGUID = NULL;
- if (pIServerList) pIServerList->Release(); //第一个指针释放
- pIServerList = NULL;
- return 1;
- }
- else if (SUCCEEDED(hr)) {
- cout << "已获取到IOPCGroupStateMgt接口..." << endl;
- }
- DWORD dwRevUpdateRate = 10;
- BOOL bActivateGroup = TRUE;
- hr = pIOPCGroupStateMgt->SetState(/*[in] RequestedUpdateRate*/ NULL, /*[out] RevisedUpdateRate */ &dwRevUpdateRate, /*[in] ActiveFlag for Group */ &bActivateGroup, /*[in] TimeBias*/ NULL, /*[in] PercentDeadband*/ NULL, /*[in] LCID*/ NULL, NULL); </span>
/*组更新状态*/
hr = pIOPCItemMgt->QueryInterface(IID_IOPCGroupStateMgt, /*OUT*/(void**)&pIOPCGroupStateMgt); //得到第十个指针
ASSERT(pIOPCGroupStateMgt);
if (FAILED(hr))
{
cout << "获取IOPCGroupStateMgt接口失败..." << endl;
if (pItemResult) CoTaskMemFree(pItemResult);
if (pErrors) CoTaskMemFree(pErrors);
CoTaskMemFree(&hOPCServer1);
CoTaskMemFree(&hOPCServer2); //第六个内存释放
CoTaskMemFree(&itemArray); //第五个内存释放
CoTaskMemFree(&clsid); //第四个内存释放
CoTaskMemFree(&catID); //第三个内存释放
CoTaskMemFree(&mqi); //第二个内存释放
CoTaskMemFree(&si); //第一个内存释放
if (pIOPCGroupStateMgt) pIOPCGroupStateMgt->Release(); //第十个指针释放
pIOPCGroupStateMgt = NULL;
if (pIOPCItemMgt) pIOPCItemMgt->Release(); //第五个指针释放
pIOPCItemMgt = NULL;
if (pIServer) pIServer->Release(); //第四个指针释放
pIServer = NULL;
if (pIUnknown) pIUnknown->Release(); //第三个指针释放
pIUnknown = NULL;
if (pIEnumGUID) pIEnumGUID->Release(); //第二个指针释放
pIEnumGUID = NULL;
if (pIServerList) pIServerList->Release(); //第一个指针释放
pIServerList = NULL;
return 1;
}
else if (SUCCEEDED(hr)) {
cout << "已获取到IOPCGroupStateMgt接口..." << endl;
}
DWORD dwRevUpdateRate = 10;
BOOL bActivateGroup = TRUE;
hr = pIOPCGroupStateMgt->SetState(/*[in] RequestedUpdateRate*/ NULL, /*[out] RevisedUpdateRate */ &dwRevUpdateRate, /*[in] ActiveFlag for Group */ &bActivateGroup, /*[in] TimeBias*/ NULL, /*[in] PercentDeadband*/ NULL, /*[in] LCID*/ NULL, NULL); </span>
然后在主程序中调用COPCDataCallback类,设置回调:
- CComObject<COPCDataCallback> *pCOPCDataCallback;
- CComObject<COPCDataCallback>::CreateInstance(&pCOPCDataCallback);
- LPUNKNOWN pCbUnk;
- pCbUnk = pCOPCDataCallback->GetUnknown();
- DWORD dwCookie;
- HRESULT hRes = AtlAdvise(pIOPCGroupStateMgt, pCbUnk, IID_IOPCDataCallback, &dwCookie);
- if (FAILED(hRes)) {
- cout << "数据订阅回调设置失败..." << endl;
- if (pItemResult) CoTaskMemFree(pItemResult);
- if (pErrors) CoTaskMemFree(pErrors);
- CoTaskMemFree(&hOPCServer1);
- CoTaskMemFree(&hOPCServer2); //第六个内存释放
- CoTaskMemFree(&itemArray); //第五个内存释放
- CoTaskMemFree(&clsid); //第四个内存释放
- CoTaskMemFree(&catID); //第三个内存释放
- CoTaskMemFree(&mqi); //第二个内存释放
- CoTaskMemFree(&si); //第一个内存释放
- if (pIOPCGroupStateMgt) pIOPCGroupStateMgt->Release(); //第十个指针释放
- pIOPCGroupStateMgt = NULL;
- if (pIOPCItemMgt) pIOPCItemMgt->Release(); //第五个指针释放
- pIOPCItemMgt = NULL;
- if (pIServer) pIServer->Release(); //第四个指针释放
- pIServer = NULL;
- if (pIUnknown) pIUnknown->Release(); //第三个指针释放
- pIUnknown = NULL;
- if (pIEnumGUID) pIEnumGUID->Release(); //第二个指针释放
- pIEnumGUID = NULL;
- if (pIServerList) pIServerList->Release(); //第一个指针释放
- pIServerList = NULL;
- return 1;
- }
- else if (SUCCEEDED(hRes)) {
- cout << "数据订阅回调设置成功..." << endl;
- }
CComObject<COPCDataCallback> *pCOPCDataCallback;
CComObject<COPCDataCallback>::CreateInstance(&pCOPCDataCallback);
LPUNKNOWN pCbUnk;
pCbUnk = pCOPCDataCallback->GetUnknown();
DWORD dwCookie;
HRESULT hRes = AtlAdvise(pIOPCGroupStateMgt, pCbUnk, IID_IOPCDataCallback, &dwCookie);
if (FAILED(hRes)) {
cout << "数据订阅回调设置失败..." << endl;
if (pItemResult) CoTaskMemFree(pItemResult);
if (pErrors) CoTaskMemFree(pErrors);
CoTaskMemFree(&hOPCServer1);
CoTaskMemFree(&hOPCServer2); //第六个内存释放
CoTaskMemFree(&itemArray); //第五个内存释放
CoTaskMemFree(&clsid); //第四个内存释放
CoTaskMemFree(&catID); //第三个内存释放
CoTaskMemFree(&mqi); //第二个内存释放
CoTaskMemFree(&si); //第一个内存释放
if (pIOPCGroupStateMgt) pIOPCGroupStateMgt->Release(); //第十个指针释放
pIOPCGroupStateMgt = NULL;
if (pIOPCItemMgt) pIOPCItemMgt->Release(); //第五个指针释放
pIOPCItemMgt = NULL;
if (pIServer) pIServer->Release(); //第四个指针释放
pIServer = NULL;
if (pIUnknown) pIUnknown->Release(); //第三个指针释放
pIUnknown = NULL;
if (pIEnumGUID) pIEnumGUID->Release(); //第二个指针释放
pIEnumGUID = NULL;
if (pIServerList) pIServerList->Release(); //第一个指针释放
pIServerList = NULL;
return 1;
}
else if (SUCCEEDED(hRes)) {
cout << "数据订阅回调设置成功..." << endl;
}
在主程序的最后,笔者加了系统暂停,以便控制台程序能够在数据变更的时候就能够获取到变更的数据:
system("pause>nul");
最后释放指针,程序退出,数据订阅完毕。