Problem Description
When developing WinCC TIA Professional HMI screens that include C scripts for an S7-1500 / S7-1500TF (e.g., S7-1511TF) project, the following symptom occurs:
- Executing a C script as a local action (tested inline or via direct script call) works correctly.
- Executing the same C script bound to a Schedule event works correctly.
- Calling the C script from another script that is activated by a Schedule event returns fault
1007001 / 4099with the text "Exception in Action" and "unresolved external function or variable". - Calling the C script from an object event (e.g., a
Button_2Click event) returns the same fault.
The function itself is present in the project (it is found during a direct/local execution), so the failure is not caused by a missing function body. The error is a linker/symbol resolution failure inside the WinCC RT script engine at the moment the runtime attempts to bind the call site to the function symbol.
Affected Environment
| Component | Value / Configuration |
|---|---|
| HMI Runtime | WinCC Runtime Professional (TIA Portal) |
| Engineering | TIA Portal (V15 / V15.1 / V16 reproduced; behavior identical across the V15+ line) |
| PLC | SIMATIC S7-1511TF (S7-1500 Technology/Fail-safe) |
| Simulation mode | PLCSIM / S7-PLCSIM (also reproducible on physical HMI panels) |
| Picture | STARTPAGE.SCREEN |
| Window / Template |
WINDOW_LEFT / RECIPES
|
| Triggering object |
Button_2 (object event, e.g., Click / Press) |
| Function called |
WriteResults (user-defined C function) |
| Header involved | globalDefinition.h |
| Runtime module | PDLRuntimeSystem |
| Cycle flag |
acycle (acyclic trigger) |
Decoding the Error: 1007001 and 4099
The full OnErrorExecute dump from the WinCC RT trace provides four correlated identifiers that must be interpreted together:
| Field | Reported Value | Meaning |
|---|---|---|
dwErrorCode1 |
1007001 |
WinCC RT action-engine error class "Exception in Action". Indicates the script VM halted while running an action triggered by a runtime event (schedule, object, or cyclic). |
dwErrorCode2 |
4099 |
Sub-code: "unresolved external function or variable". The C interpreter/loader could not bind an identifier to a function or a global data symbol at the call site. |
szErrorText |
Exception in Action |
Human-readable summary of the error class. |
szErrorTextException |
unresolved external function or variable |
Detailed exception text. This is the message that points to the actual symbol that failed to resolve. |
szFunctionName |
@2 |
Internally generated wrapper for the second compiled function in the action block. The dispatcher is the schedule/event action; the unresolved symbol is inside it. |
lpszPictureName |
STARTPAGE.SCREEN WINDOW_LEFT:RECIPES |
Picture hierarchy in which the action is currently running. |
lpszObjectName |
Button_2 |
Source object of the event that triggered execution. |
dwParamSize |
24 |
Parameter block size in bytes; useful for comparing expected vs. effective call frames when investigating parameter mismatch. |
bCycle |
acycle |
The action was dispatched as an acyclic event (mouse, keyboard, value change, etc.), not from a cyclic task. |
1007001 is the dispatcher-level error and is shared by many exception classes (division by zero, invalid pointer, type mismatch, etc.). The decisive identifier is dwErrorCode2 = 4099 together with szErrorTextException = "unresolved external function or variable". This combination is always a linkage / symbol resolution failure, not a runtime logic error.
Root Cause: globalDefinition.h Variable Scope vs. Event-Domain Compilation
WinCC TIA Professional uses a C-style scripting engine with two distinct compilation domains:
- Project-globally compiled C functions — these are the user-defined functions visible in the project tree under "C scripts > Functions". They are compiled once and registered in the global symbol table of the runtime.
-
Event-bound C actions — these are the inline scripts attached to schedules, tag events, and screen object events. They are compiled per-event into a transient unit (
@n) that links against the global symbol table at runtime.
The header file globalDefinition.h is the standard project-wide include for WinCC RT. It is automatically prepended to every C function in the project only if the function body itself was generated or edited through the TIA project tree. It is not automatically prepended to event-bound action blocks, which are compiled in a separate domain.
When a project-wide C function such as WriteResults declared a variable in globalDefinition.h, the symbol existed in the project-global domain. As long as the function was invoked from a context that also belonged to the project-global domain (direct execution, local script, or a top-level schedule that resolved the symbol through a different path), the lookup succeeded.
When WriteResults was invoked from an action block bound to a Schedule event or to Button_2, the calling context was the per-event action domain. This domain only sees the global symbol table. The function WriteResults itself was registered there, but any identifier that originated from globalDefinition.h and was referenced inside WriteResults resolved correctly only when the function was compiled as a project-globally compiled unit. The moment the runtime linked the same source against the action-domain symbol table, the unresolved-external error was raised.
Why "Identical Code" Behaves Differently
The script body is identical on disk, but the compilation context differs:
| Invocation Path | Domain | Sees globalDefinition.h Variables? |
Result |
|---|---|---|---|
Local direct execution of WriteResults
|
Project-global | Yes | OK |
Schedule event calls WriteResults directly |
Schedule-action domain (still in same TU in older builds) | Yes | OK |
Schedule event calls a wrapper script that calls WriteResults
|
Per-event action domain | No (action domain does not see local static-declared externs in globalDefinition.h) |
1007001 / 4099 |
Object event on Button_2 calls WriteResults
|
Per-event action domain | No | 1007001 / 4099 |
Resolution Procedure
Option A — Recommended: Move the variable into the function (eliminate the cross-domain dependency)
- Open the C function in the project tree (in this case
WriteResults). - Remove the global variable declaration from
globalDefinition.h. The file should return to an empty or near-empty state. - Declare the variable as a
static(or local) variable insideWriteResults:
void WriteResults(void)
{
static int s_nCounter = 0; // file-local, no external linkage
int nTmp = 0; // function-local
s_nCounter++;
/* ... original code ... */
}
- Recompile the project (TIA Portal: Project > Compile > Software (rebuild all)).
- Download / start RT and re-trigger the Schedule event and the
Button_2event.
Option B — Keep the global, but make it resolvable in the action domain
- Leave the declaration in
globalDefinition.hasextern:
// globalDefinition.h
extern int g_nResultCount; // declaration only
- Define the variable in exactly one project-global C function file (not in an action block):
// e.g., in a dedicated file: Globals.c
int g_nResultCount = 0;
- Verify that the C function using the variable (
WriteResults) does not redefine it; it should only#include "globalDefinition.h"and referenceg_nResultCount. - Rebuild and retest.
Option C — Promote the C function to a project-global function (do not call it from a wrapper)
If you have a wrapper script that just calls WriteResults from a Schedule, eliminate the wrapper. Bind the Schedule event directly to WriteResults. This keeps the call in the project-global domain and avoids the action-domain linker.
Verification
- Open the WinCC RT trace (HMI device → right-click → "Runtime → Start with trace" or use the RT Trace Viewer).
- Enable the categories: Scripting, Action, and Error.
- Force the triggering events:
- Click
Button_2inSTARTPAGE.SCREEN. - Wait for the Schedule event to fire.
- Click
- Confirm that the trace shows no
dwErrorCode1 = 1007001and noszErrorTextExceptionentries. - Confirm that the side effect of
WriteResults(e.g., the written results in the recipe view) updates as expected. - Optional — instrument the function:
printf("[WriteResults] enter, nResultCount=%d\r\n", g_nResultCount);
Check the WinCC RT log file WinCC_TIA_Portal_RT_<date>.LOG in the project runtime directory.
Common Pitfalls and Field-Proven Caveats
| Pitfall | Symptom | Fix |
|---|---|---|
Declaring the same variable in globalDefinition.h and re-declaring it without extern in another C function |
Multiple-definition error at compile time, or unresolved external at runtime | Use extern in the header, define in exactly one .c file |
| Calling a project-global C function from a wrapper that is itself a C function (rather than VB) | Same 1007001 / 4099 if the wrapper does not see the function's dependencies |
Move all transitive dependencies into the header as extern or eliminate the wrapper |
Typo in the function name at the call site (e.g., WriteResuts) |
Identical unresolved external function error |
Cross-check spelling; use IntelliSense pick-list |
| C function not actually compiled into the project (still draft / not saved) |
unresolved external function at first RT start |
Project > Compile > Software (rebuild all); check "Compile" tab for errors |
| Mixing VB scripts and C scripts and calling a C function from VB without proper declaration | Variant-type errors, occasionally 1007001 variants |
Document parameter types; in VB, call via HMIRuntime.Screens("...").ScreenItems("...") or convert to a C function call |
Using printf in a C function for debugging — output goes to the RT log, not to a screen |
Apparent "no effect" | Inspect <Project>\<HMI>\Log\ folder |
Best Practices for C Scripts in WinCC TIA Professional
-
Keep
globalDefinition.hlean. Use it only for type definitions, macros, andexterndeclarations. Place actual variable definitions in dedicated C source files. -
Avoid implicit cross-domain dependencies. If a project-global C function references a global, ensure that global is reachable from every domain that may call the function (use
extern+ single definition). - Prefer project-global functions over inline action blocks for non-trivial logic. Inline action blocks should contain only orchestration calls into project-global functions.
- Recompile after every rename. TIA Portal does not always re-validate transitive references; a full rebuild catches stale symbols.
- Use version control on C scripts. The TIA project tree stores C scripts as plain text; keep them in a Git/SVN repository outside TIA to track changes that may affect symbol resolution.
- Tag your log lines with the function name and a timestamp. The WinCC RT log rotation can swallow context otherwise:
printf("[%s][%s] event=%s value=%f\r\n",
__func__, __TIMESTAMP__, "Button_2", GetPropDouble(lpszPictureName, "value"));
- Always re-test on a clean RT start after a compile. Stale RT caches can mask symbol errors and only surface them on the first call.
Related Diagnostics: Other 1007001 Sub-Codes
dwErrorCode2 |
Typical Meaning | Likely Cause |
|---|---|---|
4099 |
Unresolved external function or variable | Missing or out-of-domain symbol (this article) |
4098 |
Type mismatch on call | Parameter type mismatch at call site (e.g., passing int where float* is expected) |
4097 |
Access violation / null pointer | Uninitialized pointer, freed handle, or invalid tag name |
4100 |
Stack overflow | Deep recursion or very large local arrays |
4101 |
Division by zero | Unguarded arithmetic with tag-driven denominator |
Whenever you see 1007001, read dwErrorCode2 first. It identifies the exception subclass, and almost always pinpoints the bug to one of five categories: symbol resolution (4099), types (4098), memory (4097), stack (4100), or arithmetic (4101).
FAQ
What does WinCC error 1007001 with sub-code 4099 mean?
It is the WinCC RT action-engine error "Exception in Action" with the detailed exception "unresolved external function or variable" (dwErrorCode1 = 1007001, dwErrorCode2 = 4099). The C script interpreter could not bind a function name or a global symbol to a definition at the call site. It is a linkage problem, not a logic error.
Why does my C function work locally but fail when called from a Schedule or button event?
Project-global C functions and event-bound C actions are compiled in two different symbol domains. The project-global function can see declarations in globalDefinition.h; the event-bound action cannot see file-local definitions from another compilation unit. Calling the function from the event domain then fails with 1007001 / 4099.
How do I fix "unresolved external function or variable" in WinCC TIA C scripts?
Move the variable from globalDefinition.h into the function itself (declare it static or as a local). If the variable must stay global, declare it extern in the header and define it in exactly one C source file. Then rebuild the project and restart the runtime.
Does this error occur on physical Comfort Panels and WinCC RT Professional?
Yes. The scripting engine and the two-domain symbol resolution model are shared between WinCC RT Professional and the Comfort/ Unified RT. The error codes 1007001 and 4099 are the same; the fix is identical.
Where do I find the full error dump for a WinCC C script fault?
The OnErrorExecute block is written to the WinCC RT log file (typically <Project>\<HMI>\Log\WinCC_TIA_Portal_RT_<date>.LOG) and is also visible in the RT Trace Viewer under the Scripting / Error categories. Search the log for dwErrorCode1: 1007001 to capture all occurrences.