Resolving WinCC C-Script Faceplate Visibility Errors

David Krause13 min read
HMI / SCADASiemensTroubleshooting
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

Resolving WinCC C-Script Faceplate Visibility Errors

Siemens WinCC Graphics Designer C-scripts that switch between two picture windows or faceplate instances are a common source of intermittent runtime defects. The most frequent failure mode is an asymmetric control flow in an if / else branch: one branch calls SetVisible(...) while the other does not. When the inactive branch executes, the picture window stays hidden and the operator sees nothing happen, even though the underlying tag value changed correctly. This reference documents the exact bug pattern, the WinCC APIs involved, and a hardened coding practice that prevents the issue class entirely.

1. Problem Description

A WinCC picture is configured with a picture-window object named ObjectControl inside the parent process picture DegermTitle.PDL. A button click triggers the OnClick C-event. The script reads a process bit WholeGrain and, depending on its state, opens either DegermBuhler5Control_a.PDL (bit = 1) or DegermBuhler5Control.PDL (bit = 0) in the picture window. It also writes a tag DegermBuhler_Name used by the child faceplate to look up its configuration block.

Observed behavior:

  • Click while WholeGrain == 0: child picture appears, tag is set, visibility is TRUE. Works.
  • Click while WholeGrain == 1: SetPictureName executes, tag is set, but the picture window is still hidden from the previous state. No visible change.

On a second click the picture appears because the previous branch set SetVisible(... TRUE). The defect is therefore intermittent and depends on operator click sequence, which is why it survives factory acceptance testing and surfaces only in production.

2. Root Cause Analysis

The C-script uses a stale-state pattern: the visibility of the picture window is set in the else branch but forgotten in the if branch. WinCC picture windows are stateful graphical objects — once a property is written by a script, the new value persists across subsequent events until the next script overwrites it. A script that only calls SetVisible(...) on the cold path leaves the warm path with whatever visibility the picture window had before the click.

Three contributing factors make this defect easy to write:

  1. API symmetry assumption. Engineers often assume that calling SetPictureName implicitly makes the target picture window visible. It does not. SetPictureName only changes the PictureName property of the picture-window object; the Visible property is independent and must be set separately.
  2. No compiler warning. WinCC's built-in C compiler (Visual C++ 6.0-derived pre-7.4, MSVC 2008/2010 in WinCC 7.4–7.5) accepts unbalanced property writes without diagnostics. There is no lint pass for "missing setter".
  3. No watchdog on hidden picture windows. The WinCC runtime does not raise an alarm or event if a SetPictureName call resolves to a hidden object. The script returns BOOL TRUE regardless of whether the user can see the change.
Engineering Rule: In WinCC, every code path that calls SetPictureName() for a user-facing picture window must also call SetVisible(..., TRUE) on the same window, unless a previous code path in the same event has already done so. A function helper is the cleanest enforcement mechanism.

3. The Fixed Script

The minimum-blast-radius fix is to mirror the SetVisible() call in both branches. The corrected script is shown below.

#include "apdefap.h"

#define TAGNAME "DP_WholeCornScaler2E"

void OnClick(char* lpszPictureName,
             char* lpszObjectName,
             char* lpszPropertyName)
{
    int TAG_1;

    TAG_1 = GetTagBit("WholeGrain");

    if (TAG_1 == 1)
    {
        SetPictureName("DegermTitle.PDL",
                       "ObjectControl",
                       "DegermBuhler5Control_a.PDL");
        SetTagChar("DegermBuhler_Name", TAGNAME);
    }
    else
    {
        SetPictureName("DegermTitle.PDL",
                       "ObjectControl",
                       "DegermBuhler5Control.PDL");
        SetTagChar("DegermBuhler_Name", TAGNAME);
    }

    /* Always make the target window visible. */
    SetVisible("DegermTitle.PDL", "ObjectControl", TRUE);
}

Key changes:

  • The visibility call is hoisted out of the conditional and runs unconditionally after the picture name and tag have been written. This guarantees the operator sees the result of every click.
  • The SetVisible call no longer relies on the previous state of the window, so the script is idempotent.
  • Picture-name strings are normalized to upper case (.PDL) on all branches to match the case WinCC stores in the picture tree. Mixed-case strings occasionally resolve correctly and sometimes fail to load depending on the project's PDL cache.

4. Recommended Hardened Version

For projects with three or more faceplate states, prefer a lookup table plus a single setter helper. This eliminates the entire class of "missing setter" defects.

#include "apdefap.h"

#define TAGNAME_WHOLE  "DP_WholeCornScaler2E"
#define HOST_PICTURE   "DegermTitle.PDL"
#define WINDOW_OBJECT  "ObjectControl"

/* Lookup table: bit value -> PDL to load. */
static const char* g_aplFaceplates[] = {
    "DegermBuhler5Control.PDL",    /* WholeGrain = 0 */
    "DegermBuhler5Control_a.PDL"   /* WholeGrain = 1 */
};

/* Helper that always performs the visibility write. */
static void ShowFaceplate(const char* pdlName)
{
    SetPictureName(HOST_PICTURE, WINDOW_OBJECT, (char*)pdlName);
    SetVisible(HOST_PICTURE, WINDOW_OBJECT, TRUE);
}

void OnClick(char* lpszPictureName,
             char* lpszObjectName,
             char* lpszPropertyName)
{
    int idx = GetTagBit("WholeGrain");

    if (idx < 0) idx = 0;
    if (idx > 1) idx = 1;

    SetTagChar("DegermBuhler_Name", TAGNAME_WHOLE);
    ShowFaceplate(g_aplFaceplates[idx]);
}

Benefits:

  • Adding a third faceplate requires one new array entry, not a new else if block.
  • The setter helper enforces the property-write pairing in one place.
  • Indexing is bounds-checked, eliminating the off-by-one risk when GetTagBit ever returns an unexpected value under a corrupt PLC connection.

5. WinCC C-Script API Reference for the Call Chain

The following tables summarize the APIs touched in this defect class. All four functions are declared in apdefap.h, which WinCC auto-includes for graphics-designer C-scripts when the project option Generate global C functions is enabled.

Function Header Return Side Effect Thread
GetTagBit(const char* tag) apdefap.h int (0/1) or -1 on error Reads tag, may block briefly on PLC GUI
SetTagChar(const char* tag, const char* val) apdefap.h BOOL Writes string tag, triggers any linked events GUI
SetPictureName(const char* pic, const char* obj, const char* pdl) apdefap.h BOOL Sets the PictureName property; does not change visibility GUI
SetVisible(const char* pic, const char* obj, BOOL show) apdefap.h BOOL Sets the Visible property; can hide a still-loaded picture GUI

Notes on each function:

5.1 GetTagBit

Returns the current bit value as an int. A return of -1 indicates a tag-management error (tag not configured, AS connection down, or wrong data type). The WinCC V7.5 manual WinCC Information System > Working with WinCC > ANSI-C function descriptions > Tag functions documents the full error matrix. The error return is not always propagated to the script's BOOL return; downstream code should check for it explicitly as in the hardened version above.

5.2 SetTagChar

Writes a string tag. Triggers any tag-triggered events configured on DegermBuhler_Name, including picture-window parameter updates. Writing from the GUI thread is synchronous, but downstream picture windows do not redraw until the next event-loop tick, so always combine with an explicit SetVisible or redraw call if the operator must see immediate feedback.

5.3 SetPictureName

Loads a PDL into a picture-window object. The picture name is the third argument, with the host picture and the picture-window object name as the first two. WinCC requires the host picture name to match the picture currently on screen (use lpszPictureName if the script is attached to a child picture). When the picture window's Independent window attribute is set, the host argument is ignored and a new top-level window is created — in that case SetVisible is still required for the new window.

5.4 SetVisible

Direct property write on the Visible attribute. The third argument is a BOOL; WinCC accepts 0/1, TRUE/FALSE, or true/false depending on the C-runtime in use. For WinCC 7.5 the runtime is MSVC 2010, so use TRUE/FALSE macros for portability. The call fails silently if the object does not exist on the host picture; check the project picture tree before referencing object names.

6. Verification Procedure

After deploying the fixed script, perform the following five-step validation. Each step is a control against one of the defect's contributing factors.

  1. Compile clean. In Graphics Designer, right-click the script and choose Compile. Confirm zero warnings, zero errors. WinCC 7.5 outputs the build log to <Project>\<Computer>\WinCC\diagnose\*.log.
  2. Runtime tag check. Use the WinCC tag simulator (Start > SIMATIC > WinCC > Tools > Tag Simulation) to toggle WholeGrain between 0 and 1 while watching the picture window in the activated runtime.
  3. Click in each direction. Click the button with WholeGrain = 0; confirm DegermBuhler5Control.PDL appears. Then set the bit to 1 and click; confirm DegermBuhler5Control_a.PDL appears. Repeat five times — no missed loads is the pass criterion.
  4. Toggle-then-click sequence. Toggle the bit in the PLC without clicking. The picture window should retain its last state. Click; it should switch immediately, with no animation flicker or blank frame.
  5. Tag value check. Open the Tag Monitor in the activated runtime and confirm DegermBuhler_Name reads DP_WholeCornScaler2E after each click. This validates the SetTagChar call.

7. Common C-Script Pitfalls in WinCC

The faceplate/visibility defect is one of several recurring failure modes. The matrix below lists the pitfalls most often observed in commissioning.

Pitfall Symptom Defensive Pattern
Picture-name case mismatch (e.g. .Pdl vs .PDL) Picture fails to load; no log entry Always uppercase the extension; quote the exact name from the PDL tree
Object name typed as caption instead of ObjectName SetVisible returns FALSE silently Use the property dialog to copy the object name verbatim
Reading a tag whose name changed during refactor GetTagBit returns -1, if branch becomes the cold path Validate GetTagBit return against -1 and log to GSC
Mixing SetVisible with picture-window Adapt picture set to No Picture loads at original size, partially off-screen Set Adapt picture = Yes on the picture window
Calling SetTagChar with a string literal shorter than the tag length Tag value truncated, child faceplate shows default config Pad to declared length with strncpy + explicit null
Forgetting to declare apdefap.h in project-level functions Compiler error: undeclared identifier SetVisible Always include apdefap.h as the first non-comment line
Writing to a picture window that has been removed in a recent redraw Script succeeds but no effect on screen Re-check the picture tree after every graphics revision

8. Debugging Techniques

WinCC provides three layers of diagnostics for C-scripts. Use them in order of cost; the runtime APDiag and GSC traces are the fastest way to localize the faceplate defect class.

8.1 APDiag Internal Tags

For WinCC V7.4 SP1 and later, APDiag exposes internal diagnostic tags. Enable them in Computer Properties > Graphics Runtime > APDiag. The relevant tags for this defect are:

  • @ScriptErrCount — increments on any script runtime exception.
  • @LastScriptError — string with the last error message.
  • @TagSetErrCount — increments on SetTag family failures.

8.2 Global Script Console (GSC)

Insert printf or sysprintf calls in the C-script to write to the GSC. Open it with WinCC Explorer > Tools > Global Script Console. A debug skeleton:

printf("OnClick: WholeGrain=%d\r\n", GetTagBit("WholeGrain"));

For WinCC 7.5 the GSC output is also mirrored to <Project>\<Computer>\WinCC\diagnose\GSC.log for offline review.

8.3 Picture Tree Browser

The Picture Tree tool in Graphics Designer lists every object in the active picture, with its ObjectName, ObjectType, and current property values. Use it to confirm that the object passed to SetVisible exists in the picture referenced as the first argument. A typo in the host picture name is the single most common reason for a silent no-op.

9. Picture Window vs. Faceplate Architecture

The example uses the term "faceplate" loosely — the loaded picture is a regular PDL opened inside a picture-window object. A true WinCC faceplate (introduced in TIA Portal WinCC and supported in WinCC 7.4 via the WinCC Faceplate Designer) encapsulates the picture-window plus its tag interface into a reusable type. The same defect class appears in faceplate instantiation, with two additional failure modes:

  • Instance visibility binding. Faceplate properties (including visibility) are bound to instance properties on the parent. If the visibility is bound to a tag, the script's SetVisible call is overridden by the next tag write. Resolve by writing the bound tag instead of the picture-window property directly.
  • Multi-instance contamination. A single script attached to a faceplate instance i may unintentionally toggle visibility of a sibling instance j if it uses a hard-coded object name. Always use the lpszObjectName parameter from the event signature for faceplate-internal scripts.

10. Performance and Best Practices

For HMI panels with limited memory (Comfort Panels, Unified Comfort Panels), follow these rules when scripting faceplate loads:

  1. Pool faceplates. Configure all operator screens as members of a small pool (typically 4–8 PDLs) and reuse them across many process tags. Loading an uncached PDL triggers a compile pass and can take 200–800 ms on a 4-inch panel.
  2. Pre-load on picture open. Call SetPictureName with the most common PDL in the parent picture's Open event, then control only SetVisible from button clicks. This eliminates the load latency on the warm path.
  3. Avoid scripting in the hot loop. Move cyclic work from C-scripts to direct tag connections wherever possible. Scripts in OnTimer events at sub-500 ms intervals cause CPU spikes on TIA Unified panels with the OpenES runtime.
  4. Validate strings before use. When a script writes a configuration block identifier into a tag, validate that the identifier exists in the project dictionary. A typo there will manifest as a faceplate that opens but shows "no data" instead of a hard error.
  5. Use configuration files, not literals. A project with 50+ faceplates should read its lookup table from a CSV or INI file via the WinCC file API. Hard-coded #define tables force a code change for every new equipment instance.

11. Migration Notes: WinCC 7.x to TIA Portal WinCC

The C-script API in TIA Portal WinCC (Unified) differs in three ways from classic WinCC 7.x:

  • JavaScript is the primary language. TIA Unified uses ECMAScript with WinCC-specific global functions (UI.OpenScreen, Screen.Items("ItemName").Visible). The C-scripts from classic WinCC do not migrate automatically.
  • No apdefap.h. The header file and the global function header convention are gone. Each screen has its own JavaScript module.
  • Async picture loading. UI.OpenScreen is asynchronous; the previous pattern of SetPictureName + SetVisible collapses into a single awaited call. The C-script defect class does not exist in TIA Unified.

For projects that must bridge the two generations, the official Siemens tool WinCC Script Migration Tool (shipped with SIMATIC WinCC V7.5 SP2) translates 70–80% of common C-scripts to JavaScript, with the remainder requiring manual review. The faceplate-visibility pattern shown in section 4 is the recommended target form during manual review.

Compatibility note: WinCC 7.4 SP1 introduced Unicode-capable C-script compilation. Earlier projects (V7.0–V7.3) compiled in MBCS and require re-validation of any string-handling code after a migration to V7.4 or later. The SetTagChar call shown above is safe in both MBCS and Unicode builds because the tag name is ASCII-only.

12. FAQ

Why does SetPictureName not show the new picture even though it returns TRUE?

SetPictureName only changes the PictureName property of the picture-window object. The Visible property is independent. A previous script event may have set Visible to FALSE, and the new call does not override it. Always pair SetPictureName with SetVisible(..., TRUE) on the same object.

How do I find the exact object name to pass to SetVisible in a WinCC C-script?

Open the picture in Graphics Designer, right-click the picture-window or faceplate object, and choose Properties. The ObjectName field in the Properties dialog shows the string to pass as the second argument. Avoid using the caption text — the caption is for display only and is localized.

Can I call GetTagBit from a faceplate instance script safely?

Yes, but only if the faceplate exposes the tag as a property or the tag name is global. Reading a tag that the faceplate does not own will work, but it creates a hidden coupling that breaks the moment the faceplate is reused for a different equipment. Prefer the faceplate's instance-property interface over direct tag reads.

What is the correct file extension case for the third argument of SetPictureName?

Use the upper-case form .PDL in every script. WinCC's PDL cache is case-sensitive on some installations and case-insensitive on others, which leads to nondeterministic load failures. The picture tree in Graphics Designer always shows the upper-case form, so use that as the reference.

How do I confirm that apdefap.h is the right include for my project?

apdefap.h is auto-generated by WinCC and located under the project's ap_lib directory. It is the correct include for all graphics-designer C-scripts. For project-global functions stored in the Global Script editor, include the equivalent header generated for the project. Missing the include causes compiler errors such as "undeclared identifier SetVisible".

Back to blog