;-------------------------------------------------------------------------------
;  colibrary procedure AllocObject
;
; -------------------------------------------------------
; This procedure was written by Ernest Murphy    9/27/00
;
;  revised 3/3/01 to simplify object creation 
;					and remove ObjectData.m_pEntry0 
;  revised 2/21/01 to remove pCustomData destructor
;
;
; Copyright (c) 9/28/00  Ernest Murphy
; For educational use only. Any commercial re-use only by written license
;
; -------------------------------------------------------
.NOLIST
.386
.model flat, stdcall  ; 32 bit memory model
option casemap :none  ; case sensitive

include     mini_win.inc	; 'just enough' of windows.inc (speeds build)
include     \masm32\include\oleaut32.inc
include     \masm32\include\ole32.inc
include     \masm32\COM\include\oaidl.inc

include     colib.inc

externdef   vtINDUnknown:IUnknown  
externdef   g_ObjectCount:DWORD

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

AllocObject PROC PUBLIC pObject:DWORD,   pClassItem:DWORD, 
                        pUnkOuter:DWORD, pCustomData:DWORD
             
;-------------------------------------------------------------------------------
; implimentation of the AllocObject method
;
;  internal function to create COM objects
;
; EXAMPLE:
;   invoke AllocObject ADDR Object, ADDR ObjectDesc, ADDR UnkOuter, ADDR CustomData
;
; Uses: eax, ecx, edx
;
;
;-------------------------------------------------------------------------------
    LOCAL pBase:DWORD, pIMap:DWORD, pObjectEntry:DWORD
    LOCAL ObjectSize:DWORD, ICount:DWORD

    mov ecx, pObject
    mov eax, NULL
    mov [ecx], eax             ; NULL the returned object
    
    ; now we define all our object data
    mov ecx, pClassItem
    mov ecx, (ClassItem PTR [ecx]).m_pIMap
    mov pIMap, ecx              ; preserve pointer for later
    mov ICount, 0
    ; count how many interfaces we support
    .REPEAT
        ; we're counting 1 too many by assuming next one exists
	  ; but thats OK, cause we need the +1 for the aggregation interface
        mov eax, (InterfaceItem PTR [ecx]).m_refiid
        inc ICount  ; assume next one exists
        add ecx, SIZEOF InterfaceItem
    .UNTIL !eax

    ; now figure the complete object size 
    ; size = SIZEOF ObjectData + (bytes requested) + (IMap + 1) * 8
    mov ecx, pClassItem
    mov eax, (ClassItem PTR [ecx]).m_ObjectSize
    mov ObjectSize, eax
    mov edx, ICount
    rol edx, 3      ; count = count * 8 (8 bytes per InterfaceItem)
    add eax, edx
    add eax, SIZEOF ObjectData
    mov ObjectSize, eax
    invoke CoTaskMemAlloc, eax

    .IF (eax==0)
        ; alloc failed
        mov eax, E_OUTOFMEMORY      ; signal the failure
        ret
    .ENDIF
    ; alloc succeeded
    mov pBase, eax
    inc g_ObjectCount           ; adjust the global lock count
    dec ICount          ; fix ICount for later use

	; compute object entry point ObjectEntry0
	mov ecx, pBase
	mov edx, pClassItem
	add ecx, [edx].ClassItem.m_ObjectSize
	add ecx, SIZEOF ObjectData
	mov pObjectEntry, ecx

    ; init m_RefCount
    mov edx, pBase
    mov (ObjectData PTR [edx]).m_RefCount, 1

    ; init p->delegated IUnknown 
    mov eax, pUnkOuter
    .IF !eax
        mov eax, pObjectEntry                   
    .ENDIF
    mov (ObjectData PTR [edx]).m_pUnkOuter, eax     ; delegated IUnknown

    ; init type info ptr m_pti
    mov (ObjectData PTR [edx]).m_pti, NULL

    ; init the current LCID to a bad lcid
    mov eax, -1
    mov (ObjectData PTR [edx]).m_lcid, eax

    ; init the list to aggregated objects
    mov (ObjectData PTR [edx]).m_pAggList, NULL

    ; init pClassItem ptr to pClassItem
    mov ecx, pClassItem
    mov (ObjectData PTR [edx]).m_pClassItem, ecx

    ; now we set up the Object Entries
    ; INDUnknown first       
    mov edx, pObjectEntry                  ; NDIunk ObjectEntry
    mov ecx, pClassItem
    mov eax, OFFSET vtINDUnknown
    mov (ObjectEntry PTR [edx]).m_pVtbl, eax
    mov eax, pBase
    mov (ObjectEntry PTR [edx]).m_pBase, eax

    ; now the other interfaces
    mov edx, pObjectEntry                  ; NDIunk ObjectEntry
    add edx, SIZEOF ObjectEntry
    mov ecx, pIMap
    .WHILE ICount  
        mov eax, (InterfaceItem PTR [ecx]).m_pVtbl
        mov (ObjectEntry PTR [edx]).m_pVtbl, eax
        mov eax, pBase
        mov (ObjectEntry PTR [edx]).m_pBase, eax
        add edx, SIZEOF ObjectEntry
        add ecx, SIZEOF InterfaceItem
        dec ICount
    .ENDW

    ; now need to call the constructor
    ; get pointer to custom data area in object
    mov ecx, pClassItem
    mov ecx, (ClassItem PTR [ecx]).m_pConstructor
    .IF ecx
        mov eax, pObjectEntry                  ; NDIunk ObjectEntry
        add eax, SIZEOF ObjectEntry            ; cast to IUnknown
        ; and get the constructor ptr
        invoke Constructor PTR ecx, eax, pCustomData       ; and call it
        .IF !eax
            ; Constructor worked
            xor eax, eax    ; mov eax, S_OK
        .ELSE
            ; something went wrong, destroy the newborn object
		invoke CoTaskMemFree, pBase
            mov eax, E_FAIL
            ret
        .ENDIF
    .ENDIF

	; cast the returned object to an IUnknown 
    mov eax, pObjectEntry
    add eax, SIZEOF ObjectEntry
    mov ecx, pObject
    mov [ecx], eax            
    ret
AllocObject ENDP
;-------------------------------------------------------------------------------

end
