1. Problem Statement and Design Goal
In Siemens WinCC (both the legacy WinCC V7.x and the TIA Portal WinCC Comfort/Advanced runtime), engineers frequently need to execute project logic on a specific client of a multi-client distributed HMI station. The runtime supports two scripting languages:
- C scripts (ANSI-C, WinCC API), which are pre-compiled at project startup and executed natively by the runtime.
- Visual Basic Scripting (VBS) actions, which are interpreted at every execution cycle.
A practical requirement arises when an automation function is required only on Client 2, but the scripting language that fits the function best is VBS - typically because VBS gives quick access to the WinCC graphics runtime object model (HMIRuntime, Screen, ScreenItems, Tags) and is easier to maintain for graphics-driven logic. WinCC, however, allows the property Computer assignment of an action to be set per computer, but this option is exposed cleanly for C actions. For VBS actions scheduled under Global Script > Actions, the computer-filter is either hidden or not honored in the same way, depending on the WinCC version and patch level.
The design goal is therefore:
- Schedule the VBS action as a cyclic action (e.g., 1 s, 2 s, or event-driven).
- Ensure the body of the VBS only executes on a single designated client.
- Avoid the recurring interpretation overhead on every other client/server where the VBS is not required.
The standard, manufacturer-supported way to achieve this is to use a local, non-shared tag as a handshake between a C action (which is the one that is assigned to a specific computer) and the VBS action (which is project-wide).
2. Why a C Action Is Used as the Gatekeeper
C actions in WinCC are compiled to native code by the WinCC project editor at compile time. Once compiled, the runtime loads the binary and invokes it on the schedule you set. Because the C action is bound to a specific computer in the project tree (right-click → Properties → Computer assignment), only that computer's runtime will ever execute it. This makes C the natural place to put the "should I run?" decision.
By contrast, a VBS action is re-parsed by the scripting host (vbscript.dll) on every cycle when it is triggered. On a four-client project, that overhead is multiplied by the number of clients that have the action in their local Global Script container - even when the work the script does is intentionally a no-op. Moving the actual work into a guarded block and using a binary tag to enable/disable the VBS is the simplest, robust pattern.
3. Prerequisites
- WinCC V7.4 SP3 or later (the pattern works from V6.x onward; tested on V7.4 SP3, V7.5 SP2, V7.5 SP3).
- WinCC Explorer with Global Script editor accessible (password may be required, depending on project settings).
- Both the client and the server sides of the distributed system must be running. Server data must be visible to the client (configured package on the server).
- A free internal tag slot, datatype
BOOL(binary), to be used as a handshake. Name suggestion:bRun_ClientOnlyLogic. - The Tag Logging and Alarm Logging services do not need to be running for this pattern - it is purely a runtime tag-based handshake.
4. Tag Architecture for the C → VBS Trigger
The handshake uses two pieces of state:
| Tag | Type | Scope | Purpose |
|---|---|---|---|
@local_machine_name |
STRING (internal) | Built-in, computer-local | Holds the Windows computer name on which the runtime is executing. Used to identify the client. |
bRun_ClientOnlyLogic |
BOOL (internal) | Computer-local on the target client | Set TRUE by the C action on the chosen client, read and reset by the VBS action. |
Because both tags are marked internal and not shared, each runtime instance has its own copy. The C action on the target client flips its local copy to TRUE every cycle; the VBS on every other runtime instance sees FALSE on its own local copy and skips the work.
5. Step-by-Step Implementation
5.1 Create the Handshake Tag
- Open WinCC Explorer → Tag Management → right-click Internal tags → Add new tag.
- Name:
bRun_ClientOnlyLogic, datatypeBinary tag(BOOL), length 1 bit. - Confirm with OK. Do not enable tag logging - this is a pure runtime handshake.
5.2 Create the C Action on the Target Client
- In WinCC Explorer, select the computer that will be the trigger client (e.g., CLIENT_02).
- Open Global Script > C actions.
- Right-click → New action. Name:
act_GateClientOnly. Trigger: Cyclic - 1 second (or the smallest cycle that meets your latency needs). - Open the Properties dialog and confirm under Computer assignment that this action is bound only to
CLIENT_02. Remove the assignment to the server and any other client.
Paste the following C body:
/* act_GateClientOnly - cyclic C action on CLIENT_02 only */
#include "apdefap.h"
int gsc_Action(void)
{
/* Local handle to the internal handshake tag */
DWORD dwTrigger = 0;
/* Read current value of the internal BOOL tag */
if (GetTagBit("bRun_ClientOnlyLogic", &dwTrigger) == FALSE)
{
/* If the tag cannot be read, abort this cycle cleanly */
return 0;
}
/* Set the trigger TRUE every cycle on this client. The VBS
is responsible for resetting it back to FALSE after consuming. */
dwTrigger = 1;
SetTagBit("bRun_ClientOnlyLogic", dwTrigger);
return 0;
}
Click Compile in the C editor toolbar. The C action must compile with 0 warnings, 0 errors. Save the action.
5.3 Create the VBS Action (Project-Wide)
- Open Global Script > VBS actions (not C actions).
- Right-click → New action. Name:
act_ClientOnlyLogic. Trigger: Cyclic - 1 second (match the C action's cycle). - Assign the action to all computers on which it should exist (the gating happens in the script, not the assignment).
Paste the following VBS body:
' act_ClientOnlyLogic - guarded VBS action
Option Explicit
Dim bTrigger
Dim sMachine
' Read the local handshake tag. This is a *local* tag on every
' runtime, so each client sees its own copy.
bTrigger = HMIRuntime.Tags("bRun_ClientOnlyLogic").Read
' Only the client where the C action is running will have bTrigger = 1
If CBool(bTrigger) = True Then
' Optional: identify which client we are on, for diagnostics.
sMachine = HMIRuntime.Tags("@local_machine_name").Read
HMIRuntime.Trace "act_ClientOnlyLogic running on: " & sMachine & vbCrLf
' ---- Place the real client-2-only logic here ----
' Example: update a header text on the active screen
HMIRuntime.Screens("MainHeader").ScreenItems("txtStation").Text = _
"Client " & sMachine & " active"
' ------------------------------------------------
' Reset the trigger so we do not run the work twice
HMIRuntime.Tags("bRun_ClientOnlyLogic").Write 0
End If
Save the action. Note that the VBS itself is loaded on every runtime that has it assigned; the body is essentially a no-op except on the gatekeeper client.
5.4 Compile, Distribute, and Activate
- Close the Global Script editor.
- From the server project, recompile the OS (WinCC Explorer > Server > Save and compile OS). Wait for OS compilation completed.
- Distribute the package to every client (Server Data > Package > Configure package > Update clients).
- On the target client, activate the WinCC runtime. After a few seconds, the VBS should be running only on that client.
6. Identifying the Local Machine
WinCC exposes several system tags (prefixed with @) that describe the local runtime. The most useful for client identification are:
| System tag | Datatype | Value |
|---|---|---|
@local_machine_name |
STRING | NetBIOS name of the local computer (e.g., CLIENT_02) |
@local_user_name |
STRING | Logged-in Windows user |
@local_os_name |
STRING | Windows edition string |
@redundant_server_state |
DWORD | Redundancy state (0=primary, 1=standby, etc.) |
You can read these in C with GetTagCharValue or in VBS with HMIRuntime.Tags("@local_machine_name").Read. They are computer-local and do not propagate through the WinCC tag system, so they are the canonical way to ask "who am I?" from inside a script.
7. Optional: Direct Script Execution via cscript
If you want the C action to launch an external .vbs file via the Windows Script Host instead of triggering an in-project VBS action, you can call cscript as an external program. Microsoft's official documentation describes this mechanism: Running a Visual Basic Scripting Edition Query.
From a WinCC C action, use the Win32 API WinExec or ShellExecute:
#include "apdefap.h"
#include <windows.h>
int gsc_Action(void)
{
/* Launch an external VBS file non-blocking */
ShellExecute(NULL, "open", "wscript.exe",
"C:\\Scripts\\ClientOnly.vbs",
NULL, SW_HIDE);
return 0;
}
Drawbacks of the external approach compared to the in-project VBS action:
- The external VBS has no access to the WinCC runtime object model. It cannot read/write WinCC tags, move the active screen, or interact with the graphic system without a separate channel (OPC UA, file, named pipe).
- Process startup latency (typically 80-300 ms) makes this unsuitable for 1 s cyclic logic.
- Process management (cleanup on runtime stop, logon changes) is your responsibility.
Use the in-project VBS approach whenever the script needs the WinCC object model; use the cscript/wscript approach only for purely external tasks (file I/O, FTP, mail, network calls) that the WinCC VBS host cannot do conveniently.
8. Performance and Compilation Considerations
| Aspect | C action | VBS action |
|---|---|---|
| Compile time | Compiled by WinCC at project compile; loaded as native code | Parsed by VBScript engine every cycle |
| First-call latency | None (already loaded) | Includes parse + compile of AST |
| Steady-state CPU | Low, deterministic | Higher; scales with code size and number of Tag-Read/Write calls |
| Object model access | Limited (C-API only) | Full HMIRuntime.* object model |
| Computer-filter friendly | Yes, via Computer assignment | No (handled via gating tag) |
| Tag access |
GetTagBit/SetTagBit - fastest path |
HMIRuntime.Tags(...).Read/Write - slightly higher overhead |
For a 1 Hz cyclic handshake between a C action and a VBS action, the expected CPU contribution on a typical WinCC client is below 0.5% of a single core, even with 100 tags being read inside the VBS body. The dominant cost in such a setup is the VBS parser, which is paid on every runtime that has the action assigned - this is the overhead the gatekeeper pattern is designed to avoid.
9. Verification
- Activate runtime on the target client only. Open the WinCC Diagnose > ApDiag tool and add the trace output of
act_ClientOnlyLogic. Confirm that the trace prints the local machine name once per cycle. - Activate runtime on a non-target client (e.g.,
CLIENT_01). The same VBS action is loaded, butbRun_ClientOnlyLogicstays FALSE because the C action is not assigned there. The trace should remain silent. - Stop and re-activate the target client runtime. Within one cycle, the C action sets the trigger; within the next cycle, the VBS body executes and resets the trigger. The latency is bounded by the cycle period of the two actions.
- Add a temporary
Tracein the C action to confirm it runs only on the intended computer:printf("act_GateClientOnly on %s\n", GetTagChar("@local_machine_name")); - For an end-to-end check, attach a WinCC Tag Simulator forcing
bRun_ClientOnlyLogic = 1on a non-target client. The VBS body should still run on that client (because the tag is forced locally), proving the gating is purely a per-runtime tag-based decision. Remove the simulator when done.
10. Troubleshooting Matrix
| Symptom | Likely root cause | Fix |
|---|---|---|
| VBS runs on all clients |
bRun_ClientOnlyLogic was created as a shared external tag instead of an internal tag |
Recreate as Internal tag, datatype Binary. Internal tags are always local to the runtime instance. |
| VBS never runs anywhere | C action fails to compile, or computer assignment is wrong | Open the C action in the Global Script editor on the target client. Look for red error markers. Confirm Properties > Computer assignment lists only the target client. |
| VBS runs twice in one cycle on the target client | VBS body does not reset the trigger | Confirm the line HMIRuntime.Tags("bRun_ClientOnlyLogic").Write 0 is present and inside the If CBool(bTrigger) = True block. |
| C action sets the tag, but VBS reads a stale value | Different cycle times between the two actions | Match the C and VBS trigger intervals. If C is 1 s and VBS is 500 ms, VBS can read TRUE twice. Either set the C action to 2x the VBS cycle, or accept the duplicate and rely on the VBS reset. |
| Trace output shows wrong machine name | The user expectation was that the script should run on the server of a redundant pair, not the client | Move the C action assignment from the client to the server. Confirm @local_machine_name at runtime to verify the new assignment. |
| Permission denied when editing the VBS action | Global Script is password-protected in the project | Open the project in WinCC Explorer as a user with author rights, or request the password from the project administrator. |
11. Best-Practice Checklist
- Always use internal tags for the handshake. Shared tags defeat the purpose.
- Always reset the trigger from the VBS body after consumption to prevent re-execution within the same cycle window.
- Match the cycle of the gatekeeper C action to the cycle of the consumer VBS action, or set the C cycle to a multiple of the VBS cycle.
- Use descriptive tag names (
bRun_<Subsystem>) to make the intent clear in the tag list. - Add a
Traceon first commissioning, then remove or comment it out for production. - If you have more than one gated subsystem on the same client, give each one its own handshake tag - do not multiplex the trigger.
- Do not call HMIRuntime APIs that require a logged-in user from the gatekeeper C action. C has no access to the user session model.
12. FAQ
Can a VBS action in WinCC be assigned to a single client only?
Not directly. The Computer assignment property in the action's Properties dialog is exposed for C actions. For VBS actions, you must gate execution at runtime, typically by using a local internal tag set by a C action that is bound to the chosen computer.
Why not just check the local machine name inside the VBS?
You can. HMIRuntime.Tags("@local_machine_name").Read returns the local computer name in any action. However, the VBS is still parsed and dispatched on every runtime that has the action assigned. The C-gated pattern keeps the VBS body a one-line no-op on non-target clients, saving the per-cycle interpretation cost.
Do I need to add the handshake tag to the package on the server?
No. Internal tags are not part of the package. They are created on each runtime independently. If you create the tag in the project on the server, the package will replicate the tag definition, but the runtime value stays local.
What happens if both clients are supposed to run the same logic at slightly different times?
Replicate the same C action on each of the two clients, each one bound to a different client computer. Use a different handshake tag per client (for example bRun_Client01Logic, bRun_Client02Logic), or a single DWORD tag whose bits encode which clients are active.
Is the pattern supported in TIA Portal WinCC (Comfort/Advanced/Professional)?
The VBS gating side works identically, because TIA Portal WinCC also re-parses VBS on every cycle. The C-action gatekeeper is not available in TIA Portal WinCC Comfort/Advanced; in WinCC Professional you can use a scheduled VB function or a C script with an execution condition bound to the client. The handshake tag pattern is portable across all three TIA variants and the legacy V7 line.