Overview
Siemens WinCC exposes the full runtime HMI API to ANSI-C, VBScript, and (in WinCC Professional) VBA. For a beginner the most common requirement is simple: a button on a picture toggles a tag, an I/O field shows the resulting state, and the tag can be routed onward to the PLC. The C-language entry point for this is the pair of functions SetTagBit() and GetTagBit(), both declared in the project header apdefap.h and dispatched automatically by the WinCC Graphics Runtime when an event fires.
This reference consolidates the correct procedure for a first WinCC C project: declaring the internal tag, attaching a C action to a button's OnClick event, reading the tag back into an I/O field, and verifying behaviour in Runtime. It also covers the start sequence in WinCC Runtime Professional, common compile-time mistakes, and the cross-reference between classic WinCC V7.x and the TIA Portal WinCC Professional editors.
Prerequisites
| Item | Requirement |
|---|---|
| Engineering tool | SIMATIC WinCC V7.5 SP2 or later, or TIA Portal V17/V18/V19/V20 with WinCC Professional / WinCC Runtime Professional |
| License | WinCC RT Professional (or RC license for the engineering station) for the targeted runtime |
| Target platform | Windows 10 LTSC 2019/2021 (64-bit) or Windows Server 2019/2022 for the runtime |
| Tag scope | An internal tag of data type BOOL in the WinCC tag management (no PLC connection required for the exercise) |
| Graphics objects | One button, one I/O field, placed in a process picture in the Graphics Designer |
| Documentation | Siemens entry ID 37572697 — WinCC Scripting: VBS, ANSI-C, VBA |
Why the First Script Fails
A typical first attempt looks like the snippet below, copied from the original forum exchange:
#include "apdefap.h"
void OnClick(char* lpszPictureName, char* lpszObjectName, char* lpszPropertyName)
{
int start;
start = 1;
}
Three things are wrong with this code from the perspective of the WinCC C runtime:
-
Local variable, not a tag. The integer
startlives on the C stack and is destroyed whenOnClickreturns. WinCC tags are stored in the tag image and only the API functions move values in or out of them. -
No
apdefap.hsemantics needed for the function body, but the header is still required so that the project-specific macro expansions and the standard tag-function prototypes are visible to the compiler. -
Wrong prototype. When the C action is attached to a mouse event such as
OnClick, the function must match the signaturevoid OnClick(char*, char*, char*). The signature above is correct, but the developer must not declare a localintthat shadows the tag name — it confuses the reader and produces a no-op at runtime.
The correct primitive for writing a boolean tag from a C action is SetTagBit(); the matching read primitive is GetTagBit(). Both are members of the standard WinCC ANSI-C tag API described in the Siemens scripting manual.
Internal Tag Configuration
Before any C code runs, declare a tag in the WinCC Tag Management. For a stand-alone, PLC-free exercise, the tag is an internal tag — its value is held in WinCC memory and not exchanged with any controller.
- Open the WinCC Explorer (WinCC V7) or the TIA Portal project tree (WinCC Professional).
- Expand HMI Tags and open the default tag table (WinCC V7: Internal Tags; TIA Portal: Default tag table of the HMI device).
- Add a new tag with the following parameters:
Parameter Value Name startData type BOOLConnection Internal tag (no PLC connection) Update cycle 1 s (sufficient for the exercise; tighten to 100 ms only if needed) Initial value 0 - Compile and save the tag table.
SetTagBit(). Siemens permits ASCII letters, digits, and the underscore. Avoid names that shadow C keywords (int, char, return). When in doubt, prefix internal tags with a project-specific token such as int_Start.C Script Anatomy: apdefap.h and the Action Signature
Every C action generated by the Graphics Designer is wrapped in a project-specific header called apdefap.h. The header lives in the project's library directory (\<project>\library\apdefap.h in WinCC V7; \<project>\<HMI>\Sources\apdefap.h in TIA Portal) and is regenerated on every compile. The header aggregates:
- The tag-function prototypes (
SetTagBit,GetTagBit,SetTagByte,GetTagWord,SetTagFloat,GetTagDouble, …). - Project-global constants declared in the Project Properties.
- User-defined function prototypes from the project function library.
For a mouse-event action, the function signature is fixed by the Graphics Designer. The three parameters are the picture name, the object name, and the property name; they are passed by the runtime and can be used for context (for example to log which button was clicked). A minimal, idiomatic OnClick body is:
#include "apdefap.h"
void OnClick(char* lpszPictureName, char* lpszObjectName, char* lpszPropertyName)
{
/* lpszPictureName, lpszObjectName, lpszPropertyName are supplied by WinCC */
SetTagBit("start", (BOOL)1);
}
SetTagBit and GetTagBit — the Boolean API
| Function | Prototype | Return value | Notes |
|---|---|---|---|
SetTagBit |
BOOL SetTagBit(LPCTSTR lpszTagName, BOOL bValue); |
TRUE on success, FALSE on error |
Writes a single boolean tag. Tag must exist and be reachable in the configured connection. |
GetTagBit |
BOOL GetTagBit(LPCTSTR lpszTagName); |
Current value of the tag, or FALSE on error |
Reads the boolean tag. Use the companion GetTagBitState or check the return value semantics for error separation. |
SetTagByte |
BOOL SetTagByte(LPCTSTR lpszTagName, BYTE bValue); |
TRUE/FALSE
|
For BYTE tags or first bit of a byte. |
SetTagWord |
BOOL SetTagBitWait, GetTagBitWait
|
Synchronous variants — block until the value has been confirmed on the PLC connection. Use only on cyclic triggers; never on high-frequency event actions. | |
Wait variants are fire-and-forget from the runtime's perspective. For internal tags the difference is academic, but for PLC tags the Wait variants can stall a C action for several hundred milliseconds and must not be used inside fast event handlers.Step-by-Step: Button → Internal Tag → I/O Field
The exercise has three deliverables: a button that sets the internal tag, an I/O field that displays the tag, and verification in Runtime.
Step 1 — Place the graphics objects
- In the Graphics Designer, open a process picture (or create a new one, e.g.
NewPdl0.PDL). - From the Standard palette, drag a Button onto the picture. Set its label to
Start. - Drag an I/O Field next to the button. Set its type to Output (read-only) for visualisation, or Input/Output if you want to write from the HMI as well.
Step 2 — Bind the I/O field to the tag
- Select the I/O field and open Properties → Output/Input → Output Value (or Process → Tag in WinCC Professional).
- Assign the tag
start. - For the field format, select Binary or Decimal. Decimal is recommended for the first build because binary displays are zero-padded and can confuse a beginner.
Step 3 — Configure the button's C action
- Select the button. In the property list, scroll to Events → Mouse → Click.
- Right-click the action icon and choose C action…. The C editor opens with a stub body.
- Replace the body with the canonical write snippet:
#include "apdefap.h"
void OnClick(char* lpszPictureName, char* lpszObjectName, char* lpszPropertyName)
{
SetTagBit("start", 1);
}
- Compile the action with OK. The Graphics Designer prompts to attach the action — confirm.
Step 4 — Read the tag back from another action (optional)
The I/O field already polls the tag each cycle, so a separate read is not strictly required. To prove that GetTagBit() works, attach a second C action to the button's Mouse → Press event:
#include "apdefap.h"
void OnPress(char* lpszPictureName, char* lpszObjectName, char* lpszPropertyName)
{
BOOL val;
val = GetTagBit("start");
/* val now holds the current value of the internal tag */
/* for diagnostic output write it to another internal tag or trigger a log */
SetTagBit("int_dbg_lastRead", val);
}
This second snippet also documents the correct usage of GetTagBit in a member function context: declare a BOOL, call the function with the tag name as a string literal, and consume the return value.
Event Types and Property Cross-Reference
Not every interaction is a click. WinCC exposes a hierarchy of event hooks, each with its own C signature. The ones most often used in first projects are listed below.
| Event | C signature | Typical use |
|---|---|---|
OnClick |
void OnClick(char*, char*, char*) |
Button command, list selection |
OnPress |
void OnPress(char*, char*, char*) |
Mouse-down (before release) |
OnRelease |
void OnRelease(char*, char*, char*) |
Mouse-up (after release) |
OnOpen |
void OnOpen(char* lpszPictureName) |
Picture-level — runs once when a picture becomes active |
OnClose |
void OnClose(char* lpszPictureName) |
Picture-level — runs when a picture is closed |
| Property change | Function name matches the property (e.g. void BackColor_OnPropertyChange(...)) |
React to dynamic attribute updates |
OnOpen, OnClose) take a single char* parameter. Object-level mouse events take three. Mixing the two is the most common reason a beginner's action silently fails to compile.Start Sequence and Runtime Activation (WinCC Runtime Professional)
For WinCC Runtime Professional, the C action and the internal tag are useless until the runtime is actually running and the picture is loaded. The Start sequence on the HMI device controls which WinCC applications launch when the project is activated. The official TIA Portal V20 documentation describes the setting as follows:
Select "Start sequence of WinCC Runtime" to specify the applications that will be started on activation of a project. "Graphics in Runtime" is always started automatically.
Source: Setting up the start sequence (RT Professional) — TIA Portal V20.
For this tutorial, ensure that the following applications are ticked in the start sequence:
- Graphics in Runtime — required (cannot be disabled).
- Tag Management Runtime — required for the internal tag to be loaded.
- Global Script Runtime — required for the C action to be executed.
If the project is started on the engineering station via the TIA Portal Start Runtime button, these components are launched by default. When the project is deployed to a target device, verify the start sequence on the target's Runtime settings page, otherwise the C action will not fire even though the picture loads.
Cross-Platform Notes: WinCC V7.x vs WinCC Professional (TIA Portal)
| Aspect | WinCC V7.5 (classic) | WinCC Professional (TIA Portal V20) |
|---|---|---|
| Tag management | WinCC Explorer → Tag Management | Project tree → HMI Tags on the HMI device |
| Graphics editor | Graphics Designer (.PDL) |
HMI screen editor (.xml-based, internal .hmi_screen) |
| C header |
apdefap.h in \library\
|
Auto-generated apdefap.h in Sources folder |
| Tag API |
SetTagBit, GetTagBit, SetTagWord, … |
Identical names; identical signatures |
| VBS support | Yes | Yes (preferred for new projects in TIA Portal) |
| Project function library | Standalone Project Functions editor | Folder Scripts → Project functions |
| Start sequence configuration | WinCC Explorer → Computer → Startup | HMI device → Runtime settings → Start sequence |
The C API has not changed materially between the two product lines, so a snippet written for WinCC V7 compiles and runs in TIA Portal WinCC Professional and vice versa. The only practical difference is the deployment path: TIA Portal stores screens in a different container but compiles to the same runtime format on the target.
Common Errors and How to Read Them
| Symptom | Root cause | Fix |
|---|---|---|
| Action compiles but the tag value never changes | Tag name typo or wrong tag table; the runtime logs Tag not found in the GSC diagnostics | Recompile the tag table; verify the tag exists with the exact spelling in the tag browser |
| Compiler error undeclared identifier 'SetTagBit' | The #include "apdefap.h" line is missing or the file was not regenerated |
Add the include at the top of the C action; recompile the project (TIA Portal: Compile → Software (rebuild all)) |
| Compiler error too few arguments to function | Wrong event signature (e.g. used the picture-level OnOpen shape on a mouse event) |
Match the signature in the table above; picture-level events take one parameter, mouse events take three |
| Runtime error Function 'SetTagBit' is not allowed for internal tags | Misnaming — the error is actually raised by the dispatcher when the tag does not exist | Confirm the tag is created as Internal in tag management and that the name passed in quotes matches exactly |
| Button click has no effect, no diagnostic output | C action is attached to a different event (e.g. Mouse → Release instead of Click) or the picture was compiled before the action was attached | Re-open the picture in the Graphics Designer, recompile, redeploy |
| Action runs once, then tag stops responding | The C action has been deployed as a cyclic task with a one-shot guard that is not reset | Confirm the action was attached as an event-driven action, not a cyclic action with a one-shot guard |
The GSC Runtime window (WinCC V7) and the diagnostics pane (TIA Portal WinCC Professional) are the only reliable places to see these messages. Open them via the Tools → Diagnostics menu or by holding Ctrl+Shift+F5 in the running picture.
Verification Checklist
- Open the project in the TIA Portal or WinCC Explorer, compile the project, and start Runtime.
- Confirm the picture loads with the button and the I/O field visible.
- Click the button. The I/O field's value must change from
0to1within the configured update cycle. - Open Tag Management Runtime (WinCC V7) or the HMI Tag Diagnostics view (TIA Portal) and confirm the value of
startis1after the click. - Click the button again. If the C action is a setter (not a toggler), the value will remain
1until another action clears it — add a Reset button withSetTagBit("start", 0);to complete the loop. - Close and re-open the picture. The tag should retain its last value because the runtime keeps internal tags across picture changes; if the picture is part of a different process, internal tag state is lost only when the runtime stops.
From Internal Tag to a Real Output
The original question's intent was to "start a light." With an internal tag the exercise is decoupled from the PLC. To drive a real output:
- Add a second tag, e.g.
PLC_Start_Light, with the appropriate connection (S7-1500, S7-1200, OPC UA, etc.) and the correct DB / output address. - Wire an internal tag-to-tag connection in the tag management, or use a direct connection from the button's tag to the PLC address. The simplest path is to make the C action write the PLC tag directly:
#include "apdefap.h"
void OnClick(char* lpszPictureName, char* lpszObjectName, char* lpszPropertyName)
{
/* write the internal tag for HMI display */
SetTagBit("start", 1);
/* write the PLC tag to drive the output */
SetTagBit("PLC_Start_Light", 1);
}
- Rebuild and re-download. Confirm the output on the controller side using the TIA Portal online view or a watch table on the S7 CPU.
Further Project Patterns
Once the basic toggle works, the same primitives scale to common industrial patterns:
-
Edge detection (rising edge). Read the previous value with
GetTagBit(), compare to the new state, and fire the side effect only on the transition from0to1. -
Toggle (latching). Use
SetTagBit("start", !GetTagBit("start"));in theOnClickevent for a single-button start/stop pattern. -
Interlock. Check one or more
GetTagBit()values (e.g.int_Permissive,int_Stop) before writing the start tag; if the interlock is open, write to a diagnostic tag and skip the start. -
Confirmation dialog. Use the WinCC function
MsgBoxin VBScript for a confirm step, or call the C equivalent via a project function. The C language does not provide a native modal dialog; route confirmations through VBS or through a popup picture.
All four patterns fit on a single OnClick action body and require no additional runtime components beyond the start sequence defined earlier.
What is the difference between SetTagBit and SetTagBitWait?
SetTagBit is asynchronous: it returns TRUE/FALSE to indicate whether the value was queued to the dispatcher, and execution of the C action continues immediately. SetTagBitWait blocks the C action until the value has been confirmed on the PLC connection, which can take hundreds of milliseconds. Use the non-Wait variant in event handlers (OnClick, OnPress) to keep the HMI responsive.
Why does the C action compile but the tag value never changes in Runtime?
The most common causes are: (1) the tag name in the string literal does not exactly match the tag in the tag table, (2) the tag is defined as a PLC tag but the connection is down, or (3) the C action is attached to a different event than the one that is firing. Open the GSC Runtime diagnostics (WinCC V7) or the HMI Tag Diagnostics view (TIA Portal) to confirm the tag exists and is reachable.
Is apdefap.h always required in a C action?
Yes. The Graphics Designer does not auto-include any standard header, and apdefap.h is the only file that declares the tag-function prototypes (SetTagBit, GetTagBit, etc.) and the project-global constants. If the include is missing, the compiler reports undeclared identifiers for the very first SetTagBit call.
Can the same C snippet run in WinCC V7 and WinCC Professional (TIA Portal)?
Yes, the C API is identical: the same function names, signatures, and header (apdefap.h) work in both products. The differences are in the project layout (Explorer vs. project tree) and the deployment pipeline, not in the runtime API.
How do I keep the internal tag's value when the picture is closed?
Internal tags live in the tag image of the running WinCC project and persist across picture changes and across operator actions. The value is only lost when the WinCC Runtime is stopped. To make the value survive a runtime restart, change the tag from "internal" to a PLC tag and store the value in a retentive DB bit on the S7 CPU, or write the value to a persistent file using a project function.