Print Enum value's Name in VBA [duplicate] - vba

Is there a way to get the enums in VBA? Something like this example for C#, but for VBA?
using System;
class EnumsExampleZ
{
private enum SiteNames
{
SomeSample = 1,
SomeOtherSample = 2,
SomeThirdSample = 3
}
static void Main()
{
Type enumType = typeof(SiteNames);
string[] enumName = enumType.GetEnumNames();
for (int i = 0; i < enumName.Length; i++)
{
Console.WriteLine(enumName[i]);
}
}
}
Lets say we have the following:
Enum FruitType
Apple = 1
Orange = 2
Plum = 3
End Enum
How can we display on the immediate window these:
Apple
Orange
Plum

There is no built-in function, though it is easy enough to roll your own in a concrete case:
Enum FruitType
Apple = 1
Orange = 2
Plum = 3
End Enum
Function EnumName(i As Long) As String
EnumName = Array("Apple","Orange","Plum")(i-1)
End Function
If you have several different enums, you could add a parameter which is the string name of the enum and Select Case on it.
Having said all this, it might possible to do something with scripting the VBA editor, though it is unlikely to be worth it (IMHO).

Parsing the VBA code yourself with the VBIDE Extensibility library is going to appear nice & simple at first, and then you're going to hit edge cases and soon realize that you need to actually implement that part of the VBA spec in order to properly and successfully parse every possible way to define an enum in VBA.
I'd go with the simple solution.
That said Rubberduck is doing pretty much exactly that, and exposes an experimental COM API that allows you to enumerate all declarations (and their references) in the VBE, effectively empowering your VBA code with reflection-like capabilities; as of 2.0.11 (the latest release), the code would look something like this:
Public Enum TestEnum
Foo
Bar
End Enum
Public Sub ListEnums()
With New Rubberduck.ParserState
.Initialize Application.VBE
.Parse
Dim item As Variant
For Each item In .UserDeclarations
Dim decl As Rubberduck.Declaration
Set decl = item
If decl.DeclarationType = DeclarationType_EnumerationMember Then
Debug.Print decl.ParentDeclaration.Name & "." & decl.Name
End If
Next
End With
End Sub
And in theory would output this:
TestEnum.Foo
TestEnum.Bar
However we (ok, I did) broke something around the 2.0.9 release, so if you try that in 2.0.11 you'll get a runtime error complaining about an invalid cast:
That should be is an easy fix that we'll patch up by 2.0.12, but note that at that point the API is still experimental and very much subject to change (feature requests are welcome!), so I wouldn't recommend using it for anything other than toy projects.

If the reason you're looking for enum names is because you mean to use them in a user interface, know that even in C# that's bad practice; in .net you could use a [DisplayAttribute] to specify a UI-friendly display string, but even then, that's not localization-friendly.
In excel-vba you can use Excel itself to remove data from your code, by entering it into a table, that can live in a hidden worksheet that can literally act as a resource file:
Then you can have a utility function that gets you the caption, given an enum value:
Public Enum SupportedLanguage
Lang_EN = 2
Lang_FR = 3
Lang_DE = 4
End Enum
Public Function GetFruitTypeName(ByVal value As FruitType, Optional ByVal langId As SupportedLanguage = Lang_EN) As String
Dim table As ListObject
Set table = MyHiddenResourceSheet.ListObjects("FruitTypeNames")
On Error Resume Next
GetFruitTypeName = Application.WorksheetFunction.Vlookup(value, table.Range, langId, False)
If Err.Number <> 0 Then GetFruitTypeName = "(unknown)"
Err.Clear
On Error GoTo 0
End Function
Or something like it. That way you keep code with code, and data with data. And you can quite easily extend it, too.

No - there is no native way to do this. You'd need to fully parse all of the user code and read the type libraries of any loaded projects and finally determine what scope each reference was referring to.
Enumerations can't be treated like reference types in VBA, and this due to the deep roots that VBA has in COM. Enums in VBA are more like aliases, and in fact, VBA doesn't even enforce type safety for them (again, because of COM interop - MIDL specs require that they are treated as a DWORD).
If you really need to do this in VBA, a good workaround would be to create your own enumeration class and use that instead.

Public Enum col: [____]: cPath: cFile: cType: End Enum
Public Const colNames$ = "Path: cFile: cType"
Not directly an answer and might look pretty ugly, but I thought it might be useful to others.
In an old project I wanted to access columns with Enum (for example row(, col.cType) = 1).
I changed the column location, name, use, etc. pretty often, but with this lazy approach I could just rearrange the Enum and then copy paste the change in the string constant, and get the table headers:
Range("A1:C1").Value2 = Split(colNames, ": c")
Names starting with _ are hidden by default, so [____] is used for padding and to avoid "cPath = 1"

I think that the marvel CPearson's site has the answer with the [_First] and [_Last] trick.
I had the need of speed up a lot of DB reading just to populate combo and list boxes with values in some Office VBA application, and I just translate them to Enums.
Of course, do a For Each like, with the For Next is a must, and the [_First] and [_Last] is the way to go. The problem is that I have a lot of non-sequential Enums, each with 10 to 40 Enum items, and code for each is too tediously.
To unify all my combo and listbox feeding needs, I adapted CPearson's trick to non-sequential Enums too:
Sub EnumValueNamesWrapingAndUnwrapingToClipboard()
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' This creates a text string of the comma separated value names of an
' Enum data type. Put the cursor anywhere within an Enum definition
' and the code will create a comma separated string of all the
' enum value names. This can be used in a Select Case for validating
' values passed to a function. If the cursor is not within an enum
' definition when the code is executed, the results are unpredicable by CPearson
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim N As Long
Dim txt As String, S As String
Dim SL As Long, EL As Long, SC As Long, EC As Long
Dim DataObj As MSForms.DataObject
Dim auxTitle As String, auxStrValue As String, strAuxCase As String
Dim counter As Integer, EnumMin As Integer, EnumMax As Integer
Dim auxValue As Variant
Dim EnumIsSequential As Boolean
Const STR_ENUM As String = "enum "
If VBE.ActiveCodePane Is Nothing Then
Exit Sub
End If
With VBE.ActiveCodePane
.GetSelection SL, SC, EL, EC
With .CodeModule
S = .Lines(SL, 1)
Do Until InStr(1, S, STR_ENUM, vbTextCompare) > 0
N = N + 1
S = .Lines(SL - N, 1)
Loop
'Function title
auxTitle = Right$(S, Len(S) - InStr(1, S, STR_ENUM, vbTextCompare) - Len(STR_ENUM) + Len(" "))
N = SL - N + 1
S = .Lines(N, 1)
Do
S = .Lines(N, 1)
If InStr(1, S, "end enum", vbTextCompare) = 0 And InStr(1, S, "'", vbTextCompare) = 0 Then
txt = txt & " " & Trim(S) & ","
End If
N = N + 1
Loop Until InStr(1, S, "end enum", vbTextCompare) > 0
ReDim auxValue(0)
ReDim Preserve auxValue(0 To StringCountOccurrences(txt, "=") - 2) 'because of [_First] and [_Last]
For counter = 1 To UBound(auxValue)
auxStrValue = RetornaElementoDesignado(counter + 1, Left(txt, Len(txt) - 1))
If counter = 1 Then
EnumMin = CInt(Trim$(Right$(auxStrValue, Len(auxStrValue) - InStrRev(auxStrValue, "="))))
auxValue(counter) = Trim$(Left$(auxStrValue, InStr(1, auxStrValue, " = ")))
ElseIf counter = UBound(auxValue) Then
EnumMax = CInt(Trim$(Right$(auxStrValue, Len(auxStrValue) - InStrRev(auxStrValue, "="))))
auxValue(counter) = Trim$(Left$(auxStrValue, InStr(1, auxStrValue, " = ")))
Else
auxValue(counter) = Trim$(Left$(auxStrValue, InStr(1, auxStrValue, " = ")))
End If
Next counter
End With
End With
EnumIsSequential = NumElements(auxValue) - 1 = EnumMax - EnumMin + 1
strAuxCase = "Function ReturnNameEnum" & auxTitle & " (ByVal WhichEnum As " & auxTitle & ")As String" & vbCrLf _
& " Select Case WhichEnum" & vbCrLf
For counter = 1 To UBound(auxValue)
strAuxCase = strAuxCase & " Case Is = " & auxTitle & "." & auxValue(counter) & vbCrLf _
& " ReturnNameEnum" & auxTitle & " = " & ParseSpecialCharsAndDataTypeForSQL(auxValue(counter), False, True, False) & vbCrLf
Next counter
If EnumIsSequential Then
strAuxCase = strAuxCase & " Case Else" & vbCrLf _
& " debug.print " & """Passed invalid """ & " & WhichEnum & " & """ WhichEnum As " & auxTitle & "! """ & vbCrLf _
& " End Select" & vbCrLf _
& "End Function" & vbCrLf _
& "Function LoadEnum" & auxTitle & "InArray () As Variant" & vbCrLf _
& " 'If Enum is Sequential" & vbCrLf _
& " Dim items() As Variant, item As Long, counter As Long" & vbCrLf _
& " For item = " & auxTitle & ".[_first] To " & auxTitle & ".[_last]" & vbCrLf _
& " counter = counter + 1" & vbCrLf _
& " Next" & vbCrLf _
& " ReDim items(counter * 2 - 1) '-1: it's 0-based..." & vbCrLf _
& " For item = " & auxTitle & ".[_first] To " & auxTitle & ".[_last]" & vbCrLf _
& " items(item * 2) = item" & vbCrLf _
& " items(item * 2 + 1) = ReturnNameEnum" & auxTitle & "(item)" & vbCrLf _
& " items(item * 2) = item" & vbCrLf _
& " Next" & vbCrLf _
& " LoadEnum" & auxTitle & "InArray=items()" & vbCrLf _
& "End Function"
Else
strAuxCase = strAuxCase & " Case Else" & vbCrLf _
& " debug.print " & """Passed invalid """ & " & WhichEnum & " & """ WhichEnum As " & auxTitle & "! """ & vbCrLf _
& " End Select" & vbCrLf _
& "End Function" & vbCrLf _
& "Function LoadEnum" & auxTitle & "InArray () As Variant" & vbCrLf _
& " 'For Non-Sequential Enum" & vbCrLf _
& " Dim items() As Variant, item As Long, ExistingEnum As Long" & vbCrLf _
& " For item = " & auxTitle & ".[_first] To " & auxTitle & ".[_last]" & vbCrLf _
& " if ReturnNameEnum" & auxTitle & "(item) <> """" then" & vbCrLf _
& " ExistingEnum = ExistingEnum + 1" & vbCrLf _
& " auxExistingEnum = auxExistingEnum & CStr(item) & "",""" & vbCrLf _
& " end if" & vbCrLf _
& " Next" & vbCrLf _
& " auxExistingEnum = Left$(auxExistingEnum, Len(auxExistingEnum) - 1)" & vbCrLf _
& " arrayExistingEnum = Split(auxExistingEnum, "","")" & vbCrLf _
& " ReDim items(ExistingEnum * 2 - 1) '-1: it's 0-based..." & vbCrLf _
& " If ReturnNameEnum" & auxTitle & "(arrayExistingEnum(item)) = """" Then GoTo continue" & vbCrLf _
& " items(item * 2) = arrayExistingEnum(item)" & vbCrLf _
& " items(item * 2 + 1) = ReturnNameEnum" & auxTitle & "(arrayExistingEnum(item))" & vbCrLf _
& "continue:" & vbCrLf _
& " Next" & vbCrLf _
& " LoadEnum" & auxTitle & "InArray=items()" & vbCrLf _
& "End Function"
End If
Set DataObj = New MSForms.DataObject
With DataObj
.SetText strAuxCase
.PutInClipboard
Debug.Print strAuxCase
End With
Set DataObj = Nothing
End Sub
I added skip comment lines - I do a lot while developing.
I did not treat Enum that is not in Ascendant order; could be done, but I'm too OCD to allow an unordered Enum ;) and ordinarily, my Enums are coming from DB with an ORDER BY on the proper value (see at end of this answer).
Of course, it depends on [_First] and [_Last] values added properly.
And, answering your question, you can do a:
?ReturnNameEnumWhateverNamedItIs(FruitType.Apple)
Apple
As a bonus, and for me the main reason to adapt the CPearson's procedure, it loads in a unidimensional array tuples of value/name of Enum; so, we can navigate all Enum values with:
auxArray=LoadEnumWhateverNameYouGaveItInArray()
For counter = lbound(auxArray) to ubound(auxArray) step 2
EnumValue = auxArray(counter)
EnumStringName = auxArray(counter+1)
Next counter
The procedure is generating one of two different functions LoadEnumWhateverNameYouGaveItInArray() versions based if Enum is sequential or not.
You can forget about the sequential; the non-sequential enum function grab both situations; I left here because I developed it first and after had to adapt it to the non-sequential case, and we never know when we'll need less code lines ;)
Notice that although Enum is natively Long, I used Integer in counter/EnumMin/EnumMax, just because the Enums that I need to iterate its names are less than hundred, like fruit names.
Hope it helps someone.
Edit:
To complete the explanation, this is the procedure that I use to extract Enum from tables and write them in a static module:
Sub CreateEnumBasedOnTableValues(ByVal EnumName As String, ByVal CnnStr As String _
, ByVal DataS As String, ByVal strSQL As String _
, ByVal EnumValueField As String, ByVal EnumNameField As String _
, ByVal TreatIllegalNames As Boolean, ByVal EliminateWhiteSpaces As Boolean _
, Optional ByVal ToEscapeWhiteSpace As String = "")
Dim DataObj As MSForms.DataObject
Dim cnn As ADODB.Connection
Dim rst As ADODB.Recordset
Dim auxEnum As String, bBracket As String, eBracket As String, auxRegex As String
Dim LastValue As Long
Set cnn = New ADODB.Connection
Set rst = New ADODB.Recordset
cnn.Open CnnStr & vbCrLf & DataS
rst.Open strSQL, cnn, adOpenForwardOnly, adLockReadOnly, adCmdText
If TreatIllegalNames Then bBracket = "[": eBracket = "]"
auxEnum = "Public Enum " & EnumName & vbCrLf
auxEnum = auxEnum & " [_First] = "
With rst
.MoveFirst
auxEnum = auxEnum & CStr(.Fields(EnumValueField)) & vbCrLf
Do While Not .EOF
auxEnum = auxEnum & " " & bBracket _
& IIf(EliminateWhiteSpaces, Replace(.Fields(EnumNameField), " ", ToEscapeWhiteSpace), .Fields(EnumNameField)) _
& eBracket & " = " & CStr(.Fields(EnumValueField)) & vbCrLf
LastValue = .Fields(EnumValueField)
.MoveNext
Loop
.Close
End With
auxEnum = auxEnum & " [_Last] = " & CStr(LastValue) & vbCrLf
auxEnum = auxEnum & "End Enum " & vbCrLf
Set rst = Nothing
cnn.Close
Set cnn = Nothing
Set DataObj = New MSForms.DataObject
With DataObj
.SetText auxEnum
.PutInClipboard
Debug.Print auxEnum
End With
Set DataObj = Nothing
End Sub
Just remember to pass the strSQL like that:
"SELECT EnumNameField, EnumValueField " & _
"FROM tblTarget WHERE EnumValueField Is NOT NULL " & _
"ORDER BY EnumValueField"
Usually, I use the EliminateWhiteSpaces boolean with ToEscapeWhiteSpace = "_", but is a personal preference.

For above "John Coleman"'s example I suggest to use next functions:
Function FruitType2Int(Fruit As FruitType)
FruitType2Int = Format("0", Fruit)
Debug.Print FruitType2Int
End Function
Function int2FruitString(i As Integer) As String
If i = FruitType2Int(Orange) Then
int2FruitString = "Orange"
ElseIf i = FruitType2Int(Plum) Then
int2FruitString = "Plum"
ElseIf i = FruitType2Int(Apple) Then
int2FruitString = "Apple"
Else
int2FruitString = "?"
End If
Debug.Print int2FruitString
End Function
Direct use of an Array indexes (without LBound() and etc.) may cause different resuts, depends on value in Option Base 1

Here is a function I wrote to get the enumeration member name from the value supplied. Additionally, it will list the enum names in a module, or list constant names in a module.
Public Enum CodeInfoEnum
ciEnums
ciConstants
End Enum
'---------------------------------------------------------------------------------------
' Procedure : CodeInfo
'
' Author : RMittelman#gmail.com
'
' Purpose : Searches a module for enumerations & constants
'
' History : 11/13/2022 Original version
' 11/14/2022 Added feature to list enums in the module
' 11/14/2022 Added feature to list constants inn the module
'
' Parameters :
'
' CodeType : A CodeInfoEnum member indicating Enums or Constants
'
' ModuleName : Optional. Name of module containing ItemName
' If missing, defaults to the module this function is called from
'
' ItemName : Optional. Name of the enumeration to examine
' If "?" or missing, returns a list of enumerations in the module
'
' EnumValue : optional. Value of the enumeration member wanted
' If missing, defaults to 0
' Ignored if CodType is not ciEnums
' Ignored if ItemName is missing or "?"
'
' Returns : - The text value of the enumeration value supplied; or
' - A list of enumeration names in the module; or
' - A list of constant names in the module
'
' Notes : Only searches in the module's Declarations section
'
'---------------------------------------------------------------------------------------
'
Public Function CodeInfo(CodeType As CodeInfoEnum, Optional ModuleName As Variant, Optional ItemName As String = "?", Optional EnumValueWanted As Variant) As String
Dim myApp As Access.Application
Dim compMod As Object
Dim modLines As Long
Dim procStart As Long
Dim procLines As Long
Dim idx As Long
Dim codeText As String
Dim foundItem As Boolean
Dim foundMember As Boolean
Dim tempVal As Variant
Dim enumVal As Long
CodeInfo = ""
Set myApp = CurrentProject.Application
If IsMissing(ModuleName) Then ModuleName = Application.VBE.ActiveCodePane.CodeModule
If ModuleName <> "" Then
Set compMod = myApp.VBE.ActiveVBProject.VBComponents(ModuleName).CodeModule
With compMod
' get declaration code
modLines = .CountOfLines
procStart = 1
procLines = .CountOfDeclarationLines
' search code text for enumeration(s)
idx = 0
foundItem = False
Do While (Not foundItem) And (idx <= procLines)
idx = idx + 1
codeText = .Lines(idx, 1)
' if ItemName is "?", build list of all desired items
If ItemName = "?" Then
Select Case CodeType
Case CodeInfoEnum.ciEnums
If codeText Like "*Enum *" Then
tempVal = Trim$(Mid$(codeText, InStr(1, codeText, "Enum", vbTextCompare) + 4))
CodeInfo = CodeInfo & "," & tempVal
End If
Case CodeInfoEnum.ciConstants
If codeText Like "*Const *" Then
tempVal = Mid$(codeText, InStr(1, codeText, "Const", vbTextCompare) + 6)
tempVal = Trim$(Left$(tempVal, InStr(1, tempVal, " ")))
CodeInfo = CodeInfo & "," & tempVal
End If
End Select
' otherwise, just see if we can find ItemName wanted
Else
foundItem = codeText Like "*Enum " & ItemName
End If
Loop
' if a specific Enum is found, look for the value wanted
If foundItem Then
enumVal = 0
foundMember = False
codeText = ""
Do While (Not foundMember) And (idx <= procLines) And (Not codeText Like "*End Enum")
idx = idx + 1
codeText = .Lines(idx, 1)
If codeText Like "*=*" Then
tempVal = Trim$(Split(codeText, "=")(1))
If IsNumeric(tempVal) Then enumVal = CLng(tempVal)
End If
If enumVal = EnumValueWanted Then
CodeInfo = Trim$(Split(codeText, "=")(0))
foundMember = True
End If
enumVal = enumVal + 1
Loop
End If
End With
If CodeInfo Like ",*" Then CodeInfo = Mid$(CodeInfo, 2)
End If
Set compMod = Nothing
Set myApp = Nothing
End Function

Any method which does not return a keyed collection or (preferably a scripting dictionary) will be prone to errors if the enumeration range is not a contiguous range, such as the case where you are using the enumeration to map to bits. My solution to this has been to develop a class of 'EnumerationDictionary' which allows arrays of the enumeration or the enumeration names to be returned, and name to be looked up given an enumeration and a string to be used to retrieve an enumeration. The example below is for colours in a word document and shows how to combine an internal enumeration with additional user defined values. Its a bit clunky but works very well.
Option Explicit
' A new enumeration for colour has been created to allow
' the inclusion of custom colours
' The wdColor enumeration values are the RGB vlaue as a decimal signed long
' For the hexadecimal representation the colours are BGR not RGB
' e.g. 0xXXBBGGRR not Ox00RRGGBB
Public Enum UserColour
Aqua = wdColorAqua '13421619 0x00CCCC33
Automatic = wdColorAutomatic '-16777216 0xFF000000
Black = wdColorBlack '0 0x00000000
Blue = wdColorBlue '16711680 0x00FF0000
BlueGray = wdColorBlueGray '10053222
BrightGreen = wdColorBrightGreen '65280 0x0000FF00
Brown = wdColorBrown '13209
DarkBlue = wdColorDarkBlue '8388608
DarkGreen = wdColorDarkGreen '13056
DarkRed = wdColorDarkRed '128 0x00000080
DarkTeal = wdColorDarkTeal '6697728
DarkYellow = wdColorDarkYellow '32896
Gold = wdColorGold '52479
Gray05 = wdColorGray05 '15987699
Gray10 = wdColorGray10 '15132390
Gray125 = wdColorGray125 '14737632
Gray15 = wdColorGray15 '14277081
Gray20 = wdColorGray20 '13421772
Gray25 = wdColorGray25 '12632256
Gray30 = wdColorGray30 '11776947
Gray35 = wdColorGray35 '10921638
Gray375 = wdColorGray375 '10526880
Gray40 = wdColorGray40 '10066329
Gray45 = wdColorGray45 '9211020
Gray50 = wdColorGray50 '8421504
Gray55 = wdColorGray55 '7566195
Gray60 = wdColorGray60 '6710886
Gray625 = wdColorGray625 '6316128
Gray65 = wdColorGray65 '5855577
Gray70 = wdColorGray70 '5000268
Gray75 = wdColorGray75 '4210752
Gray80 = wdColorGray80 '3355443
Gray85 = wdColorGray85 '2500134
Gray875 = wdColorGray875 '2105376
Gray90 = wdColorGray90 '1644825
Gray95 = wdColorGray95 '789516
Green = wdColorGreen '32768
Indigo = wdColorIndigo '10040115
Lavender = wdColorLavender '16751052
LightBlue = wdColorLightBlue '16737843
LightGreen = wdColorLightGreen '13434828
LightOrange = wdColorLightOrange '39423
LightTurquoise = wdColorLightTurquoise '16777164
LightYellow = wdColorLightYellow '10092543
Lime = wdColorLime '52377
OliveGreen = wdColorOliveGreen '13107
Orange = wdColorOrange '26367
PaleBlue = wdColorPaleBlue '16764057
Pink = wdColorPink '16711935
Plum = wdColorPlum '6697881
Red = wdColorRed '255 0x000000FF
Rose = wdColorRose '13408767
SeaGree = wdColorSeaGreen '6723891
SkyBlue = wdColorSkyBlue '16763904
Tan = wdColorTan '10079487
Teal = wdColorTeal '8421376
Turquoise = wdColorTurquoise '16776960
Violet = wdColorViolet '8388736
White = wdColorWhite '16777215 0x00FFFFFF
Yellow = wdColorYellow '65535
' Add custom s from this point onwards
HeadingBlue = &H993300 'RGB(0,51,153) 0x00993300
HeadingGreen = &H92D050 'RGB(146,208,80) 0x0050D092
End Enum
Private Type Properties
enum_gets_string As Scripting.Dictionary
string_gets_enum As Scripting.Dictionary
End Type
Private p As Properties
Private Sub Class_Initialize()
Set p.enum_gets_string = New Scripting.Dictionary
Set p.string_gets_enum = New Scripting.Dictionary
With p.enum_gets_string
.Add Key:=Aqua, Item:="Aqua"
.Add Key:=Automatic, Item:="Automatic"
.Add Key:=Black, Item:="Black"
.Add Key:=Blue, Item:="Blue"
.Add Key:=BlueGray, Item:="BlueGray"
.Add Key:=BrightGreen, Item:="BrightGreen"
.Add Key:=Brown, Item:="Brown"
.Add Key:=DarkBlue, Item:="DarkBlue"
.Add Key:=DarkGreen, Item:="DarkGreen"
.Add Key:=DarkRed, Item:="DarkRed"
.Add Key:=DarkTeal, Item:="DarkTeal"
.Add Key:=DarkYellow, Item:="DarkYellow"
.Add Key:=Gold, Item:="Gold"
.Add Key:=Gray05, Item:="Gray05"
.Add Key:=Gray10, Item:="Gray10"
.Add Key:=Gray125, Item:="Gray125"
.Add Key:=Gray15, Item:="Gray15"
.Add Key:=Gray20, Item:="Gray20"
.Add Key:=Gray25, Item:="Gray25"
.Add Key:=Gray30, Item:="Gray30"
.Add Key:=Gray35, Item:="Gray35"
.Add Key:=Gray375, Item:="Gray375"
.Add Key:=Gray40, Item:="Gray40"
.Add Key:=Gray45, Item:="Gray45"
.Add Key:=Gray50, Item:="Gray50"
.Add Key:=Gray55, Item:="Gray55"
.Add Key:=Gray60, Item:="Gray60"
.Add Key:=Gray625, Item:="Gray625"
.Add Key:=Gray65, Item:="Gray65"
.Add Key:=Gray70, Item:="Gray70"
.Add Key:=Gray75, Item:="Gray75"
.Add Key:=Gray80, Item:="Gray80"
.Add Key:=Gray85, Item:="Gray85"
.Add Key:=Gray875, Item:="Gray875"
.Add Key:=Gray90, Item:="Gray90"
.Add Key:=Gray95, Item:="Gray95"
.Add Key:=Green, Item:="Green"
.Add Key:=Indigo, Item:="Indigo"
.Add Key:=Lavender, Item:="Lavender"
.Add Key:=LightBlue, Item:="LightBlue"
.Add Key:=LightGreen, Item:="LightGreen"
.Add Key:=LightOrange, Item:="LightOrange"
.Add Key:=LightTurquoise, Item:="LightTurquoise"
.Add Key:=LightYellow, Item:="LightYellow"
.Add Key:=Lime, Item:="Lime"
.Add Key:=OliveGreen, Item:="OliveGreen"
.Add Key:=Orange, Item:="Orange"
.Add Key:=PaleBlue, Item:="PaleBlue"
.Add Key:=Pink, Item:="Pink"
.Add Key:=Plum, Item:="Plum"
.Add Key:=Red, Item:="Red"
.Add Key:=Rose, Item:="Rose"
.Add Key:=SeaGree, Item:="SeaGreen"
.Add Key:=SkyBlue, Item:="SkyBlue"
.Add Key:=Tan, Item:="Tan"
.Add Key:=Teal, Item:="Teal"
.Add Key:=Turquoise, Item:="Turquoise"
.Add Key:=Violet, Item:="Violet"
.Add Key:=White, Item:="White"
.Add Key:=Yellow, Item:="Yellow"
.Add Key:=HeadingBlue, Item:="HeadingBlue"
.Add Key:=HeadingGreen, Item:="HeadingGreen"
End With
' Now compile the reverse lookup
Set p.string_gets_enum = ReverseDictionary(p.enum_gets_string, "Reversing userCOLOUR.enum_gets_string")
End Sub
Public Property Get Items() As Variant
proj.Log.Trace s.locale, "{0}.Items", TypeName(Me)
Set Items = p.enum_gets_string.Items
End Property
Public Property Get Enums() As Variant
' Returns an array of Enums")
Set Enums = p.enum_gets_string.Keys
End Property
Public Property Get Item(ByVal this_enum As UserColour) As String
' Returns the Item for a given Enum")
Item = p.enum_gets_string.Item(this_enum)
End Property
' VBA will not allow a property/function Item of 'Enum' so we use
' ü (alt+0252) to sidestep the keyword clash for this property Item
Public Property Get Enüm(ByVal this_item As String) As UserColour
Enüm = p.string_gets_enum.Item(this_item)
End Property
Public Function HoldsEnum(ByVal this_enum As UserColour) As Boolean
HoldsEnum = p.enum_gets_string.Exists(this_enum)
End Function
Public Function LacksEnum(ByVal this_enum As UserColour) As Boolean
LacksEnum = Not Me.HoldsEnum(this_enum)
End Function
Public Function HoldsItem(ByVal this_item As String) As Boolean
HoldsItem = p.string_gets_enum.Exists(this_item)
End Function
Public Function LacksItem(ByVal this_item As String) As Boolean
LacksItem = Not Me.HoldsItem(this_item)
End Function
Public Function Count() As Long
Count = p.enum_gets_string.Count
End Function
Plus the following utility to reverse dictionaries.
Public Function ReverseDictionary(ByRef this_dict As Scripting.Dictionary) As Scripting.Dictionary
' Swaps keys for items in scripting.dictionaries.
' Keys and items must be unique which is usually the case for an enumeration
Dim my_key As Variant
Dim my_keys As Variant
Dim my_reversed_map As Scripting.Dictionary
Dim my_message As String
On Error GoTo key_is_not_unique
Set my_reversed_map = New Scripting.Dictionary
my_keys = this_dict.Keys
For Each my_key In my_keys
my_reversed_map.Add Key:=this_dict.Item(my_key), Item:=my_key
Next
Set ReverseDictionary = my_reversed_map
Exit Function
key_is_not_unique:
On Error GoTo 0
MsgBox _
Title:="Reverse Dictionary Error", _
Prompt:="The key and item are not unique Key:=" & my_key & " Item:= " & this_dict.Item(my_key), _
Buttons:=vbOKOnly
Set ReverseDictionary = Nothing
End Function

This answer is similar to some other answers here. In this example, "ExecutionMode" is the name of the enum.
Public Const ExecutionModes As String = "Development, Testing, Production"
Enum ExecutionMode
Development
Testing
Production
End Enum
Function EnumToString(lEnum As Long, sList As String) As String
' return list-item by enum
Dim aList
aList = Split(sList, ",")
aList = Application.Trim(aList)
EnumToString = aList(lEnum + 1)
End Function
Function StringToEnum(sItem As String, sList As String) As Long
' return listposition of string
' this only works for ordered, sequential enums
Dim vArray
vArray = Split(sList, ",")
vArray = Application.Trim(vArray)
Dim lPos As Long
lPos = Application.Match(sItem, vArray, 0) - 1
StringToEnum = lPos
End Function
Function ExecMode(sMode as String) As ExecutionMode
' return active mode of book, as enum
' Development, Testing, or Production
ExecMode = StringToEnum(sMode, ExecutionModes)
End Function
Function ExecModeStr(eMode as ExecutionMode)As String
' return mode as string
ExecModeStr = EnumToString(eMode, ExecutionModes)
End Function
Drawback: This only works for ordered, sequential enums, i.e., 0, 1,2,3,4, etc. If your enum values are anything else (e.g., 2, 4, 6 or &H80000000, &H80000002) then this solution will fail.
I'm sure it could be made to work with arbitrary numbers, but the trick is to minimize redundant typing and keep IntelliSense. I think some of the other answers here suffer from one or more of these problems:
Requires sequential items starting from 0 or 1,
or, Requires typing names or values or both twice,
or, Doesn't provide IntelliSense.
or, Requires Trusted Access to VBA
This solution already requires typing the labels twice. If a solution to arbitrary values requires typing the values twice then I don't consider very usable.
Creating your own enum structure seems promising. But the question is:
Which VBA data-structure will give you IntelliSense for items?
Type, Enum, Class, Module... aren't data-structures. Array, Collection, and Dictionary don't give IntelliSense for items. If we can find a data-structure will give you IntelliSense for items, then we have a viable solution to this question. I've read XML might help here.

The easiest way to look this up is by utilizing the Object Browser built into the VBA editor. If the enum is user-defined, you will need to execute the code in the VBA editor window that contains the enum to load it into memory, then you should be able to view it in the Object Browser by going to View -> Object Browser or by pressing F2. Once opened, you can view all of the enums and their constant values.
If the enum is built-in (not user-defined), you will need to look up the name of the enum in the Object Browser in order to obtain its values. Generally, these are prefixed with either Mso (Microsoft Office), Xl (only for Excel), or Vb (Visual Basic). For instance:
Mso:
Xl:
Vb:

This is easy if you use the Enum Builder in Code VBA (image below):
Give enum name and values,
Check Enum_ToString which adds code returning the enum value name string for a given enum value,
Check Declare Enum with First and Last to have these attributes added to the enum ... and press OK which inserts the code.
Now in the Immediate Window insert the single line block of code
For i = Fruit.[_First] To Fruit.[_Last]: ?Fruit_ToString(cint(i)): Next
When [Enter] returns the required list.

I realized that in some cases the code was returning the "End Enum" statement if I supplied a value 1 higher than the last enumeration member, so I fixed the code for that. Here is the latest code, including making it work with Access or Excel:
'---------------------------------------------------------------------------------------
' Procedure : CodeInfo
'
' Author : RMittelman#gmail.com
'
' Purpose : Searches a module for enumerations & constants
'
' History : 11/13/2022 Original version
' 11/14/2022 Added feature to list enums in the module
' 11/14/2022 Added feature to list constants inn the module
' 11/15/2022 Fixed error returning "End Enum" statement
'
' Parameters :
'
' CodeType : A CodeInfoEnum member indicating Enums or Constants
'
' ModuleName : Optional. Name of module containing ItemName
' If missing, defaults to the module this function is called from
'
' ItemName : Optional. Name of the enumeration to examine
' If "?" or missing, returns a list of enumerations in the module
'
' EnumValue : optional. Value of the enumeration member wanted
' If missing, defaults to 0
' Ignored if CodType is not ciEnums
' Ignored if ItemName is missing or "?"
'
' Returns : - The text value of the enumeration value supplied; or
' - A list of enumeration names in the module; or
' - A list of constant names in the module
'
' Notes : Only searches in the module's Declarations section
'
'---------------------------------------------------------------------------------------
'
Public Function CodeInfo(CodeType As CodeInfoEnum, Optional ModuleName As Variant, Optional ItemName As String = "?", Optional EnumValueWanted As Variant) As String
Dim compMod As Object
Dim modLines As Long
Dim procStart As Long
Dim procLines As Long
Dim idx As Long
Dim codeText As String
Dim foundItem As Boolean
Dim foundMember As Boolean
Dim tempVal As Variant
Dim enumVal As Long
CodeInfo = ""
If IsMissing(ModuleName) Then ModuleName = Application.VBE.ActiveCodePane.CodeModule
If ModuleName <> "" Then
Set compMod = Application.VBE.ActiveVBProject.VBComponents(ModuleName).CodeModule
With compMod
' get declaration code
modLines = .CountOfLines
procStart = 1
procLines = .CountOfDeclarationLines
' search code text for enumeration(s)
idx = 0
foundItem = False
Do While (Not foundItem) And (idx <= procLines)
idx = idx + 1
codeText = .Lines(idx, 1)
' if ItemName is "?", build list of all desired items
If ItemName = "?" Then
Select Case CodeType
Case CodeInfoEnum.ciEnums
If codeText Like "*Enum *" Then
tempVal = Trim$(Mid$(codeText, InStr(1, codeText, "Enum", vbTextCompare) + 4))
CodeInfo = CodeInfo & "," & tempVal
End If
Case CodeInfoEnum.ciConstants
If codeText Like "*Const *" Then
tempVal = Mid$(codeText, InStr(1, codeText, "Const", vbTextCompare) + 6)
tempVal = Trim$(Left$(tempVal, InStr(1, tempVal, " ")))
CodeInfo = CodeInfo & "," & tempVal
End If
End Select
' otherwise, just see if we can find ItemName wanted
Else
foundItem = codeText Like "*Enum " & ItemName
End If
Loop
' if a specific Enum is found, look for the value wanted
If foundItem Then
enumVal = 0
foundMember = False
codeText = ""
Do While (Not foundMember) And (idx <= procLines) And (Not codeText Like "*End Enum")
idx = idx + 1
codeText = .Lines(idx, 1)
' don't process the "End Enum" statement
If Not codeText Like "*End Enum" Then
' reset the next enum value if the member has a specific value
If codeText Like "*=*" Then
tempVal = Trim$(Split(codeText, "=")(1))
If IsNumeric(tempVal) Then enumVal = CLng(tempVal)
End If
If enumVal = EnumValueWanted Then
CodeInfo = Trim$(Split(codeText, "=")(0))
foundMember = True
End If
End If
enumVal = enumVal + 1
Loop
End If
End With
If CodeInfo Like ",*" Then CodeInfo = Mid$(CodeInfo, 2)
End If
Set compMod = Nothing
End Function

Related

Run-Time Error 40046: Statement too complex

I'm trying to write a code module using the InsertLines method, but am getting the
Statement too complex Error
My code boils down to this loop:
Dim extractorModule As VBComponent
With extractorModule.codeModule
Dim singItem As codeItem
Dim i As Long
For i = LBound(codeItems) To UBound(codeItems)
singItem = codeItems(i) 'array of private type with .value property
.InsertLines 5, singItem.value 'write to line 5
Next i
End With
Which loops through an array of custom codeItems, and writes their .value to a new module with .InsertLines
singItem.value is a base64 encoded string. If it is a short one, 100 characters say, like this string:
.code_content = "QXR0cmlidXRlIFZCX05hbWUgPSAic2ltcGxlTW9kdWxlIg0KUHJpdmF0ZSBhIEFzIExvbmcNCg=="
no problem. However I want a longer string, this one for example (19000 chars, contains newlines):
.code_content = "QXR0cmlidXRlIFZCX05hbWUgPSAicHJvamVjdENvbXByZXNzb3IiDQonQ29tcHJlc3NvciBtb2R1bGUsIGNvbXByZXNzZXMgYSBsb2FkIG9mIGZpbGVzIGludG8gc3RyaW5ncyB0byBleHBvcnQNCk9wdGlvbiBFeHBsaWNpdA0KUHJpdmF0ZSBUeXBlIGNvZGVJdGVtDQogICAgZXh0ZW5zaW9uIEFzIFN0cmluZw0KICAgIG1vZHVsZV9uYW1lIEFzIFN0cmluZw0KICAgIGNvZGVfY29udGVudCBBcyBTdHJpbmcNCkVuZCBUeXBlDQpQcml2YXRlIENvbnN0IFR5cGVCaW5hcnkgPSAxDQoNClB1YmxpYyBTdWIgY29tcHJlc3NQcm9qZWN0KFBhcmFtQXJyYXkgZmlsZW5hbWVzKCkpDQogICAgJ1N1YiB0byBjb252ZXJ0IHNlbGVjdGVkIGZpbGVzIGludG8gc2VsZi1leHRyYWN0aW5nIG1vZHVsZQ0KICAgICdJbnB1dDoNCiAgICAnICAgZmlsZW5hbWVzOiBhcnJheSBvZiBzdHJpbmdzIGJhc2VkIG9uIG5hbWVzIG9mIG1vZHVsZXMgaW4gcHJvamVjdA0KICAgIElmIE5vdCBwcm9qZWN0X2FjY2Vzc2libGUgVGhlbg0KICAgICAgICBNc2dCb3ggIkFjY2VzcyB0byBWQkEgcHJvamVjdCBpcyByZXN0cmljdGVkLCB0aGlzIHdvbid0IHdvcmshIg0KICAgICAgICBFeGl0IFN1Yg0KICAgIEVuZCBJZg0KDQogICAgRGltIGNvZGVJdGVtcygpIEFzIGNvZGVJdGVtDQogICAgRGltIGFycmF5U3QgQXMgTG9uZywg" & _
"YXJyYXlFbmQgQXMgTG9uZywgaSBBcyBMb25nDQogICAgYXJyYXlTdCA9IExCb3VuZChmaWxlbmFtZXMpDQogICAgYXJyYXlFbmQgPSBVQm91bmQoZmlsZW5hbWVzKQ0KICAgIFJlRGltIGNvZGVJdGVtcyhhcnJheVN0IFRvIGFycmF5RW5kKQ0KICAgIA0KICAgIERlYnVnLlByaW50ICJHZXR0aW5nIERlZmluaXRpb25zLi4uIg0KICAgIFdpdGggVGhpc1dvcmtib29rLlZCUHJvamVjdC5WQkNvbXBvbmVudHMNCiAgICAgICAgJ2xvb3AgdGhyb3VnaCBmaWxlcyBjb21wcmVzc2luZyB0aGVtIGludCA2NCBiaXQgc3RyaW5ncw0KICAgICAgICBGb3IgaSA9IGFycmF5U3QgVG8gYXJyYXlFbmQNCiAgICAgICAgICAgIGNvZGVJdGVtcyhpKSA9IG1vZHVsZURlZmluaXRpb24oZmlsZW5hbWVzKGkpKQ0KICAgICAgICBOZXh0IGkNCiAgICBFbmQgV2l0aA0KICAgIERlYnVnLlByaW50ICwgIkRlZmluaXRpb25zIHNhdmVkIg0KICAgICd3cml0ZSBzdHJpbmdzIHRvIHNrZWxldG9uIGZpbGUNCiAgICAgICAgRGVidWcuUHJpbnQgIldyaXRpbmcgZmlsZS4uLiINCiAgICB3cml0ZVNrZWxldG9uIGNvZGVJdGVtcw0KRGVidWcuUHJpbnQgIkNvbXBsZXRlIg0KRW5kIFN1Yg0KUHJpdmF0ZSBTdWIgd3JpdGVTa2VsZXRvbihjb2RlSXRlbXMoKSBBcyBjb2RlSXRlbSwgT3B0aW9uYWwgd2IgQXMgVmFyaWFudCwg" & _
"T3B0aW9uYWwgQnlSZWYgcHJvamVjdE5hbWUgQXMgU3RyaW5nID0gIm15UHJvamVjdCIpICcgLCBPcHRpb25hbCB3YiBBcyBWYXJpYW50KQ0KICAgIERpbSBpdGVtQ291bnQgQXMgTG9uZw0KICAgIGl0ZW1Db3VudCA9IFVCb3VuZChjb2RlSXRlbXMpIC0gTEJvdW5kKGNvZGVJdGVtcykgKyAxDQogICAgSWYgaXRlbUNvdW50IDwgMSBUaGVuIEV4aXQgU3ViDQogICAgDQogICAgRGltIGJvb2sgQXMgV29ya2Jvb2sNCiAgICBJZiBJc01pc3Npbmcod2IpIFRoZW4gU2V0IGJvb2sgPSBUaGlzV29ya2Jvb2sgRWxzZSBTZXQgYm9vayA9IHdiDQogICAgJ2NyZWF0ZSBzZWxmLWV4dHJhY3RpbmcgbW9kdWxlIGFuZCBzZXQgbmFtZQ0KDQogICAgRGltIGV4dHJhY3Rvck1vZHVsZSBBcyBWQkNvbXBvbmVudA0KICAgIFNldCBleHRyYWN0b3JNb2R1bGUgPSBib29rLlZCUHJvamVjdC5WQkNvbXBvbmVudHMuQWRkKHZiZXh0X2N0X1N0ZE1vZHVsZSkNCiAgICBleHRyYWN0b3JNb2R1bGUuTmFtZSA9IHByb2plY3ROYW1lICdtYXkgZXJyIGlmIGR1cGxpY2F0ZSAtIGNoYW5nZXMNCkRlYnVnLlByaW50ICwgIlByb2plY3QgZmlsZSBhZGRlZCINCiAgICAnd3JpdGUgY29kZSB0byBtb2R1bGUNCiAgICBEaW0gY29kZUluc2VydFBvaW50IEFzIExvbmcNCiAgICBjb2RlSW5zZXJ0UG9pbnQgPSBmaWxsTW9kdWxl" & _
"KGV4dHJhY3Rvck1vZHVsZS5jb2RlTW9kdWxlKSgwKSAneCBjb29yZA0KRGVidWcuUHJpbnQgLCAiUHJvamVjdCBza2VsZXRvbiB3cml0dGVuIg0KICAgICdhbW1lbmQgY29kZSB3aXRoIGNvZGVpdGVtcyBhbmQga2lsbGluZyBsaW5lDQogICAgJ1dpdGggZXh0cmFjdG9yTW9kdWxlLmNvZGVNb2R1bGUNCiAgICANCiAgICBEaW0gdiBBcyBjb2RlTW9kdWxlDQogICAgU2V0IHYgPSBleHRyYWN0b3JNb2R1bGUuY29kZU1vZHVsZQ0KICAgIHYuRGVsZXRlTGluZXMgY29kZUluc2VydFBvaW50DQogICAgRGltIHNpbmdJdGVtIEFzIGNvZGVJdGVtDQogICAgRGltIGkgQXMgTG9uZywgbG93ZXJWYWwgQXMgTG9uZywgdXBwZXJWYWwgQXMgTG9uZw0KICAgIGxvd2VyVmFsID0gTEJvdW5kKGNvZGVJdGVtcykNCiAgICB1cHBlclZhbCA9IFVCb3VuZChjb2RlSXRlbXMpDQoNCiAgICAgICAgDQogICAgJ2xvb3AgdGhyb3VnaCBhZGRpbmcgY29kZSBkZWZpbml0aW9ucw0KICAgIEZvciBpID0gbG93ZXJWYWwgVG8gdXBwZXJWYWwNCiAgICAgICAgc2luZ0l0ZW0gPSBjb2RlSXRlbXMoaSkNCiAgICAgICAgRGltIHMgQXMgU3RyaW5nOiBzID0gcHJpbnRmKFN0cmluZyg0LCB2YlRhYikgJiAiLmNvZGVfY29udGVudCA9IHswfSIsIHNpbmdJdGVtLmNvZGVfY29udGVudCkNCiAgICAgICAgRGVidWcuUHJpbnQg" & _
"LCAiRm9ybWF0dGVkIGZpbmUiDQoNCiAgICAgICAgRGVidWcuUHJpbnQgIm1hZGUgaXQgdG8iDQogICAgICAgIERlYnVnLlByaW50IHMNCiAgICAgICAgdi5JbnNlcnRMaW5lcyBjb2RlSW5zZXJ0UG9pbnQsIHMNCiAgICAgICAgRGVidWcuUHJpbnQgIm1hZGUgaXQgcGFzdCINCicgICAgICAgIC5JbnNlcnRMaW5lcyBjb2RlSW5zZXJ0UG9pbnQsIHByaW50ZihTdHJpbmcoNCwgdmJUYWIpICYgIi5tb2R1bGVfbmFtZSA9ICIiezB9IiIiLCBzaW5nSXRlbS5tb2R1bGVfbmFtZSkNCicgICAgICAgIC5JbnNlcnRMaW5lcyBjb2RlSW5zZXJ0UG9pbnQsIHByaW50ZihTdHJpbmcoNCwgdmJUYWIpICYgIi5leHRlbnNpb24gPSAiInswfSIiIiwgc2luZ0l0ZW0uZXh0ZW5zaW9uKQ0KJw0KJyAgICAgICAgLkluc2VydExpbmVzIGNvZGVJbnNlcnRQb2ludCwgcHJpbnRmKFN0cmluZygzLCB2YlRhYikgJiAiQ2FzZSB7MH0iLCBpdGVtQ291bnQpDQogICAgICAgIGl0ZW1Db3VudCA9IGl0ZW1Db3VudCAtIDENCkRlYnVnLlByaW50ICwgIkluc2VydGVkIGNvZGUgY29udGVudCBmb3IgZmlsZTogIjsgaQ0KICAgIE5leHQgaQ0KDQogICAgRGltIGtpbGxMaW5lIEFzIExvbmcgJ3BsYWNlIGZvciBhZGRpbmcgbGFzdCBiaXQgb2YgY29kZSB0byByZW1vdmUgc2VsZi1leHRyYWN0b3INCicgICAgLkZpbmQgInsx" & _
"fSIsIGtpbGxMaW5lLCAxLCAtMSwgLTENCicgICAgLlJlcGxhY2VMaW5lIGtpbGxMaW5lLCBSZXBsYWNlKC5MaW5lcyhraWxsTGluZSwgMSksICJ7MX0iLCBwcm9qZWN0TmFtZSkNCkRlYnVnLlByaW50ICwgIkluc2VydGVkIGtpbGxMaW5lIg0KICAgICdFbmQgV2l0aA0KICAgIA0KRW5kIFN1Yg0KDQoNClByaXZhdGUgRnVuY3Rpb24gbW9kdWxlRGVmaW5pdGlvbihtb2R1bGVOYW1lLCBPcHRpb25hbCB3YiBBcyBWYXJpYW50KSBBcyBjb2RlSXRlbQ0KICAgIERpbSBjb2RlTW9kdWxlIEFzIFZCQ29tcG9uZW50DQogICAgRGltIGJvb2sgQXMgV29ya2Jvb2sNCiAgICBEaW0gcmVzdWx0IEFzIGNvZGVJdGVtDQogICAgSWYgSXNNaXNzaW5nKHdiKSBUaGVuIFNldCBib29rID0gVGhpc1dvcmtib29rIEVsc2UgU2V0IGJvb2sgPSB3Yg0KICAgIFNldCBjb2RlTW9kdWxlID0gYm9vay5WQlByb2plY3QuVkJDb21wb25lbnRzKG1vZHVsZU5hbWUpDQogICAgJ2dldCBleHRlbnNpb24gYW5kIG5hbWUNCiAgICBTZWxlY3QgQ2FzZSBjb2RlTW9kdWxlLlR5cGUNCiAgICBDYXNlIHZiZXh0X2N0X1N0ZE1vZHVsZQ0KICAgICAgICByZXN1bHQuZXh0ZW5zaW9uID0gIi5iYXMiDQogICAgQ2FzZSB2YmV4dF9jdF9DbGFzc01vZHVsZQ0KICAgICAgICByZXN1bHQuZXh0ZW5zaW9uID0gIi5jbHMiDQogICAgQ2Fz" & _
"ZSB2YmV4dF9jdF9NU0Zvcm0NCiAgICAgICAgcmVzdWx0LmV4dGVuc2lvbiA9ICIuZnJtIg0KICAgIENhc2UgRWxzZQ0KICAgICAgICByZXN1bHQuZXh0ZW5zaW9uID0gIm1pc3NpbmciDQogICAgICAgIG1vZHVsZURlZmluaXRpb24gPSByZXN1bHQNCiAgICAgICAgRXhpdCBGdW5jdGlvbg0KICAgIEVuZCBTZWxlY3QNCiAgICANCiAgICByZXN1bHQubW9kdWxlX25hbWUgPSBjb2RlTW9kdWxlLk5hbWUNCiAgICAnc2F2ZSB0byB0ZW1wIHBhdGgNCiAgICBEaW0gdGVtcFBhdGggQXMgU3RyaW5nDQogICAgdGVtcFBhdGggPSBwcmludGYoInswfVx7MX17Mn0iLCBFbnZpcm9uJCgidGVtcCIpLCByZXN1bHQubW9kdWxlX25hbWUsIHJlc3VsdC5leHRlbnNpb24pDQogICAgY29kZU1vZHVsZS5FeHBvcnQgdGVtcFBhdGgNCiAgICBPbiBFcnJvciBHb1RvIHNhZmVFeGl0DQogICAgcmVzdWx0LmNvZGVfY29udGVudCA9IGNodW5raWZ5KFRvQmFzZTY0KHJlYWRCeXRlcyh0ZW1wUGF0aCkpKSAnZW5jb2RlIGFuZCBjaHVua2lmeQ0KICAgIA0Kc2FmZUV4aXQ6DQogICAgS2lsbCB0ZW1wUGF0aA0KICAgIG1vZHVsZURlZmluaXRpb24gPSByZXN1bHQNCiAgICBJZiBFcnIuTnVtYmVyIDw+IDAgVGhlbiBtb2R1bGVEZWZpbml0aW9uLmV4dGVuc2lvbiA9ICJtaXNzaW5nIg0KRW5kIEZ1bmN0aW9uDQoNClBy" & _
"aXZhdGUgRnVuY3Rpb24gcHJpbnRmKG1hc2sgQXMgU3RyaW5nLCBQYXJhbUFycmF5IHRva2VucygpKSBBcyBTdHJpbmcNCiAgICBEZWJ1Zy5QcmludCAsICIgLT4gRm9ybWF0dGluZyI7IExlbih0b2tlbnMoMCkpOyAiY2hhcnMgaW50byIsICIiIiI7IG1hc2s7ICIiIiINCiAgICBEaW0gaSBBcyBMb25nDQpPbiBFcnJvciBHb1RvIGJhZFByaW50DQogICAgRm9yIGkgPSAwIFRvIFVCb3VuZCh0b2tlbnMpDQogICAgICAgIG1hc2sgPSBSZXBsYWNlJChtYXNrLCAieyIgJiBpICYgIn0iLCB0b2tlbnMoaSkpDQogICAgTmV4dA0KICAgIHByaW50ZiA9IG1hc2sNCiAgICAgICAgRXhpdCBGdW5jdGlvbg0KYmFkUHJpbnQ6DQogICAgcHJpbnRmID0gbWFzaw0KICAgIERlYnVnLlByaW50IFN0cmluZygxMCwgIi0iKQ0KICAgIERlYnVnLlByaW50ICJQcmludEYgZXJyb3Igb24iLCB0b2tlbnMoMCkNCiAgICBEZWJ1Zy5QcmludCBTdHJpbmcoMTAsICItIikNCkVuZCBGdW5jdGlvbg0KDQpQcml2YXRlIEZ1bmN0aW9uIHByb2plY3RfYWNjZXNzaWJsZSgpIEFzIEJvb2xlYW4NCiAgICBPbiBFcnJvciBSZXN1bWUgTmV4dA0KICAgIFdpdGggVGhpc1dvcmtib29rLlZCUHJvamVjdA0KICAgICAgICBwcm9qZWN0X2FjY2Vzc2libGUgPSAuUHJvdGVjdGlvbiA9IHZiZXh0X3BwX25vbmUNCiAgICAgICAgcHJv" & _
"amVjdF9hY2Nlc3NpYmxlID0gcHJvamVjdF9hY2Nlc3NpYmxlIEFuZCBFcnIuTnVtYmVyID0gMA0KICAgIEVuZCBXaXRoDQpFbmQgRnVuY3Rpb24NCg0KUHJpdmF0ZSBGdW5jdGlvbiByZWFkQnl0ZXMoZmlsZSBBcyBTdHJpbmcpIEFzIEJ5dGUoKQ0KICBEaW0gaW5TdHJlYW0gQXMgT2JqZWN0DQogICcgQURPREIgc3RyZWFtIG9iamVjdCB1c2VkDQogIFNldCBpblN0cmVhbSA9IENyZWF0ZU9iamVjdCgiQURPREIuU3RyZWFtIikNCiAgJyBvcGVuIHdpdGggbm8gYXJndW1lbnRzIG1ha2VzIHRoZSBzdHJlYW0gYW4gZW1wdHkgY29udGFpbmVyDQogIGluU3RyZWFtLk9wZW4NCiAgaW5TdHJlYW0uVHlwZSA9IFR5cGVCaW5hcnkNCiAgaW5TdHJlYW0uTG9hZEZyb21GaWxlIChmaWxlKQ0KICByZWFkQnl0ZXMgPSBpblN0cmVhbS5SZWFkKCkNCkVuZCBGdW5jdGlvbg0KUHJpdmF0ZSBGdW5jdGlvbiBjaHVua2lmeShCeVZhbCBiYXNlIEFzIFN0cmluZywgT3B0aW9uYWwgQnlWYWwgc3RyaW5nTGVuZ3RoIEFzIExvbmcgPSA5MDApIEFzIFN0cmluZw0KJ3NwbGl0cyBhIHN0cmluZyBhdCBldmVyeSBzdHJpbmdMZW5ndGggY2hhcmFjaHRlcnMgYW5kIGRlbGltaXRzDQonMTAyNCBpcyBtYXggY2hhcnMgaW4gYSBsaW5lDQpjaHVua2lmeSA9IEpvaW4oU3BsaXRTdHJpbmcoYmFzZSwgc3RyaW5nTGVuZ3Ro" & _
"KSwgIiAmIF8iICYgdmJDckxmKQ0KRW5kIEZ1bmN0aW9uDQoNClByaXZhdGUgRnVuY3Rpb24gU3BsaXRTdHJpbmcoQnlWYWwgc3RyIEFzIFN0cmluZywgQnlWYWwgbnVtT2ZDaGFyIEFzIExvbmcpIEFzIFN0cmluZygpDQogICAgRGltIHNBcnIoKSBBcyBTdHJpbmcNCiAgICBEaW0gbkNvdW50IEFzIExvbmcNCiAgICBSZURpbSBzQXJyKChMZW4oc3RyKSAtIDEpIFwgbnVtT2ZDaGFyKQ0KICAgIERvIFdoaWxlIExlbihzdHIpDQogICAgICAgIHNBcnIobkNvdW50KSA9ICIiIiIgJiBMZWZ0JChzdHIsIG51bU9mQ2hhcikgJiAiIiIiDQogICAgICAgIHN0ciA9IE1pZCQoc3RyLCBudW1PZkNoYXIgKyAxKQ0KICAgICAgICBuQ291bnQgPSBuQ291bnQgKyAxDQogICAgTG9vcA0KICAgIFNwbGl0U3RyaW5nID0gc0Fycg0KRW5kIEZ1bmN0aW9uDQoNClByaXZhdGUgRnVuY3Rpb24gVG9CYXNlNjQoZGF0YSgpIEFzIEJ5dGUpIEFzIFN0cmluZw0KICBEaW0gYjY0KDAgVG8gNjMpIEFzIEJ5dGUsIHN0cigpIEFzIEJ5dGUsIGkmLCBqJiwgdiYsIG4mDQogIG4gPSBVQm91bmQoZGF0YSkgLSBMQm91bmQoZGF0YSkgKyAxDQogIElmIG4gVGhlbiBFbHNlIEV4aXQgRnVuY3Rpb24NCg0KICBzdHIgPSAiQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3" & _
"ODkrLyINCiAgRm9yIGkgPSAwIFRvIDEyNyBTdGVwIDINCiAgICBiNjQoaSBcIDIpID0gc3RyKGkpDQogIE5leHQNCg0KICBSZURpbSBzdHIoMCBUbyAoKG4gKyAyKSBcIDMpICogOCAtIDEpDQoNCiAgRm9yIGkgPSBMQm91bmQoZGF0YSkgVG8gVUJvdW5kKGRhdGEpIC0gKG4gTW9kIDMpIFN0ZXAgMw0KICAgIHYgPSBkYXRhKGkpICogNjU1MzYgKyBkYXRhKGkgKyAxKSAqIDI1NiYgKyBkYXRhKGkgKyAyKQ0KICAgIHN0cihqKSA9IGI2NCh2IFwgMjYyMTQ0KQ0KICAgIHN0cihqICsgMikgPSBiNjQoKHYgXCA0MDk2KSBNb2QgNjQpDQogICAgc3RyKGogKyA0KSA9IGI2NCgodiBcIDY0KSBNb2QgNjQpDQogICAgc3RyKGogKyA2KSA9IGI2NCh2IE1vZCA2NCkNCiAgICBqID0gaiArIDgNCiAgTmV4dA0KDQogIElmIG4gTW9kIDMgPSAyIFRoZW4NCiAgICB2ID0gZGF0YShuIC0gMikgKiAyNTYmICsgZGF0YShuIC0gMSkNCiAgICBzdHIoaikgPSBiNjQoKHYgXCAxMDI0JikgTW9kIDY0KQ0KICAgIHN0cihqICsgMikgPSBiNjQoKHYgXCAxNikgTW9kIDY0KQ0KICAgIHN0cihqICsgNCkgPSBiNjQoKHYgKiA0KSBNb2QgNjQpDQogICAgc3RyKGogKyA2KSA9IDYxICcgPSAnDQogIEVsc2VJZiBuIE1vZCAzID0gMSBUaGVuDQogICAgdiA9IGRhdGEobiAtIDEpDQogICAgc3RyKGopID0gYjY0KHYgXCA0" & _
"IE1vZCA2NCkNCiAgICBzdHIoaiArIDIpID0gYjY0KHYgKiAxNiBNb2QgNjQpDQogICAgc3RyKGogKyA0KSA9IDYxICcgPSAnDQogICAgc3RyKGogKyA2KSA9IDYxICcgPSAnDQogIEVuZCBJZg0KDQogIFRvQmFzZTY0ID0gc3RyDQpFbmQgRnVuY3Rpb24NCg0KUHJpdmF0ZSBGdW5jdGlvbiBmaWxsTW9kdWxlKGNvZGVTZWN0aW9uIEFzIGNvZGVNb2R1bGUpIEFzIExvbmcoKQ0KV2l0aCBjb2RlU2VjdGlvbg0KLkluc2VydExpbmVzIDEsICJPcHRpb24gRXhwbGljaXQiDQouSW5zZXJ0TGluZXMgMiwgIlByaXZhdGUgVHlwZSBjb2RlSXRlbSINCi5JbnNlcnRMaW5lcyAzLCAiICAgIGV4dGVuc2lvbiBBcyBTdHJpbmciDQouSW5zZXJ0TGluZXMgNCwgIiAgICBtb2R1bGVfbmFtZSBBcyBTdHJpbmciDQouSW5zZXJ0TGluZXMgNSwgIiAgICBjb2RlX2NvbnRlbnQgQXMgU3RyaW5nIg0KLkluc2VydExpbmVzIDYsICJFbmQgVHlwZSINCi5JbnNlcnRMaW5lcyA3LCAiIg0KLkluc2VydExpbmVzIDgsICJQcml2YXRlIENvbnN0IFR5cGVCaW5hcnkgPSAxIg0KLkluc2VydExpbmVzIDksICJQcml2YXRlIENvbnN0IEZvclJlYWRpbmcgPSAxLCBGb3JXcml0aW5nID0gMiwgRm9yQXBwZW5kaW5nID0gOCINCi5JbnNlcnRMaW5lcyAxMCwgIiINCi5JbnNlcnRMaW5lcyAxMSwgIlByaXZhdGUgRnVuY3Rpb24g" & _
"Z2V0Q29kZURlZmluaXRpb24oaXRlbU5vIEFzIExvbmcpIEFzIGNvZGVJdGVtIg0KLkluc2VydExpbmVzIDEyLCAiICAgIFdpdGggZ2V0Q29kZURlZmluaXRpb24iDQouSW5zZXJ0TGluZXMgMTMsICIgICAgICAgIFNlbGVjdCBDYXNlIGl0ZW1ObyINCi5JbnNlcnRMaW5lcyAxNCwgIiAgICAgICAgICAgICd7MH0iDQouSW5zZXJ0TGluZXMgMTUsICIgICAgICAgIENhc2UgRWxzZSINCi5JbnNlcnRMaW5lcyAxNiwgIiAgICAgICAgICAgIC5leHRlbnNpb24gPSAiIm1pc3NpbmciIiINCi5JbnNlcnRMaW5lcyAxNywgIiAgICAgICAgRW5kIFNlbGVjdCINCi5JbnNlcnRMaW5lcyAxOCwgIiAgICBFbmQgV2l0aCINCi5JbnNlcnRMaW5lcyAxOSwgIkVuZCBGdW5jdGlvbiINCi5JbnNlcnRMaW5lcyAyMCwgIiINCi5JbnNlcnRMaW5lcyAyMSwgIlB1YmxpYyBTdWIgRXh0cmFjdCgpIg0KLkluc2VydExpbmVzIDIyLCAiICAgIERpbSBjb2RlX21vZHVsZSBBcyBjb2RlSXRlbSINCi5JbnNlcnRMaW5lcyAyMywgIiAgICBEaW0gc2F2ZWRQYXRoIEFzIFN0cmluZywgYmFzZVBhdGggQXMgU3RyaW5nIg0KLkluc2VydExpbmVzIDI0LCAiICAgIERpbSBpIEFzIExvbmciDQouSW5zZXJ0TGluZXMgMjUsICIgICAgJ2NoZWNrIGlmIHZicHJvamVjdCBhY2Nlc3NpYmxlIg0KLkluc2VydExpbmVzIDI2LCAiICAg" & _
"IElmIE5vdCBwcm9qZWN0X2FjY2Vzc2libGUgVGhlbiINCi5JbnNlcnRMaW5lcyAyNywgIiAgICAgICAgTXNnQm94ICIiVGhlIFZCQSBwcm9qZWN0IGNhbm5vdCBiZSBhY2Nlc3NlZCBwcm9ncmFtbWF0aWNhbGx5IiIiDQouSW5zZXJ0TGluZXMgMjgsICIgICAgICAgIEV4aXQgU3ViIg0KLkluc2VydExpbmVzIDI5LCAiICAgIEVuZCBJZiINCi5JbnNlcnRMaW5lcyAzMCwgIiAgICAnY2hlY2sgaWYgdGVtcCBmb2xkZXIgYWNlc3NpYmxlIg0KLkluc2VydExpbmVzIDMxLCAiICAgIGkgPSAwIg0KLkluc2VydExpbmVzIDMyLCAiICAgIGJhc2VQYXRoID0gRW52aXJvbigiIlRlbXAiIikgJiAiIlwiIiINCi5JbnNlcnRMaW5lcyAzMywgIiAgICBEbyBXaGlsZSBUcnVlIg0KLkluc2VydExpbmVzIDM0LCAiICAgICAgICBpID0gaSArIDEiDQouSW5zZXJ0TGluZXMgMzUsICIgICAgICAgIGNvZGVfbW9kdWxlID0gZ2V0Q29kZURlZmluaXRpb24oaSkiDQouSW5zZXJ0TGluZXMgMzYsICIgICAgICAgIElmIGNvZGVfbW9kdWxlLmV4dGVuc2lvbiA9ICIibWlzc2luZyIiIFRoZW4iDQouSW5zZXJ0TGluZXMgMzcsICIgICAgICAgICAgICBFeGl0IERvIg0KLkluc2VydExpbmVzIDM4LCAiICAgICAgICBFbHNlIg0KLkluc2VydExpbmVzIDM5LCAiICAgICAgICAgICAgc2F2ZWRQYXRoID0gY3JlYXRlRmls" & _
"ZShjb2RlX21vZHVsZSwgYmFzZVBhdGgpIg0KLkluc2VydExpbmVzIDQwLCAiICAgICAgICAgICAgaW1wb3J0RmlsZSBzYXZlZFBhdGgiDQouSW5zZXJ0TGluZXMgNDEsICIgICAgICAgICAgICBLaWxsIHNhdmVkUGF0aCINCi5JbnNlcnRMaW5lcyA0MiwgIiAgICAgICAgRW5kIElmIg0KLkluc2VydExpbmVzIDQzLCAiICAgIExvb3AiDQouSW5zZXJ0TGluZXMgNDQsICIgICAgcmVtb3ZlbW9kdWxlICIiezF9IiIiDQouSW5zZXJ0TGluZXMgNDUsICJFbmQgU3ViIg0KLkluc2VydExpbmVzIDQ2LCAiIg0KLkluc2VydExpbmVzIDQ3LCAiUHJpdmF0ZSBGdW5jdGlvbiBwcm9qZWN0X2FjY2Vzc2libGUoKSBBcyBCb29sZWFuIg0KLkluc2VydExpbmVzIDQ4LCAiICAgIE9uIEVycm9yIFJlc3VtZSBOZXh0Ig0KLkluc2VydExpbmVzIDQ5LCAiICAgIFdpdGggVGhpc1dvcmtib29rLlZCUHJvamVjdCINCi5JbnNlcnRMaW5lcyA1MCwgIiAgICAgICAgcHJvamVjdF9hY2Nlc3NpYmxlID0gLlByb3RlY3Rpb24gPSB2YmV4dF9wcF9ub25lIg0KLkluc2VydExpbmVzIDUxLCAiICAgICAgICBwcm9qZWN0X2FjY2Vzc2libGUgPSBwcm9qZWN0X2FjY2Vzc2libGUgQW5kIEVyci5OdW1iZXIgPSAwIg0KLkluc2VydExpbmVzIDUyLCAiICAgIEVuZCBXaXRoIg0KLkluc2VydExpbmVzIDUzLCAiRW5kIEZ1bmN0" & _
"aW9uIg0KLkluc2VydExpbmVzIDU0LCAiIg0KLkluc2VydExpbmVzIDU1LCAiUHJpdmF0ZSBGdW5jdGlvbiBjcmVhdGVGaWxlKGRlZmluaXRpb24gQXMgY29kZUl0ZW0sIGZpbGVQYXRoIEFzIFZhcmlhbnQpIEFzIFN0cmluZyINCi5JbnNlcnRMaW5lcyA1NiwgIiAgICBEaW0gY29kZUluZGV4IEFzIExvbmciDQouSW5zZXJ0TGluZXMgNTcsICIgICAgRGltIG5ld0ZpbGVPYmogQXMgT2JqZWN0Ig0KLkluc2VydExpbmVzIDU4LCAiICAgIFNldCBuZXdGaWxlT2JqID0gQ3JlYXRlT2JqZWN0KCIiQURPREIuU3RyZWFtIiIpIg0KLkluc2VydExpbmVzIDU5LCAiICAgIG5ld0ZpbGVPYmouVHlwZSA9IFR5cGVCaW5hcnkiDQouSW5zZXJ0TGluZXMgNjAsICIgICAgJ09wZW4gdGhlIHN0cmVhbSBhbmQgd3JpdGUgYmluYXJ5IGRhdGEiDQouSW5zZXJ0TGluZXMgNjEsICIgICAgbmV3RmlsZU9iai5PcGVuIg0KLkluc2VydExpbmVzIDYyLCAiICAgICdjcmVhdGUgZmlsZSBmcm9tIHg2NCBzdHJpbmciDQouSW5zZXJ0TGluZXMgNjMsICIgICAgV2l0aCBkZWZpbml0aW9uIg0KLkluc2VydExpbmVzIDY0LCAiICAgICAgICBEaW0gYnl0ZXMoKSBBcyBCeXRlIg0KLkluc2VydExpbmVzIDY1LCAiICAgICAgICBEaW0gZnVsbFBhdGggQXMgU3RyaW5nIg0KLkluc2VydExpbmVzIDY2LCAiICAgICAgICBmdWxs" & _
"UGF0aCA9IGZpbGVQYXRoICYgLm1vZHVsZV9uYW1lICYgLmV4dGVuc2lvbiINCi5JbnNlcnRMaW5lcyA2NywgIiAgICAgICAgYnl0ZXMgPSBGcm9tQmFzZTY0KC5jb2RlX2NvbnRlbnQpIg0KLkluc2VydExpbmVzIDY4LCAiICAgICAgICBuZXdGaWxlT2JqLldyaXRlIGJ5dGVzIg0KLkluc2VydExpbmVzIDY5LCAiICAgICAgICBuZXdGaWxlT2JqLlNhdmVUb0ZpbGUgZnVsbFBhdGgsIEZvcldyaXRpbmciDQouSW5zZXJ0TGluZXMgNzAsICIgICAgICAgIGNyZWF0ZUZpbGUgPSBmdWxsUGF0aCINCi5JbnNlcnRMaW5lcyA3MSwgIiAgICBFbmQgV2l0aCINCi5JbnNlcnRMaW5lcyA3MiwgIkVuZCBGdW5jdGlvbiINCi5JbnNlcnRMaW5lcyA3MywgIiINCi5JbnNlcnRMaW5lcyA3NCwgIlByaXZhdGUgU3ViIGltcG9ydEZpbGUoZmlsZVBhdGggQXMgU3RyaW5nKSINCi5JbnNlcnRMaW5lcyA3NSwgIiAgICBUaGlzV29ya2Jvb2suVkJQcm9qZWN0LlZCQ29tcG9uZW50cy5JbXBvcnQgZmlsZVBhdGgiDQouSW5zZXJ0TGluZXMgNzYsICJFbmQgU3ViIg0KLkluc2VydExpbmVzIDc3LCAiIg0KLkluc2VydExpbmVzIDc4LCAiUHJpdmF0ZSBGdW5jdGlvbiByZW1vdmVtb2R1bGUobW9kdWxlTmFtZSBBcyBTdHJpbmcpIEFzIEJvb2xlYW4iDQouSW5zZXJ0TGluZXMgNzksICIgICAgT24gRXJyb3IgUmVzdW1l" & _
"IE5leHQiDQouSW5zZXJ0TGluZXMgODAsICIgICAgV2l0aCBUaGlzV29ya2Jvb2suVkJQcm9qZWN0LlZCQ29tcG9uZW50cyINCi5JbnNlcnRMaW5lcyA4MSwgIiAgICAgICAgLlJlbW92ZSAuSXRlbShtb2R1bGVOYW1lKSINCi5JbnNlcnRMaW5lcyA4MiwgIiAgICBFbmQgV2l0aCINCi5JbnNlcnRMaW5lcyA4MywgIiAgICByZW1vdmVtb2R1bGUgPSBOb3QgKEVyci5OdW1iZXIgPSA5KSINCi5JbnNlcnRMaW5lcyA4NCwgIkVuZCBGdW5jdGlvbiINCi5JbnNlcnRMaW5lcyA4NSwgIiINCi5JbnNlcnRMaW5lcyA4NiwgIlByaXZhdGUgRnVuY3Rpb24gRnJvbUJhc2U2NChUZXh0IEFzIFN0cmluZykgQXMgQnl0ZSgpIg0KLkluc2VydExpbmVzIDg3LCAiICAgIERpbSBPdXQoKSBBcyBCeXRlIg0KLkluc2VydExpbmVzIDg4LCAiICAgIERpbSBiNjQoMCBUbyAyNTUpIEFzIEJ5dGUsIHN0cigpIEFzIEJ5dGUsIGkmLCBqJiwgdiYsIGIwJiwgYjEmLCBiMiYsIGIzJiINCi5JbnNlcnRMaW5lcyA4OSwgIiAgICBPdXQgPSAiIiIiIg0KLkluc2VydExpbmVzIDkwLCAiICAgIElmIExlbihUZXh0KSBUaGVuIEVsc2UgRXhpdCBGdW5jdGlvbiINCi5JbnNlcnRMaW5lcyA5MSwgIiINCi5JbnNlcnRMaW5lcyA5MiwgIiAgICBzdHIgPSAiIiBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY2RlZmdoaWprbG1u" & _
"b3BxcnN0dXZ3eHl6MDEyMzQ1Njc4OSsvIiIiDQouSW5zZXJ0TGluZXMgOTMsICIgICAgRm9yIGkgPSAyIFRvIFVCb3VuZChzdHIpIFN0ZXAgMiINCi5JbnNlcnRMaW5lcyA5NCwgIiAgICAgICAgYjY0KHN0cihpKSkgPSBpIFwgMiINCi5JbnNlcnRMaW5lcyA5NSwgIiAgICBOZXh0Ig0KLkluc2VydExpbmVzIDk2LCAiIg0KLkluc2VydExpbmVzIDk3LCAiICAgIFJlRGltIE91dCgwIFRvICgoTGVuKFRleHQpICsgMykgXCA0KSAqIDMgLSAxKSINCi5JbnNlcnRMaW5lcyA5OCwgIiAgICBzdHIgPSBUZXh0ICYgU3RyaW5nJCgyLCAwKSINCi5JbnNlcnRMaW5lcyA5OSwgIiINCi5JbnNlcnRMaW5lcyAxMDAsICIgICAgRm9yIGkgPSAwIFRvIFVCb3VuZChzdHIpIC0gNyBTdGVwIDIiDQouSW5zZXJ0TGluZXMgMTAxLCAiICAgICAgICBiMCA9IGI2NChzdHIoaSkpIg0KLkluc2VydExpbmVzIDEwMiwgIiINCi5JbnNlcnRMaW5lcyAxMDMsICIgICAgICAgIElmIGIwIFRoZW4iDQouSW5zZXJ0TGluZXMgMTA0LCAiICAgICAgICAgICAgYjEgPSBiNjQoc3RyKGkgKyAyKSkiDQouSW5zZXJ0TGluZXMgMTA1LCAiICAgICAgICAgICAgYjIgPSBiNjQoc3RyKGkgKyA0KSkiDQouSW5zZXJ0TGluZXMgMTA2LCAiICAgICAgICAgICAgYjMgPSBiNjQoc3RyKGkgKyA2KSkiDQouSW5zZXJ0TGluZXMgMTA3LCAi" & _
"ICAgICAgICAgICAgdiA9IGIwICogMjYyMTQ0ICsgYjEgKiA0MDk2JiArIGIyICogNjQmICsgYjMgLSAyNjYzMDUiDQouSW5zZXJ0TGluZXMgMTA4LCAiICAgICAgICAgICAgT3V0KGopID0gdiBcIDY1NTM2Ig0KLkluc2VydExpbmVzIDEwOSwgIiAgICAgICAgICAgIE91dChqICsgMSkgPSAodiBcIDI1NiYpIE1vZCAyNTYiDQouSW5zZXJ0TGluZXMgMTEwLCAiICAgICAgICAgICAgT3V0KGogKyAyKSA9IHYgTW9kIDI1NiINCi5JbnNlcnRMaW5lcyAxMTEsICIgICAgICAgICAgICBqID0gaiArIDMiDQouSW5zZXJ0TGluZXMgMTEyLCAiICAgICAgICAgICAgaSA9IGkgKyA2Ig0KLkluc2VydExpbmVzIDExMywgIiAgICAgICAgRW5kIElmIg0KLkluc2VydExpbmVzIDExNCwgIiAgICBOZXh0Ig0KLkluc2VydExpbmVzIDExNSwgIiINCi5JbnNlcnRMaW5lcyAxMTYsICIgICAgSWYgYjIgPSAwIFRoZW4iDQouSW5zZXJ0TGluZXMgMTE3LCAiICAgICAgICBPdXQoaiAtIDMpID0gKHYgKyA2NSkgXCA2NTUzNiINCi5JbnNlcnRMaW5lcyAxMTgsICIgICAgICAgIGogPSBqIC0gMiINCi5JbnNlcnRMaW5lcyAxMTksICIgICAgRWxzZUlmIGIzID0gMCBUaGVuIg0KLkluc2VydExpbmVzIDEyMCwgIiAgICAgICAgT3V0KGogLSAzKSA9ICh2ICsgMSkgXCA2NTUzNiINCi5JbnNlcnRMaW5lcyAxMjEsICIg" & _
"ICAgICAgIE91dChqIC0gMikgPSAoKHYgKyAxKSBcIDI1NiYpIE1vZCAyNTYiDQouSW5zZXJ0TGluZXMgMTIyLCAiICAgICAgICBqID0gaiAtIDEiDQouSW5zZXJ0TGluZXMgMTIzLCAiICAgIEVuZCBJZiINCi5JbnNlcnRMaW5lcyAxMjQsICIiDQouSW5zZXJ0TGluZXMgMTI1LCAiICAgIFJlRGltIFByZXNlcnZlIE91dChqIC0gMSkiDQouSW5zZXJ0TGluZXMgMTI2LCAiICAgIEZyb21CYXNlNjQgPSBPdXQiDQouSW5zZXJ0TGluZXMgMTI3LCAiRW5kIEZ1bmN0aW9uIg0KRGltIHJlc3VsdCgwIFRvIDEpIEFzIExvbmcNCklmIC5GaW5kKCJ7MH0iLCByZXN1bHQoMCksIHJlc3VsdCgxKSwgLTEsIC0xKSBUaGVuICdzZWFyY2ggZm9yIHBvaW50IHRvIGluc2VydCBsaW5lcw0KICAgIGZpbGxNb2R1bGUgPSByZXN1bHQNCkVsc2UNCiAgICByZXN1bHQoMCkgPSAwDQogICAgcmVzdWx0KDEpID0gMA0KICAgIGZpbGxNb2R1bGUgPSByZXN1bHQNCkVuZCBJZg0KRW5kIFdpdGgNCkVuZCBGdW5jdGlvbg0KDQoNCg0K"
According to the docs, linefeed character vbCrLf should just make code on separate lines (what I want), so that shouldn't be causing the error.
However the length of string is not the problem either, as if .value = String(19000,"a") I have no issues. What's the cause of this error and how do I get around it?
Update
Something more re-createable:
Sub testAdd()
Dim codeStuff As codeModule
On Error Resume Next
Set codeStuff = ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_StdModule).codeModule
'check for vbProj access
If Err.Number <> 0 Then MsgBox "Access to VBProject disabled": Exit Sub
On Error GoTo 0
'try to add code
With codeStuff
Dim i As Long
For i = 1 To 3
'i = 1 fails for me
On Error Resume Next
.InsertLines 1, exampleString(i) 'causes the error
Debug.Print "Case"; i; IIf(Err.Number = 0, " suceeded", " failed with err:" & Err.Number)
On Error GoTo -1
Next i
End With
ThisWorkbook.VBProject.VBComponents.Remove codeStuff.Parent
End Sub
Function exampleString(stringType As Long) As String
Dim result As String
Select Case stringType
Case 1 'lots of linefeed
Dim bit As Long
For bit = 1 To 19
result = result & """" & String(1000, "a") & """ & _" & vbCrLf
Next bit
result = result & """" & String(1000, "a") & """"
Case 2 'long string
result = String(20000, "a")
Case Else 'short string
result = String(100, "a")
End Select
exampleString = result
End Function
There is a limit of "prolonging" lines with _ at the end, try building your string part by part:
.code_content = "first part"
.code_content = .code_content & "second part"

How to figure out if Variant contains certain text + number in format text###

I am having some trouble identifying whether a Variant/String stored in iq_Array is in the format I would like it to be. I want my code to execute only if iq_Array = iq_### with iq_### meaning that it must include the words iq_ and must be followed by one, two, or three numbers. All these are fine:
iq_9
iq_99
iq_999
But there are not:
iq_9, or iq_9999
iq_23,iq_5
iq_
iq _3
Any help here would be appreciated.
You should be able to use a wildcard #(numerical wildcard) like this:
if iq_Array(i) like "iq_#" Or iq_Array(i) like "iq_##" or iq_Array(i) like "iq_###" then
... code ....
end if
if iq_Array is not string you probably can wrap it in Cstr: Cstr(iq_Array(i))
You could use the Split Function
Extract Element Function
Function EXTRACTELEMENT(Txt As String, n, Separator As String) As String
EXTRACTELEMENT = Split(Application.Trim(Txt), Separator)(n - 1)
End Function
Code
If ele1 or ele2 of an coll of the collection is NOK, then that coll is Not OK, otherwise, it is OK
Dim coll As Collection
Dim i As Long
Dim stemp As String, ele1 As String, ele2 As String
Set coll = New Collection
coll.Add "iq_9"
coll.Add "iq_99"
coll.Add "iq_999"
coll.Add "iq_9,"
coll.Add "iq_23,iq_5"
coll.Add "iq_"
coll.Add "iq _3"
If coll.Count > 0 Then
For i = 1 To coll.Count
stemp = stemp & coll(i) & ";"
ele1 = EXTRACTELEMENT(coll(i), 1, "_")
ele2 = EXTRACTELEMENT(coll(i), 2, "_")
valueprint = valueprint & vbNewLine & coll(i) & " ele1: " & ele1 & " ele2: " & ele2 'For Debugging test
If ele1 = "iq" Then
Debug.Print vbNewLine & coll(i) & "Ele1 OK"
Else
Debug.Print vbNewLine & coll(i) & "Ele1 NOK"
End If
If IsNumeric(ele2) = True And InStr(1, ele2, ",") = 0 Then
Debug.Print vbNewLine & coll(i) & "Ele2 OK"
Else
Debug.Print vbNewLine & coll(i) & "Ele2 NOK"
End If
Next i
Debug.Print stemp & valueprint
End If
Set coll = Nothing
Optional, description of the UDF EXTRACELEMENT
Sub DescribeFunction()
Dim FuncName As String
Dim FuncDesc As String
Dim Category As String
Dim ArgDesc(1 To 3) As String
FuncName = "EXTRACTELEMENT"
FuncDesc = "Returns the nth element of a string that uses a separator character/Retorna o enésimo elemento da string que usa um caractér separador."
Category = 7 'Text category
ArgDesc(1) = "String that contains the elements/String que contém o elemento"
ArgDesc(2) = "Element number to return/ Número do elemento a retornar"
ArgDesc(3) = "Single-character element separator/ Elemento único separador (spc por padrão)"
Application.MacroOptions _
Macro:=FuncName, _
Description:=FuncDesc, _
Category:=Category, _
ArgumentDescriptions:=ArgDesc
End Sub

Crop last N lines of a string to display in userform textbox

I want to display a textlog string in a userform's textbox.
Code might look like this:
Dim public textlog as string
sub button1_click()
' do some action
textlog = textlog & event_string & vbCrLf
'event_string might exceed more than 2 line
textlog = textlog & "button1 action" & vbCrLf
userform1.textbox1.text = textlog
end sub
sub button2_click()
' do some action
textlog = textlog & event_string & vbCrLf
'event_string might exceed more than 2 line
textlog = textlog & "button2 action" & vbCrLf
userform1.textbox1.text = textlog
end sub
However, the textbox should only contain 20 lines of information, while my
the contents of my textlog will exceed 20 lines.
How can I display only the latest (last) 20 lines of the textlog in textbox1?
You can use this function to return only the last N lines of a string, and then display that in your textbox.
Note that you have to specify what the line break character is. Depending on your specific application, it could be vbCrLf, vbCr, vbLf, or even some other delimiter.
Function GetLastLines(ByVal s As String, ByVal nLinesToDisplay As Long, _
Optional ByVal lineBreakChar As String = vbCrLf)
'Split the string into an array
Dim splitString() As String
splitString = Split(s, lineBreakChar)
'How many lines are there?
Dim nLines As Long
nLines = UBound(splitString) + 1
If nLines <= nLinesToDisplay Then
'No need to remove anything. Get out.
GetLastLines = s
Exit Function
End If
'Collect last N lines in a new array
Dim lastLines() As String
ReDim lastLines(0 To nLinesToDisplay - 1)
Dim i As Long
For i = 0 To UBound(lastLines)
lastLines(i) = splitString(i + nLines - nLinesToDisplay)
Next i
'Join the lines array into a single string
GetLastLines = Join(lastLines, lineBreakChar)
End Function
Example usage:
MsgBox GetLastLines( _
"line 1" & vbCrLf & "line 2" & vbCrLf & "line 3" & vbCrLf _
& "line 4" & vbCrLf & "line 5" & vbCrLf & "line 6", _
4, vbCrLf)
Only the last 4 lines are displayed:
Note that this assumes that your last line is not terminated by a line break. If it is, then you can tweak the code to deal with that.
Alternatively, you can use Excel's built-in SUBSTITUTE function, which is useful in this particular case, because it can locate a specific instance of a given character. So instead of building arrays you can use a one-liner:
Function GetLastLines2(ByVal s As String, ByVal nLinesToDisplay As Long, _
Optional ByVal lineBreakChar As String = vbCrLf)
'An arbitrary character that will never be in your input string:
Dim delim As String: delim = Chr(1)
'How many lines are there?
Dim nLines As Long
nLines = UBound(Split(s, lineBreakChar)) + 1
If nLines <= nLinesToDisplay Then
'No need to remove anything. Get out.
GetLastLines2 = s
Exit Function
End If
'Replace one line break with delim, split the string on it,
'return only second part:
GetLastLines2 = Split( _
WorksheetFunction.Substitute( _
s, lineBreakChar, delim, nLines - nLinesToDisplay), _
delim)(1)
End Function
A = "Cat" & vbcrlf & "Tiger" & vbcrlf & "Lion" & vbcrlf & "Shark hunting florida lynxs" & vbcrlf & "Leopard" & vbcrlf & "Cheetah"
A= StrReverse(A)
NumLines = 3
i=1
For X = 1 to NumLines
i = Instr(i, A, vbcr) + 1
Next
Msgbox StrReverse(Left(A, i - 1))
This is a program that cuts or leaves lines from top or bottom of files.
To use
Cut
filter cut {t|b} {i|x} NumOfLines
Cuts the number of lines from the top or bottom of file.
t - top of the file
b - bottom of the file
i - include n lines
x - exclude n lines
Example
cscript //nologo filter.vbs cut t i 5 < "%systemroot%\win.ini"
The script
Set rs = CreateObject("ADODB.Recordset")
With rs
.Fields.Append "LineNumber", 4
.Fields.Append "Txt", 201, 5000
.Open
LineCount = 0
Do Until Inp.AtEndOfStream
LineCount = LineCount + 1
.AddNew
.Fields("LineNumber").value = LineCount
.Fields("Txt").value = Inp.readline
.UpDate
Loop
.Sort = "LineNumber ASC"
If LCase(Arg(1)) = "t" then
If LCase(Arg(2)) = "i" then
.filter = "LineNumber < " & LCase(Arg(3)) + 1
ElseIf LCase(Arg(2)) = "x" then
.filter = "LineNumber > " & LCase(Arg(3))
End If
ElseIf LCase(Arg(1)) = "b" then
If LCase(Arg(2)) = "i" then
.filter = "LineNumber > " & LineCount - LCase(Arg(3))
ElseIf LCase(Arg(2)) = "x" then
.filter = "LineNumber < " & LineCount - LCase(Arg(3)) + 1
End If
End If
Do While not .EOF
Outp.writeline .Fields("Txt").Value
.MoveNext
Loop
End With

"system resource exceeded" when running a function

I have a field called "sku" which uniquely identifies products on the table, there are about 38k products. I have a "sku generator" which uses other fields in the table to create the SKU. It's worked perfectly without an issue until I started producing SKUs for a large amount of products. I would launch the generator and it would stop around 15,000 and say "System Resource exceeded" and highlight the following code in the function:
Found = IsNull(DLookup("sku", "Loadsheet", "[sku]='" & TempSKU & "'"))
I didn't have time to fully fix the issue, so a temporary fix for me was to split the database in two, and run the sku generator seperately on both files. Now that I have more time I want to investigate why exactly it gets stuck around this number, and if there's a possibility of fixing this issue (it would save some time with splitting files and then grouping them again). I also have an issue with it getting really slow at times, but I think it's because it's processing so much when it runs. Here is the function
Option Compare Database
Private Sub Command2_Click() 'Generate SKU
Command2.Enabled = False: Command3.Enabled = False: Command2.Caption = "Generating ..."
Me.RecordSource = ""
CurrentDb.QueryDefs("ResetSKU").Execute
Me.RecordSource = "loadsheet_4"
Dim rs As Recordset, i As Long
Set rs = Me.Recordset
rs.MoveLast: rs.MoveFirst
For i = 0 To rs.RecordCount - 1
rs.AbsolutePosition = i
rs.Edit
rs.Fields("sku") = SetSKU(rs)
rs.Update
DoEvents
Next
Command2.Enabled = True: Command3.Enabled = True: Command2.Caption = "Generate SKU"
End Sub
Public Function SetSKU(rs As Recordset) As String
Dim TempStr As String, TempSKU As String, id As Integer, Found As Boolean, ColorFound As Variant
id = 1: ColorFound = DLookup("Abbreviated", "ProductColors", "[Color]='" & rs.Fields("single_color_name") & "'")
TempStr = "ORL-" & UCase(Left(rs.Fields("make"), 2)) & "-"
TempStr = TempStr & Get1stLetters(rs.Fields("model"), True) & rs.Fields("year_dash") & "-L-"
TempStr = TempStr & "WR-"
TempStr = TempStr & IIf(IsNull(ColorFound), "?", ColorFound) & "-4215-2-"
TempStr = TempStr & rs.Fields("color_code")
TempSKU = Replace(TempStr, "-L-", "-" & ADDZeros(id, 2) & "-L-")
Found = IsNull(DLookup("sku", "Loadsheet", "[sku]='" & TempSKU & "'"))
While Found = False
id = id + 1
TempSKU = Replace(TempStr, "-L-", "-" & ADDZeros(id, 2) & "-L-")
Found = IsNull(DLookup("sku", "Loadsheet", "[sku]='" & TempSKU & "'"))
Wend
If id > 1 Then
' MsgBox TempSKU
End If
SetSKU = TempSKU
End Function
Public Function Get1stLetters(Mystr As String, Optional twoLetters As Boolean = False) As String
Dim i As Integer
Get1stLetters = ""
For i = 0 To UBound(Split(Mystr, " ")) 'ubound gets the number of the elements
If i = 0 And twoLetters Then
Get1stLetters = Get1stLetters & UCase(Left(Split(Mystr, " ")(i), 2))
GoTo continueFor
End If
Get1stLetters = Get1stLetters & UCase(Left(Split(Mystr, " ")(i), 1))
continueFor:
Next
End Function
Public Function ADDZeros(N As Integer, MAX As Integer) As String
Dim NL As Integer
NL = Len(CStr(N))
If NL < MAX Then
ADDZeros = "0" & N 'StrDup(MAX - NL, "0") & N
Else: ADDZeros = N
End If
End Function
Notes: This function also calls other functions as well that adds a unique identifier to the SKU and also outputs the first letter of each word of the product
Also I'm running on 64 bit access.
If you require any other info let me know, I didn't post the other functions but if needed let me know.
thanks.
I am not 100% sure how you have split the Database into two files and that you are running the generator on both files. However I have a few suggestion to the function you are using.
I would not pass the recordset object to this function. I would rather pass the ID or unique identifier, and generate the recordset in the function. This could be a good start for efficiency.
Next, declare all objects explicitly, to avoid library ambiguity. rs As DAO.Recordset. Try to make use of inbuilt functions, like Nz().
Could Get1stLetters method be replaced with a simple Left() function? How about ADDZeros method?
Using DLookup might be a bit messy, how about a DCount instead? Could the following be any use now?
Public Function SetSKU(unqID As Long) As String
Dim TempStr As String, TempSKU As String
Dim id As Integer
Dim ColorFound As String
Dim rs As DAO.Recordset
id = 1
Set rs = CurrentDB.OpenRecordset("SELECT single_color_name, make, model, year_dash, color_code " & _
"FROM yourTableName WHERE uniqueColumn = " & unqID)
ColorFound = Nz(DLookup("Abbreviated", "ProductColors", "[Color]='" & rs.Fields("single_color_name") & "'"), "?")
TempStr = "ORL-" & UCase(Left(rs.Fields("make"), 2)) & "-"
TempStr = TempStr & Get1stLetters(rs.Fields("model"), True) & rs.Fields("year_dash") & "-L-"
TempStr = TempStr & "WR-"
TempStr = TempStr & ColorFound & "-4215-2-"
TempStr = TempStr & rs.Fields("color_code")
TempSKU = Replace(TempStr, "-L-", "-" & ADDZeros(id, 2) & "-L-")
While DCount("*", "Loadsheet", "[sku]='" & TempSKU & "'") <> 0
id = id + 1
TempSKU = Replace(TempStr, "-L-", "-" & ADDZeros(id, 2) & "-L-")
Wend
If id > 1 Then
'MsgBox TempSKU'
End If
Set rs = Nothing
SetSKU = TempSKU
End Function

Text templating engine/function in Visual Basic for Applications

A common usage of VBA at my company is generation of source code based on information entered in Excel tables. Given VBA's native string manipulation, the code that does this is tedious to write and not very readable.
A simple example (these get more complex) is:
Print #fileIdentifier, SSpace(9) & "update_" & print_string & "[" & CStr(j) & "] <= 1'b0;"
Print #fileIdentifier, SSpace(9) & print_string & "_ack_meta" & "[" & CStr(j) & "] <= 1'b0;"
Print #fileIdentifier, SSpace(9) & print_string & "_ack_sync" & "[" & CStr(j) & "] <= 1'b0;"
I am looking for a solution in VBA that would allow me to specify this using a "text template", so define a text that would look something like this
update_#name#[#bit#] <= 1'b0;
#name#_ack_meta[#bit#] <= 1'b0;
#name#_ack_sync[#bit#] <= 1'b0;
and have a function/method call, passing the values of #name# and #bit#, replace all instances of #name# and #bit# with corresponding values.
Basic insertion function:
Function insert(template As String, ParamArray inserts() As Variant) As String
Dim i As Long
For i = 0 To UBound(inserts)
template = Replace$(template, "%" & i + 1 & "%", inserts(i))
Next
'// some special cases perhaps
template = Replace$(template, "%SSPACE%", SSpace(9))
template = Replace$(template, "\r\n", VbCrLf)
insert = template
End Function
For
?insert("Foo %1% Bar %2% Qux %3% (%1%)", "A", "B", "C")
Foo A Bar B Qux C (A)
Map (add a reference to Microsoft Scripting Runtime):
Dim col As New Scripting.Dictionary
col("name") = "bob"
col("age") = 35
MsgBox insert2("Hello %name% you are %age%", col)
...
Function insert2(template As String, map As Scripting.Dictionary) As String
Dim name
For Each name In map.Keys()
template = Replace$(template, "%" & name & "%", map(name))
Next
insert2 = template
End Function
Alex K., thank you for the solution.
Here is how I expanded it (please feel free to let me know if there is a better way of doing this)
Function FillTemplateGeneric(template As Variant, map As Scripting.Dictionary) As String
Dim name
Dim out_text As String
' Handle multiple ways of receiving the template string
If VarType(template) = vbString Then
out_text = template
ElseIf VarType(template) = vbArray Then
out_text = Join(template, vbCrLf)
ElseIf TypeName(template) = "String()" Then
out_text = Join(template, vbCrLf)
ElseIf TypeName(template) = "Variant()" And TypeName(template(LBound(template, 1))) = "String" Then
out_text = Join(template, vbCrLf)
Else
MsgBox "Unknown Var Type passed to FillTemplateGeneric as first argument:" & vbCrLf & TypeName(template)
Err.Raise vbObjectError + 513, "FillTemplateGeneric", "Unknown Var Type passed to FillTemplateGeneric as first argument:" & vbCrLf & TypeName(template)
End If
For Each name In map.Keys()
out_text = Replace$(out_text, "%" & name & "%", map(name))
Next
FillTemplateGeneric = out_text
End Function
This allows for it to accept calls in multiple formats:
' Common dictionary for expansion
Dim col As New Scripting.Dictionary
col("name") = print_string
' Using inline text for template
MsgBox FillTemplateGeneric("test text with %name% name - just string", col)
' Using a multi-line string
Dim template As String
templ_text = " update_%name% <= 1'b0; // 1 - manual multi-line string" & _
vbCrLf & " %name%_ack_meta <= 1'b0; // " & _
vbCrLf & " %name%_ack_sync <= 1'b0; // "
MsgBox FillTemplateGeneric(templ_text, col)
' Using an array of strings
Dim ttext(1 To 3) As String
ttext(1) = " update_%name% <= 1'b0; // 2 - manual array of strings"
ttext(2) = " %name%_ack_meta <= 1'b0; // "
ttext(3) = " %name%_ack_sync <= 1'b0; // "
MsgBox FillTemplateGeneric(ttext, col)
' Using an inline array of strings
MsgBox FillTemplateGeneric(Array( _
" update_%name% <= 1'b0; // 3 - immediate array of strings", _
" %name%_ack_meta <= 1'b0; // ", _
" %name%_ack_sync <= 1'b0; // " _
), col)