Represent a standalone class implements a hash table, which in many cases can be a substitute for the dictionary (Dictionary) of Scripting runtime. Implements all the same methods as in the dictionary, and add new ones. Includes support transfer through the For Each, you can also set the mode of transfer of keys/values, as compared to the previous version fixes bugs departure from the environment during your stay in the loop For Each, and there are no restrictions on the nested loops. Run fast enough on my computer about as well (even a bit faster) as a dictionary with binary comparison, when the text comparison works almost 2 times faster than the dictionary. As keys are allowed Variant variables with types of vbEmpty to vbDecimal inclusive. Numeric keys must be unique, ie -1, True, -1e0 - the same key as in the dictionary. New method EnumMode - determines the current mode of transfer. Valid values ENUM_BY_KEY, ENUM_BY_VALUE. Upon entering the For Each loop starts listing the parameter that is set this property. For example, you can list the keys in the main loop, the attached values or keys first and then the value. Also setting this property in windows Locals or Watch You can toggle the display with keys to values and vice versa.
Implementation itself is an array of doubly-linked lists, where the array indexes - the hash values of the corresponding keys. To support enumeration is used enumerator object. Implementing an interface IEnumVariant and IUnknown for the enumerator is written in assembly language:
Code:
[BITS 32]

QueryInterface:
    mov eax,[esp+4]         ; ObjPtr
    inc dword [eax+4]       ; Counter++
    mov ecx, [esp+0xc]
    mov [ecx],eax           ; ppvObject = ObjPtr
    xor eax,eax             ; Success
    ret 0xc

AddRef:
    mov eax,[esp+4]         ; ObjPtr
    inc dword [eax+4]       ; Counter++
    mov eax, [eax+4]        ; Counter return
    ret 0x4

Release:
    mov eax,[esp+4]         ; ObjPtr
    dec dword [eax+4]       ; Counter--
    jz  RemoveObject        ; if (Counter == 0)
    mov eax, [eax+4]        ; Counter return
    ret 0x4
RemoveObject:
    push    eax             ; lpMem
    push    0x00000001      ; HEAP_NO_SERIALIZE
    call    0x12345678      ; GetProcessHeap
    push    eax             ; hHeap
    call    0x12345678      ; HeapFree
    xor eax,eax             ; Counter = 0
    ret 0x4

IEnumVariant_Next:
    push ebx
    push edi
    push esi

    mov esi, [esp+0x10]     ; ObjPtr
    mov ebx, [esp+0x14]     ; ebx = celt
    mov edi, [esp+0x18]     ; rgVar

NextItem:

        movsx   eax, word [esi+0x8] ; Pointer.Hash
        inc eax
        jz  ExitCycle           ; if (Pointer.Hash == -1)
        dec eax
        mov ecx, [esi+0xc]      ; DataPtr
        mov ecx, [ecx+eax*8+4]  ; ecx = tItem.tElement
        movzx   eax, word [esi+0xA] ; Pointer.Index
        imul    ax, ax, 0x28        ;
        movzx   eax, ax         ; eax = Pointer.Index * sizeof(tElement)
        mov ecx, [ecx+0xc]      ; ecx = *tElement(0)
        lea ecx, [ecx+eax]      ; *tElement(Pointer.Index)
        mov eax, [ecx+0x20]
        add ecx, [esi+0x14]     ; ecx += OffsetVarinat
        mov [esi+0x8], eax      ; Pointer = tElement(Pointer.Index).Next
        push    ecx             ; pvargSrc
        push    edi             ; pvargDest == rgVar
        call    0x12345678      ; VariantCopy

        add edi, 0x10
        dec ebx
        jne NextItem
        
ExitCycle:
    
    test ebx, ebx
    setne   dl              ; if (ebx = 0) dl = 0 else dl = 1
    movzx   esi, dl         ; edx = dl
    
    mov edi, [esp+0x1c]     ; pCeltFetched
    test edi, edi
    je ExitFunction
    
    mov eax, [esp+0x14]     ; eax = celt
    sub eax, ebx
    mov     [edi], eax      ; pCeltFetched = count

ExitFunction:
    
    mov eax, esi
    pop esi
    pop edi
    pop ebx
    ret 0x10

IEnumVariant_Skip:

    mov edx, [esp+0x04]     ; ObjPtr
    mov eax, [edx+0x8]      ; Pointer.Hash
    mov edx, [edx+0xc]      ; DataPtr

NextItem_2:
        
        inc ax
        jz  ExitCycle_2         ; if (Pointer.Hash == -1)
        dec ax
        
        movzx   ecx, ax         ; ecx = Pointer.Hash
        mov ecx, [edx+ecx*8+4]  ; ecx = tItem.tElement
        shr eax, 0x10           ; eax = Pointer.Index
        imul    ax, ax, 0x28    ;

        mov ecx, [ecx+0xc]      ; ecx = *tElement(0)
        mov eax, [ecx+eax+0x20] ; eax = tElement(Pointer.Index).Next
        
        dec dword [esp+0x08]    ; celt--
        jne NextItem_2
        
        xor edx, edx

ExitCycle_2:
    
    test edx, edx
    setne   dl              ; if (edx = 0) dl = 0 else dl = 1
    mov eax, edx
    
    ret 0x08

IEnumVariant_Reset:
    mov eax, [esp+0x04]     ; ObjPtr
    mov edx, [eax+0x10]     ; First
    mov [eax+0x08], edx     ; Pointer = First
    xor eax, eax
    ret 0x4
Code is generated only when the first object, and is used by all subsequent objects. The address is stored in the environment variable, as I did in subclassing.