A QuickOPC subscription can register successfully yet fail to deliver usable change notifications to a Windows service. In the reported case, the same DLL and subscription code worked in a Windows Forms application, while synchronous read and write operations also worked from the service. Server logs showed that the tag was subscribed and a callback was sent after a value change. This isolates the investigation to callback delivery, service security context, or event processing rather than basic server access.
Identify the failing communication path
| Observed behavior | Engineering conclusion |
|---|---|
| Read and write operations succeed | The service can reach the OPC servers for request-response operations. |
| The same DLL works in a Windows Forms client | The subscription and handler code can operate in an interactive process context. |
| The server records a successful subscription | The item identifier and subscription request reach the server. |
| The server reports sending a callback | Investigate the callback path and client-side event handling. |
| Changing the service account from system to local administrator or domain administrator does not help | Account privilege alone does not resolve the observed failure. |
The evidence does not prove where the callback is lost. DCOM infrastructure may block or discard it, or the event may arrive with an exception that the application does not expose clearly. Treat these as separate hypotheses and verify each one.
Inspect the ItemChanged event completely
The handler processes a value only when RaiseItemRead is not null, the event arguments are not null, and e.Vtq is not null. It reports an error only when e.Vtq is null and e.Exception is non-null. Instrument every branch so that a callback containing an exception cannot be mistaken for a missing callback.
MyEasyDAClient.ItemChanged += MyEasyDAClient_ItemChanged;
private static void MyEasyDAClient_ItemChanged(
object sender,
EasyDAItemChangedEventArgs e)
{
// Record entry into the handler before evaluating any condition.
// Record whether RaiseItemRead, e, e.Vtq, and e.Exception are null.
// If e.Exception is present, record its complete diagnostic details.
// If e.Vtq is present, record the value and subscription state.
}
Also verify that the event handler is attached before SubscribeItem runs and that the shared EasyDAClient instance remains alive for the subscription lifetime. The supplied fragment does not establish the actual initialization order or object lifetime.
Test service and DCOM security boundaries
- Run the client and OPC server locally. If local callbacks work but remote callbacks fail, focus on the remote DCOM callback path.
- Test the supported settings of
EasyDAClient.ClientParameters.UseCustomSecurity,EasyDAClient.ClientParameters.TurnOffActivationSecurity, andEasyDAClient.ClientParameters.TurnOffCallSecurity. The evidence does not specify the correct values, so change one controlled configuration at a time and record the result. - Trigger a known tag value change and correlate the server subscription log, server callback log, client handler-entry log, and event exception log.
- Repeat the same test against both the Keepware OPC server and the Simatic OPC server. A failure across both servers points toward the Windows service, callback infrastructure, or client configuration rather than one server implementation.
The reported environment uses EasyOPC v5.23 on Windows Server 2008. Do not assume that a DCOM configuration change is effective merely because request-response reads and writes succeed; subscription notifications use a callback path that requires separate verification.
FAQ
Why do QuickOPC reads work but subscription events fail in a Windows service?
Reads and writes confirm the request-response path, not the callback path. When the server records the subscription and sends a callback, inspect DCOM callback delivery and the client event arguments.
Which QuickOPC security settings should I test for missing callbacks?
Test UseCustomSecurity, TurnOffActivationSecurity, and TurnOffCallSecurity one controlled configuration at a time. The evidence does not identify one universally correct combination.
How can I tell whether ItemChanged fired with an error?
Log entry into MyEasyDAClient_ItemChanged before any null checks, then inspect both e.Vtq and e.Exception. A non-null exception can explain why no tag value reaches the database even though the callback event occurred.