WinCC Alarm Control 1000 Alarm Limit: MsgFilterSQL Workaround

David Krause12 min read
SiemensTroubleshootingWinCC
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

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.

Important: WinCC Unified V20 carries the same architectural pattern: the documentation states that "each page shows a maximum of 1000 alarms. The alarm control shows a maximum of 100 alarms". The filtering technique is therefore relevant for both WinCC 7.x and the newer WinCC Unified Runtime. See the Siemens TIA Portal documentation: Display logged alarms (RT Unified) — WinCC Unified V20 for the modern platform equivalent.

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

  1. WinCC 7.0 SP3 or later, with the alarm logging option licensed and a configured alarm archive.
  2. 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.
  3. Operator authorization to call the WinCC scripting interface (VBScript runtime is enabled by default; confirm in Computer Properties > Runtime > VBScript).
  4. Familiarity with the message-class and message-type IDs configured in your alarm logging project (see WinCC Information System > Alarm Logging > Message Blocks).
  5. 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
Padding note: the 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
TEXT1TEXT10 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)

  1. Open the picture that hosts the Alarm Control in Graphics Designer.
  2. Select the Alarm Control and confirm the Object Name (default ControlAlarm). Use that name in ScreenItems(...).
  3. Insert a button. Open Properties > Events > Mouse > Click Action and choose VBScript.
  4. Paste the VBScript from the previous section. Adjust the column list and date logic to match your project.
  5. Compile the picture. Activate the project.
  6. In Runtime, click the button. The AlarmOCX re-fetches using the filter; rows older than the previous 1000-row cursor become visible.

Verification

  1. 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.
  2. Row count check: right-click the control → Properties > Statistics. The total filtered count must exceed 1000 to prove the page boundary is bypassed.
  3. Direct DB readback: in SQL Server Management Studio, run SELECT COUNT(*) FROM CC_AlgEvHlth_ WHERE DATETIME >= '<your filter start>' and compare to the AlarmOCX count. They must match.
  4. 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. MsgFilterSQL is 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 Date variable to MsgFilterSQL directly.
  • 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.

Back to blog