Problem Overview
The WinCC ODK (Open Development Kit) C/C++ API exposes the MSRTEnumArchivData function for enumerating Alarm Logging messages within a defined time window. When integrating this function into an export routine that mirrors a working TLGGetArchivDataEx archive-value export, the call frequently returns FALSE with a populated CMN_ERROR structure:
Error in MSRTEnumArchivData: E1= 0x00000001 ; E2= 0x00000000
Connection to the message system run time could not be set up!
File [F:\GEN_AKTUELL\HMI_WINCC_01_SRC\SC_RT_SERVER\ServerServices\Server\MsRtCli\src\app\CMsRt.cpp]
Line [4099]
The Tag Logging counterpart TLGGetArchivDataEx continues to function, indicating that the ODK transport itself is intact. The failure is isolated to the Messaging Runtime (MSRT) subsystem. This article documents the root cause, the corrective pattern, and the verification procedure to ensure repeatable Alarm Logging export.
Technical Background: WinCC ODK MSRT API
The MSRT prefix denotes the WinCC Messaging System Runtime client interface. Three functions form the minimum session envelope:
| Function | Purpose | Return Type |
|---|---|---|
MSRTStartMsgService |
Opens a session to the alarm runtime, installs a user callback, and applies a MSG_FILTER_STRUCT. |
BOOL + CMN_ERROR
|
MSRTEnumArchivData |
Enumerates or reads archived messages matched by the active filter. |
BOOL + CMN_ERROR
|
MSRTStopMsgService |
Closes the session and detaches the callback. |
BOOL + CMN_ERROR
|
The official reference is the WinCC ODK Programmer's Manual entry for "MSRT - functions for message access". The API is delivered with WinCC V7.x and is identical (with minor additions) across V7.0 through V7.5 SP2. The header files ship under \<WinCC>\ApiLib\MsrtApi\:
-
MsrtCli.h— function prototypes -
CustData.h—MSG_FILTER_STRUCT,MSG_DATA_STRUCT, archive flags -
CmnErr.h—CMN_ERRORdefinition
The header path is configured in the Visual Studio project as C/C++ → Additional Include Directories; the matching libraries (MsrtCli.lib, ScRtSrv.lib) are added under Linker → Input → Additional Dependencies.
Decoding Error 0x00000001
The CMN_ERROR layout used by the ODK is:
typedef struct _CMN_ERROR {
DWORD dwError1; // subsystem ID or HRESULT-equivalent
DWORD dwError2; // function-specific error code
TCHAR szErrorText[256];
DWORD dwReserved1[4];
DWORD dwReserved2;
} CMN_ERROR;
For the MSRT subsystem, dwError1 = 0x00000001 combined with the runtime string "Connection to the message system run time could not be set up!" identifies error class MSRT_E_NO_CONNECTION. It is raised internally on file CMsRt.cpp at the call site of MSRTEnumArchivData and is returned when the runtime reference counted handle associated with the dwServiceID is no longer valid — either because the service was never started, already closed, or detached while a callback was still in flight.
| dwError1 | Symbolic Class | Typical Root Cause |
|---|---|---|
| 0x00000001 | MSRT_E_NO_CONNECTION | Service handle invalid, runtime not active, or callback detached prematurely |
| 0x00000002 | MSRT_E_FILTER_INVALID |
MSG_FILTER_STRUCT contains an unknown filter name or zeroed time slice |
| 0x00000003 | MSRT_E_NO_DATA | Time window contains no archived messages |
| 0x00000010 | MSRT_E_ACCESS_DENIED | User lacks "Configure remote messaging" OS right |
Root Cause: Premature Service Shutdown
The regression is lifecycle-related. The runtime delivers archived messages asynchronously to the callback registered with MSRTStartMsgService. If the caller invokes MSRTStopMsgService before the callback has drained its queue — or, more commonly, immediately after a synchronous MSRTEnumArchivData whose data is still being delivered — the runtime decrements the session reference count to zero. The next internal access from MSRTEnumArchivData finds the handle dereferenced and returns MSRT_E_NO_CONNECTION.
The second contributing condition is the runtime activation. The Alarm Logging runtime must be started before any ODK consumer calls MSRTStartMsgService. WinCC Graphical Runtime and WinCC ServiceMode have independent configuration surfaces:
- Computer properties → Startup — tick Alarm Logging Runtime. Activated in WinCC Explorer by selecting the computer, opening Properties, switching to the Start-up tab.
-
ServiceMode project — the service-mode configuration script under
Siemens\WinCC\<project>\config\ServiceModeConfig.xmlmust contain<AlarmLogging enabled="1"/>; otherwise the service-mode process exits the messaging component. -
ODK connection path — the project receiving the ODK call must be the one whose
MSRTCliinstance resolves the named pipe\\.\pipe\MsRtCli_<project>. A cross-project call requires either dual start or explicit project mapping.
Solution: Correct Callback and Session Lifecycle
Apply both fixes together: ensure runtime activation, and never close the service while the callback is active. The pattern below is the minimum correct sequence.
// Callback must be thread-safe; do not allocate from inside it.
void CALLBACK MyMSGServiceNotifyCallback(
DWORD dwServiceID,
DWORD dwMsg,
DWORD dwCount,
LPMSG_DATA_STRUCT pMsgData,
LPVOID pUserData)
{
EnterCriticalSection(&g_cs);
for (DWORD i = 0; i < dwCount; ++i) {
// Persist pMsgData[i] or stage for MSRTEnumArchivData to read.
g_pending.push_back(pMsgData[i]);
}
LeaveCriticalSection(&g_cs);
}
// Producer thread / ODK caller
BOOL ret;
CMN_ERROR Error;
DWORD dwServiceID = 0;
MSG_FILTER_STRUCT MsgFilter;
SYSTEMTIME t0, t1;
memset(&Error, 0, sizeof(CMN_ERROR));
memset(&MsgFilter, 0, sizeof(MSG_FILTER_STRUCT));
_tcscpy(MsgFilter.szFilterName, _T("DAILY_BACKUP"));
MsgFilter.dwFilter = MSG_FILTER_DATE | MSG_FILTER_TIME;
GetLocalTime(&t0); t0.wHour = 0; t0.wMinute = 0; t0.wSecond = 0; t0.wMilliseconds = 0;
GetLocalTime(&t1); t1.wHour = 23; t1.wMinute = 59; t1.wSecond = 59; t1.wMilliseconds = 999;
MsgFilter.st[0] = t0;
MsgFilter.st[1] = t1;
ret = MSRTStartMsgService(&dwServiceID, MyMSGServiceNotifyCallback,
&MsgFilter, MSG_NOTIFY_MASK_ALL, NULL, &Error);
if (!ret) { /* inspect Error.dwError1 */ return; }
// Give the callback a chance to drain the first batch.
Sleep(500);
DWORD dwFlags = MSG_ARCHIV_ENUM_DESC;
ret = MSRTEnumArchivData(dwServiceID, FALSE, 0xFFFFFFFF, dwFlags, &Error);
if (!ret) { /* inspect Error.dwError1; do NOT stop service on transient */
if (Error.dwError1 != 0x00000003) {
MSRTStopMsgService(dwServiceID, &Error);
return;
}
}
// IMPORTANT: only stop the service after the callback has gone idle.
// In MFC/Win32 call MSG_PEEK via dwMsg == MSG_NOTIFY_MASK_END_OF_TRANSFER
// or simply wait until g_pending stabilises.
while (g_callbacksInFlight) { Sleep(50); }
MSRTStopMsgService(dwServiceID, &Error);
Notes on the example:
- The callback runs on an internal MSRT worker thread. It is the user's responsibility to synchronize access to shared state; use a
CRITICAL_SECTIONor equivalent. - The
MSG_FILTER_STRUCTfieldszFilterNamecan reference a preconfigured filter archive (defined in Alarm Logging), but for time-windowed enumeration theMSG_FILTER_DATE | MSG_FILTER_TIMEflags with explicitst[0]/st[1]are sufficient. - On
MSRT_E_NO_DATA(0x00000003) the session remains valid — the export simply produced an empty window. Do not stop the service.
Runtime Activation Checklist
Confirm the messaging runtime is loaded before launching the ODK consumer:
- Open WinCC Explorer on the WinCC server computer.
- Right-click the computer icon → Properties → tab Start-up.
- Tick Alarm Logging Runtime; click Apply.
- For ServiceMode deployments, verify the registry value
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Siemens\Automation\WinCC\<project>\AlarmhasActive = 1. - Re-cycle the runtime (WinCC Explorer → Computer → Stop Runtime → Start Runtime). The named pipe
MsRtCli_<project>appears once activation completes; verify via Process Explorer. - Confirm the user account running the ODK application has the SE_ACTIVATE_WINCC_REMOTE privilege. The ODK functions interact via WinCC inter-process named pipes; ACLs on
\\.\pipe\MsRtCli_*must grant access.
Step-by-Step: Resolving the Failure
-
Reproduce — Run the original code with the WinCC runtime active. Capture the exact
CMN_ERRORtriple (dwError1,dwError2,szErrorText). -
Confirm runtime — Open WinCC Alarm Control on the server. Trigger a test message (use
MSRTCreateMsgfrom a test harness). If the message appears in WinCC Alarm Control but the ODK API reportsMSRT_E_NO_CONNECTION, runtime is fine. -
Verify callback — Add a debug counter incremented inside
MyMSGServiceNotifyCallback. If it increments butMSRTEnumArchivDatastill fails, the service was stopped prematurely. -
Apply the lifecycle fix — Refactor as in the code sample: do not stop the service inside the same scope that calls
MSRTEnumArchivData; wait until callbacks drain. - Cross-check archive — Inspect Alarm Logging → Archive Configuration. The relevant archive must be set to All events or contain the message classes enumerated by the filter.
-
Permission check — From the ODK application server, attempt to access
\\<WinCCServer>\pipe\MsRtCli_<project>via rawCreateFile; anERROR_ACCESS_DENIEDreveals an ACL problem. -
Re-run — Confirm
MSRTEnumArchivDatanow returnsTRUEand that the export text file contains the expected entries.
Verification and Test Procedure
| Check | Expected Outcome | Diagnostic |
|---|---|---|
MSRTStartMsgService |
TRUE, dwServiceID > 0 |
If FALSE with dwError1 = 0x10: missing privilege |
| Callback firing | Counter increments after MSRTCreateMsg
|
If never increments: callback pointer invalid or pipe not connected |
MSRTEnumArchivData |
TRUE; szErrorText empty |
FALSE with 0x03: empty window, valid state |
| Export file lines | Matches Alarm Logging row count for window | Significant delta suggests tz or DST misalignment |
MSRTStopMsgService |
TRUE and process exits cleanly |
FALSE after drain: reentrant stop attempt — guard with critical section |
The Diagnostic Data Recorder (channel "API Monitor") under WinCC Explorer can be enabled to capture all ODK function calls and their resulting CMN_ERROR structures for offline review. Outputs are written to <project>\Diagnosis\.
Related API Functions
The MSRT family is presented in the WinCC ODK reference manual:
-
MSRTCreateMsg— injects a single message into the live queue; useful to verify connectivity independent of archived data. -
MSRTGetMsgServiceInfo— reports the current session state and the archive backend selected forMSG_FILTER_NAME. -
MSRTSetMsgFilter— applies a new filter to an open session; combined with the existingszFilterNameyou can rotate windows without restarting the service. -
MSRTReadMessageQueue— alternative entry point for live messages (non-archived); referenced in the ODK manual section on message system access.
The Tag Logging sibling API uses the TLG prefix (TLGGetArchivDataEx, TLGStart, TLGStop) and follows a similar three-phase lifecycle. When building a unified export, the recommended pattern is two parallel sessions — one TL session, one MSRT session — each closed independently in its own draining loop. The ODK does not provide a single function that unifies both subsystems.
Operational Notes and Caveats
- The
MSG_FILTER_DATE | MSG_FILTER_TIMEcombination returns messages whose appearance time falls inside the window. For state and acknowledgement times, change thedwFiltertoMSG_FILTER_STATE_DATE | MSG_FILTER_STATE_TIME. - WinCC archives messages in UTC and renders to the project local time. Time windows that span a daylight savings boundary can drop or duplicate entries; always cross-reference local time and UTC inside the export routine.
- When the ODK application runs on a different computer than the WinCC server, configure the named-pipe remote access under Computer Properties → Remote and ensure the export account has identical credentials on both ends; otherwise
MSRTStartMsgServicewill fail withMSRT_E_NO_CONNECTIONfor an entirely different reason (network reachability vs. lifecycle). - The ODK runtime DLLs (
MsrtCli.dll,ScRtSrv.dll) are loaded into the WinCC process boundary. Mixing debug and release builds across the consumer and the runtime introduces heap corruption that masks itself as connection errors; compile every component with the same Configuration and matching platform (x86 vs x64).
MSRTStopMsgService while the callback may still be active. The resulting MSRT_E_NO_CONNECTION on the next enumeration is the symptom described above but can also mask as sporadic data loss without leaving any error in szErrorText. Add an end-of-transfer notification in MSG_NOTIFY_MASK_END_OF_TRANSFER before the stop call, and join the callback thread deterministically.FAQ
What does MSRTEnumArchivData error 0x00000001 mean?
It maps to MSRT_E_NO_CONNECTION: the session handle returned by MSRTStartMsgService is no longer valid. The most common cause is calling MSRTStopMsgService while the registered callback is still active, or invoking the enumerator before the runtime reference count is non-zero.
Does the Alarm Logging runtime need to be running before MSRTStartMsgService?
Yes. The runtime must be activated under Computer Properties → Start-up → Alarm Logging Runtime and the named pipe MsRtCli_<project> must exist. Confirm with WinCC Alarm Control or call MSRTCreateMsg first as a connectivity test.
How do I export messages from a specific time window?
Build a MSG_FILTER_STRUCT with dwFilter = MSG_FILTER_DATE | MSG_FILTER_TIME and populate st[0] / st[1] with SYSTEMTIME values for the window. Pass the struct to MSRTStartMsgService before invoking MSRTEnumArchivData with MSG_ARCHIV_ENUM_DESC.
Why does TLGGetArchivDataEx work but MSRTEnumArchivData fails?
The TL and MSRT subsystems are independent. Tag Logging and Alarm Logging both have their own runtime activation toggles and named pipes. Tag Logging can be active while Alarm Logging is off, explaining the asymmetry.
Can I run MSRT enumeration from a remote computer?
Yes, when WinCC Remote is licensed and the named-pipe ACLs allow the connecting account. Use the same domain or mirrored local account on both machines. Mismatched credentials return MSRT_E_NO_CONNECTION via the pipe layer before any function returns.