Collection Extender

Author: Dave
Date: 08.31.16 - 11:34pm

I love the vb6 collection object. I frequently use it to hold object arrays and cycle through them with for each. Its also nice to have a .count = 0 to see if its empty instead of having to include an AryIsEmpty(ary) function to see if its been initialized yet.

One weak spot of collections is its key mechanism. It kind of sucks in a couple ways. First, it really needs a built in keyExists method, since trying to access an nonexistant key, or trying to add an existing key both raise errors. an ensureUniqueKey(suggested) is also handy.

One other weakness is, there is no way to tell what an item key is once its set. I mean usually you know, or have it stored somewhere else..but what if you had to search a collection for a value and find it..which key was this? I have had a need to translate the two back and forth before.

One more weakness is that you can not use a numeric key, or even a key which starts with a number. I need this fairly often wanting to add keys for things such as window handles, object pointers, database id etc.

So over at this week, some guys have been talking about collections and the internal structures. Elroys post on a Wrapper for VB6 Collections really caught my interest.

Using these internal structures, they figured out a way to enum all the Keys for the objects, get KeyForIndex, ChangeKey, ChangeIndex etc. Very cool stuff.

Many moons ago i wrote my own collection wrapper, but I dont think it ever survived outside of the one or two projects I used it in. If i remember correctly, I couldnt replicate the for each support that I love so much without adding the extra step of making the underlying collection public in the class. I also tried tracking all the keys in a parallel array to have an enumkeys methods or key for index method. It was bulky and not quite stable and what if an item is removed. Not pretty. This is a better way to do it.

One approach I ended up using in these situations was to always use a small class wrapper for data with my own name, key, value elements. I would add them to the vb collection without a key, then access them through my own lookup methods. Works and is stable, but you end up writing lots of stub classes and search methods.

After playing with the class for a while and digesting what was there, I think I have boiled it down to a final incarnation I want to use.

Since collections get used ALLOT, i want to keep the wrapper itself down to a minimum. The extended methods which are cool to have, but not a daily driver will be a separate reusable class that can be loaded on demand.

Dim extras As CCollectionExtender

'initilize the class on demand (memory saver)
Property Get ext() As CCollectionExtender
    If extras Is Nothing Then
        Set extras = New CCollectionExtender
        extras.setTarget c
    End If
    Set ext = extras
End Property

My final layout looks like this.

Add, Clear, fromFile, toFile, Item (default), KeyExists, keyForIndex, Remove, toString, uniqueKey, ext (as above), Count, isEmpty

changeIndex, changeIndexByKey, changeKey, changeKeyByIndex, fromArray, fromFile, indexForKey, KeyExists, keyforIndex, Keys, toArray, toFile, uniqueKey, isEmpty, toString

There is some duplication between the two, thats because the CCollectionExtender can also be used on its own with a regular collection using a setTarget method. No need to tie it so tightly to CollectionEx that it cant stand on its own.

I havent used it in a real project yet, but I think its a keeper.

  • I really like having Count as a property so you can mouse over it in the IDE and see the value,
  • keyForIndex will solve a real problem I have had intermittently for years,
  • KeyExists will be built in,
  • isEmpty while small is highly readable,
  • toString will be super usable for debugging or an interm to toArray,
  • toFile/fromFile will be create to both save a collection to disk and reload it, as well as providing an easy basis for a clone method to make a full copy (including keys) and an easy way to append one collection onto the other (including keys).

Lots of really cool stuff! Big thanks goes out to Elroy and all the authors involved in discovering these structures.


After really going through all the methods and digesting what was done, it turns out you only need one extra primitive that mucks with CopyMemory and the internal collection structures.

All the rest of the methods such as enum keys can be accomplished with just the keyFromIndex(index as long).

This methods takes a vb6 collection, and return the key associated with the element at a given index.

Private c as Collection

Public Function keyForIndex(index As Long) As String
    ' Get a key based on its index value.  Must be in range, or error.
    Dim i     As Long
    Dim ptr   As Long
    Dim sKey  As String
    If index < 1 Or index > c.Count Then
        Err.Raise 9
        Exit Function
    End If
    If index <= c.Count / 2 Then                                ' Start from front.
        CopyMemory ptr, ByVal ObjPtr(c) + &H18, 4               ' First item pointer of collection header.
        For i = 2 To index
            CopyMemory ptr, ByVal ptr + &H18, 4                 ' Next item pointer of collection item.
        Next i
    Else                                                        ' Start from end and go back.
        CopyMemory ptr, ByVal ObjPtr(c) + &H1C, 4               ' Last item pointer of collection header.
        For i = c.Count - 1 To index Step -1
            CopyMemory ptr, ByVal ptr + &H14, 4                 ' Previous item pointer of collection item.
        Next i
    End If
    i = StrPtr(sKey)                                            ' Save string pointer because we're going to borrow the string.
    CopyMemory ByVal VarPtr(sKey), ByVal ptr + &H10, 4          ' Key string of collection item.
    keyForIndex = Base16Decode(sKey)                                ' Move key into property's return.
    CopyMemory ByVal VarPtr(sKey), i, 4                         ' Put string pointer back to keep memory straight.
End Function

Comments: (1)

On 09.01.16 - 12:10pm Dave wrote:
One more note. I had to look at the look at the code above a bit before it made sense what it was doing. It looks like it has to walk a linked list with flink and blink members.

The reason for If index <= c.Count / 2 Then is a matter of minimizing the number of structures you have to walk through. If its in the first half of the list, walk the flink members until you hit that index. If its in the last half of the collection, start at the end and walk backward.

Its a smart optimization. A mention of flink and blink should bring it into focus..

Leave Comment:
Email: (not shown)
Message: (Required)
Math Question: 65 + 29 = ? followed by the letter: S 

About Me
More Blogs
Main Site
Posts: (year)
2024 (1)
     RegJump Vb
2023 (9)
     VB6 Virtual Files
     File handles across dlls
     python abort / script timeout
     VB6 Python embed w/debugger
     python embedding
     VB6 IDE Enhancements
     No Sleep
     A2W no ATL
2022 (4)
     More VB6 - C data passing
     Vb6 Asm listing
     Byte Array C to VB6
     Planet Source Code DVDs
2021 (2)
     Obscure VB
     VB6 IDE SP6
2020 (4)
     BSTR from C Dll to VB
     Cpp Memory Manipulation
     ActiveX Binary Compatability
2019 (5)
     Console tricks
     FireFox temp dir
     OCX License
     Extract substring
     VB6 Console Apps
2018 (6)
     VB6 UDTs
     VB6 Debugger View As Hex tooltips
     VB6 - C Share registry data
     VB6 Addin Missing Menus
     VB6 Class Init Params
     VB6 isIn function
2017 (6)
     Python and VB6
     Python pros and cons
     download web Dir
     vc rand in python
     VB6 Language Enhancement
     Register .NET as COM
2016 (22)
     VB6 CDECL
     UDT Tricks pt2
     Remote Data Extraction
     Collection Extender
     VB6 FindResource
     DirList Single Click
     Reset CheckPoint VPN Policy
     VB6 BSTR Oddities Explained
     SafeArrays in C
     BSTR and Variant in C++
     Property let optional args
     Misc Libs
     Enum Named Pipes
     Vb6 Collection in C++
     VB6 Overloaded Methods
     VB6 Syncronous Socket
     Simple IPC
     VB6 Auto Resize Form Elements
     Mach3 Automation
     Exit For in While
2015 (15)
     C# self register ocx
     VB6 Class Method Pointers
     Duktape Debug Protocol
     QtScript 4 VB
     Vb6 Named Args
     vb6 Addin Part 2
     VB6 Addin vrs Toolbars
     OpenFile Dialog MultiSelect
     Duktape Example
     DukTape JS
     VB6 Unsigned
     .Net version
     TitleBar Height
     .NET again
     VB6 Self Register OCXs
2014 (25)
     Query Last 12 Mos
     Progid from Interface ID
     VB6 to C Array Examples
     Human Readable Variant Type
     ScriptBasic COM Integration
     CodeView Addin
     ScriptBasic - Part 2
     Script Env
     MSCOMCTL Win7 Error
     printf override
     History Combo
     Disable IE
     API Hooking in VB6
     Addin Hook Events
     FastBuild Addin
     VB6 MemoryWindow
     Link C Obj Files into VB6
     Vb6 Standard Dlls
     CStr for Pascal
     Lazarus Review
     asprintf for VS
     VB6 GlobalMultiUse
     Scintilla in VB6
     Dynamic Highlight
     WinVerifyTrust, CryptMsgGetParam VB6
2013 (4)
     MS GLEE Graphing
     printf for VB6
     C# App Config
     Tero DES C# Test
2012 (10)
     VC 2008 Bit Fields
     Speed trap
     C# Db Class Generator
     VB6 vrs .NET (again)
     FireFox Whois Extension
     git and vb6
     Code Additions
     Compiled date to string
     C# ListView Sorter
     VB6 Wish List
2011 (7)
     C# Process Injection
     CAPTCHA Bots
     C# PE Offset Calculator
     VB6 Async Download
     Show Desktop
     coding philosophy
     Code release
2010 (11)
     Dll Not Found in IDE
     Advanced MSScript Control
     random tip
     Clipart / Vector Art
     VB6 Callback from C#
     Binary data from VB6 to C#
     CSharp and MsScriptControl
     HexDumper functions
     Js Beautify From VB6 or C#
     vb6 FormPos
     Inline Asm w VB6
2009 (3)
     The .NET Fiasco
     One rub on computers
     Universal extractor