Problem Description
When SIMATIC WinCC V7.x runtime graphics host a popup picture that contains a single WinCC Alarm Control (historically the AlarmOCX ActiveX control), multiple motor objects on the process screen frequently share the same alarm control surface during runtime. If the alarm stream is not filtered to the requesting motor before the control becomes visible, the control displays every pending message in the configured message class — including messages for motors that have no relationship to the popup call. The symptom in the field is that an operator opening motor M1's popup sees alarms from motor M2, M3 ... up to all ten motors configured in the popup template, with no visual separation between motor-specific events and shared plant alarms.
Adding additional electromotors to an existing project without re-engineering the popup's filter expression makes this symptom appear immediately: the first motor's alarms may still be visible because a default filter was set during commissioning, while subsequent motors inherit the empty filter string and either display the entire alarm buffer or no alarm rows at all if the wrong message class was targeted.
WinCC Alarm Control Architecture
The WinCC Alarm Control is a Win32 ActiveX control that subscribes to the message server over the WinCC internal data channel. The control holds two filter expressions:
- MsgFilterSQL: a SQL WHERE clause applied during the request to the message archive. Controls the long-term archive visibility at the message server level.
- MsgFilter (legacy property): a name-list filter used in WinCC V6 and pre-V7 projects. Causes confusion because both filters are exposed simultaneously but operate on different layers of the message stack.
In WinCC V7.4 and later, all filtering of the runtime alarm view should be performed through the Message Filter dialog accessible from the control's context menu, which writes a SQL statement into MsgFilterSQL. The legacy "Selection" tab of the control lets an engineer restrict by message class, priority, and date — these restrictions are appended into MsgFilterSQL when "Apply" is pressed.
Each Alarm Control object on a picture owns its own MsgFilterSQL string at runtime. If no script ever calls SetPropChar with that property, the control shows every message that the WinCC Message Server reports — there is no default "per-motor" filter in WinCC; the default is "no filter". The filter is therefore not preserved between runtime sessions and is not inherited from design-time filter dialogs unless an explicit SetPropChar write is added to the picture's C-action.
How MsgFilterSQL Drives the Per-Motor View
MsgFilterSQL accepts a SQL WHERE clause that runs against the WinCC message archive views. Typical per-motor clauses look like one of the following forms:
MsgFilterSQL = "TEXT LIKE '*Motor_1*'"
MsgFilterSQL = "MSGNR = 1000001 OR MSGNR = 1000002"
MsgFilterSQL = "PARAMETER 1 = 'Motor_1'"
MsgFilterSQL = "PARAMETER 2 = 'M01' AND CLASS = 2"
The exact columns available inside the WHERE clause depend on which message archive configuration is loaded. WinCC exposes the following usable columns inside SQL filters at runtime:
| Column | Type | Content |
|---|---|---|
| MSGNR | Integer | Unique numeric ID of the message |
| Date / Time / Ms | Date / Time / Integer | Time the message was raised |
| STATE | Integer | Alarm state bit field (raised=1, cleared=2, acknowledged=4) |
| PRIORITY | Integer | Priority configured for the message |
| CLASS | Integer | Numeric message class ID |
| TYPE | Integer | Alarm type (alarm, warning, fault, etc.) |
| TEXT | String | Message text associated with the alarm |
| PARAMETER 1..10 | String | Associated value text #1..#10 |
| USER | String | Operator station that raised the message |
| COMPUTER | String | WinCC server name |
| AGREEMENT | String | Comment if state is acknowledged |
The alarm control can be filtered server-side via the picture-level SQL filter or on the archived historian. When the filter references columns that are not present in the loaded message archive configuration, the control silently displays no rows. That silent failure is the most common reason a per-motor filter "does not work" — the WHERE clause is syntactically valid but semantically empty for the running archive.
Root Cause: Why Alarms Disappear After Adding a Motor
When an engineer re-uses an existing popup picture for a new motor and the picture's Alarm Control was pre-configured with one motor's filter, the third variable in the equation is the script that applies the filter at popup open. The original C action that the project shipped with either:
- Contains a literal string (e.g.
"MSGNR = 1000001") inside the C source — adding a motor does not change the literal, so the new motor's messages never match. - References a non-existent picture object or attribute tag, so the SetPropChar call silently fails at runtime.
- Uses a global tag whose value is set by another script that runs out of order, leaving
MsgFilterSQLin the empty default state when the popup opens. - Was inserted directly into the alarm logging "StandardFunctions", where SetPropChar cannot resolve lpszObjectName and the property write is rejected.
The diagnostic C-action the original engineer wrote:
printf("focus Filter %s\r\n",
GetPropChar(lpszPictureName, lpszObjectName, "MsgFilterSQL"));
is a read-back probe. It returns the current filter string at the moment the Alarm Control receives keyboard focus. It does not change behaviour. Three diagnostic questions emerge from executing it:
- Is the printed filter string correct for the motor whose alarm control is on screen?
- Does the WHERE clause reference columns present in the runtime archive?
- Are the message numbers being filtered valid for the alarm server?
Diagnostic Procedure with apdiag.exe
apdiag.exe (WinCC APDiag) is shipped under %ProgramFiles%\Siemens\Automation\WinCC\Utools\ on a default WinCC V7 installation, and under C:\Program Files\Siemens\WinCC\Utools\ on WinCC V6. The tool writes a timestamped trace into a file named apdiag.txt in the runtime project's <ProjectName>\<ComputerName>\ directory. Each printf() and each C-script function call made from a runtime picture is logged with the function's return value and any diagnostic data the developer added.
- Launch the WinCC project in runtime (RT) on the engineering station or — in a multi-user setup — on the server. Open the popup picture of interest on the screen that would normally trigger it.
- Start APDiag on the same machine through the WinCC Explorer menu Tools -> APDiag, or directly from the Utools folder.
- Activate tracing by selecting the categories C-Script and Picture Functions. Leave the tool running in the background.
- Cause the C action to fire — for an OnFocus event, click into the alarm control on the popup. For an OnOpen event on the picture, open the popup.
- Open the resulting log file. The line printed by the printf probe will appear with the timestamp of the focus event. The output will be of the form:
focus Filter MSGNR = 1000001
Compare the printed string with the expected filter. A blank value, a malformed SQL fragment, or the same MSGNR as the previous motor all point to the wrong filter expression being applied to the popup. - Use APDiag's Find dialog to locate every call to SetPropChar/GetPropChar on the runtime picture and verify the ordering relative to the popup's OnOpen picture event.
For projects that cannot install the WinCC diagnostic GUI on operator terminals, apdiag also runs head-less under the runtime service; after the runtime has run with the tool active, the apdiag.txt log can be retrieved from the project directory and inspected offline.
Implementing the Per-Motor Filter via SetPropChar
The fix is to call SetPropChar against the Alarm Control's MsgFilterSQL property immediately before the popup becomes visible. The recommended location for this call is the picture's OnOpen C-action, executed with the popup's target motor passed in via the picture's tag-prefix mechanism or via a project tag that is written by the calling picture button before the picture-open call.
A robust implementation passes the motor identifier as a parameter into the popup's tag-prefix. WinCC's tag-prefix mechanism propagates a value into the picture's C-scripts via the runtime context variable PictureParameter. The OnOpen script then constructs the WHERE clause and applies it to the alarm control.
Step 1: Configure the Tag Prefix on the Popup Picture Window
- In the WinCC Graphics Designer, double-click the picture object that loads the popup (e.g. the screen that hosts the button calling
OpenPicture). - In the "Picture Name" property, click the ... button and select the popup picture from the selection dialog.
- Switch the dialog's mode from "Fixed Picture" to "Picture by Tag Prefix". In the source tag field, select the variable that holds the motor ID for the popup call (e.g.
PopupMotorID, or a multi-instance point). - Press "Apply". The picture object's tag-prefix is now bound to the source tag.
Step 2: Transfer the Motor Identifier Into the OnOpen Scope
Add a global tag PopupMotorID of type unsigned 16-bit integer. Each motor's screen button must set this tag to its own motor number before invoking OpenPicture("Popup_Alarm", ...). A drop-down of motor faceplates can write the tag in the button's OnClick C-action:
// Replace lpszObjectName with the motor ID at compile time per button.
SetTagWord("PopupMotorID", 1); // for Motor_1
// OpenPicture pops the picture using the tag-prefixed binding.
OpenPicture("Popup_Alarm.pdl");
This works for at most 256 motors before the cast requires adjustment. For larger fleets, change the tag to a 32-bit integer.
Step 3: Apply the Filter Inside the Popup Picture
Place the Alarm Control on the popup picture named Popup_Alarm.pdl. Open the picture's OnOpen event in C and add the script:
// Reads the motor ID propagated through the picture's tag prefix.
// The runtime GetTagDWord call returns the value of the bound tag.
DWORD dwMotorID = GetTagDWord("PopupMotorID");
// Build a SQL clause that filters to a single motor's alarms.
// The convention here uses PARAMETER 1 as the motor identifier,
// which is set on each alarm trigger in the alarm logging configuration.
char szFilter[256];
sprintf_s(szFilter, sizeof(szFilter),
"PARAMETER 1 = 'Motor_%lu'",
(unsigned long) dwMotorID);
// Apply the filter to the alarm control on this picture.
// lpszPictureName and lpszObjectName are scoped by the OnOpen picture event.
SetPropChar(lpszPictureName, lpszObjectName,
"MsgFilterSQL", szFilter);
// Optional: scroll to last record and force a refresh of the visible window
SetPropBool(lpszPictureName, lpszObjectName,
"MsgSelLast", TRUE);
// Diagnostic print — confirms the filter was applied during this open
printf("focus Filter %s\r\n",
GetPropChar(lpszPictureName, lpszObjectName, "MsgFilterSQL"));
Step 4: Optional — Filter by Multiple Numeric Message Numbers
If the alarm logging convention does not write the motor ID into Process Value 1, the engineer can build a list of MSGNR values that correspond to the active motor. A mapping structure keeps maintenance simple:
static const DWORD M001_Msgs[] = { 1000001, 1000011, 1000021 };
static const DWORD M002_Msgs[] = { 1000002, 1000012, 1000022 };
// Build an "MSGNR IN (a, b, c)" string
char szFilter[256] = "MSGNR IN (";
const DWORD *pList = NULL;
size_t nItems = 0;
switch (dwMotorID)
{
case 1: pList = M001_Msgs; nItems = sizeof(M001_Msgs)/sizeof(*M001_Msgs); break;
case 2: pList = M002_Msgs; nItems = sizeof(M002_Msgs)/sizeof(*M002_Msgs); break;
default: /* clear filter */ szFilter[0] = '\0';
}
for (size_t i = 0; i < nItems; ++i) {
char szTmp[32];
sprintf_s(szTmp, sizeof(szTmp), "%s%lu", (i ? ", " : ""), pList[i]);
strcat_s(szFilter, sizeof(szFilter), szTmp);
}
strcat_s(szFilter, sizeof(szFilter), ")");
SetPropChar(lpszPictureName, lpszObjectName,
"MsgFilterSQL", szFilter);
Step-by-Step Verification Checklist
After deploying the corrected filter script, run the following verification sequence:
- Static syntax check: Recompile the popup picture in the Graphics Designer. A missing semicolon in the sprintf format or a wrong property name in SetPropChar will raise a C-script compiler error at design time. Hold CTRL while clicking "Compile" to force a full rebuild.
-
Runtime read-back via APDiag: Open each motor's popup in turn, run APDiag, and grep the log for the line beginning
focus Filter. Each motor should yield a differentPARAMETER 1 = 'Motor_N'string when filtered. -
Manual message injection: From the WinCC Alarm Logging editor, click Trigger Acknowledgement / Status on a test alarm that has
PARAMETER 1 = 'Motor_1'in its Process Value block. Only the popup for motor 1 should display the message. - Cross-popup check: Open motor 2's popup while a Motor_1 alarm is already active. The Motor_1 alarm must not appear in Motor 2's view.
-
Filter clear: Add a "Clear Filter" button on the popup that, on click, calls
SetPropChar(lpszPictureName, lpszObjectName, "MsgFilterSQL", ""). Confirm all messages are restored when the clear is executed. - Print on filter change: Keep the printf-probe from the original action in the OnOpen event so each popup open logs its filter value. This gives a permanent test on the live project that can be verified from apdiag.txt after each commissioning update.
Common Errors
| Symptom | Likely Cause | Remediation |
|---|---|---|
| Popup shows no messages at all | SQL filter references a column not present in the message archive configuration | Check the "Process Variables" tab in the message class — confirm PARAMETER 1 is enabled; replace filter with "MSGNR = ..." to test archive visibility |
| Popup always shows the same motor's alarms | Filter string is a literal constant — tag-prefix not used | Replace literal with a sprintf-built string driven by the tag prefix; verify APDiag shows the change |
| Filter applied, but alarm control does not update | SetPropChar runs after the Alarm Control's OnOpen has already requested its first filter; control caches the result | Call SetPropChar before any "Refresh" or "Scroll to bottom" property writes; use OnOpen, not OnVisible |
| Filter applies to first popup but not subsequent ones | Each popup shares a single AlarmOCX instance under a global reference; lpszObjectName returns the same instance name each time | Use picture-local control with a unique name on each popup; verify the named reference resolves at picture open |
| Compiled C-script fails to load | Picture was never opened in the Graphics Designer with the Alarm Control present | Compile the popup picture manually; recompile by holding CTRL and clicking "Compile" |
| Motor 1 popup is fine, Motor 2 shows no alarm rows | Wrong message class bound to the Alarm Control — Motor 2 only has alarms in a different class | Set the alarm control's "Selected Message Classes" to include the motor 2 class as well |
| Filter string contains stray line breaks or NULs after sprintf | Improper buffer size; sprintf truncated | Use sprintf_s with explicit size; check that the last byte of szFilter is 0 |
| Filter string is set but changes after a few seconds | A second C-action overwrites MsgFilterSQL — for example OnVisible running after OnOpen | Search for every reference to MsgFilterSQL in the project (Edit > Find in Editor across all PDLs) and remove duplicates |
Operational Notes
- The Alarm Control cannot survive a runtime tag-prefix change. If the operator changes the motor selection at runtime without re-opening the picture, the OnOpen won't re-fire. Either force the popup to re-open on every change, or move the SetPropChar call into a property change-trigger C-action that reads the tag prefix on each tag-change event.
- Multiple Alarm Controls on a single popup are supported if the picture host can carry more than one ActiveX instance; they would typically be split by message class (faults vs warnings), but each receives its own SetPropChar call.
- Database connection strings for the message server must be present in the WinCC project for SQL-based filters to evaluate. A missing CS_ArchiveBacklog connection prevents MsgFilterSQL from being applied at all.
- WinCC V7.4 SP1 and later support a built-in alarm view with the "Allow multi-line filter" property. Set to FALSE for compact filter strings, TRUE when the filter must span multiple rows of the message archive configuration — the engineers' GUI has its own filter editor when this is enabled.
- WinCC V7.5 introduced the Alarm Control Professional option with extended SQL filter expressions including more flexible string matching. For projects that must report on cross-message relationships, migrating to Alarm Control Professional gives access to a wider column set.
- Filter strings are evaluated on the WinCC Message Server each time the Alarm Control refreshes its visible buffer. Sending a query with a LIKE '%Motor%' pattern against a message archive that retains 30 days of history places a noticeable load on SQL Server; for hot-path filters, prefer direct MSGNR or PARAMETER equality comparisons. The printf probe should be removed (or its target redirected to a non-debug file) before the popup reaches production.
Reference Summary
-
Control object: WinCC Alarm Control (AlarmOCX), runtime property
MsgFilterSQL. See the Siemens SIMATIC WinCC support portal for product documentation under the SIMATIC HMI / WinCC product family. -
C-API functions:
GetPropChar,SetPropChar,GetPropBool,SetPropBool— scoped to a runtime picture bylpszPictureNameandlpszObjectName. -
Diagnostic tool:
apdiag.exein%ProgramFiles%\Siemens\Automation\WinCC\Utools\(WinCC V7) or%ProgramFiles%\Siemens\WinCC\Utools\(WinCC V6). Writesapdiag.txtin the runtime project's computer-specific folder. - Filter placement: Picture's OnOpen C-action — never on a global script or in Alarm Logging.
- Help resource: WinCC Information System shipped with the installation — search "Working with the Alarm Control" and "Selection" dialog for column references.
Frequently Asked Questions
Why is the alarm control called AlarmOCX in C-scripts but Alarm Control in the Graphics Designer?
Both names refer to the same component. "Alarm Control" is the official product name; "AlarmOCX" is the underlying ActiveX class name visible in C-scripting APIs and in the WinCC Information System. The two are interchangeable — SetPropChar and GetPropChar address the same object regardless of which name is used in the documentation.
How do I run apdiag.exe when WinCC is running under a service account?
Start WinCC Runtime first from the engineering station, then start APDiag through the WinCC Explorer menu Tools -> APDiag, and enable the categories C-Script and Picture Functions. The log file is written to the runtime project's computer-specific folder (\<server>\<ProjectName>\<ComputerName>\). When running under a service account with no interactive logon, schedule APDiag via the runtime's startup list instead.
Can I set MsgFilterSQL from a global script?
No. The picture-scoped context that SetPropChar requires is only available from a picture's C-action. A global script does not have access to lpszPictureName or lpszObjectName for any specific Alarm Control object, so the property write is rejected by the runtime. Configure the SetPropChar call from the picture's OnOpen event (or a property-change trigger), not from a project-wide standard function.
The popup opens but the alarm control filter is wrong — could it be cached?
Yes, the runtime Alarm Control caches the filter string for the lifetime of the picture. If a previous popup session set a different filter and the picture is being re-used, the property change may not propagate until the picture is closed and re-opened. Force the picture to close via OpenPicture on a different picture, then OpenPicture the original picture again to guarantee a fresh OnOpen firing.
Which WinCC version introduced tagged picture-prefix propagation?
Picture tag-prefixes have been available since WinCC V6 SP2. WinCC V7.4 and V7.5 reuse the same mechanism but extend support to structured tag references for instance-style projects. Projects that rely on the tag-prefix propagation must ensure that the popup picture's tag-prefix is bound to a project-wide tag at compile time in the Graphics Designer — runtime-only bindings outside the dialog are not supported by the runtime engine.