Fixing Invalid Array Use Error in WinCC VBScript String Tags

David Krause12 min read
SiemensTroubleshootingWinCC
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

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.

Tag count limit: Comfort Panels cap the number of HMI tags per project. Check the panel's tag budget (TP700 = 2048, TP1200 = 4096) before exploding a single 12-element array into 12 tags. The PLC tag count is unaffected because the tags are HMI-side aliases.

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:

  1. Compile the project in TIA Portal and download to the HMI panel. Confirm no compile warnings on the changed tag definitions.
  2. Place a temporary button that runs the diagnostic snippet from Diagnostic Script Snippets. Trace output must show TypeName: Variant() for the array under test.
  3. Bind the diagnostic output to a text field or view it through the WinCC trace viewer (apdiag.trace for Advanced / Professional runtime).
  4. Trigger the recipe load in the PLC and confirm each ingredient name is logged exactly once, in order, with the correct value.
  5. 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 use but instead reports the empty value gracefully.
  6. 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, Split produces an empty element rather than omitting it. Filter these out with If Len(aNames(i)) > 0 Then before 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 use ChrW() 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 = 12 tags performs within 5 ms on a TP1200 Comfort. Workaround 2's Join call 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 / Set operations 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), SmartTags still 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-level HMIRuntime.Tags object, the prefix is required.

References and Related Standards

Consult the following official documentation when implementing the workarounds:

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.

Back to blog