www.pudn.com > VB-KAOQINXITONG.zip > modFunctionDelegator.bas


Attribute VB_Name = "modFunctionDelegator" 
'*************************************************************** 
' (c) Copyright 2000 Matthew J. Curland 
' 
' This file is from the CD-ROM accompanying the book: 
' Advanced Visual Basic 6: Power Techniques for Everyday Programs 
'   Author: Matthew Curland 
'   Published by: Addison-Wesley, July 2000 
'   ISBN: 0-201-70712-8 
'   http://www.PowerVB.com 
' 
' You are entitled to license free distribution of any application 
'   that uses this file if you own a copy of the book, or if you 
'   have obtained the file from a source approved by the author. You 
'   may redistribute this file only with express written permission 
'   of the author. 
' 
' This file depends on: 
'   References: 
'     VBoostTypes6.olb (VBoost Object Types (6.0)) 
'   Files: 
'     None 
'   Minimal VBoost conditionals: 
'     None 
'   Conditional Compilation Values: 
'     FUNCTIONDELEGATOR_NOSTACK = 1 'eliminates support for InitDelegator 
'     FUNCTIONDELEGATOR_NOHEAP = 1  'eliminates support for NewDelegator 
' 
' This file is discussed in Chapter 11. 
'*************************************************************** 
Option Explicit 
 
'Here's the magic asm for doing the function pointer call. 
'The stack comes in with the following: 
'esp: return address 
'esp + 4: this pointer for FunctionDelegator 
'All that we need to do is remove the this pointer from the 
'stack, replace it with the return address, then jmp to the 
'correct function.  In other words, we're just squeezing the 
'this pointer completely out of the picture. 
'The code is: 
'pop ecx (stores return address) 
'pop eax (gets the this pointer) 
'push ecx (restores the return address) 
'jmp DWORD PTR [eax + 4] (jump to address at this + 4, 3 byte instruction) 
'The corresponding byte stream for this is: 59 58 51 FF 60 04 
'We pad these six bytes with two int 3 commands (CC CC) to get eight 
'bytes, which can be stored in a Currency constant. 
'Note that the memory location of this constant is not executable, so 
'it must be copied into a currency variable.  The address of the variable 
'is then used as the forwarding function. 
 
#Const STACKALLOCSUPPORT = FUNCTIONDELEGATOR_NOSTACK = 0 
#Const HEAPALLOCSUPPORT = FUNCTIONDELEGATOR_NOHEAP = 0 
 
Private Const cDelegateASM As Currency = -368956918007638.6215@ 
 
Private m_DelegateASM As Currency 
 
Private Type DelegatorVTables 
    VTable(7) As Long 'OKQI vtable in 0 to 3, FailQI vtable in 4 to 7 
End Type 
 
#If STACKALLOCSUPPORT Then 
'Structure for a stack allocated Delegator 
Private m_VTables As DelegatorVTables 
Private m_pVTableOKQI As Long       'Pointer to vtable, no allocation version 
Private m_pVTableFailQI As Long     'Pointer to vtable, no allocation version 
Public Type FunctionDelegator 
    pVTable As Long  'This has to stay at offset 0 
    pfn As Long      'This has to stay at offset 4 
End Type 
#End If 'STACKALLOCSUPPORT 
 
#If HEAPALLOCSUPPORT Then 
'Structure for a heap allocated Delegator 
Private m_VTablesHeapAlloc As DelegatorVTables 
Private m_pVTableHeapAllocOKQI As Long   'Pointer to vtable, heap version 
Private m_pVTableHeapAllocFailQI As Long 'Pointer to vtable, heap version 
Private Type FunctionDelegatorHeapAlloc 
    pVTable As Long  'This has to stay at offset 0 
    pfn As Long      'This has to stay at offset 4 
    cRefs As Long 
End Type 
#End If 'HEAPALLOCSUPPORT 
 
#If STACKALLOCSUPPORT Then 
'Functions to initialize a Delegator object on an existing FunctionDelegator 
Public Function InitDelegator(Delegator As FunctionDelegator, Optional ByVal pfn As Long) As IUnknown 
    If m_pVTableOKQI = 0 Then InitVTables 
    With Delegator 
        .pVTable = m_pVTableOKQI 
        .pfn = pfn 
    End With 
    CopyMemory InitDelegator, VarPtr(Delegator), 4 
End Function 
 
Private Sub InitVTables() 
Dim pAddRefRelease As Long 
    With m_VTables 
        .VTable(0) = FuncAddr(AddressOf QueryInterfaceOK) 
        .VTable(4) = FuncAddr(AddressOf QueryInterfaceFail) 
        pAddRefRelease = FuncAddr(AddressOf AddRefRelease) 
        .VTable(1) = pAddRefRelease 
        .VTable(5) = pAddRefRelease 
        .VTable(2) = pAddRefRelease 
        .VTable(6) = pAddRefRelease 
        m_DelegateASM = cDelegateASM 
        .VTable(3) = VarPtr(m_DelegateASM) 
        .VTable(7) = .VTable(3) 
        m_pVTableOKQI = VarPtr(.VTable(0)) 
        m_pVTableFailQI = VarPtr(.VTable(4)) 
    End With 
End Sub 
 
Private Function QueryInterfaceOK(This As FunctionDelegator, riid As Long, pvObj As Long) As Long 
    pvObj = VarPtr(This) 
    This.pVTable = m_pVTableFailQI 
End Function 
Private Function AddRefRelease(ByVal This As Long) As Long 
    'Nothing to do, memory not refcounted 
End Function 
#End If 'STACKALLOCSUPPORT 
 
#If HEAPALLOCSUPPORT Then 
'Functions to create a refcounted version of the function pointer wrapper object 
Public Function NewDelegator(ByVal pfn As Long) As IUnknown 
Dim Struct As FunctionDelegatorHeapAlloc 
Dim ThisPtr As Long 
    If m_pVTableHeapAllocOKQI = 0 Then InitHeapAllocVTables 
    With Struct 
        ThisPtr = CoTaskMemAlloc(LenB(Struct)) 
        If ThisPtr = 0 Then Err.Raise 7 
        .pVTable = m_pVTableHeapAllocOKQI 
        .cRefs = 1 
        .pfn = pfn 
        CopyMemory ByVal ThisPtr, Struct, LenB(Struct) 
        CopyMemory NewDelegator, ThisPtr, 4 
    End With 
End Function 
 
Private Sub InitHeapAllocVTables() 
    With m_VTablesHeapAlloc 
        .VTable(0) = FuncAddr(AddressOf QueryInterfaceHeapAllocOK) 
        .VTable(4) = FuncAddr(AddressOf QueryInterfaceFail) 
        .VTable(1) = FuncAddr(AddressOf AddRefHeapAlloc) 
        .VTable(5) = .VTable(1) 
        .VTable(2) = FuncAddr(AddressOf ReleaseHeapAlloc) 
        .VTable(6) = .VTable(2) 
        m_DelegateASM = cDelegateASM 
        .VTable(3) = VarPtr(m_DelegateASM) 
        .VTable(7) = .VTable(3) 
        m_pVTableHeapAllocOKQI = VarPtr(.VTable(0)) 
        m_pVTableHeapAllocFailQI = VarPtr(.VTable(4)) 
    End With 
End Sub 
 
Private Function QueryInterfaceHeapAllocOK(This As FunctionDelegatorHeapAlloc, riid As Long, pvObj As Long) As Long 
    With This 
        pvObj = VarPtr(.pVTable) 
        .cRefs = .cRefs + 1 
        .pVTable = m_pVTableHeapAllocFailQI 
    End With 
End Function 
 
Private Function AddRefHeapAlloc(This As FunctionDelegatorHeapAlloc) As Long 
    With This 
        .cRefs = .cRefs + 1 
        AddRefHeapAlloc = .cRefs 
    End With 
End Function 
 
Private Function ReleaseHeapAlloc(This As FunctionDelegatorHeapAlloc) As Long 
    With This 
        .cRefs = .cRefs - 1 
        ReleaseHeapAlloc = .cRefs 
        If .cRefs = 0 Then 
            'Don't try to step over FreeBuffer, we're freeing 
            'This, and the debugger could die. 
            CoTaskMemFree VarPtr(.pVTable) 
        End If 
    End With 
End Function 
#End If 'HEAPALLOCSUPPORT 
 
Private Function QueryInterfaceFail(ByVal This As Long, riid As Long, pvObj As Long) As Long 
    pvObj = 0 
    QueryInterfaceFail = E_NOINTERFACE 
End Function 
 
Private Function FuncAddr(ByVal pfn As Long) As Long 
    FuncAddr = pfn 
End Function