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.