1
\$\begingroup\$

Intro

I've recently come across a problem when getting metadata from files in explorer. Meta Data can be read off a file using the GetDetailsOf function, which takes a magic integer argument - this integer dictates the type of metadata to return.

I wanted a function that would be able to get bits of metadata based on whichever of these magic numbers you specify as arguments. I wanted to be able to use bitwise logic as outlined in the Composite Values section of Chip Pearson's Enum article.

This lead to 2 specifications:

1) An Enum to represent the magic numbers

2) Another Enum to use in my composite selection, where each value corresponds to a magic number, but does not equal it

Which gives a table of correspondence between the Enums

magicConstant | bitValue  | enumName (suffixed with _Const and _Bit respectively)
-------------------------------------
|     0       |    2^0    |  enum1   |
|     7       |    2^1    |  enum2   |
|     21      |    2^2    |  enum3   |

Which write something like like:

Dim value As bitValue
value = enum1_Bit + enum3_Bit 'with intellisense

If value And enum1_Bit Then 'check if bit in composite value
    getInfo(enum1_Const) 'use corresponding *magic number*
End If
If value And enum2_Bit Then
    getInfo(enum2_Const)
End If
If value And enum3_Bit Then
    getInfo(enum3_Const)
End If

Implementation

Based on that idea, I decided to create a class with a function/functions that could take one bit of info (one of the enums), and output another. In addition to the 2 Enums above, I made 1 more enum as an index to loop through the values. I also included a String representation of the enum for printing. The result was this class:

'''
'Collection class to map Enums onto the properties they represent
'''
Option Explicit


Private infoSet() As PropertyInfo
Private enumBitMap As Scripting.Dictionary       'use bit constant as key
Private enumTitleMap As Scripting.Dictionary     'use title as key
Private enumConstantMap As Scripting.Dictionary  'usesproperty constant as key


Public Property Get InfoFromBit(ByRef lookupVal As PropertyBit) As PropertyInfo
    Dim location As PropertyIndex
    Dim lookupMap As Scripting.Dictionary
    Set lookupMap = BitMap
    location = lookupMap(Str$(lookupVal))
    InfoFromBit = InfoArray(location)
End Property

Public Property Get InfoFromTitle(ByVal lookupVal As String) As PropertyInfo
    Dim location As PropertyIndex
    Dim lookupMap As Scripting.Dictionary
    Set lookupMap = TitleMap
    location = lookupMap(lookupVal)
    InfoFromTitle = InfoArray(location)
End Property

Public Property Get InfoFromConstant(ByRef lookupVal As PropertyConstant) As PropertyInfo
    Dim location As PropertyIndex
    Dim lookupMap As Scripting.Dictionary
    Set lookupMap = ConstantMap
    location = lookupMap(Str$(lookupVal))
    InfoFromConstant = InfoArray(location)
End Property

Public Property Get InfoFromIndex(ByRef lookupVal As PropertyIndex) As PropertyInfo
    InfoFromIndex = InfoArray(lookupVal)
End Property

Private Property Get BitMap() As Scripting.Dictionary
    If enumBitMap Is Nothing Then
Debug.Print "Generating Bit map"
        Set enumBitMap = New Scripting.Dictionary
        With enumBitMap
            .Add Str$(PropertyBit.Tooltip_Bit), PropertyIndex.Tooltip_Index
            .Add Str$(PropertyBit.Name_Bit), PropertyIndex.Name_Index
            .Add Str$(PropertyBit.Date_Taken_Bit), PropertyIndex.Date_Taken_Index
            .Add Str$(PropertyBit.Size_Bit), PropertyIndex.Size_Index
            .Add Str$(PropertyBit.Perceived_Type_Bit), PropertyIndex.Perceived_Type_Index
            .Add Str$(PropertyBit.Kind_Bit), PropertyIndex.Kind_Index
            .Add Str$(PropertyBit.Date_Created_Bit), PropertyIndex.Date_Created_Index
            .Add Str$(PropertyBit.Date_Modified_Bit), PropertyIndex.Date_Modified_Index
            .Add Str$(PropertyBit.Camera_Model_Bit), PropertyIndex.Camera_Model_Index
            .Add Str$(PropertyBit.Dimensions_Bit), PropertyIndex.Dimensions_Index
            .Add Str$(PropertyBit.File_Extension_Bit), PropertyIndex.File_Extension_Index
            .Add Str$(PropertyBit.fileName_Bit), PropertyIndex.fileName_Index
            .Add Str$(PropertyBit.Folder_Path_Bit), PropertyIndex.Folder_Path_Index
            .Add Str$(PropertyBit.Folder_Name_Bit), PropertyIndex.Folder_Name_Index
            .Add Str$(PropertyBit.Path_Bit), PropertyIndex.Path_Index
            .Add Str$(PropertyBit.Item_type_Bit), PropertyIndex.Item_type_Index
        End With
    End If
    Set BitMap = enumBitMap
End Property

Private Property Get ConstantMap() As Scripting.Dictionary
    If enumConstantMap Is Nothing Then
Debug.Print "Generating Constant map"
        Set enumConstantMap = New Scripting.Dictionary
        With enumConstantMap
            .Add Str$(PropertyConstant.Tooltip_Constant), PropertyIndex.Tooltip_Index
            .Add Str$(PropertyConstant.Name_Constant), PropertyIndex.Name_Index
            .Add Str$(PropertyConstant.Date_Taken_Constant), PropertyIndex.Date_Taken_Index
            .Add Str$(PropertyConstant.Size_Constant), PropertyIndex.Size_Index
            .Add Str$(PropertyConstant.Perceived_Type_Constant), PropertyIndex.Perceived_Type_Index
            .Add Str$(PropertyConstant.Kind_Constant), PropertyIndex.Kind_Index
            .Add Str$(PropertyConstant.Date_Created_Constant), PropertyIndex.Date_Created_Index
            .Add Str$(PropertyConstant.Date_Modified_Constant), PropertyIndex.Date_Modified_Index
            .Add Str$(PropertyConstant.Camera_Model_Constant), PropertyIndex.Camera_Model_Index
            .Add Str$(PropertyConstant.Dimensions_Constant), PropertyIndex.Dimensions_Index
            .Add Str$(PropertyConstant.File_Extension_Constant), PropertyIndex.File_Extension_Index
            .Add Str$(PropertyConstant.fileName_Constant), PropertyIndex.fileName_Index
            .Add Str$(PropertyConstant.Folder_Path_Constant), PropertyIndex.Folder_Path_Index
            .Add Str$(PropertyConstant.Folder_Name_Constant), PropertyIndex.Folder_Name_Index
            .Add Str$(PropertyConstant.Path_Constant), PropertyIndex.Path_Index
            .Add Str$(PropertyConstant.Item_type_Constant), PropertyIndex.Item_type_Index
        End With
    End If
    Set ConstantMap = enumConstantMap
End Property

Private Property Get TitleMap() As Scripting.Dictionary
    If enumTitleMap Is Nothing Then
Debug.Print "Generating Title map"
        Set enumTitleMap = New Scripting.Dictionary
        With enumTitleMap
            .Add "Tooltip", PropertyIndex.Tooltip_Index
            .Add "Name", PropertyIndex.Name_Index
            .Add "Date_Taken", PropertyIndex.Date_Taken_Index
            .Add "Size", PropertyIndex.Size_Index
            .Add "Perceived_Type", PropertyIndex.Perceived_Type_Index
            .Add "Kind", PropertyIndex.Kind_Index
            .Add "Date_Created", PropertyIndex.Date_Created_Index
            .Add "Date_Modified", PropertyIndex.Date_Modified_Index
            .Add "Camera_Model", PropertyIndex.Camera_Model_Index
            .Add "Dimensions", PropertyIndex.Dimensions_Index
            .Add "File_Extension", PropertyIndex.File_Extension_Index
            .Add "fileName", PropertyIndex.fileName_Index
            .Add "Folder_Path", PropertyIndex.Folder_Path_Index
            .Add "Folder_Name", PropertyIndex.Folder_Name_Index
            .Add "Path", PropertyIndex.Path_Index
            .Add "Item_type", PropertyIndex.Item_type_Index
        End With
    End If
    Set TitleMap = enumTitleMap
End Property

Private Property Get InfoArray(ByRef location As PropertyIndex) As PropertyInfo 'array containing all info for maps
    'check if array exists, if not, populate it, if so skip it
    On Error Resume Next
    Dim testVal As Long
    Dim isEmpty As Boolean
    testVal = UBound(infoSet)
    isEmpty = Err.Number = 9                     'means array empty
    On Error GoTo 0

    If isEmpty Then
Debug.Print "Generating Info map"
        ReDim infoSet(PropertyIndex.[_First] To PropertyIndex.[_Last] - 1)
        'add info of form property type top map
        'Get keys
        Dim titleKeys As Variant
        Dim constantKeys As Variant
        Dim bitKeys As Variant
        titleKeys = TitleMap.Keys
        constantKeys = ConstantMap.Keys
        bitKeys = BitMap.Keys
        Dim propertyI As PropertyIndex
        For propertyI = LBound(infoSet) To UBound(infoSet)
            With infoSet(propertyI)              'keys are 0-15, infoset is 1-16
                .propertyInfoBitMap = bitKeys(propertyI - 1)
                .propertyInfoConstant = constantKeys(propertyI - 1)
                .propertyInfoIndex = propertyI
                .propertyInfoTitle = titleKeys(propertyI - 1)
            End With
        Next propertyI
    End If
    InfoArray = infoSet(location)
End Property

Private Sub Class_Terminate()
    Set enumBitMap = Nothing
    Set enumTitleMap = Nothing
    Set enumConstantMap = Nothing
End Sub

References (in a normal module) these data types:

Public Type PropertyInfo
    propertyInfoIndex As PropertyIndex           'Index of each property in total list - loop Start_ to End_'
    propertyInfoBitMap As PropertyBit            'used with bitwise operations to see what's selected
    propertyInfoTitle As String                  'Name when called for methods
    propertyInfoConstant As PropertyConstant     'value in explorer
End Type

Public Enum PropertyBit                          'powers of 2 to lookup
    Tooltip_Bit = 1
    Name_Bit = 2
    Date_Taken_Bit = 2 ^ 2
    Size_Bit = 2 ^ 3                             'filesize
    Perceived_Type_Bit = 2 ^ 4                   'Image for all png/...
    Kind_Bit = 2 ^ 5                             'Picture
    Date_Created_Bit = 2 ^ 6
    Date_Modified_Bit = 2 ^ 7
    Camera_Model_Bit = 2 ^ 8
    Dimensions_Bit = 2 ^ 9
    File_Extension_Bit = 2 ^ 10
    fileName_Bit = 2 ^ 11
    Folder_Path_Bit = 2 ^ 12
    Folder_Name_Bit = 2 ^ 13
    Path_Bit = 2 ^ 14
    Item_type_Bit = 2 ^ 15                       'e.g. JPEG image
End Enum

Public Enum PropertyConstant                     'same as key
    Tooltip_Constant = -1
    Name_Constant = 0
    Date_Taken_Constant = 12
    Size_Constant = 1                            'filesize
    Perceived_Type_Constant = 9                  'Image for all png/...
    Kind_Constant = 11                           'Picture
    Date_Created_Constant = 4
    Date_Modified_Constant = 3
    Camera_Model_Constant = 30
    Dimensions_Constant = 31
    File_Extension_Constant = 157
    fileName_Constant = 158
    Folder_Path_Constant = 184
    Folder_Name_Constant = 183
    Path_Constant = 187
    Item_type_Constant = 2                       'e.g. JPEG image
End Enum

Public Enum PropertyIndex                        'same as key
    [_First] = 1
    Tooltip_Index = 1
    Name_Index
    Date_Taken_Index
    Size_Index                                   'filesize
    Perceived_Type_Index                         'Image for all png/...
    Kind_Index                                   'Picture
    Date_Created_Index
    Date_Modified_Index
    Camera_Model_Index
    Dimensions_Index
    File_Extension_Index
    fileName_Index
    Folder_Path_Index
    Folder_Name_Index
    Path_Index
    Item_type_Index                              'e.g. JPEG image
    [_Last]
End Enum

And here's some tests:

Sub testenumMap()
    Dim enumMap As AlbumDataEnumMap
    Set enumMap = New AlbumDataEnumMap
    Debug.Assert enumMap.InfoFromBit(File_Extension_Bit).propertyInfoTitle = "File_Extension"
    Debug.Assert enumMap.InfoFromConstant(Dimensions_Constant).propertyInfoBitMap = Dimensions_Bit
    Debug.Assert enumMap.InfoFromTitle("Tooltip").propertyInfoConstant = Tooltip_Constant
    Debug.Assert enumMap.InfoFromConstant(Tooltip_Constant).propertyInfoIndex = [_First]
End Sub

I've now found another application of this sort of class, so I thought I'd post to see what I could improve before using it in the new application.

\$\endgroup\$
  • \$\begingroup\$ So once you initiate the class and have all the maps, how would you use it? To look something up, I mean? I can see 7 tables populated, but I don't know what input to put. \$\endgroup\$ – Raystafarian Mar 21 '18 at 22:51
  • \$\begingroup\$ @Raystafarian You see in the test code at the bottom of my post; something like a = enumMap.InfoFromSomeSource(Source) means you give one piece of info to the map - specifying what kind of info you're giving. This is equivalent to giving it the value of 1 cell in a table, and the column name. It looks up the value in the column, finds the row number, then spits out the entire row as a PropertyInfo type into a. You can then dot through a with a.propertyInfoTypeIWant. That's equivalent to giving a table a row and a column number, and asking for the cell value at the intersection. \$\endgroup\$ – Greedo Mar 22 '18 at 16:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.