Solved

Enumerating controls in a VB form in another application

Posted on 2004-09-11
11
714 Views
Last Modified: 2012-08-13
I have a visual basic exe (say abc.exe) that, when launched, shows a form with various contains. Some are window controls and some are not (e.g. label).
I know the window handle  of the form. Is it possible for me to write a code in Vb (or any other language) so that I can get all the controls in the form of abc.exe?

The complete code is not required. Just some idea of how to proceed will suffice.

I do not have the access to the source code of abc.exe.

Thank you,
Avinash
0
Comment
Question by:avinash_sahay
  • 6
  • 4
11 Comments
 
LVL 10

Expert Comment

by:anv
Comment Utility
hi  avinash_sahay

as per me , this cannot be done...
0
 
LVL 2

Author Comment

by:avinash_sahay
Comment Utility
I also thought the same. Then, I felt that somebody may have an idea. There is a VB application not written by me. Given any point, I have to get the details of the control at that point in the application. If the control is a window control, then it is easy. I can call the api WindowFromPoint to get the control's handle. But the application contains lots of windowless controls, e.g., labels and some third party controls. How to identify those. Msaa (Microsoft Active Accessibility) does not help.
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
0
 
LVL 2

Author Comment

by:avinash_sahay
Comment Utility
I cannot use the answer to the PAQ. This is because that solution says how to get all window child controls. But I need to know how to get windowless controls. Or, else I need to identify a VB control at a point even if the VB control is windowless (e.g. label) and is in some other application.
0
 
LVL 85

Accepted Solution

by:
Mike Tomlinson earned 100 total points
Comment Utility
Take a look at this very long PAQ.  It uses some very low level calls to get the label name and caption:
http://www.experts-exchange.com/Programming/Programming_Languages/Visual_Basic/Q_21018080.html

Below is the code posted by jkozee.  It works only the hWnds of compiled VB EXEs (not when they are run in the IDE).

Option Explicit

Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Const VBM_WINDOWTITLEADDR = &H1091
Private Declare Function SendMessage Lib "USER32.DLL" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, wParam As Any, lParam As Any) As Long

Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal blnheritHandle As Long, ByVal dwAppProcessId As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long

Private Const PROCESS_VM_READ = (&H10)
Private Const PROCESS_VM_WRITE = (&H20)
Private Const PROCESS_VM_OPERATION = (&H8)
Private Const PROCESS_QUERY_INFORMATION = (&H400)
Private Const PROCESS_READ_WRITE_QUERY = PROCESS_VM_READ + PROCESS_VM_WRITE + PROCESS_VM_OPERATION + PROCESS_QUERY_INFORMATION
Private Const MEM_PRIVATE = &H20000
Private Const MEM_COMMIT = &H1000

Private Type MEMORY_BASIC_INFORMATION ' 28 bytes
    BaseAddress As Long
    AllocationBase As Long
    AllocationProtect As Long
    RegionSize As Long
    State As Long
    Protect As Long
    lType As Long
End Type
Private Declare Function VirtualQueryEx& Lib "kernel32" (ByVal hProcess As Long, lpAddress As Any, lpBuffer As MEMORY_BASIC_INFORMATION, ByVal dwLength As Long)
Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
 
Private abBuffer() As Byte 'Heap Buffer
Private hProcess As Long
Private lBaseAddress As Long

Public Sub GetFormLabels(hwnd As Long)
    Dim sClass As String
    Dim lRet As Long
    Dim pid As Long
    Dim lFormCaptionHeapAddress As Long
   
    Dim lpMem As Long
    Dim lLenMBI As Long
    Dim lBytesRead As Long
    Dim mbi As MEMORY_BASIC_INFORMATION
   
    'Make sure we are working with a VB Form hWnd
    sClass = Space(256)
    lRet = GetClassName(hwnd, sClass, 255)
    sClass = Left(sClass, lRet)
    If Not sClass = "ThunderRT6FormDC" Then
        MsgBox "This function only works on VB RunTime 6 Forms ThunderFormRT6DC"
        Exit Sub
    End If
   
    'Now get the internal heap address of the form caption.  All that we need can be found in this heap (hopefully!)
    'This is done with a little undocumented SendMessage magic
    lFormCaptionHeapAddress = SendMessage(hwnd, VBM_WINDOWTITLEADDR, ByVal 0&, ByVal 0&)
   
    'Get a handle on the process with required access
    lRet = GetWindowThreadProcessId(hwnd, pid)
    If pid = 0 Then
        MsgBox "Unable to determine pid of this hwnd."
        Exit Sub
    End If
    hProcess = OpenProcess(PROCESS_READ_WRITE_QUERY, False, pid)
   
    'Get the Heap at the caption point
    lLenMBI = Len(mbi)
    lpMem = lFormCaptionHeapAddress
    mbi.AllocationBase = lpMem
    mbi.BaseAddress = lpMem
    lRet = VirtualQueryEx(hProcess, ByVal lpMem, mbi, lLenMBI)
    If lRet <> lLenMBI Then GoTo Finished
   
    'Now go back and get the entire heap
    lBaseAddress = mbi.AllocationBase
    lpMem = lBaseAddress
    mbi.BaseAddress = lBaseAddress
    mbi.RegionSize = 0
    lRet = VirtualQueryEx(hProcess, ByVal lpMem, mbi, lLenMBI)
    If lRet <> lLenMBI Then GoTo Finished
   
    'A couple of sanity checks, just to be safe
    If Not ((mbi.lType = MEM_PRIVATE) And (mbi.State = MEM_COMMIT) And mbi.RegionSize > 0) Then
        MsgBox "Unexpected Heap Type, State, or Size."
        GoTo Finished
    End If
   
     'Allocate a buffer and read it in
    ReDim abBuffer(mbi.AllocationBase To mbi.AllocationBase + mbi.RegionSize - 1)
    ReadProcessMemory hProcess, ByVal mbi.BaseAddress, abBuffer(LBound(abBuffer)), mbi.RegionSize, lBytesRead
   
    'So far, so good.  Things get messy from here.  We have to
    'do some manual parsing of the buffer to get what we are after.  To
    'make things easier, I'll will get every label on every form in the
    'exe.  Otherwise, you will need to first find the form that is
    'reference the caption.  Then find every label between it and the next
    'form.
   
    Dim iCnt As Integer
    Dim al() As Long
   
    'Print all of the label captions
    If EnumVBObjectPtrs("VB.Label", 44, al) > 0 Then
        For iCnt = LBound(al) To UBound(al)
            Debug.Print "Hit at: " & Hex(al(iCnt) + 44), "Object At: " & Hex(al(iCnt)), GetLabelCaption(al(iCnt)), GetLabelName(al(iCnt))
        Next iCnt
    End If
   
Finished:
    CloseHandle hProcess
    abBuffer() = ""
End Sub

Private Function GetLabelName(lpObjPtr As Long) As String
    Dim lpMem As Long
    Dim lLenMBI As Long
    Dim lBytesRead As Long
    Dim mbi As MEMORY_BASIC_INFORMATION
    Dim lRet As Long
    Dim ab() As Byte
    Dim lStrPtr As Long
    Dim lInfoPtr As Long

    'Get the local pointer to object info
    CopyMemory lInfoPtr, abBuffer(lpObjPtr + 60), 4
   
    'Get the pointer to label name
    CopyMemory lStrPtr, abBuffer(lInfoPtr + 4), 4

    'Get the EXE at the name point
    lLenMBI = Len(mbi)
    lpMem = lStrPtr
    mbi.AllocationBase = lpMem
    mbi.BaseAddress = lpMem
    lRet = VirtualQueryEx(hProcess, ByVal lpMem, mbi, lLenMBI)
    If lRet <> lLenMBI Then Exit Function
   
    'Read in the EXE Heap
    ReDim ab(0 To mbi.RegionSize - 1)
    ReadProcessMemory hProcess, ByVal mbi.BaseAddress, ab(LBound(ab)), mbi.RegionSize, lBytesRead
   
    GetLabelName = StrConv(MidB(ab, lStrPtr - mbi.BaseAddress + 1, 260), vbUnicode)
    GetLabelName = Left$(GetLabelName, InStr(GetLabelName, vbNullChar) - 1)
End Function

Private Function GetLabelCaption(lpObjPtr As Long) As String
    Dim lStrPtr As Long

    'Get local pointer to caption
    CopyMemory lStrPtr, abBuffer(lpObjPtr + 136), 4
   
    'Get caption
    If lStrPtr <> 0 Then
        GetLabelCaption = StrConv(MidB(abBuffer, lStrPtr - lBaseAddress + 1, 260), vbUnicode)
    End If
    GetLabelCaption = Left$(GetLabelCaption, InStr(GetLabelCaption, vbNullChar) - 1)
End Function

'This function will search the buffer for a given VBObjectIDString, then
'find the start of that control by searching for a refence to it in the 600
'bytes prior.
'It then finds any object of that type by searching the buffer for any
'references to the Heap Location of that control, and adds it to the enumeration
'if the reference hit position is at the correct offset (pos-offset = lBaseAddress)
'setting the EnumObj entry to the start location (local buffer address) and
'returns the counrt
Private Function EnumVBObjectPtrs(VBObjectIDString As String, _
                                  lOffset As Long, _
                                  EnumObj() As Long) As Integer
    Dim abObjectPtr(0 To 3) As Byte 'LittleEndian byte array of the Heap Address of the VBObject
    Dim abBaseAddress(0 To 3) As Byte 'LittleEndian byte array of the Heap Base Memory Address
    Dim abLong(0 To 3) As Byte 'Byte array for ptr manipulation
    Dim lPtr As Long 'Buffer pointer for search hits
    Dim iCnt As Integer
    Dim alRet() As Long
   
    'Find the location of the VBObjectIDString string
    lPtr = InStrB(1, abBuffer, StrConv(VBObjectIDString, vbFromUnicode)) - 1
    If lPtr = -1 Then Exit Function
    lPtr = lBaseAddress + lPtr
   
    'We now need to find the location that points to the start of the object
    'which should be 244 bytes prior (on XP at least) we go back 600 just in
    'case.  This is at offset 36, so we'll need to adjust back to the beginning
    'of the object
    CopyMemory abLong(0), lPtr, 4
    lPtr = InStrB(lPtr - lBaseAddress - 600, abBuffer, abLong) - 1
    If lPtr = -1 Then Exit Function
    lPtr = lPtr + lBaseAddress - 36 'Adjust back to the beginning of the object
    CopyMemory abObjectPtr(0), lPtr, 4
   
    'Turn the lBaseAddress into LittleEndian byte array for searching
    CopyMemory abBaseAddress(0), lBaseAddress, 4
   
    'Loop through the buffer
    lPtr = 1
    Do Until lPtr = 0
        'Find a reference to this object
        lPtr = InStrB(lPtr, abBuffer, abObjectPtr)
        If lPtr > 0 Then
            'make sure that this is really a VB object
            'move back from the offset of the object
            'and make sure that it has the correct base memory value
            If InStrB(lPtr - lOffset - 1, abBuffer, abBaseAddress) = lPtr - lOffset Then
                ReDim Preserve alRet(0 To iCnt)
                alRet(iCnt) = lPtr + lBaseAddress - lOffset - 1
                iCnt = iCnt + 1
            End If
            'Keep searching from the next byte
            lPtr = lPtr + 1
        End If
    Loop
   
    EnumVBObjectPtrs = iCnt
    EnumObj = alRet

End Function
0
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 
LVL 2

Author Comment

by:avinash_sahay
Comment Utility
Hi,
It seems that the solution will work for me. The application contains windowless controls of various types, not just label. But if I get the answer to how to get labels, I hope I will be able to do some modification, if required, to get other controls. I will try the code that you have mentioned and get back soon.
Thanks,
Avinash
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
I did test the code and was able to get it to work.

I'm sure the author spent much time using trial and error to get this work as such low level things are undocumented.

Good Luck,

Idle_Mind
0
 
LVL 2

Author Comment

by:avinash_sahay
Comment Utility
It works on a sample vb application but not on the exe on which I want it to work.
When
lPtr = InStrB(1, abBuffer, StrConv(VBObjectIDString, vbFromUnicode)) - 1

is executed, it returns lPtr = -1

I am not able to understand why.
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
I have NO idea how this code works.

Idle_Mind
0
 
LVL 2

Author Comment

by:avinash_sahay
Comment Utility
What you have given is something that I did not know. I will try to find out why it is failing for this exe.
0
 
LVL 2

Author Comment

by:avinash_sahay
Comment Utility
I was wondering if it could somehow be possible to get the form object itself.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
VBA Check for Character 9 69
Window placement 17 64
bit defender blocks good applications 2 50
vb6 connector to mongodb 2 23
Have you ever wanted to restrict the users input in a textbox to numbers, and while doing that make sure that they can't 'cheat' by pasting in non-numeric text? Of course you can do that with code you write yourself but it's tedious and error-prone …
When designing a form there are several BorderStyles to choose from, all of which can be classified as either 'Fixed' or 'Sizable' and I'd guess that 'Fixed Single' or one of the other fixed types is the most popular choice. I assume it's the most p…
Show developers how to use a criteria form to limit the data that appears on an Access report. It is a common requirement that users can specify the criteria for a report at runtime. The easiest way to accomplish this is using a criteria form that a…
This lesson covers basic error handling code in Microsoft Excel using VBA. This is the first lesson in a 3-part series that uses code to loop through an Excel spreadsheet in VBA and then fix errors, taking advantage of error handling code. This l…

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

6 Experts available now in Live!

Get 1:1 Help Now