Quantcast
Channel: VBForums - CodeBank - Visual Basic 6 and earlier
Viewing all articles
Browse latest Browse all 1529

Function for checking if a property or method exists inside a object

$
0
0
After searching for different ways to check if a method exists, i found that most of them were either slow (using TLI objects) or used error handling while also calling the method, then after some search about VB6 internals, found out that all VB6 objects have a IDispatch interface, which implements a method called "GetIdsOfNames", which is able to check if a method/property name exists and returns an ID used internally to call that method
So i made a function that uses DispCallFunc to call "GetIdsOfNames" of any VB6 object, so you can check if a method exists even without calling it (that also means you can check if a form has a method without even loading it!) while also being a little faster than the usual error handling way

The internal structure of all VB6 objects uses these interfaces :
IUNKNOWN
''->QueryInterface
''->AddRef
''->ReleaseRef
''IDISPATCH
''-> GetTypeInfoCount
''-> GetTypeInfo
''-> GetIDsOfNames
which from what i searched are supposed to be exactly like that in memory, so if you call
DispCallFunc with offset 0, means you are calling QueryInterface
and DispCallFunc with offset 20 is calling GetIdsOfNames

I debugged this approach by using API Monitor, it shows me exactly what function DispCallFunc is calling and what
parameters are going to the function, mostly things you won't know normally because VB6 IDE would just crash if you make a mistake with function pointers and parameters

Code:

Enum tagCALLCONV
    CC_FASTCALL = 0
    CC_CDECL = 1
    CC_MSCPASCAL = CC_CDECL + 1
    CC_PASCAL = CC_MSCPASCAL
    CC_MACPASCAL = CC_PASCAL + 1
    CC_STDCALL = CC_MACPASCAL + 1
    CC_FPFASTCALL = CC_STDCALL + 1
    CC_SYSCALL = CC_FPFASTCALL + 1
    CC_MPWCDECL = CC_SYSCALL + 1
    CC_MPWPASCAL = CC_MPWCDECL + 1
    CC_MAX = CC_MPWPASCAL
End Enum

Private Const GET_IDS_OF_NAMES_VTABLE_OFFSET As Long = 20

Private Const LOCALE_USER_DEFAULT = &H400
 

Private Declare Function DispCallFunc Lib "OleAut32.dll" _
(ByVal pvInstance As Long, _
  ByVal oVft As Long, _
  ByVal cc As tagCALLCONV, _
  ByVal vtReturn As VbVarType, _
  ByVal cActuals As Long, _
  ByRef prgvt As Integer, _
  ByRef prgpvarg As Long, _
  ByRef pvargResult As Variant) As Long
 
Private Type VariantIdsOfNames
    GUID As Variant
    Name As Variant
    cName As Variant
    LCID As Variant
    DISPID As Variant
End Type

Code:

Public Function HasMethod(ByRef ObjectTest As Object, ByRef MethodName As String) As Boolean
Static VarTypes(4) As Integer
Static VariantPtr(4) As Long
Static IDispatchChecker As VariantIdsOfNames
Static ResultID As Long
Static Initialized As Boolean
Dim Result As Variant


If Initialized = False Then
    With IDispatchChecker
   
        '8 caracteres = 16 bytes = 128 bits  = IID_NULL (GUID zerado)
        .GUID = String$(8, vbNullChar)
        .cName = 1& 'Checks only one name
        .LCID = LOCALE_USER_DEFAULT  'Default locale ID
        .DISPID = VarPtr(ResultID)
        VarTypes(0) = vbString
        VarTypes(1) = vbLong
        VarTypes(2) = vbLong
        VarTypes(3) = vbLong
        VarTypes(4) = vbLong
        VariantPtr(0) = VarPtr(.GUID)
        VariantPtr(1) = VarPtr(.Name)
        VariantPtr(2) = VarPtr(.cName)
        VariantPtr(3) = VarPtr(.LCID)
        VariantPtr(4) = VarPtr(.DISPID)
    End With
End If
IDispatchChecker.Name = VarPtr(MethodName) 'Uses a BSTR pointer
DispCallFunc ObjPtr(ObjectTest), GET_IDS_OF_NAMES_VTABLE_OFFSET, CC_STDCALL, _
            vbLong, 5, VarTypes(0), VariantPtr(0), Result
           
'Result is non-zero when function fails
If Result = 0 Then HasMethod = True
End Function


Viewing all articles
Browse latest Browse all 1529

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>