Problem Overview
When programming WinCC Runtime HMI screens in C (WinCC V7.x or WinCC Professional), a common pattern is to read the active picture window's TagPrefix and use it as a namespace prefix to address a process tag. The following code skeleton is representative of the failure pattern engineers encounter:
char *a; // parent picture window name
char *b; // scratch buffer
char *c; // parent picture name
char *d; // resolved tag name
a = GetParentPictureWindow(lpszPictureName);
c = GetParentPicture(lpszPictureName);
b = strcat(c, ".pdl");
d = GetTagPrefix(b, a);
SetTagBit(d, 1);
The script compiles, the variable d is populated (verified with printf), and the program reaches the final line, where execution fails. Two distinct defects hide inside the snippet: an unsafe strcat on a WinCC-owned string pointer, and a malformed tag name passed to SetTagBit. Each defect produces a different observable symptom — undefined behavior vs. a "Tag not found" runtime error — and both must be corrected to obtain a stable script.
Root Cause Analysis
Defect 1 — Undefined behavior from in-place strcat on a WinCC-allocated buffer
The GetParentPicture() function returns a pointer to an internal string buffer that is owned and managed by the WinCC runtime. WinCC's C API contract is that callers treat this pointer as read-only. Appending ".pdl" to that buffer with strcat(c, ".pdl") violates the contract in two ways:
- The runtime buffer has a fixed allocation size; writing past the end overwrites adjacent heap metadata or other WinCC structures.
- Even when the write fits, you have corrupted state that the runtime will re-use on the next graphics dispatch cycle, leading to a delayed crash, a hang, or a silent mis-read on the next call to any of the picture APIs.
This is the typical cause of "works the first time, crashes the second" behavior observed during commissioning. The fix is to copy the string into a caller-owned buffer before any concatenation, and to size that buffer to the worst case WinCC's documentation specifies (typically 256 bytes for tag names, 512 bytes for picture paths).
Defect 2 — Malformed tag identifier passed to SetTagBit
SetTagBit() expects a fully qualified tag name in the WinCC tag namespace, for example Plant1_Area3_Motor_HMI.Start. It does not accept picture names, .pdl file names, or arbitrary concatenations thereof. The GetTagPrefix() call returns the configured prefix string that the picture window was instantiated with; the offset portion of the tag is supplied by the picture's link configuration, not by the C script. The original code treats d (the prefix) as if it were a complete tag, which is why WinCC reports the tag as non-existent.
Environment and Prerequisites
- Siemens WinCC V7.x or WinCC Professional (TIA Portal) runtime with C scripting enabled on the HMI device.
- A picture window configured with a dynamic
TagPrefixproperty in the Properties pane. See the Siemens TIA Portal VBS object model reference for the TagPrefix property for the equivalent VBS semantics — the same property name and value semantics apply in C. - A consistent tag-naming convention in the AS/HMI connection: every tag the picture uses must share the same suffix after the prefix boundary (e.g.,
{Prefix}_Start,{Prefix}_Stop,{Prefix}_Ack). - Global functions header
apdefap.hincluded in the script project so the WinCC C API prototypes are visible.
Method 1 — TagPrefix With Consistent Tag Offset
This is the supported pattern when the picture window's internal links all use the same offset suffix. The picture is designed once with relative tag references, and the prefix is swapped at runtime to redirect every link to a new underlying tag.
Design constraint
Every tag configured on the picture's I/O fields, buttons, and I/O symbols must follow the form {TagPrefix}<Suffix> where <Suffix> is identical across all controls. Example suffixes: _Start, _Stop, _Status. If the suffixes are not identical, you cannot redirect the picture's tags with a single prefix change.
Working C implementation
#include "apdefap.h"
void OnClick_YesButton(char* lpszPictureName, char* lpszObjectName)
{
/* Caller-owned scratch buffer. WinCC tag names are limited
to MAX_TAGNAME (255) characters in V7.x. */
char szTag[256];
char szPrefix[128];
char* pszParentWindow = NULL;
char* pszParentPicture = NULL;
DWORD dwState = 0;
pszParentWindow = GetParentPictureWindow(lpszPictureName);
pszParentPicture = GetParentPicture(lpszPictureName);
if (pszParentWindow == NULL || pszParentPicture == NULL)
{
/* Picture is not hosted inside a picture window.
No TagPrefix to resolve; abort. */
return;
}
/* Read the configured prefix into a local buffer.
GetTagPrefix copies the prefix into the caller's buffer;
it does NOT return a pointer into WinCC-owned memory. */
GetTagPrefix(pszParentPicture, pszParentWindow, szPrefix, sizeof(szPrefix));
/* Construct the full tag name: {Prefix}_Start.
snprintf is bounds-checked and avoids the strcat hazard. */
_snprintf(szTag, sizeof(szTag), "%s_Start", szPrefix);
/* Defensive check: confirm the tag exists before writing. */
if (GetTagBitWait(szTag, &dwState, 1) != 0)
{
/* Tag not present in the AS connection or in the HMI tag table.
Log and exit. */
printf("TagPrefix set failed: tag '%s' not found\n", szTag);
return;
}
SetTagBit(szTag, 1);
}
.pdl to the picture name. WinCC's tag-namespace functions accept the logical picture name (the base file name without extension), not the file path. Adding the extension produces a tag name that no I/O field will match.Method 2 — Direct Link With PDLRTSetLink
When the underlying tag structure varies between invocations — different tag types, different suffixes, or even different AS connection points — the TagPrefix pattern cannot work because the picture's static link configuration fixes the suffix. The alternative is to override the link dynamically using the WinCC graphics runtime link API.
The relevant function is PDLRTSetLink(), which is exported by the WinCC graphics runtime. It accepts a fully qualified link source string and re-points the I/O field at the new tag at runtime.
#include "apdefap.h"
void OnClick_YesButton(char* lpszPictureName, char* lpszObjectName)
{
char szLink[512];
char szPrefix[128];
char* pszParentWindow = NULL;
char* pszParentPicture = NULL;
pszParentWindow = GetParentPictureWindow(lpszPictureName);
pszParentPicture = GetParentPicture(lpszPictureName);
if (pszParentWindow == NULL || pszParentPicture == NULL)
return;
GetTagPrefix(pszParentPicture, pszParentWindow, szPrefix, sizeof(szPrefix));
/* Different tags this time — no fixed suffix required. */
_snprintf(szLink, sizeof(szLink), "PLC1::S7_Distributed::Motor_%s.CmdStart", szPrefix);
/* Re-point the named I/O field on the hosted picture.
The link parameter is the field's object name on the child picture. */
PDLRTSetLink(lpszPictureName, lpszObjectName, szLink);
/* The new link is a bit tag; drive it high. */
SetTagBit(szLink, 1);
}
Method 2 is the more flexible pattern and is the recommended approach for adaptive HMI applications where the same picture window is reused for many different process units with structurally different tag sets. The trade-off is that the link change is one-shot: each field must be redirected explicitly.
Memory-Safe C Coding in WinCC
The original strcat bug is the most common memory error in WinCC C scripts because engineers coming from managed-runtime environments assume WinCC returns strings they own. They do not. The following table summarizes the ownership semantics of each API the script uses.
| Function | Return type | Ownership | Writable? |
|---|---|---|---|
GetParentPictureWindow() |
char* |
WinCC runtime | No — read-only, do not free |
GetParentPicture() |
char* |
WinCC runtime | No — read-only, do not free |
GetTagPrefix(pic, win, buf, len) |
int (bytes written) |
Caller supplies buf
|
buf is caller-owned and writable |
GetTagBitWait() |
int (state code) |
Returns through out-parameter | Out-parameter is caller-owned |
SetTagBit() |
BOOL |
No allocation | Tag name string is caller-owned; pass a copy |
Rule of thumb: if a WinCC C API returns a char*, treat the pointer as a loaner. Copy it into a local buffer with strncpy or snprintf before you do anything beyond a printf. Functions that take a buffer + length pair (the Get* family) are the safe alternative and should be preferred.
Function Reference
| Function | Purpose | Typical use |
|---|---|---|
GetParentPictureWindow(const char* lpszPictureName) |
Returns the name of the picture window that hosts the calling picture, or NULL. |
Identify which picture window context the script is running in. |
GetParentPicture(const char* lpszPictureName) |
Returns the parent picture's name (the .pdl base name). | Required input to GetTagPrefix. |
GetTagPrefix(pic, win, buf, len) |
Copies the TagPrefix configured on the picture window into buf. |
Read the dynamic prefix string. |
SetTagBit(tag, value) |
Writes a single bit of a WinCC tag. Tag must exist in the tag management. | Final write step after constructing the qualified tag name. |
GetTagBitWait(tag, pState, timeout_ms) |
Synchronous read with timeout; useful as a tag-existence check. | Pre-flight validation before SetTagBit. |
PDLRTSetLink(pic, obj, link) |
Re-points a graphic object's I/O link at runtime. | Method 2 — dynamic link override when suffix varies. |
Verification Procedure
- Open the WinCC Graphics Designer, select the picture window, and confirm the TagPrefix property is bound to a tag (e.g., a WinCC string tag named
CurrentUnit) or a script that returns a prefix such asUnit_3. - Add a Global Script action bound to the "Yes" button. Use the Method 1 code from this article.
- In the WinCC Tag Simulator, ensure at least one tag matching
{Prefix}_Startexists for the prefix the picture window is currently displaying. Example: ifCurrentUnit=Unit_3, confirm thatUnit_3_Startis defined in the tag management. - Start Runtime. Click "Yes." Use the Tag Diagnostics view (WinCC Explorer > Tools > Tag Diagnostics) to confirm the value of
{Prefix}_Starthas been set to 1. - Click "No." Confirm the same tag is reset to 0 by a complementary
SetTagBit(szTag, 0)call in the "No" button handler. - Open the Global Script diagnostic output (or the
aplog.txtfile in the WinCC project directory) and confirm no Tag not found or memory-access error messages are logged across 100+ click cycles.
Troubleshooting Matrix
| Symptom | Likely cause | Fix |
|---|---|---|
Tag not found on SetTagBit
|
Tag name passed to SetTagBit is a prefix only, not a qualified tag. |
Append the configured suffix (_Start, _Stop, etc.) to the prefix before calling SetTagBit. |
Tag name includes .pdl
|
strcat(c, ".pdl") applied to a WinCC-owned pointer. |
Remove the .pdl append. Use the picture base name as returned by GetParentPicture(). |
| Runtime crashes after 5–20 minutes of operation | Heap corruption from strcat overwriting WinCC internal buffers. |
Copy WinCC-returned strings into caller-owned buffers with strncpy / snprintf and stop modifying them in place. |
| Prefix value is empty when read back | Picture window's TagPrefix property is not bound, or the bound tag is empty at the time the script runs. | Verify the binding in the Graphics Designer. Add a printf of the prefix to the script to confirm a non-empty value at the call site. |
| Tag is set on the wrong underlying tag | Suffix mismatch between what the script appends and what the picture's I/O fields expect. | Audit every I/O field on the hosted picture and confirm the suffix portion of every link is identical. If they differ, switch to Method 2 (PDLRTSetLink). |
SetTagBit returns FALSE but the tag exists |
Tag is a word/dword, not a bit tag. The bit address within the word is not specified. | Use SetTagWord with an appropriate bitmask, or split the variable into a dedicated Bool tag in the AS. |
| No effect in Runtime, but the script reports success | Picture window's TagPrefix is evaluated only on picture load; subsequent tag-prefix changes do not propagate to already-resolved links. |
Force a picture reload after changing the prefix with OpenPicture() / ClosePicture(), or use PDLRTSetLink to redirect the specific fields. |
Best Practices
- Reserve a fixed-size
charbuffer for every string the script constructs. Reasonable default sizes: 128 bytes for a single prefix, 256 bytes for a tag name, 512 bytes for a fully qualified PLC link path. - Never call
strcpyorstrcaton a pointer returned by a WinCCGet*function. Always copy first. - Validate tag existence with
GetTagBitWait(or the type-appropriate read function) before any write. A single Tag not found event in a 24-hour production run indicates a configuration drift that will not fix itself. - Standardize the picture's link suffix in the Graphics Designer before authoring any C scripts against the picture window. The script's responsibility is to supply the prefix; the picture's responsibility is to supply the suffix.
- If the tag namespace is highly dynamic (different tag types per invocation), design the picture with placeholder links and use
PDLRTSetLinkfrom a startup action to point each placeholder at the correct tag at runtime. - Log every TagPrefix resolution to the diagnostic output during commissioning. Remove the verbose logging before production sign-off, but leave a single-line trace so post-incident analysis is possible.
FAQ
Why does my script crash only after several hours of Runtime operation?
The strcat(c, ".pdl") call in the original code overwrites memory adjacent to the WinCC-owned picture-name buffer. The damage accumulates over time as WinCC reuses the corrupted heap region. Replace the in-place concatenation with snprintf into a local buffer sized for the worst case (512 bytes for path strings).
How do I confirm that the TagPrefix property is being read correctly?
Use the buffer-fill form GetTagPrefix(pszParentPicture, pszParentWindow, szPrefix, sizeof(szPrefix)) and write the result to the diagnostic output with printf("Prefix=[%s]\n", szPrefix). The square brackets make trailing whitespace and missing-prefix cases immediately visible.
Can I set a tag with only a prefix, without appending any suffix?
No. WinCC tags are addressed by their full path in the tag management. The picture window's TagPrefix only contributes the leading portion of that path; the suffix is defined by each I/O field's static link configuration. The script must concatenate the configured suffix before calling SetTagBit.
When should I use PDLRTSetLink instead of TagPrefix?
Use PDLRTSetLink when the picture window is reused across process units that do not share a common tag-name suffix, or when the same picture must address tags of different types (for example, a bit tag for one unit and a word tag for another). The TagPrefix pattern is simpler but requires a uniform suffix across all links on the picture.
Does this approach work in TIA Portal WinCC Professional, or only in WinCC V7?
The C API functions GetParentPictureWindow, GetParentPicture, GetTagPrefix, and SetTagBit exist in both environments. TIA Portal WinCC Professional additionally exposes a VBS object model where the TagPrefix property can be read and written via HmiRuntime.Screens("...").ScreenItems("PictureWindow").TagPrefix. The same ownership and suffix constraints apply.