Resolving WinCC Professional C Script void and Header Errors

David Krause10 min read
SiemensTutorial / How-toWinCC
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

Overview

Siemens WinCC Professional (TIA Portal V16 and later) supports ANSI-C as one of its two scripting languages (the other being VB Script). Engineers migrating from WinCC V7 into WinCC Professional frequently encounter three classes of errors in the C editor:

  1. Function-prototype mismatches between int gscAction(void) (V7) and the Professional generator naming convention (void CFunction_1()).
  2. Missing or unresolved #include "apdefap.h" preprocessor directives.
  3. Compile errors on the void keyword that disappear when the return type is changed to void*.

All three errors trace back to misunderstandings about the C language itself, not WinCC. This reference explains the language semantics behind each error, then maps them onto the specific naming and project conventions enforced by WinCC Professional V16 Update 6 (and SP variants of V17/V18 that retain the same scripting engine).

Scope: WinCC Professional V16 Update 6 (engineering release 16.0.6.x) is the reference target. Behavior is identical in V17 and V18 unless explicitly stated. WinCC Comfort Panels use the same C interpreter but with a reduced API.

void vs void* in C: Language Semantics

The root cause of the third error is a basic C type-system distinction that has nothing to do with WinCC. Per the ISO/IEC 9899 C standard (summarized on Wikipedia's C language reference), C is a statically typed procedural language in which every function must declare exactly one return type.

Type Meaning Return value Typical use
void Function returns nothing None (empty return) Procedures, side-effect routines, WinCC actions, callbacks
void* Function returns a generic pointer Address of an unspecified object malloc(), bsearch(), low-level allocation
int Function returns an integer status code Numeric (typically 0 = OK) main(), gscAction(), error reporting

When the compiler reports "error on void" but accepts void*, the engineer has almost always inserted an asterisk by mistake, or has read a forum post that suggested returning void* as a workaround. Both behaviours are wrong: a WinCC C action is invoked by the runtime scheduler and the scheduler does not inspect a return value. The correct signature is void, not void*. A void* return causes the compiler to embed an EAX/register return sequence that the scheduler never reads, wastes one register, and is misleading to any other engineer reading the script.

Rule: If the function does not return a value to its caller, declare it void. Use void* only when the caller will cast the returned address back into a concrete type.

Why WinCC Reports an Error on Plain void

In WinCC Professional, the C script body you type is automatically wrapped by the code generator into a function whose prototype matches the script type. For a scheduled action the wrapper looks like:

void ScheduledAction_1(void)
{
   /* user code begins */
   ...
   /* user code ends */
}

The wrapper itself is void. If you also type int or void* as the leading return type inside the editor, you create a nested function definition, which is a C syntax error. The solution is to leave the first line of the script blank or to start directly with declarations and statements. Do not type a return type at the top of a WinCC script body unless you are writing a function block (FB-style) callable from another script.

WinCC Professional C Scripting Environment

WinCC Professional places the C editor under HMI Tags > [screen] > Events > [event] > C-Action, and under Schedules for periodic actions. The runtime engine compiles the script at startup with the bundled MSVC-compatible C compiler shipped with the WinCC Runtime Professional binaries.

Script Types

Script kind Trigger Wrapper prototype Use case
Project function Call from VB or C void ProjectFunction(void) Reusable utility
Scheduled action Cyclic timer / event void ScheduledX_N(void) Polling, periodic logic
Tag-triggered action Tag change void TagTriggered_N(void) Reactive logic
Window event Open/close/click void WindowEvent_N(void) HMI event handling

Each wrapper is generated by the WinCC code generator based on the script's name. The visible editor in TIA Portal only shows the body between the braces; the wrapper is appended automatically and is not editable. Therefore the engineer never needs (and should never include) a return type or function name at the top of the script.

Function Prototype Conversion from WinCC V7 to WinCC Professional

WinCC V7 exposes a different API surface. In V7 the user enters the full function signature, including int gscAction(void), because the V7 generator does not auto-wrap the body. The runtime invokes the action through a global symbol whose name is the script name (e.g. gscAction) and checks the integer return code.

In Professional the global-scheme no longer exists; the generator owns the wrapper. The correct migration rule is:

  1. Delete the int gscAction(void) line entirely.
  2. Delete the matching trailing }.
  3. Insert the remaining body verbatim into a Professional scheduled action.
  4. Configure the trigger (cycle, event, tag change) in the schedule dialog instead of inside the script.

Side-by-Side Conversion Example

WinCC V7 source:

int gscAction(void)
{
   int iValue = GetTagWord("Motor_Speed");
   SetTagFloat("Motor_Speed_FLT", (float)iValue);
   return 0;
}

WinCC Professional target (project function form):

/* Project Function: ConvertMotorSpeed */
{
   DWORD dwValue = 0;
   float fResult = 0.0f;

   dwValue = GetTagWordState("Motor_Speed", NULL);
   if (dwValue == 0)
   {
      fResult = (float)GetTagWord("Motor_Speed");
      SetTagFloat("Motor_Speed_FLT", fResult);
   }
}
API note: Several V7 APIs are renamed in Professional. GetTagWord() retains its name, but state-aware variants use the *State suffix. Always confirm the API in the Siemens Online Support WinCC Professional reference manual for the target firmware version.

Header Includes in WinCC C Scripts

The #include "apdefap.h" directive originates from WinCC V7. In V7, apdefap.h defines the prototypes for the Graphics Designer runtime API, including GetTag*, SetTag*, SetPicture, and other block-mode functions. The header lives in the V7 project subdirectory \Library\apdefap.h.

Why the Include Fails in WinCC Professional

WinCC Professional does not ship apdefap.h in the TIA project tree. The WinCC Professional compiler ships its own headers as a precompiled set inside the WinCC Runtime Professional installation directory. The script editor links against those internal headers automatically; the user does not write any #include for the standard WinCC API.

Typing #include "apdefap.h" at the top of a Professional script produces one of two errors:

  • Cannot find file 'apdefap.h' – the project does not contain the file.
  • Redefinition of GetTagWord – the user added the header from an old V7 project into the TIA project, but the runtime also defines the same prototypes, causing duplicate symbol errors.

Correct Approach in WinCC Professional

  1. Delete the #include "apdefap.h" line.
  2. Call the standard API functions directly (GetTagDWord, SetTagFloat, etc.).
  3. If a custom header is required for project-wide constants, add it to the project's Scripts > C-Scripts > Project Headers node so the generator picks it up from the project path.

Creating a Custom Header

If you genuinely need a project header, place a plain C header in the project path and reference it. For example, create project_includes.h:

#ifndef PROJECT_INCLUDES_H
#define PROJECT_INCLUDES_H

#define MAX_RETRIES  3
#define PUMP_TAG     "Pump_01_Status"

#endif

Reference it as #include "project_includes.h". The Professional generator adds the project's script folder to the include path automatically.

Global Actions vs Scheduled Tasks

WinCC V7 exposes a tree node called Global Scripts in the project. Engineers can drop C files into the global action pool; each file becomes a standalone function callable by name. WinCC Professional replaces this with the Schedules node.

Concept WinCC V7 WinCC Professional
Container Global Scripts Schedules
Entry name gscAction User-defined, e.g. CFunction_1
Signature typed by user Yes (full prototype) No (body only)
Trigger configured Inside code via API In the schedule trigger dialog
Hot reload Manual recompile Compile on save in TIA

The visible difference is in the trigger model. In V7, a global action either ran once on startup (if named gscAction) or was invoked from another script. In Professional, every scheduled action must be bound to a trigger – cycle (e.g. every 500 ms), tag change, or window event. There is no "fire-once on startup" idiom; engineers either set a 1-second cycle and gate with a static flag, or trigger on the HMI Started event of the runtime.

Step-by-Step Migration from WinCC V7 to WinCC Professional

Prerequisites

  • TIA Portal V16 Update 6 (or later SP) installed with WinCC Professional.
  • Source V7 project archived (.sav) and accessible.
  • WinCC Professional license key matching the number of tags and PowerTags.

Migration Procedure

  1. Open the V7 project in WinCC Explorer, export all global actions to .pas text files via Global Scripts > right-click > Export.
  2. Create a new TIA Portal project. Add a WinCC Professional HMI station.
  3. Navigate to HMI > Schedules > Add new schedule. Create one schedule per V7 action, naming each according to the V7 function name for traceability.
  4. Open each schedule, set the trigger (cycle / event), and paste the V7 script body into the C editor.
  5. Remove the first line (int gscAction(void)) and the closing brace of the V7 function.
  6. Delete any #include "apdefap.h" directive.
  7. Replace V7-only APIs (e.g. PWRTAuthenticate) with the Professional equivalents listed in the TIA WinCC Professional reference manual.
  8. Compile the HMI station. Resolve any remaining warnings.
  9. Download to the runtime and verify in the diagnostics view.

Verification

  • The schedule appears in Schedules > Active after download.
  • WinCC Runtime Professional diagnostic window shows no C compile errors.
  • The first execution cycle matches the configured trigger interval.
  • Tags read/written by the script update as expected.

Common Compile Errors and Resolutions

Error message Cause Resolution
Syntax error: nested function definition Typed a return type at top of script body Delete the leading int/void* line
Cannot find include file 'apdefap.h' V7 header imported into Professional Remove the include; use the auto-linked Professional API
Redefinition of GetTagWord apdefap.h added AND Professional headers auto-linked Remove the V7 header
Undeclared identifier 'gscAction' V7 entry name still referenced Rename call sites to new Professional function name
Function should return a value Body ends without return on non-void function Confirm wrapper is void; remove stray prototype
Type mismatch: DWORD vs INT Mixed WinCC API variants Use GetTagDWord with DWORD cast

Best Practices for WinCC Professional C Scripts

  • One action per concern. Split large V7 monoliths into multiple Professional schedules; each schedule should perform one logical task.
  • Prefer project functions over inline code. Centralise tag-name strings and constants in a project function or header to ease renaming during tag refactors.
  • Avoid global state. WinCC Professional restarts the runtime cleanly; any static variable resets. Use HMI tags for cross-cycle state instead.
  • Guard tag I/O. Wrap every GetTag* call with the corresponding *State variant when status is critical for control logic.
  • Cycle budget. A 100 ms cycle consumes noticeably more CPU than a 500 ms cycle; size the trigger to the actual process need.
  • Disable in test mode. Use the runtime's Simulation / Test toggle or a project-side #ifdef _DEBUG to skip heavy logic during commissioning.

Diagnostic Workflow When Scripts Fail to Compile

  1. Open Project Tree > HMI > Diagnostics and expand the C-Script node.
  2. Read the first error – subsequent errors are frequently cascaded.
  3. Open the offending script in the C editor and locate the line referenced.
  4. If the error mentions apdefap.h, search the entire script for #include directives; remove all V7-specific ones.
  5. If the error mentions void, scroll to the top of the script body and confirm no return type has been typed.
  6. Recompile. Verify the error count drops.
  7. If unresolved, export the script to a standalone file and compile with the WinCC Runtime Professional C compiler in isolation for a more detailed error log.

FAQ

Why does my WinCC Professional script accept void* but reject void?

The rejection is a symptom, not the cause. You have typed a return type at the top of the script body, which creates a nested function definition. Remove the typed return type; the Professional generator supplies the wrapper automatically, so the body must start with declarations or statements, not with a prototype.

Where do I put apdefap.h for WinCC Professional?

You do not. WinCC Professional does not require apdefap.h. The runtime compiles against internal headers automatically. Delete the #include "apdefap.h" line and call the API functions directly.

How do I migrate int gscAction(void) from WinCC V7 to Professional?

Delete the int gscAction(void) header line and the closing brace. Paste the remaining body into a Professional schedule. Configure the trigger in the schedule dialog instead of inside the script. The wrapper becomes void ScheduledX_N(void), owned by the generator.

Can I run a WinCC V7 script unchanged inside WinCC Professional?

No. WinCC Professional uses a different API surface and a different trigger model. The C syntax is similar, but wrapper generation, header conventions, and tag-access functions all differ. Always migrate the body and adapt the wrapper per the procedure above.

What trigger replaces WinCC V7 startup-once global actions?

Use a Professional schedule with the "HMI Started" event trigger for one-time startup actions, or a tag change on a startup-complete flag. There is no equivalent of the implicit V7 gscAction invocation; every Professional action must declare its trigger explicitly.

Back to blog