Elroy, do you try to pass an UDT as member of UDT? Maybe you have to use the modified class which I provide, which do something simple: It takes a filename using a random name. Also the class can be create any number of objects.
In my Interpreter I have install a way to read/write fields (just for experiment), but not for using arguments to fields (although trick provide a way, I need some more information, to not allow the user to pass arguments to those fields who didn't have arguments). On way to know that, is when the user make an UDT, so I can handle it by using a json or xml to store the info, temporary until the program of user return to immediate mode (which means the actual program, the environment of M2000 as I named it, still running). So I need to read a class some info for UDT (and also Enum variable types, which for now I have also problem to get a list). For now I can read modules/functions/properties of a class and export a list of arguments, although when I found UDT I can't get the name. I am not using typelib to handle.
This is my sub for getting info:
Code:
Public Function GetAllMembers(mList As FastCollection, obj As Object _
) As Boolean
Dim IDsp As IDispatch.IDispatchM2000
Dim IDsp1 As IDispatch.IDispatchM2000
Dim riid As IDispatch.IID
Dim params As IDispatch.DISPPARAMS
Dim Excep As IDispatch.EXCEPINFO
Dim mAttr As TYPEATTR
Set mList = New FastCollection
Dim ppFuncDesc As Long, fncdsc As FUNCDESC, cFuncs As Long
Dim ppVarDesc As Long, vardsc As VARDESC
Dim ParamDesc As TPARAMDESC, hlp As Long, pRefType As Long
Dim TypeDesc As TTYPEDESC, retval$
Dim ret As Long, pctinfo As Long, ppTInfo As Long, typeInf As IUnknown
Dim pAttr As Long
Dim tKind As Long
Set IDsp = obj
Dim cFncs As Long, CVars As Long, ttt$
Dim i As Long
Dim j As Long
Dim strNames() As String, strName As String, aName As String, count As Long
Dim acc As Long, CTX As Long
Const TYPEFLAG_FDUAL = &H40
Const TYPEFLAG_FPREDECLID = &H8
'' may have a GET and a LET for same name
mList.AllowAnyKey
ret = IDsp.GetTypeInfo(ByVal 0, ByVal 0, ppTInfo)
If ppTInfo = 0 Or ret <> 0 Then
If Err Then Err.Clear
Exit Function
End If
If IDsp.GetTypeInfoCount(count) <> &H80004001 Then
Debug.Print ">>>>>>>>>>", count
End If
Set typeInf = ResolveObjPtrNoRef(ppTInfo)
ITypeInfo_GetTypeAttr typeInf, pAttr
If pAttr = 0 Then Set typeInf = Nothing: Exit Function
memcpy mAttr, ByVal pAttr, Len(mAttr)
If (mAttr.wTypeFlags And TYPEFLAG_FPREDECLID) = &H8 Then
ITypeInfo_ReleaseTypeAttr typeInf, pAttr
Set typeInf = Nothing
Exit Function
End If
If (mAttr.wTypeFlags And TYPEFLAG_FDUAL) Then
If mAttr.typekind <> TKIND_DISPATCH Then
ITypeInfo_GetRefTypeOfImplType typeInf, -1, pRefType
ITypeInfo_ReleaseTypeAttr typeInf, pAttr
ITypeInfo_GetRefTypeInfo typeInf, pRefType, ppTInfo
Set typeInf = ResolveObjPtrNoRef(ppTInfo)
ITypeInfo_GetTypeAttr typeInf, pAttr
memcpy mAttr, ByVal pAttr, Len(mAttr)
End If
End If
If TKIND_DISPATCH = mAttr.typekind Then
cFuncs = mAttr.cFuncs '' mAttr.cVars
For j = 0 To mAttr.cFuncs - 1
ITypeInfo_GetFuncDesc typeInf, j, ppFuncDesc
CpyMem fncdsc, ByVal ppFuncDesc, Len(fncdsc)
acc = fncdsc.lprgelemdescParam
ret = ITypeInfo_GetDocumentation(typeInf, fncdsc.memid, strName, vbNullString, CTX, vbNullString)
mList.AddKey UCase(strName), ""
Select Case fncdsc.invkind
Case INVOKE_EVENTFUNC:
strName = "Event " + strName
Case INVOKE_FUNC:
If fncdsc.elemdesc.vt = 24 Then
strName = "Sub " + strName
Else
strName = "Function " + strName
End If
Case INVOKE_PROPERTY_GET:
strName = "Property Get " + strName
Case INVOKE_PROPERTY_PUT:
strName = "Property Let " + strName
Case INVOKE_PROPERTY_PUTREF:
strName = "Property Set " + strName
End Select
mList.ToEnd ' move to last
ProcTask2 Basestack1
hlp = fncdsc.cParams
If hlp > 0 Then
cFncs = 0
ReDim strNames(fncdsc.cParams + 1) As String
aName = vbNullString
ret = ITypeInfo_GetDocumentation(typeInf, fncdsc.memid, aName, vbNullString, CTX, vbNullString)
ret = ITypeInfo_GetNames(typeInf, fncdsc.memid, strNames(), fncdsc.cParams + 1, cFncs)
If Not ret Then
strName = strName + "("
For i = 1 To hlp
If IsBadCodePtr(acc) = 0 Then
CopyBytes Len(ParamDesc), VarPtr(ParamDesc), ByVal acc + 8
CopyBytes Len(TypeDesc), VarPtr(TypeDesc), ByVal acc
End If
acc = acc + 16
ttt$ = ""
retval$ = ""
If strNames(i) = "" Then strNames(i) = "Value"
If (ParamDesc.wParamFlags And PARAMFLAG_FRETVAL) = &H8 Then
retval$ = " as " + stringifyTypeDesc(TypeDesc, typeInf)
Else
If (ParamDesc.wParamFlags And PARAMFLAG_FIN) = &H1 Then ttt$ = "in "
If (ParamDesc.wParamFlags And PARAMFLAG_FOUT) = &H2 Then ttt$ = ttt$ + "out "
If i > (hlp - fncdsc.cParamsOpt) And fncdsc.cParamsOpt <> 0 Then
strName = strName + "[" + ttt$ + strNames(i) + " " + stringifyTypeDesc(TypeDesc, typeInf) + "]"
Else
If fncdsc.cParamsOpt = 0 And (ParamDesc.wParamFlags And PARAMFLAG_FOPT) > 0 Then
strName = strName + "[" + ttt$ + strNames(i) + " " + stringifyTypeDesc(TypeDesc, typeInf) + "]"
Else
strName = strName + ttt$ + strNames(i) + " " + stringifyTypeDesc(TypeDesc, typeInf)
End If
End If
If i < hlp Then strName = strName + ", "
End If
Next i
strName = strName + ")"
End If
End If
If retval$ = "" Then
If fncdsc.elemdesc.vt = 24 Then
mList.Value = strName
Else
CopyBytes Len(TypeDesc), VarPtr(TypeDesc), VarPtr(fncdsc.elemdesc.pTypeDesc)
mList.Value = strName + " as " + stringifyTypeDesc(TypeDesc, typeInf)
End If
Else
mList.Value = strName + retval$
End If
ITypeInfo_ReleaseFuncDesc typeInf, ppFuncDesc
Next j
ReDim strNames(1) As String
End If
ITypeInfo_ReleaseTypeAttr typeInf, pAttr
Set typeInf = Nothing
Set IDsp = Nothing
GetAllMembers = True
End Function
I use a way to call a function on an interface by using offset of vtable. So ITypeInfo_GetTypeAttr is a call on &HC offset for that object (if the obj is a ITypeInfo)
See the double Varptr(), pReturn need one, but because point to ppTypeAttr need another from ppTypeAttr.
I found this way from modUnregCOM from Trick (2015)
Code:
Public Sub ITypeInfo_GetTypeAttr( _
ByVal obj As IUnknown, _
ByRef ppTypeAttr As Long)
Dim resultCall As Long
Dim pReturn As Variant
ppTypeAttr = 0
pReturn = VarPtr(ppTypeAttr)
resultCall = DispCallFunc(obj, &HC, CC_STDCALL, vbEmpty, 1, vbLong, VarPtr(pReturn), 0)
If ppTypeAttr = 0 Then Exit Sub
If resultCall Then Err.Raise resultCall
End Sub