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
[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
);
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 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;
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.
[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
);
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 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.
[in] DWORD dwTransid,
[in] OPCHANDLE hGroup,
[in] HRESULT hrMasterError,
[in] DWORD dwCount,
[in, sizeis(dwCount)] OPCHANDLE * phClientItems,
[in, sizeis(dwCount)] HRESULT * pError
);
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
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.
[in] DWORD dwTransid,
[in] OPCHANDLE hGroup
);
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
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.
[in] LPWSTR szReason
);
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.
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.
[in] FORMATETC * pFE,
[in] STGMEDIUM * pSTM
);
Parameters | Description |
pFE | the format of the data being receive by the sink |
pSTM | the storage medium containing the data. |
Comments
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;
fe.cfFormat = OPCSTMFORMATDATA or
OPCSTMFORMATWRITECOMPLETE.
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]
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 |
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
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.
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. |
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. |
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
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
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 SAFEARRAYs 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.