1. APDiag Diagnostic Tool Overview
The APDiag (Application Diagnostic) tool is the primary runtime telemetry sink for WinCC V6/V7 (and WinCC Professional / TIA Portal HMI) projects that contain ANSI-C or VB script logic. APDiag writes a continuous stream of events to a dedicated output window: scheduled actions, function calls, internal lifecycle events (OpenPicture, ClosePicture, OnTime, Mouse events), warnings, and—most importantly—access violations raised by the WinCC C runtime.
Per the Siemens KB article "How do you use the diagnostic tool APDiag to debug C scripts?" (entry ID 22196775), developers should treat APDiag as the first stop whenever an HMI panel throws a red General Protection Fault banner, the runtime disappears from the task list, or a picture appears to "freeze" on partial load. Activate APDiag from WinCC Explorer > Tools > APDiag (or start APDiag.exe from the WinCC installation directory), and leave the window docked while reproducing the fault.
2. Anatomy of the General Protection Fault
A general protection fault (GPF) is the WinCC C runtime's translation of a Windows access violation. The fault means the script dereferenced a pointer that did not contain a valid, allocated memory address. In a WinCC runtime, this almost always means one of the following happened inside a C function or project function:
- A pointer to a string (LPCSTR,
char*) was passed uninitialized to a WinCC API such asGetPropChar,SetPropChar,GetLink,SetLink, or any property I/O helper. - A buffer obtained from
SysMalloc,malloc, or stack-allocatedchar arr[N]was read or written past its end. - A picture or object handle that had already been closed/freed was re-used.
- A
printf/DebugTracecall used a%sspecifier with aNULLargument.
APDiag prints a structured record for the GPF. The record typically includes the faulting module (ccalg.dll, cclib.dll, pdllrt.dll, or the user DLL), the exception code (0xC0000005 STATUS_ACCESS_VIOLATION on x86), and two identifiers that are decisive for narrowing the cause:
-
lpszObjectName— the WinCC object (picture, control, tag, screen window) the script tried to address. -
lpszPropertyName— the property/attribute of that object.
When both fields show the literal text NULL, the fault did not occur inside a property helper at all. It occurred in the C runtime while resolving the call, and the dispatcher simply has no context. The fault is therefore in the script's own code path, not in a WinCC API misuse.
3. Reading the APDiag Output Window Trace
A typical trace block for this class of fault looks like the following (representative; field labels and order may vary slightly by WinCC version):
@100,2025-03-14 08:42:11.034,GPF, PROJECT\BASE, NULL, NULL, Access violation (0xC0000005) reading 0x00000000
@100,2025-03-14 08:42:11.034,TRACE, PROJECT\BASE, Event: OpenPicture
@100,2025-03-14 08:42:11.034,TRACE, PROJECT\BASE, Caller: @1d
@100,2025-03-14 08:42:11.035,TRACE, PROJECT\BASE, Stack: PdlRt!OpenPicture+0x47
@100,2025-03-14 08:42:11.035,TRACE, PROJECT\BASE, ccalg!ExecuteProjectFunction+0x12c
@100,2025-03-14 08:42:11.035,TRACE, PROJECT\BASE, user32!DispatchMessage+0xa
Key field meanings:
| Field | Meaning | What to look for |
|---|---|---|
PROJECT\BASE |
Logical address of the faulting object. BASE means the script is bound to the picture itself, not a child object. |
If the address is PROJECT\<PictureName>.<ObjectName>, the fault is on a child. |
lpszObjectName: NULL |
The runtime could not resolve an object name argument. | Almost always means the C script passed a NULL or dangling pointer where the dispatcher expected a valid name. |
lpszPropertyName: NULL |
The runtime could not resolve a property name argument. | Same root cause; combined with NULL object name, the script is the problem, not the object. |
Event: OpenPicture |
The lifecycle event that was active when the fault occurred. | Drives the search: inspect the picture's OpenPicture handler first. |
Caller: @1d |
Encrypted action ID assigned by WinCC to the script at compile time. | Map @1d back to a named action via the procedure in FAQ 8385493. |
Stack: |
Native call stack at the moment of the exception. | If ccalg/cclib is in the stack, the fault is inside WinCC's C interpreter; if only user32/PdlRt is present, the script returned an invalid handle. |
4. Locating the Triggering Picture and Event
When lpszObjectName and lpszPropertyName are both NULL and the address is PROJECT\BASE, the fault is on an event of the picture itself, not on a child object. Open Graphics Designer, select the picture whose name is reported after PROJECT\ (e.g. PROJECT\BASE.pdl = the base/start picture), right-click the canvas, and choose Properties > Events.
Inspect, in this order:
- OpenPicture — fires once when the picture is loaded. Most common source of intermittent GPFs because the runtime is still wiring internal handles.
- Open / OpenPictureOnce — variants of the same event in older projects.
- ClosePicture — common when memory is freed twice or when a project function still holds a reference.
- Hotkeys / Mouse events — if the fault correlates with operator input, check the mouse/keyboard events bound to the picture.
For every event handler, expand the C action and verify the function it calls. Note the function name; you will need it to fix the string handling issue described in Section 10.
5. NULL lpszObjectName and lpszPropertyName: What They Mean
The dispatcher in pdllrt.dll populates lpszObjectName/lpszPropertyName from the function arguments at the moment the C interpreter invokes an internal helper. When the dispatcher prints NULL for both fields, two scenarios are possible:
| Scenario | How to confirm | Typical signature |
|---|---|---|
| Script called an API that expects an object name string with a NULL or uninitialized argument. | Search the script for any literal NULL passed to GetPropChar, SetPropChar, GetLink, SetLink, GetProperty. |
GetPropChar(NULL, "Visible"); |
| Script read past the end of a buffer and the next read hit unmapped memory. | Inspect every strcpy, strcat, sprintf, printf call; check return values from SysMalloc/malloc. |
char buf[16]; strcpy(buf, szLongString); |
Script used a local char* variable without initializing it. |
Compile the project with WinCC's C editor; uninitialized local warnings will be flagged. | char* p; printf("%s", p); |
| Project function called from the event is itself a wrapper that passes the wrong argument count. | Open the called project function; compare the expected signature with the call site. | DoSomething("Visible"); // expected (lpszObj, lpszProp) |
In all four scenarios the dispatcher still records the target of the call (the picture on which the event fired), but it cannot record the argument because the argument was never valid. That is why both fields read NULL.
6. C-Script String Handling as the Root Cause
The Siemens FAQ "How do you efficiently and reliably program the processing of character strings (pointer to char, "String") in WinCC C scripts?" (entry ID 7929092) is the canonical reference for this class of fault. The root cause pattern in nearly every case is one of the following:
-
Mixing WinCC
String(Pascal-style, length-prefixed) with Cchar*(NUL-terminated) without conversion. Functions such asstrlen,strcpy, andsprintfread until they find'\0'; WinCCStringvalues embed a length prefix and do not necessarily contain a NUL byte in the data area. - Aliasing a literal constant. Writing into a string literal pointer is undefined behavior. WinCC stores some names in read-only memory.
-
Off-by-one on the buffer size.
char buf[32]; strcpy(buf, src);crashes whenstrlen(src) >= 32. The fault may be intermittent because the read past the end either returns garbage (no crash) or crosses a page boundary (crash). -
Using
GetPropChar/SetPropCharwith a NULL object or property argument. The dispatcher then cannot populatelpszObjectName/lpszPropertyNameand reportsNULLfor both.
This explains the symptom in the field report: a project function bound to the picture's OpenPicture event triggered a GPF; the dispatcher printed lpszObjectName: NULL, lpszPropertyName: NULL; and after a reboot the fault did not recur for a while. The reboot is irrelevant—the script is still wrong, it just happened not to land on an unmapped page this time.
7. Identifying the Action via @xxx Codes
APDiag references the offending action by its encrypted short ID, e.g. @1d. The mapping from @1d back to the C function that the developer wrote is not stored in a public file, but per Siemens FAQ "How can I determine the name of an action via the error message 'Execute Error in Action @xxx'?" (entry ID 8385493) you can recover it through the project database:
- Close the WinCC Runtime.
- Open the project database with an editor that supports the WinCC project structure (or use the WinCC Project migrator /
CCProjectMgr.exein export mode). - Search the file
<ProjectName>.pck(the packed project file) for the action ID. The actual function name appears in the metadata table next to the@xxxslot. - Alternatively, open the Graphics Designer, select the picture, and look at every event handler. The C function invoked in the handler is the one whose compiled form has the matching
@xxxID; you can identify it by clicking each action and noting its function call.
// @1d OpenPicture handler for BASE.pdl. Reopen the project after any recompile; the @xxx ID is reassigned on every full build and may shift.8. GSC Runtime: Scheduled vs. Running Events
The GSC Runtime (Global Script C Runtime) tab in the WinCC Explorer shows the actions that have a schedule (a time trigger, a variable trigger, or an event trigger). It does not show actions that are currently executing on a picture event, because picture events are dispatched by the Graphics Designer runtime, not the scheduler.
To observe picture events as they fire, use the APDiag window with the Trace Events filter enabled. The trace will print a line for every event that the Graphics Designer dispatches, including OpenPicture, ClosePicture, MouseLButtonDown, Hotkey, and any custom-named event. This is the only reliable way to confirm whether the OpenPicture handler is actually being entered when the picture loads.
9. Memory Reservation Failures and Intermittent Faults
A random GPF (occurs on some starts, not others) is almost always one of the following:
- Heap layout. The same offending read can land on a valid or invalid page depending on what the runtime has allocated. After a reboot, the heap is empty and the read often succeeds; after the runtime has allocated many handles, the read crosses a guard page.
-
Uninitialized local
charbuffer. Stack contents are non-deterministic. Achar buf[256];declared but nevermemsetto zero will contain a random pointer-sized value atbuf[0..7]; reading the buffer as a string gives random data and may eventually dereference a non-pointer. -
Re-entrancy.
OpenPicturecan fire twice in a single load (e.g. when the start picture is the same as the configured picture for a screen window, or when a screen window is configured to re-open on tag change). A handler that frees memory in the first call crashes on the second. -
Race with the project function library. If a C function from a project DLL is called from
OpenPicturewhile the DLL is still being initialized, the function pointer is valid but the internal state is not.
This is why a reboot "fixes" the fault for a while: the heap state is reset, but the script bug is unchanged.
10. Correct String Handling per Siemens FAQ 7929092
Apply the following rules, summarized from FAQ 7929092, to the failing function:
-
Never call
strcpy/strcat/sprintfon a WinCCStringdirectly. Convert withGetCharArrayorSetCharArray, or useSysStringoperations. -
Initialize every local buffer. Use
char buf[64] = {0};ormemset(buf, 0, sizeof(buf));. -
Always check pointer return values.
char* p = SysMalloc(64); if (!p) { /* error path */ return; } -
Use
snprintfwith explicit length. Replace everysprintf(buf, "%s", src)withsnprintf(buf, sizeof(buf), "%s", src). -
Use
strncpyand explicitly NUL-terminate.strncpy(buf, src, sizeof(buf)-1); buf[sizeof(buf)-1] = '\0'; -
Match allocation to deallocation. Memory from
SysMallocmust be released withSysFree; memory frommallocwithfree; never mix. -
Do not pass
NULLto property helpers. Validate object and property name arguments before callingGetPropChar,SetPropChar,GetLink,SetLink.
Example fix for a typical OpenPicture handler that reads a picture name from a tag and applies it to a property:
// BAD: passes tag value straight into the dispatcher, may be NULL or too long
char* p = GetTagChar("PicName");
SetPropChar(lpszPictureName, "BackPicture", p);
// GOOD: validate, copy with bound, NUL-terminate
char* p = GetTagChar("PicName");
if (p == NULL) {
printf("[OpenPicture] Tag 'PicName' returned NULL, aborting property set\n");
return;
}
char buf[260] = {0};
snprintf(buf, sizeof(buf), "%s", p);
if (strlen(buf) == 0) {
printf("[OpenPicture] Tag 'PicName' is empty, aborting property set\n");
return;
}
SetPropChar(lpszPictureName, "BackPicture", buf);
11. Step-by-Step Diagnostic Procedure
Use this ordered procedure every time you see a GPF in the APDiag output window:
-
Open APDiag from
Start > Siemens Automation > WinCC > APDiag(or from the WinCC Explorer). Confirm the output window shows a live timestamp. -
Reproduce the fault by triggering the action (e.g. switch to the picture that has the
OpenPicturehandler). -
Capture the trace block that contains
GPFand the subsequentStacklines. Note the address (PROJECT\BASEin the typical case) and theCaller: @xxxline. -
Resolve the picture from the address. If the address is
PROJECT\<Name>, open<Name>.pdlin the Graphics Designer. If it isPROJECT\BASE, open the start picture configured in the project properties. -
Resolve the event from the
Event:line. Open the picture's Properties > Events and find that event's C action. -
Resolve the function by mapping
@xxxto a function name using the procedure in FAQ 8385493, or by cross-referencing the action's function call. -
Inspect the function for the patterns listed in Section 5 and Section 6. Use a static analyzer (WinCC's C editor marks uninitialized variables and bad pointer use) and add
printf/DebugTracecalls at every API entry point. - Fix the function following the rules in Section 10. Re-test.
- Rebuild and re-deploy the project. Restart the runtime and reproduce the original trigger. Confirm no further GPF appears in APDiag.
12. Verification and Prevention Checklist
| Check | Pass criterion |
|---|---|
| APDiag output is clean across 20 start/stop cycles. | No GPF, no Execute Error, no NULL object/property names. |
All local char buffers are initialized. |
Project passes the C editor's uninitialized-variable check. |
No raw strcpy/strcat/sprintf against WinCC String values. |
Grep for these calls returns zero matches in the project source. |
| All pointer return values are checked. | Compiler warning level is set to maximum; no uninitialized-pointer warnings. |
Allocation/deallocation pairs match (SysMalloc/SysFree, malloc/free). |
No mixed allocator calls; no double-free. |
OpenPicture handlers are idempotent. |
Loading the same picture twice in a row does not crash. |
Every C action has a comment with its @xxx ID. |
Action ID is recoverable from the source after a rebuild. |
| Re-entry on the same event is guarded. | A static int busy = 0; flag prevents nested entry where the function is not re-entrant. |
Run the verification procedure on every project that contains C scripts. The cost of the checklist is small compared with the cost of a runtime crash on an operator HMI panel.
13. Frequently Asked Questions
What does "general protection fault" mean in the APDiag output?
A general protection fault is a Windows access violation (0xC0000005) raised when a C script in WinCC dereferences a pointer that is NULL, dangling, or points outside the allocated buffer. The script tried to read or write memory it does not own, and the OS terminated the action.
Why are both lpszObjectName and lpszPropertyName shown as NULL in APDiag?
The dispatcher in pdllrt.dll populates these fields from the script's argument list at the moment of the call. If the script passed a NULL or uninitialized string to a property helper such as GetPropChar or SetPropChar, the dispatcher has no valid name to record and prints NULL for both. The fault is in the calling script, not in the object or property itself.
How do I map an @xxx code back to the C function I wrote?
Follow the procedure in Siemens FAQ 8385493. Open the packed project file (<ProjectName>.pck) or cross-reference the action's function call in the Graphics Designer. Add a comment with the @xxx ID at the top of every C action so you can identify the function after a rebuild reassigns IDs.
Why does the GPF occur sometimes and not others, and a reboot seems to fix it?
The fault is intermittent because the offending memory read either succeeds or crosses an unmapped page depending on the heap layout. A reboot resets the heap and the read succeeds again, but the script bug is unchanged. Fix the string handling per Siemens FAQ 7929092 instead of relying on a reboot.
Where is the official Siemens reference for string handling in WinCC C scripts?
Siemens FAQ 7929092, "How do you efficiently and reliably program the processing of character strings (pointer to char, 'String') in WinCC C scripts?" is the canonical reference. It covers the difference between WinCC String and C char*, the use of SysMalloc/SysFree, and the patterns that prevent NULL-pointer GPFs.