;--------------------------------------------------------------------------
; IEnumVariant implimentation
;   
;  this interface is meant to be a general purpose enumeration object.
;  The object is created with a pointer to it's custom data list.
;--------------------------------------------------------------------------
; Implimentors data:
;  the following items must be included in the implimentor's files:
;
; USEAGE:
;
; ; create an enumerator object
; invoke AllocObject, ADDR pIEnum, pObjectItem, NULL, ADDR pItemList
;
; pItemList referes to a IEnumVariantListHeader struct, followed by a 
;  series of variant types. 
;
; EXAMPLE:
;
;IEnumVariantListHeader STRUCT 
;    cListSize       DWORD   0               ; size of list in bytes       
;    ElementSize     DWORD   SIZEOF VARIANT  ; size of one element in bytes       
;    cItems          DWORD   0               ; size of list in elements       
;IEnumVariantListHeader ENDS    
;   element1        VARTANT { }
;   element2        VARTANT { }
;   ...
;   elementN        VARTANT { }
;
;
;--------------------------------------------------------------------------

.NOLIST
.386
.model flat, stdcall  ; 32 bit memory model
option casemap :none  ; case sensitive

;--------------------------------------------------------------------------
include     \masm32\include\windows.inc	
include     \masm32\include\oleaut32.inc
include     \masm32\include\ole32.inc
include     \masm32\include\masm32.inc
include     \masm32\include\kernel32.inc
include     \masm32\COM\include\oaidl.inc
include     \masm32\COM\include\colib.inc
include     cEnumVar.inc

PUBLIC      EnumVariantClassItem
PUBLIC      IID_IEnumVARIANT
PUBLIC      vtIEnumVariant
PUBLIC      IEnumVariantIMap

;--------------------------------------------------------------------------
.data

DeclareGuid         IID_IEnumVARIANT

EnumVariantClassItem _EnumVariantClassItem { }

IEnumVariantIMap InterfaceItem {pIID_IEnumVARIANT, OFFSET vtIEnumVariant}
    END_INTERFACE_MAP

vtIEnumVariant      IEnumVariant { AddRef, Release, QueryInterface, , , , }

;--------------------------------------------------------------------------
.code

;--------------------------------------------------------------------------
; IEnumVariant implimentation
;--------------------------------------------------------------------------
CreateIEnumVariant PROC PUBLIC this_, pCustomData:DWORD

    ; Need to point our list 
    ; to the correct place (**List in pCustomData)
    pObjectData this_, ecx          ; keep pData in ecx
    mov eax, pCustomData
    mov (IEnumVariantData PTR [ecx]).m_ppList, eax
    ; we use a 1-based list to keep automation script controllers happy
    ; (we also use NULL to indicate a bad list pointer)
    ; so reset the count
    mov (IEnumVariantData PTR [ecx]).m_cCurrent, 1
    xor eax, eax
    ret
CreateIEnumVariant ENDP

;--------------------------------------------------------------------------
IEnumVariant_Clone PROC PUBLIC this_:DWORD, ppen:DWORD
    LOCAL pData:DWORD, pClassItem:DWORD, pCustomData:DWORD, pObj:DWORD
    
    ; Creates another connection point enumerator with the same state 
    ; as the current enumerator to iterate over the same list. 

    ; walk to get the ClassItem reference
    pObjectBase this_, ecx          ; ref to the rairly used Object Data struct
    mov eax, (ObjectData PTR[ecx]).m_pClassItem     
    mov pClassItem, eax
    pObjectData this_, ecx          ; keep pData in ecx
    mov pData, eax
    mov eax, (IEnumVariantData PTR[ecx]).m_ppList
    mov pCustomData, eax
    ; create a brand-new object of our type
    ; it's identicle except for the internal count state
    ; because it's created as count = 1
    invoke AllocObject, ppen, pClassItem, NULL, pCustomData
    .IF_SUCCEEDED
        ; now we need to set the count to ours
        mov ecx, pData
        mov ecx, (IEnumVariantData PTR[ecx]).m_cCurrent
        mov edx, ppen
        mov edx, [edx]
        mov pObj, edx       ; put object reference where we can use it
        coinvoke pObj, IEnumVariant, Skip, ecx
        xor eax, eax        ; return S_OK
    ;ELSE
        ; AllocObject failed, just return the same fauling retval
    .ENDIF
    ret
IEnumVariant_Clone ENDP
;--------------------------------------------------------------------------
IEnumVariant_Next PROC PUBLIC this_:DWORD, celt:DWORD, 
                       rgelt:DWORD, pceltFetched:DWORD 

    LOCAL  pNewList:DWORD, pData:DWORD, pListHeader:DWORD,pList:DWORD
    LOCAL  cCopyItems:DWORD, cCopyBytes:DWORD,pCurrentItem:DWORD
    LOCAL  retval:DWORD
    ; celt The number of elements to retrieve. 
    ; rgelt The elements retrieved. 
    ; pceltFetched The actual number of elements retrieved. 

    ; cCopyBytes:   count of bytes to return (sizeof DataCopy)
    ; cCopyItems:   count of items to return 
    ; pNewList:     ptr to newly created heap for return params
    ; pCurrentItem: ptr to existing cCount item in list
    ; pData:        ptr to object data area
    ; pListHeader:  ptr to list of variants (header)
    ; pList:        ptr to list of variants itself
    ; retval:       holds the return val
    ;
    ; Retrieves the next celt items in the enumeration sequence. 
    ;  If there are fewer than the requested number of elements left 
    ;  in the sequence, it retrieves the remaining elements. 
    ;   
    ; The number of elements actually retrieved is returned through 
    ;  pceltFetched (unless the caller passed in NULL for that parameter),
    ;  pointer to the number of elements actually supplied in rgelt. 
    ;  Caller can pass in NULL if celt is one. 
    ;
    ;    Return Value
    ;       S_OK if the number of elements supplied is celt
    ;       E_POINTER for the various pointer errors
    ;       S_FALSE otherwise.
    
    ; pointer check
    .IF !pceltFetched
        .IF celt != 1
            ; pceltFetched may be = NULL only if celt = 1
            mov eax, E_POINTER
            ret
        .ENDIF
    .ENDIF
    .IF !rgelt
        ; rgelt may be not be NULL 
        mov eax, E_POINTER
        ret
    .ENDIF
    mov retval, S_OK
    pObjectData this_, ecx          ; keep pData in ecx
    mov pData, ecx
    mov eax, (IEnumVariantData PTR [ecx]).m_cCurrent
    .IF eax == NULL
        ; we're all done, list pointer already beyond end of list
        mov ecx, pceltFetched
        xor eax, eax
        mov [ecx], eax      ; zero the rgelt return count
        mov ecx, rgelt
        mov [ecx], eax      ; be nice, null the return pointer
        mov eax, S_FALSE
        ret
    .ENDIF
    ; OK, so let's go to work
    ; get the list reference
    mov ecx, pData
    mov edx, (IEnumVariantData PTR [ecx]).m_ppList
    mov edx, [edx]  ; defererence the pointer (now have address of list itself in edx)
    ; and save them
    mov pListHeader, edx
    mov pList, edx
    add pList, SIZEOF IEnumVariantListHeader
    mov eax, celt
    add eax, (IEnumVariantData PTR [ecx]).m_cCurrent
    dec eax
    mov ecx, (IEnumVariantListHeader PTR [edx]).cItems
    .IF eax > ecx
        ; nope, don't have celt items left to send
        ; adjust the total
        mov eax, (IEnumVariantListHeader PTR [edx]).cItems
        mov ecx, pData
        sub eax, (IEnumVariantData PTR [ecx]).m_cCurrent
        inc eax
        mov retval, S_FALSE
    .ELSE
        mov eax, celt
    .ENDIF
    ; count in eax is now fine, use it
    mov cCopyItems, eax
    imul eax, SIZEOF VARIANT
    mov cCopyBytes, eax
    mov ecx, pData
    mov eax, (IEnumVariantData PTR [ecx]).m_cCurrent
    dec eax
    imul eax, SIZEOF VARIANT
    add eax, pList
    mov pCurrentItem, eax
    invoke CoTaskMemAlloc, cCopyBytes
    mov pNewList, eax
    mov ecx, rgelt
    mov [ecx], eax      ; set the rgelt returned list
    invoke MemCopy, pCurrentItem, pNewList, cCopyBytes
    ; now increase current count 
    mov ecx, pData
    mov eax, cCopyItems
    add (IEnumVariantData PTR [ecx]).m_cCurrent, eax    
    ; "Check for IUnk and Addref"
    .WHILE cCopyItems > 0
        mov ecx, pNewList
        movsx eax, (VARIANT PTR [ecx]).vt
        .IF (eax == VT_UNKNOWN) || (eax == VT_DISPATCH)
            ; oops, we got interface pointers here.
            ; gotta AddRef em before we return them  (Rule A3)
            mov ecx, (VARIANT PTR [ecx]).punkVal
            coinvoke ecx, IUnknown, AddRef
        .ENDIF
        add pNewList, SIZEOF VARIANT
        dec cCopyItems
    .ENDW        
    mov eax, retval
    ret
IEnumVariant_Next ENDP

;--------------------------------------------------------------------------
IEnumVariant_Skip PROC PUBLIC this_:DWORD, celt:DWORD 
    LOCAL pData:DWORD
    
    ; Instructs the enumerator to skip the next cConnections elements in 
    ; the enumeration so that the next call to IEnumVariant::Next will not 
    ; return those elements. 

    pObjectData this_, ecx          ; keep pData in ecx
    mov pData, eax
    mov eax, (IEnumVariantData PTR [ecx]).m_cCurrent
    .IF eax != NULL
        ; get list reference
        mov edx, (IEnumVariantData PTR [ecx]).m_ppList
        mov edx, [edx]  ; defererence the pointer (now have address of list in edx)
        ; bump the index by celt
        mov eax, celt
        add (IEnumVariantData PTR [ecx]).m_cCurrent, eax
        ; see if we went too far
        mov eax, (IEnumVariantListHeader PTR [edx]).cListSize
        mov edx, (IEnumVariantData PTR [ecx]).m_cCurrent
        .IF edx > eax
            ; we went past the end of our list,
            ; set the pointer to BAD (NULL)
            mov (IEnumVariantData PTR [ecx]).m_cCurrent, NULL
        .ENDIF
    ;ELSE
        ; we're all done, list pointer already beyond end of list
    .ENDIF
    xor eax, eax        ; return S_OK
    ret
IEnumVariant_Skip ENDP

;--------------------------------------------------------------------------
IEnumVariant_Reset PROC PUBLIC this_:DWORD
    ; Instructs the enumerator to position itself at the beginning 
    ; of the list of elements. 

    pObjectData this_, ecx
    ; we use a 1-based list to keep automation script controllers happy
    mov (IEnumVariantData PTR [ecx]).m_cCurrent, 1
    xor eax, eax        ; return S_OK
    ret
IEnumVariant_Reset ENDP
;--------------------------------------------------------------------------

;ENDIF
 end        ;IEnumVariantImpl