6

is there a proper way to count elements of an enum in VBA ? At the moment, I leave an enum value such as KeepThisOneHere in the following example

Enum TestEnum
   ValueA
   ValueB
   ValueC
   KeepThisOneHere
End Enum

I use the last value to know the size... I don't like this solution, because I am not sure I have a guarantee the values will always be indexed the same way, and the code might be changed by a third party who might add values after this last special one, silently breaking the rest of the code.

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
BuZz
  • 16,318
  • 31
  • 86
  • 141

5 Answers5

5

Not sure on the etiquette here, so I'll post it and if advised, I'll come back and delete it. Chip Pearson posted this code on the Code Cage Forums (http://www.thecodecage.com/forumz/microsoft-excel-forum/170961-loop-enumeration-constants.html). I don't have the TypeLinInfo DLL on my machine, so I can't test it (I'm sure google will turn up places to download TLBINF32.dll). Nonetheless, here is his entire post to save someone else from registering for a forum:

You can do this ONLY IF you have the TypeLibInfo DLL installed on your computer. In VBA, go to the Tools menu, choose References, and scroll down to "TypeLib Info". If this item exists, check it. If it does not exist, then quit reading because you can't do what you want to do. The file name of the DLL you need is TLBINF32.dll.

The following code shows how to get the names and values in the XLYesNoGuess enum:

Sub AAA()
    Dim TLIApp As TLI.TLIApplication
    Dim TLILibInfo As TLI.TypeLibInfo
    Dim MemInfo As TLI.MemberInfo
    Dim N As Long
    Dim S As String
    Dim ConstName As String

    Set TLIApp = New TLI.TLIApplication
    Set TLILibInfo = New TLI.TypeLibInfo
    Set TLILibInfo = TLIApp.TypeLibInfoFromFile( _
        ThisWorkbook.VBProject.References("EXCEL").FullPath)

    ConstName = "XLYesNoGuess"
    For Each MemInfo In _
        TLILibInfo.Constants.NamedItem(ConstName).Members
        S = MemInfo.Name
        N = MemInfo.Value
        Debug.Print S, CStr(N)
    Next MemInfo
End Sub

Using this knowledge, you can create two useful functions. EnumNames returns an array of strings containing the names of the values in an enum:

Function EnumNames(EnumGroupName As String) As String()
    Dim TLIApp As TLI.TLIApplication
    Dim TLILibInfo As TLI.TypeLibInfo
    Dim MemInfo As TLI.MemberInfo
    Dim Arr() As String
    Dim Ndx As Long
    Set TLIApp = New TLI.TLIApplication
    Set TLILibInfo = New TLI.TypeLibInfo
    Set TLILibInfo = TLIApp.TypeLibInfoFromFile( _
        ThisWorkbook.VBProject.References("EXCEL").FullPath)
    On Error Resume Next
    With TLILibInfo.Constants.NamedItem(EnumGroupName)
        ReDim Arr(1 To .Members.Count)
        For Each MemInfo In .Members
            Ndx = Ndx + 1
            Arr(Ndx) = MemInfo.Name
        Next MemInfo
    End With

    EnumNames = Arr
End Function

You would call this function with code such as:

Sub ZZZ()
    Dim Arr() As String
    Dim N As Long
    Arr = EnumNames("XLYesNoGuess")
    For N = LBound(Arr) To UBound(Arr)
        Debug.Print Arr(N)
    Next N
End Sub

You can also create a function to test if a value is defined for an enum:

Function IsValidValue(EnumGroupName As String, Value As Long) As
    Boolean
    Dim TLIApp As TLI.TLIApplication
    Dim TLILibInfo As TLI.TypeLibInfo
    Dim MemInfo As TLI.MemberInfo
    Dim Ndx As Long
    Set TLIApp = New TLI.TLIApplication
    Set TLILibInfo = New TLI.TypeLibInfo
    Set TLILibInfo = TLIApp.TypeLibInfoFromFile( _
        ThisWorkbook.VBProject.References("EXCEL").FullPath)
    On Error Resume Next
    With TLILibInfo.Constants.NamedItem(EnumGroupName)
        For Ndx = 1 To .Members.Count
            If .Members(Ndx).Value = Value Then
                IsValidValue = True
                Exit Function
            End If
        Next Ndx
    End With
    IsValidValue = False
End Function

This function returns True if Value is defined for EnumGroupName or False if it is not defined. You would call this function with code like the following:

Sub ABC()
    Dim B As Boolean
    B = IsValidValue("XLYesNoGuess", xlYes)
    Debug.Print B ' True for xlYes
    B = IsValidValue("XLYesNoGuess", 12345)
    Debug.Print B ' False for 12345
End Sub

Cordially, Chip Pearson Microsoft MVP 1998 - 2010 Pearson Software Consulting, LLC www.cpearson.com [email on web site]

Geoff
  • 8,551
  • 1
  • 43
  • 50
sous2817
  • 3,915
  • 2
  • 33
  • 34
  • 2
    It looks to me like this is designed to extract the data from the Excel type information, which would work for Excel types but not for user defined types. – Geoff Apr 24 '13 at 14:37
  • This line: `Set TLILibInfo = New TLI.TypeLibInfo` is redundant. Every time you use it, you assign a completely new object in the next line. – mielk May 27 '17 at 21:54
  • tlbinf32.dll is not the only source of TLI.TLIApplication. If you have Visual Studio it comes with vstlbinf.dll but this needs to be registered manually. I'm running VS 2012 on 64-bit windows 8.1 and got this working by running these commands from a cmd prompt started as administrator: cd C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE regsvr32 vstlbinf.dll The script didn't work "natively"for me even then but works fine if I run the cscript command from a 32-bit cmd window, which you can start using C:\Windows\SysWOW64\cmd.exe – snozzwangler Oct 18 '14 at 21:10
2

Here's an example of my workaround, which is pretty straightforward:

Enum FileSpecFields
    FileSpecFields_Start                    '(zero-based)
        FileNameIdx = FileSpecFields_Start
        FolderNameIdx
        BasePathIdx
        FullPathIdx
        CopyStatus
    FileSpecFields_End = CopyStatus
End Enum

'...

ReDim FileSpecList(1 To MaxFiles, FileSpecFields_Start To FileSpecFields_End) As String

'...

But note that, if you are using a one-based Enum you may have to adjust the _End value definition, depending on how you're using it. Also, for zero-based Enums, the _End value is not the same as its count of items. And, if you add items at the end, you must update the _End value's definition accordingly. Finally, if your enum is a non-contiguous range of values, all bets are off with this approach!

pstraton
  • 1,080
  • 14
  • 9
  • 2
    One suggestion - I do the same thing you do, but I actually name the first and last enums `[_First]` and `[_Last]`. VBA hides enums that begin with an underscore in the intellisense dropdowns, so you don't have to see them normally, but you can still reference them by `FileSpecFields.[_First]` for example. – Blackhawk Jul 29 '16 at 20:43
  • Great solution! But i don't understand why it's necessary to mark the first enum. Just mark the last one: `Enum MyEnum myItem1 myItem2 myItem3 myLast End Enum`. Then get the number of enums with `myLast - 1`. Assume 0-based, which enum's are by default. – johny why Jul 13 '21 at 21:21
  • 1
    The point of FileSpecFields_Start is that it, and FileSpecFields_End, are a kind of metadata. If you just use myItem1 (or FileNameIdx in my case) instead, then you're mixing metadata with non-metadata information. It can work, of course, if you're careful, but is less logically rigorous. And, yes, Chip Pearson's method for hiding the metadata is, as always, excellent. – pstraton Jun 07 '22 at 00:34
1

there isn't a way to get the count.

What you have to do is loop through the elements of the Enum until you get to the last one.

Chip Pearson has some good tips on Enumerated constants: Chip Pearson: Enum Variable Type

Community
  • 1
  • 1
Our Man in Bananas
  • 5,809
  • 21
  • 91
  • 148
0

If you know the enum type(s) on design-time you could transform them into a Static Property Get MyEnumColl() as Collection ... (no class needed, initialized on 1st access statically) and thus easily loop through them or count them like shown here

Andreas Covidiot
  • 4,286
  • 5
  • 51
  • 96
-1
Sub count()
    Dim n, c

    For n = headers.frstItem To headers.lastItem
        c = c + 1
    Next
    Debug.Print c
End Sub
Nick
  • 138,499
  • 22
  • 57
  • 95
Rick M
  • 1
  • [A.k.a.](https://devblogs.microsoft.com/oldnewthing/20111227-00/?p=8793) `headers.lastItem - headers.frstItem + 1`. Which will only work if the enum values are consecutive and in increasing order. – GSerg Feb 06 '21 at 06:54