⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 advisesink.cpp

📁 用VC编写的OPC 客户端程序
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// **************************************************************************
// advisesink.cpp
//
// Description:
//	Implements the CKAdviseSink class and IAdviseSink COM interface.  Groups
//	in OPC Servers can use the IAdviseSink interface to advise us of data
//  change events.
//
//	OPC 1.0 requires that only the OnDataChange member function of the 
//	IAdviseSink interface be implemented. None of the others (OnClose, 
//	OnSave etc.) should ever get called.
//
//	The IAdviseSink interface is used by OPC version 1.0.  OPC version 2.0
//	uses the IOPCDataCallback interface instead.  If you know that your 
//  server(s) will only be using OPC version 2.0, you don't really need this.
//
// DISCLAIMER:
//	This programming example is provided "AS IS".  As such Kepware, Inc.
//	makes no claims to the worthiness of the code and does not warranty
//	the code to be error free.  It is provided freely and can be used in
//	your own projects.  If you do find this code useful, place a little
//	marketing plug for Kepware in your code.  While we would love to help
//	every one who is trying to write a great OPC client application, the 
//	uniqueness of every project and the limited number of hours in a day 
//	simply prevents us from doing so.  If you really find yourself in a
//	bind, please contact Kepware's technical support.  We will not be able
//	to assist you with server related problems unless you are using KepServer
//	or KepServerEx.
// **************************************************************************


#include "stdafx.h"
#include "opctestclient.h"
#include "advisesink.h"
#include "item.h"
#include "group.h"


// **************************************************************************
// CKAdviseSink ()
//
// Description:
//	Constructor.  Reference count is initialized to zero.
//
// Parameters:
//	none
//
// Returns:
//	none
// **************************************************************************
CKAdviseSink::CKAdviseSink () : m_cRef (0)
	{
	}

// **************************************************************************
// QueryInterface ()
//
// Description:
//	This function is called to obtain a pointer to one of the COM interfaces
//	objects of this class support (in this case only IAdviseSink and its base
//	class IUnknown).  As is standard COM practice, our reference count is 
//	incremented upon a successful query.  
//
//  This is one of the 3 member functions all COM interfaces must implement.	
//
// Parameters:
//	REFIID		riid			Requested interface type: IID_IUnknown,
//								  or IID_IAdviseSink.
//	LPVOID		*ppInterface	Pointer to requested interface.
//
// Returns:
//	STDMETHODIMP -
//		S_OK - Query successful, ppInterface set to requested pointer.
//		E_INVALIDARG - One of the arguments was invalid.
//		E_NOINTERFACE - Requested interface is not supported.
// **************************************************************************
STDMETHODIMP CKAdviseSink::QueryInterface (REFIID riid, LPVOID *ppInterface)
	{
	// Validate ppInterface.  Return with "invalid argument" error code if invalid:
	if (ppInterface == NULL)
		return (E_INVALIDARG);

	// Standard COM practice requires that we invalidate output arguments
	// if an error is encountered.  Let's assume an error for now and invalidate
	// ppInterface.  We will reset it to a valid interface pointer later if we
	// determine requested ID is valid:
	*ppInterface = NULL;

	// Reset ppInterface if requested interface type is valid:
	if (riid == IID_IUnknown)
		*ppInterface = (IUnknown*) this;
	else if (riid == IID_IAdviseSink)
		*ppInterface = (IAdviseSink*) this;
	else
		{
		// We have been asked for an interface we don't support.  Return 
		// immediately with "no interface" error code.  ppInterface should
		// still be NULL as required by COM.
		return (E_NOINTERFACE);
		}

	// If we made it here, then the query was successful and ppInterface
	// has been set to requested interface pointer.  Standard COM practice
	// requires us to increment our reference count now.
	AddRef ();

	// Return with "success" code:
	return (S_OK);
	}

// **************************************************************************
// AddRef ()
//
// Description:
//	This function is called to increment our reference count.  Caller should 
//	increment our reference count each time a new pointer to this interface
//	is created (except when obtained by a call to QueryInterface which will
//  bump the reference count on its behalf). 
//
//	This is one of the 3 member functions all COM interfaces must implement.  
//
// Parameters:
//	none
//
// Returns:
//	STDMETHODIMP_(ULONG) - Reference count resulting from this call.
// **************************************************************************
STDMETHODIMP_(ULONG) CKAdviseSink::AddRef ()
	{
	// Increment the reference count then return value:
	return (++m_cRef);
	}

// **************************************************************************
// Release ()
//
// Description:
//	This function is called to decrement our reference count.  Caller should
//	decrement our reference count just before each pointer to this interface 
//	is destroyed (goes out of scope).  Standard COM practice requires us to
//  self-delete once the refernce count returns to zero.
//
//	This is one of the 3 member functions all COM interfaces must implement. 
//
// Parameters:
//	none
//
// Returns:
//	STDMETHODIMP_(ULONG) - Reference count resulting from this call.
// **************************************************************************
STDMETHODIMP_(ULONG) CKAdviseSink::Release ()
	{
	// Decrement reference count and return immediately if not zero:
	if (--m_cRef > 0)
		return (m_cRef);

	// If we make it here, then the reference count is zero.  We are 
	// therefore obliged to delete ourselves:
	delete this;

	// Return our refence count, which is zero now:
	return (0);
	}

// **************************************************************************
// OnDataChange ()
//
// Description:
//	This function is provided to handle notifications from an OPC Group that
//  one or more of its items we are linked to have data changes.  This will
//  get called after asynchronous read and write requests complete, or as an
//  unsolicited notification from the OPC Server.  (We have not implemented
//	1.0 asynchronous refreshes so we don't expect refresh complete 
//	notifications.)
//
//	This is the only IAdviseSink member function OPC 1.0 requires us to 
//	implement.  None of the others (OnClose, OnSave etc.) should ever get
//	called.
//	
// Parameters:
//	FORMATETC	*pFormatEtc		Format of data in storage medium.
//	STGMEDIUM	*pMedium		Storage medium containing the data.
//
// Returns:
//	STDMETHODIMP_(void)
// **************************************************************************
STDMETHODIMP_(void) CKAdviseSink::OnDataChange (FORMATETC *pFormatEtc, STGMEDIUM *pMedium)
	{
	HRESULT hr;
	IStream *pStream = NULL;
	LARGE_INTEGER dlibMove;

	// Check for valid pointers.  Return if invalid:
	if ((pFormatEtc == NULL) || (pMedium == NULL))
		return;
	
	// Check that type of storage medium is handle to global memory.  Return if not:
	if ((pFormatEtc->tymed != TYMED_HGLOBAL) && (pMedium->tymed != TYMED_HGLOBAL))
		return;

	// Obtain an IStream instance from the global memory handle.
	hr = CreateStreamOnHGlobal (pMedium->hGlobal,	// global memory handle
								TRUE,				// delete memory when stream is release
								&pStream);			// put the stream here

	// Make sure we succeeded.  Release stream and return if not:
	if (FAILED (hr))
		{
		TRACE (_T("OTC: CKAdviseSink - CreateStreamOnHGlobal failed.\r\n"));
		pStream->Release ();
		return;
		}

	// Seek to the beginning of the stream.
	dlibMove.LowPart = dlibMove.HighPart = 0;

	hr = pStream->Seek (dlibMove,			// relative offset set to
					    STREAM_SEEK_SET,	// beginning of the stream
						NULL);				// don't care about the new seek pointer

	// Make sure we succeeded.  Release stream and return if not:
	if (FAILED (hr))
		{
		TRACE (_T("OTC: CKAdviseSink - IStream::Seek Beginning failed.\r\n"));
		pStream->Release ();
		return;
		}

	//////////////////////////////////////////////////	
	// DATA CHANGE WITHOUT TIMESTAMP NOTIFICATION	//
	//////////////////////////////////////////////////
	if (pFormatEtc->cfFormat == CF_DATA_CHANGE)
		{
		OPCGROUPHEADER groupheader;
		OPCITEMHEADER2 itemheader;
		VARIANT vtVal;

		// Read the group header:
		hr = pStream->Read (&groupheader,				// where to put the stream data
							sizeof (OPCGROUPHEADER),	// how much to read
							NULL);						// don't care about how many bytes are read

		// Release stream and return if read failed:
		if (FAILED (hr))
			{
			TRACE (_T("OTC: CKAdviseSink - IStream::Read GROUPHEADER failed.\r\n"));
			pStream->Release ();
			return;
			}

		// Post a "read complete" message if this is a read/refresh transaction
		// complete but not for unsolicited data change events.  Data change 
		// events will have a zero transaction ID.
		if (groupheader.dwTransactionID != 0)
			{
			// The client group handle in the group header structure gives us 
			// a pointer to the destination CKGroup:
			CKGroup *pGroup = (CKGroup *) groupheader.hClientGroup;

			// Wrap use of pGroup in exception handler in case pointer is bad:
			try 
				{
				// Log transaction complete status:
				LogMsg (IDS_ASYNC10_READXACT_COMPLETE, groupheader.dwTransactionID, groupheader.dwItemCount,
					pGroup->GetName (), groupheader.hrStatus);
				}
			catch (...)
				{
				ASSERT (FALSE);
				}
			}

		// Initialize a variant to place a data value in:
		VariantInit (&vtVal);

		// Loop over items in the stream:
		for (DWORD dwItem = 0; dwItem < groupheader.dwItemCount; dwItem++)
			{
			// Compute offset of next item:
			dlibMove.HighPart = 0;
			dlibMove.LowPart = sizeof (OPCGROUPHEADER) + (dwItem * sizeof (OPCITEMHEADER2));

			// Seek to next item:
			hr = pStream->Seek (dlibMove, STREAM_SEEK_SET, NULL);

			// If seek failed, try to get next item:
			if (FAILED (hr))
				{
				TRACE (_T("OTC: CKAdviseSink - IStream::Seek Next Item failed.\r\n"));
				continue;
				}

			// Read the item header:
			hr = pStream->Read (&itemheader, sizeof (OPCITEMHEADER2), NULL);

			// If read failed, try to get next item:
			if (FAILED (hr))
				{
				TRACE (_T("OTC: CKAdviseSink - IStream::Read Item Header failed.\r\n"));
				continue;
				}

			// Seek to beginning of data:
			dlibMove.LowPart = itemheader.dwValueOffset;	
			hr = pStream->Seek (dlibMove, STREAM_SEEK_SET, NULL);

			// If seek failed, try to get next item:
			if (FAILED (hr))
				{
				TRACE (_T("OTC: CKAdviseSink - IStream::Seek Data failed.\r\n"));
				continue;
				}

			// Read the variant data:
			hr = pStream->Read (&vtVal, sizeof (VARIANT), NULL);

			// If read failed, try to get next item:
			if (FAILED (hr))
				{
				TRACE (_T("OTC: CKAdviseSink - IStream::Read Variant failed.\r\n"));
				continue;
				}

			// A little more work for strings and arrays:
			switch (vtVal.vt)
				{
				case VT_BSTR:
					{
					DWORD dwLength = 0;

					// Read string length:
					hr = pStream->Read (&dwLength, sizeof (DWORD), NULL); 
        
					// If read failed, try to get next item:
					if (FAILED (hr))
						{
						TRACE (_T("OTC: CKAdviseSink - IStream::Read string size failed.\r\n"));
						continue;
						}

					if (dwLength == 0)
						vtVal.bstrVal = NULL;
					else 
						{
						// Allocate memory for string.  We will free this once we copy
						// the contents of the string into the item object:
						vtVal.bstrVal = SysAllocStringLen (NULL, dwLength);

						if (vtVal.bstrVal != NULL)
							{
							// Read string data:
							hr = pStream->Read (vtVal.bstrVal, (dwLength + 1) * sizeof (OLECHAR), NULL);

							// If read failed, try to get next item:
							if (FAILED (hr))
								{
								TRACE (_T("OTC: CKAdviseSink - IStream::Read string value failed.\r\n"));
								SysFreeString (vtVal.bstrVal);
								continue;
								}
							}
						}
					}
					break;

				case VT_UI1	| VT_ARRAY:
				case VT_I1	| VT_ARRAY:
				case VT_UI2	| VT_ARRAY:
				case VT_I2	| VT_ARRAY:
				case VT_UI4	| VT_ARRAY:
				case VT_I4	| VT_ARRAY:
				case VT_R4	| VT_ARRAY:

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -