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 vbforums.com 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.

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


CCollectionExtender:
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.

Benifits:
  • 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.

Addemdum:

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:
Name:
Email: (not shown)
Message: (Required)
Math Question: 9 + 5 = ? followed by the letter: Z 



About Me
More Blogs
Main Site
Posts:
Console tricks
FireFox temp dir
OCX License
Extract substring
VB6 Console Apps
VB6 UDTs
VB6 Debugger View As Hex tooltips
VB6 - C Share registry data
VB6 Addin Missing Menus
VB6 Class Init Params
VB6 isIn function
Python and VB6
Python pros and cons
download web Dir
vc rand in python
VB6 Language Enhancement
Register .NET as COM
VB6 CDECL
UDT Tricks pt2
Remote Data Extraction
Collection Extender
VB6 FindResource
CDO.Message
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
EXPORT FUNCDNAME Warning
VB6 Syncronous Socket
Simple IPC
VB6 Auto Resize Form Elements
Mach3 Automation
Exit For in While
C# self register ocx
VB6 Class Method Pointers
JS Debugger
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
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
MS GLEE Graphing
printf for VB6
C# App Config
Tero DES C# Test
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
C# Process Injection
CAPTCHA Bots
C# PE Offset Calculator
VB6 Async Download
Show Desktop
coding philosophy
Code release
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
The .NET Fiasco
One rub on computers
Universal extractor