Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

iTypeInfo - Get info from TypeAttr Struct - Enum Com Members


  • Please log in to reply
10 replies to this topic
jethrow
  • Moderators
  • 2854 posts
  • Last active: May 17 2017 01:57 AM
  • Joined: 24 May 2009
I am trying to get information from the TypeAttr Structure, which is obtained from the iTypeInfo Interface of a Com Object. My previous attempts can be found here, though I thought it was appropriate to start a new thread. Here is what I have for getting the TypeAttr Structure (hopefully this is correct):
sd := ComObjCreate("Scripting.Dictionary")
pti := GetTypeInfo(sd)

GetTypeAttr := vTable(pti, 3)
DllCall(GetTypeAttr, "ptr",pti, "ptr*",typeAttr)

MsgBox, %typeAttr% ; <-- pointer to the typeAttr structure?

; cleanup:
ReleaseTypeAttr := vTable(pti, 19)
DllCall(ReleaseTypeAttr, "ptr",pti, "ptr",typeAttr)
ObjRelease(pti)



GetTypeInfo(ptr) {
    if ComObjType(ptr)=9
        ptr := ComObjValue(ptr)
    GetTypeInfo := vTable(ptr, 4)
    if DllCall(GetTypeInfo, "ptr",ptr, "uint",0, "uint",0, "ptr*",pti)=0
        return, pti
}
vTable(ptr, n) { ; see ComObjQuery documentation
    return NumGet(NumGet(ptr+0), n*A_PtrSize)
}
Specifically, I was tyring to access the cFuncs field. I would think it would be something along the lines of:
MsgBox, % NumGet(typeAttr, 8*A_PtrSize)
Any help/guidance would be appreciated :) .

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
I don't have much time at the moment, but I think this will help: the guid field is GUID structure, not a pointer to a GUID structure.

jethrow
  • Moderators
  • 2854 posts
  • Last active: May 17 2017 01:57 AM
  • Joined: 24 May 2009

I don't have much time at the moment ...

Thanks for the response. I was hoping there might be others with the means to help as well ... please don't feel any obligation to respond.

... the guid field is GUID structure, not a pointer to a GUID structure.

So you're saying that in my previous post, the variable typeAttr is actually a GUID? Is there any way I could test that to know it's a GUID? I had suspected this since it was used with QueryInterface in ws4ahk. So then, do I have to set up my GetTypeAttr call differently to get the typeAttr structure?
VarSetCapacity(typeAttr, 100) ; not sure what capacity to even request
GetTypeAttr := vTable(pti, 3)
DllCall(GetTypeAttr, "ptr",pti, "ptr",&typeAttr)

MsgBox, % NumGet(typeAttr) ; <--- GUID?
... or is this entirely the wrong direction. Sorry if this all sounds ignorant - working with DllCalls & Structures is a weak point of mine as of now ...

...is there any way to check if a property is read-only (other than by catching the COM error that occurs) ?

Probably, via ITypeInfo.

... fragman's question is what re-engaged me in trying to figure this out.

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005

Is there any way I could test that to know it's a GUID?


I am guessing that you could try StringFromGUID2()

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

So you're saying that in my previous post, the variable typeAttr is actually a GUID?

No, I was talking about "the guid field" at the start of the TYPEATTR structure. Since a GUID is 16 bytes, the second field is at offset 16. Add the size of each field between that and cFuncs, and you have the offset of cFuncs. Only one field is a pointer, so 8*A_PtrSize is obviously incorrect.

jethrow
  • Moderators
  • 2854 posts
  • Last active: May 17 2017 01:57 AM
  • Joined: 24 May 2009

Thanks for the insight :) . Here's what I was trying to accomplish (64-bit only):

InvKind := {1:"[method]", 2:"[get]", 4:"[put]", 8:"[putref]"} ; <-- ?
sd := ComObjCreate("Scripting.Dictionary")
pti := GetTypeInfo(sd)

{ ; Com Methods
   GetTypeAttr := vTable(pti, 3)
   ReleaseTypeAttr := vTable(pti, 19)
   GetFuncDesc := vTable(pti, 5)
   ReleaseFuncDesc := vTable(pti, 20)
   GetDocumentation := vTable(pti, 12)
}
{ ; get cFuncs (number of functions)
   DllCall(GetTypeAttr, "ptr",pti, "ptr*",typeAttr)
   cFuncs := NumGet(typeAttr+0, 48, "short")
   DllCall(ReleaseTypeAttr, "ptr",pti, "ptr",typeAttr)
}
Loop, %cFuncs% { ; get Member IDs
   DllCall(GetFuncDesc, "ptr",pti, "int",A_Index-1, "ptr*",FuncDesc)
   ID := NumGet(FuncDesc+0, "short") ; get Member ID
   n := NumGet(FuncDesc+0, 28, "short") ; get InvKind
   ; Args := NumGet(FuncDesc+0, 36, "short") ; get Num of Args
   ; Opt := NumGet(FuncDesc+0, 38, "short") ; get Num of Opt Args
   DllCall(ReleaseFuncDesc, "ptr",pti, "ptr",FuncDesc)
   DllCall(GetDocumentation, "ptr",pti, "int",ID, "ptr*",Name, "ptr",0, "ptr",0, "ptr",0)
   t .= ID "`t" StrGet(Name) " " InvKind[n] "`n"
}
{ ; formatting & cleanup
   t := SubStr(t,1,-1)
   Sort, t, ND`n
   ObjRelease(pti)
}
MsgBox, , % ComObjType(sd, "Name") " Interface", % "ID`tName`n-----------------------------`n" t


GetTypeInfo(ptr) {
   if ComObjType(ptr)=9
      ptr := ComObjValue(ptr)
   GetTypeInfo := vTable(ptr, 4)
   if DllCall(GetTypeInfo, "ptr",ptr, "uint",0, "uint",0, "ptr*",pti)=0
      return, pti
}
vTable(ptr, n) { ; see ComObjQuery documentation
   return NumGet(NumGet(ptr+0), n*A_PtrSize)
}

I couldn't find any documentation for the values returned from the InvKind field, so I guessed what they were. Also, this example won't return inherited members. For example, the iWebBrowser2 Interface won't return members from the iWebBrowser Interface or the IWebBrowserApp interface using the code above. That would be the next step. Any advice/criticizms would be appreciated. Again, thank for the responses :) .



Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
INVOKEKIND Enumeration - looks like you guessed right.
ITypeInfo::GetRefTypeOfImplType - for "the next step."

jethrow
  • Moderators
  • 2854 posts
  • Last active: May 17 2017 01:57 AM
  • Joined: 24 May 2009

INVOKEKIND Enumeration - looks like you guessed right.
ITypeInfo::GetRefTypeOfImplType - for "the next step."

Genius ... :)
sd := ComObjCreate("Shell.Explorer")
pti := GetTypeInfo(sd)
MsgBox, , % ComObjType(sd, "Name") " Interface", % "ID`tName`n-----------------------------`n" EnumComMembers(pti)
 
EnumComMembers(pti) { ; releases ITypeInfo Interface
                static InvKind := {1:"[method]", 2:"[get]", 4:"[put]", 8:"[putref]"}
                { ; Com Methods
                                GetTypeAttr := vTable(pti, 3)
                                ReleaseTypeAttr := vTable(pti, 19)
                                GetRefTypeOfImplType := vTable(pti, 8)
                                GetRefTypeInfo := vTable(pti, 14)
                                GetFuncDesc := vTable(pti, 5)
                                ReleaseFuncDesc := vTable(pti, 20)
                                GetDocumentation := vTable(pti, 12)
                }
                { ; get cFuncs (number of functions)
                                DllCall(GetTypeAttr, "ptr",pti, "ptr*",typeAttr)
                                cFuncs := NumGet(typeAttr+0, 40+A_PtrSize, "short")
                                cImplTypes := NumGet(typeAttr+0, 44+A_PtrSize, "short")
                                DllCall(ReleaseTypeAttr, "ptr",pti, "ptr",typeAttr)
                }
                if cImplTypes { ; Get Inherited Class (cImplTypes should be true)
                                DllCall(GetRefTypeOfImplType, "ptr",pti, "int",0, "int*",pRefType)
                                DllCall(GetRefTypeInfo, "ptr",pti, "ptr",pRefType, "ptr*", pti2)
                                DllCall(GetDocumentation, "ptr",pti2, "int",-1, "ptr*",Name, "ptr",0, "ptr",0, "ptr",0) ; get Interface Name
                                if StrGet(Name) != "IDispatch"
                                                t .= EnumComMembers(pti2) "`n"
                                else,
                                                ObjRelease(pti2)
                }
                Loop, %cFuncs% { ; get Member IDs
                                DllCall(GetFuncDesc, "ptr",pti, "int",A_Index-1, "ptr*",FuncDesc)
                                ID := NumGet(FuncDesc+0, "short") ; get Member ID
                                n := NumGet(FuncDesc+0, 4+3*A_PtrSize, "int") ; get InvKind
                                ; Args := NumGet(FuncDesc+0, 12+3*A_PtrSize, "short") ; get Num of Args
                                ; Opt := NumGet(FuncDesc+0, 14+3*A_PtrSize, "short") ; get Num of Opt Args
                                DllCall(ReleaseFuncDesc, "ptr",pti, "ptr",FuncDesc)
                                DllCall(GetDocumentation, "ptr",pti, "int",ID, "ptr*",Name, "ptr",0, "ptr",0, "ptr",0)
                                if StrGet(Name) ; Exclude Members that didn't return a Name
                                                t .= ID "`t" StrGet(Name) "  " InvKind[n] "`n"
                }
                { ; formatting & cleanup
                                t := SubStr(t,1,-1)
                                Sort, t, ND`n
                                ObjRelease(pti)
                }
                return, t
}
 
 
GetTypeInfo(ptr) {
    if ComObjType(ptr)=9
        ptr := ComObjValue(ptr)
    { ; Check if *ptr* has ITypeInfo Interface
        GetTypeInfoCount := vtable(ptr, 3)
        DllCall(GetTypeInfoCount, "ptr",ptr, "ptr*",HasITypeInfo)
        if Not HasITypeInfo {
            MsgBox ITypeInfo Interface not supported
            Exit
        }
    }
    GetTypeInfo := vTable(ptr, 4)
    if DllCall(GetTypeInfo, "ptr",ptr, "uint",0, "uint",0, "ptr*",pti)=0
        return, pti
}
vTable(ptr, n) { ; see ComObjQuery documentation
    return NumGet(NumGet(ptr+0), n*A_PtrSize)
}
Output:
ID Name
-----------------------------
-550 Refresh [method]
-525 ReadyState [get]
-515 HWND [get]
0 Name [get]
100 GoBack [method]
101 GoForward [method]
102 GoHome [method]
103 GoSearch [method]
104 Navigate [method]
105 Refresh2 [method]
106 Stop [method]
200 Application [get]
201 Parent [get]
202 Container [get]
203 Document [get]
204 TopLevelContainer [get]
205 Type [get]
206 Left [get]
206 Left [put]
207 Top [put]
207 Top [get]
208 Width [put]
208 Width [get]
209 Height [get]
209 Height [put]
210 LocationName [get]
211 LocationURL [get]
212 Busy [get]
300 Quit [method]
301 ClientToWindow [method]
302 PutProperty [method]
303 GetProperty [method]
400 FullName [get]
401 Path [get]
402 Visible [get]
402 Visible [put]
403 StatusBar [put]
403 StatusBar [get]
404 StatusText [put]
404 StatusText [get]
405 ToolBar [get]
405 ToolBar [put]
406 MenuBar [get]
406 MenuBar [put]
407 FullScreen [get]
407 FullScreen [put]
500 Navigate2 [method]
501 QueryStatusWB [method]
502 ExecWB [method]
503 ShowBrowserBar [method]
550 Offline [get]
550 Offline [put]
551 Silent [put]
551 Silent [get]
552 RegisterAsBrowser [put]
552 RegisterAsBrowser [get]
553 RegisterAsDropTarget [put]
553 RegisterAsDropTarget [get]
554 TheaterMode [get]
554 TheaterMode [put]
555 AddressBar [put]
555 AddressBar [get]
556 Resizable [put]
556 Resizable [get]


tinku99
  • Members
  • 560 posts
  • Last active: Feb 08 2015 12:54 AM
  • Joined: 03 Aug 2007
Since it is solved and useful
moderator: please move to scripts section

tank
  • Administrators
  • 4345 posts
  • AutoHotkey Foundation
  • Last active: Oct 13 2016 01:04 AM
  • Joined: 21 Dec 2007
Cant beleive i missed this thread for almost a year.
I kneel before Jethrow
Never lose.
WIN or LEARN.

andregarcia73
  • Members
  • 1 posts
  • Last active: Dec 11 2015 01:44 AM
  • Joined: 09 Dec 2015

My contribution to this beautiful topic:

 

sd := ComObjCreate("Shell.Explorer")
Lib.COM.ShowMembers(sd, "sd")
 

 

;###########################################################################
;###########################################################################
;##
;## Lib - COM - Component Object Model
;## by Andre Garcia - andre.garcia@xpnet.com.br
;##
;###########################################################################
;###########################################################################
    ;##
    ;##    Requirements:
    ;##        Previous Lib object where this object will be attached
    ;##        For Debug, global methods deb and PCS
    ;##
    ;###########################################################################
    ;###########################################################################

        ; Attaches the Lib
        Lib.COM                        := {}

    ;===========================================================================
    ;===========================================================================
    ;==
    ;== "Private" Error/Validate Methods
    ;==
    ;===========================================================================
    ;===========================================================================

    ;---------------------------------------------------------------------------
    ; Get COM Members
    ;---------------------------------------------------------------------------

        ;---------------------------------------------------------------------------
        ; https://autohotkey.c...bers-itypeinfo/
        ;---------------------------------------------------------------------------

        ;---------------------------------------------------------------------------
        ;    ; Original Code
        ;    EnumComMembers(pti) {
        ;       ;deb("EnumComMembers")
        ;       static InvKind := {1:"method", 2:"get", 4:"put", 8:"putref"}
        ;       if ComObjType(pti)=9 { ; if Com Object, get iTypeInfo Interface
        ;          ptr := ComObjValue(pti)
        ;          { ; Check if *ptr* has ITypeInfo Interface
        ;             GetTypeInfoCount := vtable(ptr, 3)
        ;             DllCall(GetTypeInfoCount, "ptr",ptr, "ptr*",HasITypeInfo)
        ;             if Not HasITypeInfo {
        ;                MsgBox ITypeInfo Interface not supported
        ;                return
        ;             }
        ;          }
        ;          GetTypeInfo := vTable(ptr, 4)
        ;          if DllCall(GetTypeInfo, "ptr",ptr, "uint",0, "uint",0, "ptr*",pti)!=0
        ;             return
        ;       }
        ;       { ; Com Methods
        ;          GetTypeAttr := vTable(pti, 3)
        ;          ReleaseTypeAttr := vTable(pti, 19)
        ;          GetRefTypeOfImplType := vTable(pti, 8)
        ;          GetRefTypeInfo := vTable(pti, 14)
        ;          GetFuncDesc := vTable(pti, 5)
        ;          ReleaseFuncDesc := vTable(pti, 20)
        ;          GetDocumentation := vTable(pti, 12)
        ;       }
        ;       { ; get cFuncs (number of functions)
        ;          DllCall(GetTypeAttr, "ptr",pti, "ptr*",typeAttr)
        ;          cFuncs := NumGet(typeAttr+0, 40+A_PtrSize, "short")
        ;          cImplTypes := NumGet(typeAttr+0, 44+A_PtrSize, "short")
        ;          DllCall(ReleaseTypeAttr, "ptr",pti, "ptr",typeAttr)
        ;       }
        ;       if cImplTypes { ; Get Inherited Class (cImplTypes should be true)
        ;          DllCall(GetRefTypeOfImplType, "ptr",pti, "int",0, "int*",pRefType)
        ;          DllCall(GetRefTypeInfo, "ptr",pti, "ptr",pRefType, "ptr*", pti2)
        ;          DllCall(GetDocumentation, "ptr",pti2, "int",-1, "ptr*",Name, "ptr",0, "ptr",0, "ptr",0) ; get Interface Name
        ;          if StrGet(Name) != "IDispatch"
        ;             t .= EnumComMembers(pti2) "`n"
        ;          else
        ;             ObjRelease(pti2)
        ;       }
        ;       Loop, %cFuncs% { ; get Member IDs
        ;          DllCall(GetFuncDesc, "ptr",pti, "int",A_Index-1, "ptr*",FuncDesc)
        ;          ID := NumGet(FuncDesc+0, "short") ; get Member ID
        ;          n := NumGet(FuncDesc+0, 4+3*A_PtrSize, "int") ; get InvKind
        ;          ; Args := NumGet(FuncDesc+0, 12+3*A_PtrSize, "short") ; get Num of Args
        ;          ; Opt := NumGet(FuncDesc+0, 14+3*A_PtrSize, "short") ; get Num of Opt Args
        ;          DllCall(ReleaseFuncDesc, "ptr",pti, "ptr",FuncDesc)
        ;          DllCall(GetDocumentation, "ptr",pti, "int",ID, "ptr*",Name, "ptr",0, "ptr",0, "ptr",0)
        ;          if StrGet(Name, "UTF-16") ; Exclude Members that didn't return a Name
        ;            {
        ;            ; t .= ID "`t" StrGet(Name, "UTF-16") "`t" InvKind[n] "`n"
        ;            t .= ID "`t" InvKind[n] "`t" StrGet(Name, "UTF-16") "`n"
        ;            }
        ;       }
        ;       { ; formatting & cleanup
        ;          t := SubStr(t,1,-1)
        ;          Sort, t, ND`n
        ;          ObjRelease(pti)
        ;       }
        ;       return t
        ;    }
        ;    vTable(ptr, n) { ; see ComObjQuery documentation
        ;        return NumGet(NumGet(ptr+0), n*A_PtrSize)
        ;    }
        ;---------------------------------------------------------------------------

    Lib.COM.GetMembers                := Func("Lib_COM_GetMembers")
    Lib_COM_GetMembers(this, pti, Arg_Members = -1)    ; pti is ComObj pointer
        {
        ;    deb("Lib_COM_GetMembers")

        ; Create Members (if not created yet)
        if (Arg_Members == -1)
            {
            Members                    := Object()
            Members.Length            := 0
            Members.Errors            := ""
            }
        else
            Members                    := Arg_Members

        ;---------------------------------------------------------------------------
        ; ComObjType
        ;---------------------------------------------------------------------------
        ;        VT_EMPTY     =      0  ; No value
        ;        VT_NULL      =      1  ; SQL-style Null
        ;        VT_I2        =      2  ; 16-bit signed int
        ;        VT_I4        =      3  ; 32-bit signed int
        ;        VT_R4        =      4  ; 32-bit floating-point number
        ;        VT_R8        =      5  ; 64-bit floating-point number
        ;        VT_CY        =      6  ; Currency
        ;        VT_DATE      =      7  ; Date
        ;        VT_BSTR      =      8  ; COM string (Unicode string with length prefix)
        ;        VT_DISPATCH  =      9  ; COM object
        ;        VT_ERROR     =    0xA  ; Error code (32-bit integer)
        ;        VT_BOOL      =    0xB  ; Boolean True (-1) or False (0)
        ;        VT_VARIANT   =    0xC  ; VARIANT (must be combined with VT_ARRAY or VT_BYREF)
        ;        VT_UNKNOWN   =    0xD  ; IUnknown interface pointer
        ;        VT_DECIMAL   =    0xE  ; (not supported)
        ;        VT_I1        =   0x10  ; 8-bit signed int
        ;        VT_UI1       =   0x11  ; 8-bit unsigned int
        ;        VT_UI2       =   0x12  ; 16-bit unsigned int
        ;        VT_UI4       =   0x13  ; 32-bit unsigned int
        ;        VT_I8        =   0x14  ; 64-bit signed int
        ;        VT_UI8       =   0x15  ; 64-bit unsigned int
        ;        VT_INT       =   0x16  ; Signed machine int
        ;        VT_UINT      =   0x17  ; Unsigned machine int
        ;        VT_RECORD    =   0x24  ; User-defined type -- NOT SUPPORTED
        ;        VT_ARRAY     = 0x2000  ; SAFEARRAY
        ;        VT_BYREF     = 0x4000  ; Pointer to another type of value
        ;
        ;         VT_ARRAY and VT_BYREF are combined with another value (using bitwise OR)
        ;         to specify the exact type. For instance, 0x2003 identifies a SAFEARRAY
        ;         of 32-bit signed integers and 0x400C identifies a pointer to a VARIANT.
        ;---------------------------------------------------------------------------

        ;   static InvKind := {1:"method", 2:"get", 4:"put", 8:"putref"}
        ;    deb("ComObjType(pti) = " ComObjType(pti))
        ; if Com Object, get iTypeInfo Interface
        if (ComObjType(pti) = 9)
            {
            ptr                        := ComObjValue(pti)
            ; Check if *ptr* has ITypeInfo Interface
            GetTypeInfoCount        := this.vTable(ptr, 3)
            ;    deb("GetTypeInfoCount = " GetTypeInfoCount)
            DllCall(GetTypeInfoCount, "ptr", ptr, "ptr*", HasITypeInfo)
            ;    deb("HasITypeInfo = " HasITypeInfo)
            if (! HasITypeInfo)
                {
                ;    MsgBox ITypeInfo Interface not supported
                Members.Errors        .= "ITypeInfo Interface not supported`n"
                return Members
                }
            GetTypeInfo                := this.vTable(ptr, 4)
            ;    deb("GetTypeInfo = " GetTypeInfo)
            Temp                    := DllCall(GetTypeInfo, "ptr", ptr, "uint", 0, "uint", 0, "ptr*", pti)
            ;    deb("Temp = " Temp)
            if (Temp != 0)
                {
                Members.Errors        .= "GetTypeInfo != 0`n"
                return Members
                }
            }

        ; Com Methods
        GetTypeAttr                    := this.vTable(pti, 3)
        ReleaseTypeAttr                := this.vTable(pti, 19)
        GetRefTypeOfImplType        := this.vTable(pti, 8)
        GetRefTypeInfo                := this.vTable(pti, 14)
        GetFuncDesc                    := this.vTable(pti, 5)
        ReleaseFuncDesc                := this.vTable(pti, 20)
        GetDocumentation            := this.vTable(pti, 12)
        ;    deb("GetTypeAttr = "            GetTypeAttr)
        ;    deb("ReleaseTypeAttr = "        ReleaseTypeAttr)
        ;    deb("GetRefTypeOfImplType = "    GetRefTypeOfImplType)
        ;    deb("GetRefTypeInfo = "            GetRefTypeInfo)
        ;    deb("GetFuncDesc = "            GetFuncDesc)
        ;    deb("ReleaseFuncDesc = "        ReleaseFuncDesc)
        ;    deb("GetDocumentation = "        GetDocumentation)

        ; get cFuncs (number of functions)
        DllCall(GetTypeAttr, "ptr", pti, "ptr*", typeAttr)
        cFuncs                        := NumGet(typeAttr + 0, 40 + A_PtrSize, "short")
        cImplTypes                    := NumGet(typeAttr + 0, 44 + A_PtrSize, "short")
        DllCall(ReleaseTypeAttr, "ptr", pti, "ptr", typeAttr)
        ;    deb("cFuncs = "                    cFuncs)
        ;    deb("cImplTypes = "                cImplTypes)

        ; Get Inherited Class (cImplTypes should be true)
        if (cImplTypes)
            {
            DllCall(GetRefTypeOfImplType,    "ptr", pti,        "int", 0,            "int*", pRefType)
            DllCall(GetRefTypeInfo,            "ptr", pti,        "ptr", pRefType,    "ptr*", pti2)
            DllCall(GetDocumentation,        "ptr", pti2,    "int", -1,            "ptr*", Name, "ptr", 0, "ptr", 0, "ptr", 0) ; get Interface Name
            ;    deb("Name = "                    Name, StrGet(Name), StrGet(Name) != "IDispatch")

            ; Reenter ?
            if (StrGet(Name) != "IDispatch")
                {
                ;    deb("REENTER")
                this.GetMembers(pti2, Members)
                }
            else
                ObjRelease(pti2)
            }

        ; get Member IDs
        Loop, %cFuncs%
            {
            ;    deb("Loop, %cFuncs%", A_Index, cFuncs)
            DllCall(GetFuncDesc,            "ptr", pti,        "int", A_Index - 1, "ptr*", FuncDesc)
            ID                        := NumGet(FuncDesc + 0, "short")                        ; get Member ID
            n                        := NumGet(FuncDesc + 0,  4 + 3 * A_PtrSize, "int")        ; get InvKind
            ; Args                    := NumGet(FuncDesc + 0, 12 + 3 * A_PtrSize, "short")    ; get Num of Args
            ; Opt                    := NumGet(FuncDesc + 0, 14 + 3 * A_PtrSize, "short")    ; get Num of Opt Args
            DllCall(ReleaseFuncDesc,        "ptr", pti,        "ptr", FuncDesc)
            DllCall(GetDocumentation,        "ptr", pti,        "int", ID, "ptr*", Name, "ptr",0, "ptr",0, "ptr",0)
            ;    deb("ID = "                        ID)
            ;    deb("Name = "                    Name, StrGet(Name, "UTF-16"))
            ; Exclude Members that didn't return a Name
            Member                    := {}
            Member.ID                := ID
            Member.Name                := StrGet(Name, "UTF-16")
            ;    deb("Loop, %cFuncs%", A_Index, cFuncs, Member.Name)
            if (Member.Name)
                {
                Member.Kind            := this.GetInvKind(n)
                ;    PCS(Member, "Member")
                Members.Insert(Member)
                Members.Length++
                }
            ;    break
            }

        ; Cleanup
        ObjRelease(pti)

        ; Sort Members
        this.MembersSort(Members, "Lib_COM_OnMembersSort")

        ; Returns
        ;    deb("Retuning Members")
        return Members
        }

    ;---------------------------------------------------------------------------
    ; vTable
    ;---------------------------------------------------------------------------

    Lib.COM.vTable                    := Func("Lib_COM_vTable")
    Lib_COM_vTable(this, ptr, n)
        {
        ;    deb("Lib_COM_vTable", "ptr = " ptr, "n = " n, "A_PtrSize = " A_PtrSize)

        ; See ComObjQuery documentation
        return NumGet(NumGet(ptr+0), n * A_PtrSize)
        }

    ;---------------------------------------------------------------------------
    ; GetInvKind
    ;---------------------------------------------------------------------------

    Lib.COM.GetInvKind                := Func("Lib_COM_GetInvKind")
    Lib_COM_GetInvKind(this, n)
        {
        ;    deb("Lib_COM_GetInvKind", "n = " n, n = 1)
        ;    static InvKind := {1:"method", 2:"get", 4:"put", 8:"putref"}
        ;    return InvKind[n]
        if (n = 1)
            return "method"
        if (n = 2)
            return "get"
        if (n = 4)
            return "put"
        if (n = 8)
            return "putref"
        return "unknown (" n ")"
        }

    ;---------------------------------------------------------------------------
    ; OnSort Members
    ;---------------------------------------------------------------------------

    Lib_COM_OnMembersSort(Member_I, Member_J)
        {
        ;    deb("Lib_COM_OnMembersSort", Member_I.ID, Member_J.ID)
        ;    return  -1

        ; ID
        if (Member_I.ID < Member_J.ID)
            return -1
        if (Member_I.ID > Member_J.ID)
            return  1

        ; Name
        if (Member_I.Name < Member_J.Name)
            return -1
        if (Member_I.Name > Member_J.Name)
            return  1

        ; Kind
        ;if (Member_I.Kind < Member_J.Kind)
        ;    return -1
        ;if (Member_I.Kind > Member_J.Kind)
        ;    return  1

        ; Equals !
        return  0
        }

    ;---------------------------------------------------------------------------
    ; Sort Members
    ;---------------------------------------------------------------------------

    Lib.COM.MembersSort                := Func("Lib_COM_MembersSort")
    Lib_COM_MembersSort(this, Members, OnSort)
        {
        ;    deb("Lib_COM_MembersSort", "Members.Length = " Members.Length)
        ;    return
        M                            := Members.Length
        ;    M                        := 5
        Loop, % M - 1
            {
            I                        := A_Index
            Loop, % M - I
                {
                J                    := A_Index + I
                ;    deb(I, J)
                Member_I            := Members[I]
                Member_J            := Members[J]
                Result                := %OnSort%(Member_I, Member_J)
                if (Result <= 0)
                    continue
                Temp                := Members[I]
                Members[I]            := Members[J]
                Members[J]            := Temp
                }
            }
        }

    ;---------------------------------------------------------------------------
    ; Show COM Members
    ;---------------------------------------------------------------------------

    Lib.COM.ShowMembers                := Func("Lib_COM_ShowMembers")
    Lib_COM_ShowMembers(this, pti, Caption = "")    ; pti is ComObj pointer
        {
        ;    deb("Lib_COM_ShowMembers")
        Members                        := this.GetMembers(pti)
        ;    deb("Members.Length = "        Members.Length)

        ; Loop
        ToShow                        := "`n"
        ToShow                        .= "=========== ShowMembers`n"
        if (Caption != "")
            {
            ToShow                    .= "----------- Caption:`n"
            ToShow                    .= Caption "`n"
            ToShow                    .= "-----------`n"
            }
        if (Members.Errors != "")
            {
            ToShow                    .= "----------- Errors:`n"
            ToShow                    .= Members.Errors
            ToShow                    .= "-----------`n"
            }
        ToShow                        .= "Members.Length = " Members.Length "`n"
        ToShow                        .= "Index`tID`tKind`tName`n"
        ToShow                        .= "=====`t==`t====`t====`n"
        Loop, % Members.Length
            {
            Member                    := Members[A_Index]
            ;    deb("Member[" A_Index "], ID = " Member.ID ", Name = " Member.Name ", Kind = " Member.Kind)
            ;    deb(Member.ID "`t" Member.Name "`t" Member.Kind)
            ToShow                    .= A_Index "`t" Member.ID "`t" Member.Kind "`t" Member.Name "`n"
            }
        ToShow                        .= "===========`n"
        deb(ToShow)
        }

;###########################################################################
;###########################################################################
 

 

Output:

 

=========== ShowMembers
----------- Caption:
sd
-----------
Members.Length = 64
Index    ID    Kind    Name
=====    ==    ====    ====
1    -550    method    Refresh
2    -525    get    ReadyState
3    -515    get    HWND
4    0    get    Name
5    100    method    GoBack
6    101    method    GoForward
7    102    method    GoHome
8    103    method    GoSearch
9    104    method    Navigate
10    105    method    Refresh2
11    106    method    Stop
12    200    get    Application
13    201    get    Parent
14    202    get    Container
15    203    get    Document
16    204    get    TopLevelContainer
17    205    get    Type
18    206    put    Left
19    206    get    Left
20    207    put    Top
21    207    get    Top
22    208    put    Width
23    208    get    Width
24    209    put    Height
25    209    get    Height
26    210    get    LocationName
27    211    get    LocationURL
28    212    get    Busy
29    300    method    Quit
30    301    method    ClientToWindow
31    302    method    PutProperty
32    303    method    GetProperty
33    400    get    FullName
34    401    get    Path
35    402    put    Visible
36    402    get    Visible
37    403    put    StatusBar
38    403    get    StatusBar
39    404    put    StatusText
40    404    get    StatusText
41    405    put    ToolBar
42    405    get    ToolBar
43    406    put    MenuBar
44    406    get    MenuBar
45    407    put    FullScreen
46    407    get    FullScreen
47    500    method    Navigate2
48    501    method    QueryStatusWB
49    502    method    ExecWB
50    503    method    ShowBrowserBar
51    550    get    Offline
52    550    put    Offline
53    551    get    Silent
54    551    put    Silent
55    552    get    RegisterAsBrowser
56    552    put    RegisterAsBrowser
57    553    get    RegisterAsDropTarget
58    553    put    RegisterAsDropTarget
59    554    get    TheaterMode
60    554    put    TheaterMode
61    555    get    AddressBar
62    555    put    AddressBar
63    556    get    Resizable
64    556    put    Resizable
===========