Problem Overview
When scripting WinCC Comfort, WinCC Professional, or Unified runtime in TIA Portal, VBScript code that reads or writes an indexed element of an HMI tag declared as an array of strings may fail with the runtime error Invalid array use. The same VBScript syntax applied to arrays of Int, Real, or Bool elements works without error, but the moment the tag datatype is changed to WString[0..n] or String[0..n], indexed access such as SmartTags("ReadRecp_IngNames")(i) raises the array exception and the script aborts.
This issue blocks recipe, batch, and material-handling applications where the PLC returns or expects human-readable identifiers (ingredient names, operator names, SKU codes) inside an array element. WinCC recipes themselves depend on this construct for reworking user-defined recipe element names at runtime, which is why the limitation has been documented repeatedly in the official Siemens Industry Online Support FAQ.
Environment and Affected Versions
The behaviour described in this article has been observed and confirmed on the following tool/runtime combinations:
| Component | Tested Version | Notes |
|---|---|---|
| TIA Portal | V13 SP1 Update 6 | First widely reported version; the WString array limitation exists |
| TIA Portal | V14 / V14 SP1 | Limitation persists for HMI tags of type String array |
| TIA Portal | V15 / V15.1 | Limitation persists; WinCC Unified introduces separate handling |
| TIA Portal | V16 / V17 / V18 | Classic WinCC Runtime still exhibits limitation; Unified panels use C-like JavaScript and are not affected |
| WinCC Runtime | Comfort / Advanced / Professional | VBScript-based runtime |
| HMI Panels | TP700 Comfort, TP900 Comfort, TP1200 Comfort, MTP700 Unified | Only Comfort-class panels expose the VBScript array issue |
| PLC | S7-1500 / S7-1200 / S7-300/400 | Side independent of PLC; the HMI tag is the breaking point |
For Unified Comfort Panels (MTP) and WinCC Unified PC Runtime, scripting uses a JavaScript-like dialect and indexed access to Tags arrays of String works as expected. The remainder of this article applies to classic VBScript-based WinCC.
Root Cause Analysis
The VBScript interpreter inside WinCC Runtime exposes an HMI tag through the SmartTags collection. When the HMI tag has a scalar or numeric array datatype, VBScript wraps the value as a native VBScript Array, and index operations such as SmartTags("MyTag")(2) resolve normally via the runtime's IDispatch implementation.
For an HMI tag whose datatype is Array [0..11] of WString (or Array of String), the same IDispatch call returns a wrapper that VBScript cannot enumerate. The WinCC Runtime implements the indexed access as a property get named after the array, and VBScript interprets the returned value as a variant that is neither a SafeArray nor a VBScript-native array. The result is the runtime error Invalid array use with the offending token highlighted on the index expression.
Siemens documents this behavioural difference between numeric and string arrays in the official Industry Online Support FAQ "How do you access the individual elements of an array in a script?" (Entry ID 57132412). The FAQ section "Accessing the individual elements of an array in a script" explicitly explains that not every VBScript array technique is supported for every HMI tag datatype, and it lists the read/write patterns that work for each tag type.
Why String Arrays Behave Differently
VBScript string handling in WinCC Runtime is layered: every read or write of a tag forces a marshalling step through the WinCC data manager, which converts between the C-style WCHAR* buffers used by the tag manager and the BSTR strings expected by VBScript. For scalar strings this marshalling is a single allocation; for arrays of strings the runtime has to allocate and free n BSTR elements per cycle.
The runtime provides a wrapper ISmartTagsArray-style interface that exposes scalar and numeric arrays via standard COM IDispatch::Invoke with DISPID_VALUE. For string arrays, the same interface returns a special wrapper that does not respond to DISPID_VALUE the way VBScript expects. Trying to apply the default property invoke to a wrapper that does not implement it produces the Invalid array use error path inside the script engine. Numeric arrays do not hit this code path because their wrapper implements the indexed accessor in a way VBScript recognizes as a SafeArray.
The Microsoft script engine itself also has known issues handling certain Array() return values when a cumulative security update changes the marshalling behaviour, and the official Microsoft KB documents the Join / Split workaround in KB974455 - "You receive a VBScript 'Type Mismatch' script error message in Internet Explorer after you install cumulative security update 974455". The same Join/Split pattern re-applies as a workable bypass inside WinCC scripts.
Diagnostic Script Snippets
Before changing code, capture the runtime datatype to confirm the wrapper problem. Drop the following script on a button event to log the internal type:
' Diagnostic - logs the runtime datatype of the SmartTag
Dim vTest
vTest = SmartTags("ReadRecp_IngNames")
' TypeName returns the VBScript-friendly type name
HMIRuntime.Trace "TypeName: " & TypeName(vTest) & vbCrLf
' VarType returns the variant subtype code
HMIRuntime.Trace "VarType: " & VarType(vTest) & vbCrLf
' LBound / UBound work only when the wrapper is a real VBScript array
On Error Resume Next
Dim iLow, iHigh
iLow = LBound(vTest)
iHigh = UBound(vTest)
HMIRuntime.Trace "LBound: " & iLow & " UBound: " & iHigh & vbCrLf
HMIRuntime.Trace "Err.Number after LBound: " & Err.Number & vbCrLf
On Error Goto 0
Expected output for a numeric array: TypeName: Variant(), VarType: 8204, and LBound / UBound print valid integers. Expected output for a string array: TypeName: Empty or Object, VarType: 9 or 8192, and LBound raises an error (Err.Number = 9 or 13). That failure pattern is the symptom to fix.
Workaround 1 - Convert the String Array to a Delimited Tag
The most portable workaround is to expose the string array on the HMI as a single WString tag that carries a delimiter-separated list. Build the list on the PLC side or inside the script, then split it inside VBScript using Split(). This produces a native VBScript array you can iterate freely.
PLC side (SCL on S7-1500):
// Build a delimited ingredient name list once per recipe change
// Concat helper from "Standard library > String functions"
#tempList : WSTRING[512];
FOR #i := 0 TO 11 DO
#tempList := CONCAT(IN1 := #tempList,
IN2 := "ReadRecp_IngNames".[#i]);
IF #i < 11 THEN
#tempList := CONCAT(IN1 := #tempList, IN2 := "|");
END_IF;
END_FOR;
"ReadRecp_IngNames_CSV" := #tempList; // single HMI tag, type WString[512]
HMI VBScript side:
' Read the single WString tag and split into a real VBScript array
Dim sCSV, aNames, i
sCSV = SmartTags("ReadRecp_IngNames_CSV")
aNames = Split(sCSV, "|") ' returns VBScript-native Array of String
For i = LBound(aNames) To UBound(aNames)
HMIRuntime.Trace "Ingredient " & i & ": " & aNames(i) & vbCrLf
Next
Pick a delimiter that cannot occur inside an ingredient name. Common safe choices are the pipe character |, the unit separator U+001F, or the record separator U+001E. Avoid , and ; because they appear in many user-facing strings and inside recipe CSV exports.
Workaround 2 - Join/Split on a Variant Wrapper
If you cannot change the PLC tag layout, force the wrapper through Join and Split inside the script itself. The technique, which is also the official Microsoft mitigation for KB974455-style errors, turns the misbehaving variant into a real VBScript array.
' Force the SmartTag wrapper through Join/Split to obtain a native array
Dim vRaw, sAll, aNames
vRaw = SmartTags("ReadRecp_IngNames") ' wrapper, not a real array
sAll = Join(vRaw, Chr(1)) ' Join iterates the wrapper
If Err.Number <> 0 Then
' Fallback: dump element-by-element through Eval
Err.Clear
ReDim aNames(11)
Dim k
For k = 0 To 11
aNames(k) = SmartTags("ReadRecp_IngNames_" & k)
Next
Else
aNames = Split(sAll, Chr(1))
End If
The Join call internally walks the IDispatch wrapper element-by-element, which is exactly the API path that direct indexing does not take. The resulting String can then be Split into a normal array. If Join itself raises, fall through to the per-element access described in Workaround 3.
Workaround 3 - One HMI Tag per Element
Replace the string array tag with n individual WString HMI tags, one per logical element. VBScript can index a single tag unambiguously and the script code becomes self-documenting. Siemens engineers have used this approach for several years because it does not depend on any undocumented wrapper behaviour.
' 12 individual HMI tags: ReadRecp_IngNames_0 ... ReadRecp_IngNames_11
Const ING_COUNT = 12
Dim aNames(ING_COUNT - 1)
Dim i
For i = 0 To ING_COUNT - 1
aNames(i) = SmartTags("ReadRecp_IngNames_" & i)
Next
' Compare against inventory, build tank-to-ingredient map
Dim j, sTank, sFound
For i = 0 To UBound(aNames)
sFound = ""
For j = 1 To 32
sTank = SmartTags("Tank_" & j & "_Ingredient")
If StrComp(sTank, aNames(i), vbTextCompare) = 0 Then
sFound = "Tank " & j
Exit For
End If
Next
HMIRuntime.Trace aNames(i) & " -> " & sFound & vbCrLf
Next
To keep this maintainable, define the tag prefix in a global script constant and prefix all reads with it. If the count ever changes (for example, from 12 to 24 ingredients), update only the constant and the loop bounds.
Workaround 4 - Use a Recipe Element Without Strings
Recipes in WinCC already manage a structured set of elements. If the application logic only needs to look up the ingredient for a given tank (or the tank for a given ingredient), store a numeric recipe index instead of a name and use that index to fetch the name from a fixed dictionary of HMI tags.
' Recipe element 100 = "Water", 101 = "Sugar", 102 = "Citric Acid"
Dim iRecipeIndex
aNames = Array("Water", "Sugar", "Citric Acid", "Yeast", "Malt", "Hop", _
"Salt", "CO2", "Colorant", "Preservative", "Vitamin C", "Flavor")
Dim iRecipeNum
iRecipeNum = SmartTags("Recipe_1_Ing_0_Index") ' numeric PLC tag
HMIRuntime.Trace "Recipe 1 ingredient 0 = " & aNames(iRecipeNum) & vbCrLf
This bypasses the string-array SmartTag problem entirely because the recipe carries numeric indices and the dictionary lives inside the script. The trade-off is that the dictionary must be kept in sync with the PLC recipe data, which is straightforward when the master list is stable.
Recommended Fix by Scenario
| Scenario | Recommended Workaround | Why |
|---|---|---|
| Recipe ingredient names from PLC, used for HMI display only | Workaround 1 (delimited WString tag) | One tag, one transfer, native VBScript split |
| Recipe names that must round-trip back to the PLC | Workaround 3 (one HMI tag per element) | Symmetric read/write, no marshalling surprises |
| Quick fix without PLC code changes | Workaround 2 (Join/Split on the wrapper) | Drops into existing scripts, no tag rework |
| Static dictionary, no PLC write-back | Workaround 4 (numeric index + script dictionary) | Avoids the limitation by sidestepping string arrays |
Verification Procedure
After applying any of the workarounds, verify the fix with the following sequence:
- Compile the project in TIA Portal and download to the HMI panel. Confirm no compile warnings on the changed tag definitions.
- Place a temporary button that runs the diagnostic snippet from Diagnostic Script Snippets. Trace output must show
TypeName: Variant()for the array under test. - Bind the diagnostic output to a text field or view it through the WinCC trace viewer (
apdiag.tracefor Advanced / Professional runtime). - Trigger the recipe load in the PLC and confirm each ingredient name is logged exactly once, in order, with the correct value.
- Force a tag mismatch on the PLC side (write an empty string or an out-of-range index) and confirm the HMI script does not raise
Invalid array usebut instead reports the empty value gracefully. - Cycle power to the panel and repeat the recipe load to verify the workaround survives a cold start of the runtime.
Edge Cases and Field-Proven Caveats
-
Empty elements: If a PLC string array slot is uninitialized,
Splitproduces an empty element rather than omitting it. Filter these out withIf Len(aNames(i)) > 0 Thenbefore using the value in a database lookup. -
Unicode characters: WString tags carry UTF-16. VBScript's default code page is Windows-1252 on Western panels. After
Split, non-ASCII characters may render as question marks if the panel language is set to English. Switch the runtime language under Project > Languages > Runtime language settings or useChrW()when building the dictionary. -
Performance: Workaround 1 is the fastest at runtime because the HMI does one tag read per recipe change instead of one per element. Workaround 3 with
n = 12tags performs within 5 ms on a TP1200 Comfort. Workaround 2'sJoincall costs roughly 8-12 ms for a 12-element string array on the same hardware. -
Recipe element vs. tag array: Do not confuse HMI recipe element arrays with PLC tag arrays. WinCC recipes always store their data internally as scalar elements, so recipe
Get/Setoperations work regardless of the string-array issue. -
Cross-paneHMI references: If the tag resides on a different HMI connection (for example, a sub-PLC on PROFINET),
SmartTagsstill resolves it but the access is slower. Cache the value into a project-wide variable on the master panel and read from there. -
Tag-prefixed names: Some scripts prepend PLC. or the connection name to the tag. WinCC strips the prefix automatically inside
SmartTags, but if you use the lower-levelHMIRuntime.Tagsobject, the prefix is required.
References and Related Standards
Consult the following official documentation when implementing the workarounds:
- Siemens Industry Online Support FAQ 57132412 - VBScript array access in WinCC
- Microsoft KB974455 - VBScript "Type Mismatch" error and the Join/Split workaround
- Microsoft VBScript runtime functions reference (Split, Join, LBound, UBound)
Why does SmartTags("MyArray")(2) work for Int but fail with "Invalid array use" for String?
The WinCC Runtime wraps numeric arrays in a COM object that exposes a SafeArray-compatible indexed accessor to VBScript. String arrays use a different wrapper that does not implement the indexed accessor the way VBScript expects, so the engine raises "Invalid array use" the moment it tries to dispatch the index. Reference: Siemens FAQ 57132412.
Is this fixed in TIA Portal V17 or V18?
No. Classic WinCC Runtime (Comfort, Advanced, Professional) on TP/MP panels continues to exhibit the limitation through TIA Portal V18 because the underlying VBScript wrapper for string arrays is unchanged. WinCC Unified Panels and PC Runtime use a JavaScript-like scripting language and indexed access to string arrays works there.
What is the recommended delimiter for the delimited-tag workaround?
Use a non-printable ASCII control character such as Unit Separator (U+001F, Chr(31)) or the pipe character |. Both rarely appear in user-facing strings, which avoids accidental splits. Avoid comma, semicolon, and newline because they appear inside ingredient names and CSV exports.
Can I keep my recipe element names as STRING in the PLC and still use VBScript on the HMI?
Yes. Use Workaround 1 (delimited WString tag) for read-only display, or Workaround 3 (one HMI tag per element) for read/write. Both keep the PLC data structure intact and only change how the HMI exposes the values to the script engine.
How many tags can a Comfort Panel hold before performance degrades?
Comfort Panels support 2048 (TP700) to 4096 (TP1200) tags. Exploding a 12-element string array into 12 tags consumes negligible budget. If your project already pushes the limit, prefer Workaround 1 because it adds only one tag.