Client Side Interfaces

IOPCDataCallback

In order to use connection points, the client must create an object that supports both the IUnknown and IOPCDataCallback Interface. The client would pass a pointer to the IUnknown interface (NOT the IOPCDataCallback) to the Advise method of the proper IConnectionPoint in the server (as obtained from IConnectionPointContainer:: FindConnectionPoint or EnumConnectionPoints). The Server will call QueryInterface on the client object to obtain the IOPCDataCallback interface. Note that the transaction must be performed in this way in order for the interface marshalling to work properly for Local or Remote servers.

All of the methods below must be implemented by the client.

This Interface will be called as a result of changes in the data of the group (OnDataChange) and also as a result of calls to the IOPCAsyncIO2 interface.

Note: although it is not recommended, the client could change the active status of the group or items while an Async call is outstanding. The server should be able to deal with this in a reasonable fashion (i.e. not crash) although the exact behavior is undefined.

Note: memory management follows the standard COM rules. That is, the server allocates 'in' parameters and frees them after the client returns. The client only frees 'out' parameters. In the case of these callbacks there are no 'out' parameters so all memory is owned by the server.

IOPCDataCallback::OnDataChange

HRESULT OnDataChange(

[in] DWORD dwTransid,

[in] OPCHANDLE hGroup,

[in] HRESULT hrMasterquality,

[in] HRESULT hrMastererror,

[in] DWORD dwCount,

[in, sizeis(dwCount)] OPCHANDLE * phClientItems,

[in, sizeis(dwCount)] VARIANT * pvValues,

[in, sizeis(dwCount)] WORD * pwQualities,

[in, sizeis(dwCount)] FILETIME * pftTimeStamps,

[in, sizeis(dwCount)] HRESULT *pErrors

);
 
 

Description This method is provided by the client to handle notifications from the OPC Group for exception based data changes and Refreshes.
Parameters Description
dwTransid 0 if the call is the result of an ordinary subscription. non-0 if the call is the result of a Refresh.
hGroup The Client handle of the group
hrMasterquality S_OK if OPC_QUALITY_MASK for all ‘qualities’ are OPC_QUALITY_GOOD, S_FALSE otherwise.
hrMastererror S_OK if all ‘errors are S_OK, S_FALSE otherwise.
dwCount The number of items in the client handle list
phClientItems The list of client handles for the items which have changed.
pvValues A List of VARIANTS containing the values (in RequestedDataType) for the items which have changed.
pwQualities A List of Quality values for the items
pftTimeStamps A list of TimeStamps for the items
pErrors A list of HRESULTS for the items. If the quality of a data item has changed to UNCERTAIN or BAD., this field allows the server to return additional server specific errors which provide more useful information to the user. See below.

 

HRESULT Return Codes
Return Code Description
S_OK The client must always return S_OK.

‘pErrors’ Return Codes
Return Code Description
S_OK The returned data for this item quality is GOOD.
E_FAIL The Operation failed for this item.
OPC_E_BADRIGHTS The item is or has become not readable.
OPC_E_UNKNOWNITEMID The item is no longer available in the server address space.
S_xxx, E_xxx S_xxx - Vendor specific information can be provided if this item quality is other than GOOD.

E_xxx - Vendor specific error if this item cannot be accessed.

These vendor specific codes can be passed to GetErrorString().

Comments

For any S_xxx pErrors code the client should assume the curresponding Value, Quality and Timestamp are well defined although the Quality may be UNCERTAIN or BAD. It is recommended (but not required) that server vendors provide additional information here regarding UNCERTAIN or BAD items.

For any FAILED ppError code the client should assume the curresponding Value, Quality and Timestamp are undefined. In fact the Server must set the corresponding Value VARIANT to VT_EMPTY so that it can be marshalled properly.

This section will discuss the reasons why the client may receive callbacks.

Callbacks can occur for the following reasons;

The 'errors' array can return additional information in the case where the server is having problems obtaining data for an Item. These vendor specific errors could contain helpful information about communications errors or device status. E_FAIL, while allowed, is generally not a very helpful error to return.

Note: although it is not recommended, the client could change the active status of the group or items while an Async call is outstanding. The server should be able to deal with this in a reasonable fashion (i.e. not crash) although the exact behavior is undefined.

During cleanup after the callback the Server must be sure to do a VariantClear() on each of the value Variants.
 
 

IOPCDataCallback::OnReadComplete HRESULT OnReadComplete(

[in] DWORD dwTransid,

[in] OPCHANDLE hGroup,

[in] HRESULT hrMasterquality,

[in] HRESULT hrMastererror,

[in] DWORD dwCount,

[in, sizeis(dwCount)] OPCHANDLE * phClientItems,

[in, sizeis(dwCount)] VARIANT * pvValues,

[in, sizeis(dwCount)] WORD * pwQualities,

[in, sizeis(dwCount)] FILETIME * pftTimeStamps,

[in, sizeis(dwCount)] HRESULT *pErrors

);
 
 

Description This method is provided by the client to handle notifications from the OPC Group on completion of Async Reads.
Parameters Description
dwTransid The TransactionID returned to the client when the Read was initiated.
hGroup The Client handle of the group
hrMasterquality S_OK if OPC_QUALITY_MASK for all ‘qualities’ are OPC_QUALITY_GOOD, S_FALSE otherwise.
hrMastererror S_OK if all ‘errors are S_OK, S_FALSE otherwise.
dwCount The number of items in the client handle, values, qualities, times and errors lists. This may be less than the number of items passed to Read. Items for whic errors were detected and returned from Read are not included in the callback.
phClientItems The list of client handles for the items which were read. This is NOT guarenteed to be in any particular order although it will match the values, qualities, times and errors array.
pvValues A List of VARIANTS containing the values (in RequestedDataType) for the items.
pwQualities A List of Quality values for the items
pftTimeStamps A list of TimeStamps for the items
pErrors A list of HRESULTS for the items. If the system is unable to return data for an item, this field allows the server to return additional server specific errors which provide more useful information to the user.

HRESULT Return Codes
Return Code Description
S_OK The client must always return S_OK

‘pErrors’ Return Codes
Return Code Description
S_OK The returned data for this item quality is GOOD.
E_FAIL The Read failed for this item
OPC_E_BADRIGHTS The item is not readable
OPC_E_INVALIDHANDLE The passed item handle was invalid. (Generally this should already have been tested by AsyncIO2::Read).
OPC_E_UNKNOWNITEMID The item is no longer available in the server address space. 
S_xxx, E_xxx S_xxx - Vendor specific information can be provided if this item quality is other than GOOD.

E_xxx - Vendor specific error if this item cannot be accessed.

These vendor specific codes can be passed to GetErrorString().

Comments

For any S_xxx pErrors code the client should assume the curresponding Value, Quality and Timestamp are well defined although the Quality may be UNCERTAIN or BAD. It is recommended (but not required) that server vendors provide additional information here regarding UNCERTAIN or BAD items.

For any FAILED ppError code the client should assume the curresponding Value, Quality and Timestamp are undefined. In fact the Server must set the corresponding Value VARIANT to VT_EMPTY so that it can be marshalled properly.

Items for which an error (E_xxx) was returned in the initial AsyncIO2 Read request will NOT be returned here. I.e. the returned list may be ‘sparse’. Also the order of the returned list is not specified (it may not match the order of the list passed to read).

This Callback occurs only after an AsyncIO2 Read.

The 'pErrors' array can return additional information in the case where the server is having problems obtaining data for an Item. These vendor specific errors could contain helpful information about communications errors or device status. E_FAIL, while allowed, is generally not a very helpful error to return.
 
 

IOPCDataCallback::OnWriteComplete HRESULT OnWriteComplete(

[in] DWORD dwTransid,

[in] OPCHANDLE hGroup,

[in] HRESULT hrMasterError,

[in] DWORD dwCount,

[in, sizeis(dwCount)] OPCHANDLE * phClientItems,

[in, sizeis(dwCount)] HRESULT * pError

);
 
 

Description This method is provided by the client to handle notifications from the OPC Group on completion of AsyncIO2 Writes.
Parameters Description
dwTransid The TransactionID returned to the client when the Write was initiated.
hGroup The Client handle of the group
hrMasterError S_OK if all ‘errors are S_OK, S_FALSE otherwise.
dwCount The number of items in the client handle and errors list. This may be less than the number of items passed to Write. . Items for which errors were detected and returned from Write are not included in the callback.
phClientItems The list of client handles for the items which were written. This is NOT guarenteed to be in any particular order although it must match the ‘errors’ array.
pErrors A List of HRESULTs for the items. Note that Servers are allowed to define vendor specific error codes here. These codes can be passed to GetErrorString().

HRESULT Return Codes
Return Code Description
S_OK The client must always return S_OK

‘pErrors’ Return Codes
Return Code Description
S_OK The data item was written.
OPC_E_BADRIGHTS The item is not writable.
OPC_E_INVALIDHANDLE The passed item handle was invalid. (Generally this should already have been tested by AsyncIO2::Write).
OPC_E_UNKNOWNITEMID The item is no longer available in the server address space. 
S_xxx, E_xxx S_xxx - the data item was written but there is a vendor specific warning (for example the value was clamped).

E_xxx - the data item was NOT written and there is a vendor specific error which provides more information (for example the device is offline). These codes can be passed to GetErrorString().

Comments

Items for which an error (E_xxx) was returned in the initial AsyncIO2 Write request will NOT be returned here. I.e. the returned list may be ‘sparse’. Also the order of the returned list is not specified (it may not match the order of the list passed to write).

This Callback occurs only after an AsyncIO2 Write.

The 'errors' array can return additional information in the case where the server is having problems accessing data for an Item. These vendor specific errors could contain helpful information about communications errors or device status. E_FAIL, while allowed, is generally not a very helpful error to return.
 
 
 
 

IOPCDataCallback::OnCancelComplete HRESULT OnCancelComplete(

[in] DWORD dwTransid,

[in] OPCHANDLE hGroup

);
 
 

Description This method is provided by the client to handle notifications from the OPC Group on completion of Async Cancel.
Parameters Description
dwTransid The TransactionID provided by the client when the Read, Write or Refresh was initiated.
hGroup The Client handle of the group

HRESULT Return Codes
Return Code Description
S_OK The client must always return S_OK

Comments

This Callback occurs only after an AsyncIO2 Cancel. Note that if the Cancel Request returned S_OK then the client can expect to receive this callback. If the Cancel request Failed then the client should NOT receive this callback


IOPCShutdown

In order to use this connection point, the client must create an object that supports both the IUnknown and IOPCShutdown Interface. The client would pass a pointer to the IUnknown interface (NOT the IOPCShutdown) to the Advise method of the proper IConnectionPoint in the server (as obtained from IConnectionPointContainer:: FindConnectionPoint or EnumConnectionPoints). The Server will call QueryInterface on the client object to obtain the IOPCShutdown interface. Note that the transaction must be performed in this way in order for the interface marshalling to work properly for Local or Remote servers.

The ShutdownRequest method on this Interface will be called when the server needs to shutdown. The client should release all connections and interfaces for this server.

A client which is connected to multiple OPCServers (for example Data access and/or other servers such as Alarms and events servers from one or more vendors) should maintain separate shutdown callbacks for each object since any server can shut down independently of the others.

IOPCShutdown::ShutdownRequest HRESULT ShutdownRequest (

[in] LPWSTR szReason

);
 
 

Description This method is provided by the client so that the server can request that the client disconnect from the server. The client should UnAdvise all connections, Remove all groups and release all interfaces.
Parameters Description
szReason An optional text string provided by the server indicating the reason for the shutdown. The server may pass a pointer to a NUL string if no reason is provided.

 

HRESULT Return Codes
Return Code Description
S_OK The client must always return S_OK.

Comments

The shutdown connection point is on a ‘per COM object’ basis. That is, it relates to the object created by CoCreate… If a client connects to multiple COM objects then it should monitor each one separately for shutdown requests.
 
 
 
 

IAdviseSink (old)

The client need only provide a full implementation of OnDataChange. The other methods of IAdviseSink can be implemented as stubs since they will never be called. Callbacks can occur for several reasons; simple Subscription, Async Read, Async Write, Refresh. A client can be written such that it performs several of these operations in parallel. In this case the client can determine the ‘cause’ of a particular callback by examining first the data format as provided in the FORMATETC and second the Transaction ID as contained in the stream.

Because of the nature of the asynchronous calls, OLE requires that no synchronous calls are made from a method that has been called asynchronously (as all of the IAdviseSink methods are) which would cause the asynchronous function to be blocked. It is very important that the methods that are called asynchronously (the IAdviseSink methods) have limited processing, and return quickly. Lengthy processing should be done outside of the context of the asynchronous method that has been invoked.

It is client application responsibility to keep up with the data changes that the server has been configured by the client application to send. The client should assume that the server may send data at the update rate specified in the group, and that for each group that identical throughput may occur. Various Windows and OLE related internal errors can result if the server sends data faster than the client can receive it. The performance of the OPC servers and OPC clients is highly tied to the developers implementation of these critical interfaces.
 

IAdviseSink::OnDataChange

void OnDataChange (

[in] FORMATETC * pFE,

[in] STGMEDIUM * pSTM

);
 
 

Description This method is provided by the client to handle notifications from the OPC Group for exception based data changes, Async reads and Refreshes and Async Write Complete.
Parameters Description
pFE the format of the data being receive by the sink
pSTM the storage medium containing the data.

Comments

This section will discuss the reasons why the client may receive callbacks, the contents of FORMATETC and the contents of the STGMEDIUM.

Note that the caller (the server) owns and will free the storage since the parameters are all 'in's.

The client should NOT free the STGMEDIUM. Also note that the storage is valid only for the duration of the OnDataChange call.

Callbacks can occur for several reasons;

The FORMATETC will be filled in as follows;

fe.cfFormat = OPCSTMFORMATDATA or

OPCSTMFORMATDATATIME or

OPCSTMFORMATWRITECOMPLETE.

fe.ptd = NULL;

fe.dwAspect = DVASPECT_CONTENT;

fe.lindex = -1;

fe.tymed = TYMED_HGLOBAL;

The storage medium will always be TYMED_HGLOBAL (for computability with DCOM). The global memory handle can be found in pSTM.hGlobal. GlobalLock() can be used to convert this to a pointer.

The data stored in the global memory by the server will have one of several structures depending on the Format (which depends on the event that generated the data). Although the data resides in this structure in global memory, we refer to it as a ‘data stream’.

These three formats are summarized below and are described in detail later in the document.

OPCSTMFORMATDATATIME Data with TimeStamp

The data consists of a group header followed by one or more item headers followed by the data.

OPCGROUPHEADER

OPCITEMHEADER1[hdr.dwItemCount]

VARIANTS[hdr.dwItemCount]

OPCSTMFORMATDATA Data without TimeStamp

The data consists of a group header followed by one or more item headers followed by the data.

OPCGROUPHEADER

OPCITEMHEADER2[hdr.dwItemCount]

VARIANTS[hdr.dwItemCount]

OPCSTMFORMATWRITECOMPLETE Async Write Complete

The data consists of a group header followed by one or more item headers followed by the data.

OPCGROUPHEADERWRITE

OPCITEMHEADERWRITE[hdr.dwItemCount]
 
 

IAdviseSink - Data Stream Formats (old) This section describes the data structures associated with the three stream formats used in the IDataObject / IAdviseSink connection. It also discusses the critical issue of the Packing of these streams and structures. These formats are also discussed in the Client Side Custom Interface section.

The following table shows the clipboard format names.

"OPCSTMFORMATDATA" Used for On Data Change, Refresh and Async Read
"OPCSTMFORMATDATATIME" Used for On Data Change, Refresh and Async Read
"OPCSTMFORMATWRITECOMPLETE" Used for Async Write
Clients and servers must ‘Register’ these stream formats by calling the windows function RegisterClipboardFormat(); OPCGROUPHEADER

typedef struct {

DWORD dwSize;

DWORD dwItemCount;

OPCHANDLE hClientGroup;

DWORD dwTransactionID;

HRESULT hrStatus;

} OPCGROUPHEADER;

This structure can appear at the head of the OPCSTMFORMATDATA or OPCSTMFORMATDATATIME data stream. It is followed by an array of OPCITEMHEADER1s or OPCITEMHEADER2s.

Member Description
dwSize The Total size of the data stream (the header, all item headers and all data)
dwItemCount The number of Itemheaders which follow. This will vary depending on the number of values being reported.
hClientGroup The client provided handle for the group for which data is being reported. This allows a single OnDataChange handler to identify which of many possible groups are reporting data.
dwTransactionID For normal subscriptions this is 0

For Async operations Refresh or Read this is the transaction ID returned by the method.

hrStatus The status of the asynchronous request (including OnDataChange). This enables error codes (e.g. E_OUTOFMEMORY) to be returned in the case of an asynchronous request failing in the server. A status of S_FALSE should be returned when the read operation was successful, but one or more items has a quality status of BAD or UNCERTAIN.

Comment

If the hrStatus is any FAILED code then the server must return dwItemCount as 0.

There are no ITEM level HRESULT error codes returned at this time. The only item level status information available to the callback function is the Quality Field.

OPCITEMHEADER1

typedef struct {

OPCHANDLE hClient;

DWORD dwValueOffset;

WORD wQuality;

WORD wReserved;

FILETIME ftTimeStampItem;

} OPCITEMHEADER1;

An array of these structures appears in the stream following the GROUPHEADER for OPCSTMFORMATDATATIME. The serialized data (in the form of Variants) appears after this array.

Member Description
hClient The client provided handle associated with this item
dwValueOffset The offset in the data stream (the global memory section) of the serialized variant which contains the data.
wQuality The Quality bits for the data.
ftTimeStampItem The TimeStamp for the data.

 

OPCITEMHEADER2

typedef struct {

OPCHANDLE hClient;

DWORD dwValueOffset;

WORD wQuality;

WORD wReserved;

} OPCITEMHEADER2;

An array of these structures appears in the stream following the GROUPHEADER for OPCSTMFORMATDATA. The serialized data (in the form of Variants) appears after this array.
 
 

Member Description
hClient The client provided handle associated with this item
dwValueOffset The offset in the data stream (the global memory section) of the serialized variant which contains the data.
wQuality The Quality bits for the data.

 
 
 

OPCGROUPHEADERWRITE

typedef struct {

DWORD dwItemCount;

OPCHANDLE hClientGroup;

DWORD dwTransactionID;

HRESULT hrStatus;

} OPCGROUPHEADERWRITE;

This structure can appear at the head of the data stream. It is followed by an array of OPCITEMHEADERWRITEs.

Member Description
dwItemCount The number of Itemheaders which follow. This will vary depending on the number of values being reported.
hClientGroup The client provided handle for the group for which data is being reported. This allows a single OnDataChange handler to identify which of many possible groups are reporting data.
dwTransactionID This is the transaction ID returned by the IOPCAsyncIO::Write method.
hrStatus The status of the asynchronous write request. This enables error codes (e.g. E_OUTOFMEMORY) to be returned in the case of an asynchronous request failing in the server.

Comment

If the hrStatus is any FAILED code then the server must return dwItemCount as 0. OPCITEMHEADERWRITE

typedef struct {

OPCHANDLE hClient;

HRESULT dwError;

} OPCITEMHEADERWRITE;

An array of these structures appears in the stream following the GROUPHEADERWRITE.
 
 

Member Description
hClient The client provided handle associated with this item
dwError The HRESULTs for each of the items that was written.

 

Comment

The item level HRESULTs for Write are the same as those returned for Sync Write.
 
 
Marshaling the Data (Variants) into the Stream It is important that all servers which use the IDataObject interface marshal the item data into the stream in exactly the same way since the stream itself is exposed to the client. As mentioned above, the various GROUPHEADERs are written first without padding into the stream followed by as many ITEMHEADERs as needed. The ITEMHEADERs must be followed by the data itself. Again the data must be written in exactly the same way without padding by all servers. This data is always in the form of one of the VARIANT types listed earlier. For variant types contained within the variant union itself these are written via:

memcpy(dest, source, sizeof(tagVARIANT));

For a BSTR the union is followed without padding by an image of the BSTR. The BSTR image will include the terminating NUL (WIDE char). Note that BSTRs contain WIDE chars which are 2 bytes each. The BSTR starts with a DWORD byte count followed by 'count' bytes of data followed by 2 bytes of 0. Thus the total space required for the BSTR is the number of bytes specified in count + 6 (4 for the DWORD count and 2 for the trailing NUL).

For VT_ARRAY the data is the VARIANT union followed by the SAFEARRAY structure (with one SAFEARRAYBOUND, pvData = NULL) followed by the data items themselves (the contents of the SAFEARRAY’s HGLOBAL). Again, everything including the data items is completely unpadded.

Currently OPC supports only a one dimensional SAFEARRAY.

Clearly any pointers in the SAFEARRAY and VARIANT unions need to be recreated by the receiver when the data is unmarshalled and stored locally.