Results 1 to 3 of 3

Thread: Calling Public Method of lightweight Class

  1. #1

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2013
    Posts
    658

    Calling Public Method of lightweight Class

    Hi,

    I have been experimenting with some code I found on the internet that creates a lightweight com object in memory.

    I have slightly amended the original code in order to add a Public Method named (MyMethod) and I would like to know how to call this Method once the lightweight class has been instanciated.


    Here is the lightweight object code in a bas module :
    Code:
    Option Explicit
    
    Private Type IID
        Data1 As Long
        Data2 As Integer
        Data3 As Integer
        Data4(0 To 7) As Byte
    End Type
    
    ' Instance data
    Private Type MyClassData
        vtblPtr As Long
        RefCount As Long
    #If Win64 Then ' If it is a 64-bit version
        Padding As Long
    #End If
    End Type
    
    'Virtual function table data
    Private Type IUnknownVtbl
        QueryInterface As Long
        AddRef As Long
        Release As Long
        MyMethod As Long
    End Type
    
    Private Const S_OK As Long = 0
    Private Const E_NOINTERFACE As Long = &H80004002
    Private Const E_POINTER As Long = &H80004003
    
    Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" _
        (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
    
    Public Declare Function CoTaskMemAlloc Lib "ole32.dll" _
        (ByVal cb As Long) As Long
    
    Public Declare Sub CoTaskMemFree Lib "ole32.dll" _
        (ByVal pv As Long)
    
    
    ' Variables that keep your own instance in while VBA is running
    Dim m_unk As IUnknown
    
    'Function used to assign a function address to a variable
    Private Function GetAddressOf(ByVal func As Long) As Long
        GetAddressOf = func
    End Function
    
    ' Returns a pointer to the combined size of MyClassData and IUnknownVtbl
    Private Function CreateInstanceMemory() As Long
        Dim p As Long, d As MyClassData, v As IUnknownVtbl
    
        ' 'Create data that matches the size of MyClassData and IUnknownVtbl
        p = CoTaskMemAlloc(Len(d) + Len(v))
        If p <> 0 Then
            'Make sure the first reference count is 1.
            d.RefCount = 1
           'Since IUnknownVtbl is placed immediately after MyClassData, set p to the address obtained by adding the size of MyClassData.
            d.vtblPtr = p + Len(d)
            'Fill the beginning of the allocated memory block with MyClassData data
            Call CopyMemory(ByVal p, d, Len(d))
            'Create virtual function table
            v.QueryInterface = GetAddressOf(AddressOf My_QueryInterface)
            v.AddRef = GetAddressOf(AddressOf My_AddRef)
            v.Release = GetAddressOf(AddressOf My_Release)
            v.MyMethod = GetAddressOf(AddressOf My_Method)
    
           'Copy the virtual function table to the p + Len (d) part
            Call CopyMemory(ByVal d.vtblPtr, v, Len(v))
        End If
        CreateInstanceMemory = p
    End Function
    
    
    'HRESULT STDMETHODCALLTYPE QueryInterface (THIS_ REFIID refiid, LPVOID FAR * ppv)
    'A function called when requesting a conversion to another interface
    '(ppv is defined by ByVal to check NULL just in case)
    Private Function My_QueryInterface(ByVal This As Long, ByRef refiid As IID, ByVal ppv As Long) As Long
        Debug.Print "My_QueryInterface"
        If ppv = 0 Then
            Debug.Print "  E_POINTER"
            My_QueryInterface = E_POINTER
            Exit Function
        End If
        ' IID_IUnknown: {00000000-0000-0000-C000-000000000046} Check if
        If refiid.Data1 = 0 And refiid.Data2 = 0 And refiid.Data3 = 0 And _
            refiid.Data4(0) = &HC0 And refiid.Data4(1) = 0 And _
            refiid.Data4(2) = 0 And refiid.Data4(3) = 0 And _
            refiid.Data4(4) = 0 And refiid.Data4(5) = 0 And _
            refiid.Data4(6) = 0 And refiid.Data4(7) = &H46 Then
            'For IID_IUnknown, copy the address of This (the value of This) to the tip of the pointer pointed to by ppv.
            Debug.Print "  IID_IUnknown"
            Call CopyMemory(ByVal ppv, This, Len(This))
            'Further increase the reference count
            Call My_AddRef(This)
            My_QueryInterface = S_OK
            Exit Function
        End If
       'Only IID_IUnknown is supported
        Debug.Print "  E_NOINTERFACE"
        My_QueryInterface = E_NOINTERFACE
    End Function
    
    
    'ULONG STDMETHODCALLTYPE AddRef (THIS)
    'A function called to increase the reference count
    Private Function My_AddRef(ByVal This As Long) As Long
        Dim d As MyClassData
        'Copy the instance data to d once and
        'Write back after increasing the reference count
        Call CopyMemory(d, ByVal This, Len(d))
        d.RefCount = d.RefCount + 1
        Debug.Print "My_AddRef: new RefCount ="; d.RefCount
        Call CopyMemory(ByVal This, d, Len(d))
        'The return value is a reference count
        My_AddRef = d.RefCount
    End Function
    
    
    'ULONG STDMETHODCALLTYPE Release (THIS)
    'Function called to decrement the reference count (discard when 0)
    Private Function My_Release(ByVal This As Long) As Long
        Dim d As MyClassData
      'Copy the instance data to d once and
        'Write back after reducing the reference count
        Call CopyMemory(d, ByVal This, Len(d))
        d.RefCount = d.RefCount - 1
        Debug.Print "My_Release: new RefCount ="; d.RefCount
        Call CopyMemory(ByVal This, d, Len(d))
        'When the reference count reaches 0, destroy it with CoTaskMemFree.
        If d.RefCount = 0 Then
            Call CoTaskMemFree(This)
           'Call the end function
            Call OnExit
        End If
        'The return value is a reference count
        My_Release = d.RefCount
    End Function
    
    'ONLY PUBLIC METHOD
    Private Function My_Method(ByVal This As Long) As Long
            MsgBox "my method"
    End Function

    And here is how I create the lightweight class instance:
    Code:
    Public Sub Test()
    
        Dim p As Long
        Dim unk As IUnknown
    
       'Create an instance
        p = CreateInstanceMemory()
        If p = 0 Then Exit Sub
    
       'Set unk to the instance pointed to by p
        Call CopyMemory(unk, p, Len(p))
    
        'Call MyMethod (Only Public Method)
        Call unk.MyMethod  '<==== Won't Work
    
    End Sub
    The instance is created successfully but, I don't know how to call the only Public Method of the Class after instanciation.

    Can someone tell me how I should proceed ?

    Thanks.

  2. #2
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,169

    Re: Calling Public Method of lightweight Class

    You need a real interface declared in a typelib for this to work.

    Your Dim unk As IUnknown must be changed to Dim unk As IMyInterface so that unk.MyMethod is available in IDE and the compiler knows how to generate callsite for it -- mostly needs to know method offset in interface vtable and number+type of params+retval.

    Or you can try DispCallByVtbl function available here in the forums which can call any method by index without having the actual interface referenced in the IDE.

    cheers,
    </wqw>

  3. #3

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2013
    Posts
    658

    Re: Calling Public Method of lightweight Class

    Quote Originally Posted by wqweto View Post
    You need a real interface declared in a typelib for this to work.

    Your Dim unk As IUnknown must be changed to Dim unk As IMyInterface so that unk.MyMethod is available in IDE and the compiler knows how to generate callsite for it -- mostly needs to know method offset in interface vtable and number+type of params+retval.

    Or you can try DispCallByVtbl function available here in the forums which can call any method by index without having the actual interface referenced in the IDE.

    cheers,
    </wqw>
    Thanks.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width