Problem: WinCC Alarm Control Caps Visible Alarms at 1000
Siemens WinCC 7.x Alarm Control (the AlarmOCX active-X control embedded in a WinCC Runtime picture) renders a maximum of 1000 message rows per view. In a 24/7 process, a high-traffic message class, or an HMI with many digital events, the operator only sees the latest 1000 alarms in the on-screen list. Anything older is not deleted from the archive database — the AlarmOCX simply refuses to page through it. Operators and engineers therefore cannot trace a current trip back to events from 15 days, 30 days, or any earlier window without changing the filter on the control.
This is a display limit, not an archive limit. The underlying CC_AlgEvHlth / CC_AlgEvAlm alarm logging tables in the SQL Server continue to hold every archived event, governed by the configured segment size and retention time. The fix is to drive the AlarmOCX MsgFilterSQL property with a date-bounded filter so the control only requests rows inside the operator’s chosen window.
Root Cause: AlarmOCX Paging Architecture
The WinCC Alarm Control is implemented as a C++ OCX that pulls message rows from the alarm server in pages. The default page size is 1000 rows. The OCX maintains a cursor at the head of the visible list and only re-fetches when the operator scrolls or when a filter changes. Without a filter, the cursor lives on the most recent 1000 events; older rows are present in the archive but never requested by the control. Even increasing the segment size on the alarm archive does not help, because the control does not ask for them.
Solution Overview: Drive MsgFilterSQL from VBScript or C
The AlarmOCX exposes a property called MsgFilterSQL. Assigning a valid SQL WHERE clause to that property re-positions the cursor and forces the control to load only the rows that match. Combined with a dynamic date expression built from Now (or a configurable offset), this is the supported way to navigate past the 1000-row page boundary.
Two integration paths are available, depending on where the trigger lives:
- VBScript in the WinCC picture — the most common route, attached to a button Mouse event or a picture-open event.
- C / C++ WinCC API — for ODK-based add-ins, global scripts that call the OCX interface directly, or third-party diagnostic tools that link against the WinCC ODK.
Prerequisites
- WinCC 7.0 SP3 or later, with the alarm logging option licensed and a configured alarm archive.
- An Alarm Control placed in a process picture and named via the Object Name field. The default WinCC name is
ControlAlarm; the example below uses that name. - Operator authorization to call the WinCC scripting interface (VBScript runtime is enabled by default; confirm in Computer Properties > Runtime > VBScript).
- Familiarity with the message-class and message-type IDs configured in your alarm logging project (see WinCC Information System > Alarm Logging > Message Blocks).
- For C: WinCC ODK installed and a Visual Studio project linked against the ODK / WinCC OLE libraries.
Step-by-Step: VBScript Filter for Alarms Older Than Today
The following example shows a complete VBScript handler that filters the Alarm Control to today’s incoming events (a typical operator workflow: show me everything from 00:00:00 to now). Replace the fixed date logic with DateAdd calls to roll the window back 15 days or more.
' Attach to: Button "ShowToday" > Events > Mouse > Click Action (VBScript)
Option Explicit
Dim objAlarmControl
Dim sToday
Dim sSQLFilter
' Build the start-of-day timestamp in the form yyyy-m-d h:m:s.nnn
sToday = DatePart("yyyy", Now) & "-" _
& DatePart("m", Now) & "-" _
& DatePart("d", Now) & " 0:00:00.000"
' Compose the SQL filter. The trailing single-quote inside the string is
' REQUIRED: the AlarmOCX expects the DATETIME literal to be enclosed in
' single quotes and the property assignment closes the string after it.
sSQLFilter = "CLASS IN (18) AND TYPE IN (273) " _
& "AND TEXT6 LIKE '%REDRT%' " _
& "AND DATETIME >= '" & sToday & "'"
Set objAlarmControl = ScreenItems("ControlAlarm")
objAlarmControl.MsgFilterSQL = sSQLFilter
Rolling the Window Back 15 Days
To replicate the original question — show me alarms from 15 days ago — replace the static start-of-day expression with a relative date using DateAdd:
Dim dFrom
dFrom = DateAdd("d", -15, Now)
Dim sFrom
sFrom = Year(dFrom) & "-" _
& Right("0" & Month(dFrom), 2) & "-" _
& Right("0" & Day(dFrom), 2) & " " _
& Right("0" & Hour(dFrom), 2) & ":" _
& Right("0" & Minute(dFrom),2) & ":" _
& Right("0" & Second(dFrom),2) & ".000"
Dim sSQLFilter
sSQLFilter = "DATETIME >= '" & sFrom & "' AND DATETIME <= '" _
& FormatDateTime(Now, 3) & "'"
ScreenItems("ControlAlarm").MsgFilterSQL = sSQLFilter
Right("0" & ... pattern is required because DateAdd returns a Date whose Month/Day/Hour methods drop the leading zero. The alarm logging table stores the literal in yyyy-MM-dd HH:mm:ss.fff form, so a single-digit month such as 3 for March must become 03 in the string.
Step-by-Step: C / C++ Implementation via ODK
For ODK or external OPC clients, the AlarmOCX is also a COM object. The IMsgFilterSQL put-property maps directly to the VBScript property above.
// WinCC ODK / OLE Automation example (MSVC, C++)
#import "MSWinCCAlarmControl.ocx" rename_namespace("WCCAlarm")
HRESULT SetAlarmFilter(IDispatch* pAlarmCtrl, BSTR bstrSql)
{
if (!pAlarmCtrl) return E_POINTER;
DISPID dispid = 0;
LPOLESTR name = L"MsgFilterSQL";
HRESULT hr = pAlarmCtrl->GetIDsOfNames(IID_NULL, &name, 1,
LOCALE_USER_DEFAULT, &dispid);
if (FAILED(hr)) return hr;
DISPPARAMS dp = { 0 };
VARIANTARG vArg;
VariantInit(&vArg);
V_VT(&vArg) = VT_BSTR;
V_BSTR(&vArg) = SysAllocString(bstrSql);
dp.rgvarg = &vArg;
dp.cArgs = 1;
dp.rgdispidNamedArgs = NULL;
dp.cNamedArgs = 0;
EXCEPINFO ei = { 0 };
hr = pAlarmCtrl->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
DISPATCH_PROPERTYPUT, &dp, NULL, &ei, NULL);
VariantClear(&vArg);
return hr;
}
void Demo()
{
// Build filter in the same yyyy-M-d H:m:s.fff shape the OCX expects.
SYSTEMTIME st; GetLocalTime(&st);
wchar_t buf[64];
swprintf_s(buf, L"DATETIME >= '%04d-%02d-%02d 0:00:00.000'",
st.wYear, st.wMonth, st.wDay);
// pAlarmCtrl is the IDispatch* of the AlarmOCX on the picture.
SetAlarmFilter(pAlarmCtrl, SysAllocString(buf));
}
SQL Filter Syntax Reference
The string assigned to MsgFilterSQL is a SQL WHERE fragment — not a full statement. The OCX wraps it internally against the alarm archive view. The following columns are available; the exact subset depends on the message blocks enabled in the project.
| Column | Meaning | Typical Use |
|---|---|---|
DATETIME |
Event time of the alarm (archive column, yyyy-MM-dd HH:mm:ss.fff) |
Date-range windowing, the single most important filter |
MSGNUMBER |
Configured alarm number | Focus on a specific known trip / fault |
CLASS |
Message class ID (e.g. 1=alarm, 2=warning, 18=operator instruction) |
Filter by severity bucket |
TYPE |
Message state bitmask — 273 indicates came in & acknowledged
|
Hide still-pending events |
STATE |
Current state (came in / went out / acknowledged) | Open vs. closed events |
PRIORITY |
Configured priority 0–16 | High-priority filter |
TEXT1…TEXT10
|
User-defined message text blocks (process value placeholders) |
LIKE '%REDRT%' to search a block by substring |
AGNR |
Source / agent ID of the controller that raised the alarm | Filter by AS station |
USER |
User name that acknowledged the event | Audit-style filtering |
Operators: AND, OR, IN (...), LIKE, BETWEEN, >=, <= are all supported. Avoid SELECT — the OCX does not allow full statements, only the boolean expression.
Common Filter Recipes
| Use Case | MsgFilterSQL Value |
|---|---|
| Show last 7 days of all alarms | DATETIME >= '<TodayMinus7> 0:00:00.000' |
| Show a specific alarm number | MSGNUMBER = 1001234 |
| Show only unacknowledged alarms of class 1 (alarms) | CLASS IN (1) AND STATE IN (1) |
| Show alarms whose user block 6 contains the word “REDRT” (e.g. redundancy switchover events) | TEXT6 LIKE '%REDRT%' |
| Show alarms from controller AS01 only | AGNR = 'AS01' |
| Show “came in” events of class 18 between two timestamps | CLASS IN (18) AND TYPE IN (273) AND DATETIME BETWEEN '<t1>' AND '<t2>' |
| Combine a date window with a text block search | DATETIME >= '<t1>' AND TEXT6 LIKE '%REDRT%' |
How the Filter Bypasses the 1000 Limit
The AlarmOCX cursor is reset whenever MsgFilterSQL changes. The control re-issues a SELECT against the archive view, this time carrying the new WHERE clause. The page size of 1000 still applies per filter result-set, but the result-set itself is now bounded by your filter — e.g. the events that match DATETIME >= '2024-01-01 0:00:00.000' fit on one page, no matter how many tens of thousands of rows exist outside that window.
For very long windows, the operator can chain multiple filter presses: jump to “last month”, then refine by message class. Each filter change is one cursor reset; no extra configuration on the archive side is required.
Configuration Checklist (WinCC 7.0 SP3 and later)
- Open the picture that hosts the Alarm Control in Graphics Designer.
- Select the Alarm Control and confirm the Object Name (default
ControlAlarm). Use that name inScreenItems(...). - Insert a button. Open Properties > Events > Mouse > Click Action and choose VBScript.
- Paste the VBScript from the previous section. Adjust the column list and date logic to match your project.
- Compile the picture. Activate the project.
- In Runtime, click the button. The AlarmOCX re-fetches using the filter; rows older than the previous 1000-row cursor become visible.
Verification
- Visual: in Runtime, click the filter button. The message list should refresh in <2 s for typical archives; longer windows may take 5–10 s the first time.
- Row count check: right-click the control → Properties > Statistics. The total filtered count must exceed 1000 to prove the page boundary is bypassed.
-
Direct DB readback: in SQL Server Management Studio, run
SELECT COUNT(*) FROM CC_AlgEvHlth_and compare to the AlarmOCX count. They must match.WHERE DATETIME >= '<your filter start>' -
Time-window round-trip: set a filter for
DATETIME BETWEEN '<old>' AND '<new>', clear it, then re-apply. Counts must remain stable — this proves the filter is deterministic and is the only path to old data.
Troubleshooting Matrix
| Symptom | Likely Cause | Resolution |
|---|---|---|
| No rows appear after the filter is set | Single-quote pair around DATETIME literal is missing or doubled |
Verify the constructed string ends with DATETIME >= '<ts>'; the trailing single quote is part of the property value |
| Filter applies but still shows only 1000 rows | Filter expression reduces to TRUE (e.g. DATETIME >= '') |
Log the SQL string to a tag and inspect; an empty time literal matches everything and behaves like no filter |
| Compile error “Object doesn't support this property” | Wrong object name or the AlarmOCX is not a WinCC Control on this picture | Confirm Object Name in the Graphics Designer matches the string passed to ScreenItems()
|
| Older alarms visible in SQL but not in WinCC | Alarm archive segment for the requested period is on disk and not loaded into the runtime database | Open Tag Logging / Alarm Logging Editor > Archives > Segment and ensure the segment is set to Linkable and the WinCC service has read access to the <project>\ArchiveManager directory |
VBScript hangs at ScreenItems(...)
|
The picture that hosts the AlarmOCX is not the active picture at the moment of click | Use HMIRuntime.Screens("YourScreen").ScreenItems("ControlAlarm") for cross-picture access |
| Filter accepts a C-style literal but WinCC 7 rejects it | ANSI vs. Unicode: the OCX expects a BSTR/VBScript string with regional settings producing yyyy-M-d
|
Force the locale-invariant format yyyy-MM-dd; do not use FormatDateTime with vbLongDate
|
| Alarms disappear when filter is cleared | The default filter property MsgFilter differs from MsgFilterSQL; clearing the wrong one has no effect |
Always assign a known SQL string; assign an empty string to remove the filter explicitly |
| WinCC Unified V20 shows only 100 alarms in the on-screen control | Architectural limit of the WinCC Unified alarm control; not a project bug | Use the alarm log view (control type Alarm Control — Log) which can page through 1000 per page; reference the Siemens Unified V20 display logged alarms documentation |
Performance Notes
The alarm archive is indexed on DATETIME by default. Date-only filters resolve inside that index and finish in single-digit milliseconds for archives up to a few million rows. Compound filters that include LIKE '%x%' on text blocks force a table scan, because the leading wildcard prevents index use. For audit-heavy plants, prefer the MSGNUMBER or CLASS filter as the primary predicate and the LIKE text filter as the secondary one.
Filter assignments are cheap; the cost is in the first render of the result-set. Once the page is loaded, scrolling inside the OCX is bounded by the page size and is fast.
Field-Proven Caveats
-
One filter at a time.
MsgFilterSQLis a single string. The AlarmOCX will not OR together filters assigned from different scripts; the last assignment wins. -
Date format is locale-sensitive. Build the literal manually as shown; never pass a
Datevariable toMsgFilterSQLdirectly. -
Quoting matters. The
TEXT6 LIKE '%REDRT%'pattern relies on a single-quote pair around the substring. Doubling or omitting it produces a runtime error that the OCX reports as a silent empty result-set. - Authorized scripts only. In projects that disable VBScript runtime, the filter must be applied through a C/C++ ODK add-in or through a global C script that calls the OCX interface.
FAQ
Why does my WinCC 7.0 SP3 Alarm Control only show 1000 alarms?
The AlarmOCX pages results in 1000-row windows. Anything outside the most recent 1000 is still archived in SQL Server but is not requested by the control. Assign a MsgFilterSQL with a DATETIME window to re-position the cursor and access older events.
What is the difference between MsgFilter and MsgFilterSQL?
MsgFilter is a graphical / selection-based filter that respects the Alarm Control’s property dialog. MsgFilterSQL is a free-form SQL WHERE fragment. Use MsgFilterSQL for date arithmetic, compound boolean logic, and LIKE searches against user text blocks such as TEXT6.
How do I show alarms from 15 days ago in WinCC 7?
Build a VBScript that calls DateAdd("d", -15, Now), formats the result as yyyy-MM-dd HH:mm:ss.000, and assigns MsgFilterSQL = "DATETIME >= '<that timestamp>'" to the AlarmOCX. Use the example in this article and adjust the offset.
Does the same 1000-alarm limit exist in WinCC Unified V20?
Yes. The official documentation states that each page shows a maximum of 1000 alarms and the on-screen alarm control shows a maximum of 100 alarms. The SQL filter approach is conceptually the same, although the JavaScript API in Unified differs from VBScript in WinCC 7. See the Siemens Unified V20 logged-alarm documentation.
How do I call MsgFilterSQL from a C or C++ program?
Obtain the IDispatch* of the AlarmOCX, look up the DISPID of MsgFilterSQL via GetIDsOfNames, wrap a BSTR in a VARIANT, and call Invoke with DISPATCH_PROPERTYPUT. The full code is in the C section of this article.