Get Tooltip Text, 3rd Party App

  • Thread starter Matthew Herbert
  • Start date
M

Matthew Herbert

All,

Has anyone ever tried to get the tooltip text from a third party
application's button? (I'm working with an application called "Research
Wizard," but the code works just as well for Internet Explorer). I'm willing
to post the code (which is rather lengthy) if someone feels they can help
(but please read on to see if you think that you should reply to this post).
[Besides, this post is rather lengthy to begin with, even without the code.]

I am able to do the following (with help from a number of other posts):
- Enumerate the windows to get the target window handle
- Get the toolbar handle from the window handle
- Count the number of buttons on the toolbar (for looping purposes)
- Loop through each button
- Click each button

Despite being able to click the button, I can't seem to get the tooltip text
for the button.

To give you an idea of some of the APIs involved, I'm using the following:
FindWindow
EnumChildWindows
GetClassName
SendMessage
OpenProcess
VirtualAllocEx
VirtualFreeEx
ReadProcessMemory
CloseHandle
GetWindowThreadProcessId

Here are some of the SendMessage wMsg arguments I’m using:
TB_BUTTONCOUNT
TB_GETBUTTON
WM_COMMAND
TTM_ENUMTOOLS (trying to get this to work properly)
TTM_GETTEXT (trying to get this to work properly)

Structures used:
TBBUTTON
TOOLINFO

I found a post that suggests sending TTM_ENUMTOOLS with a pointer to the
TOOLINFO structure (Type TOOLINFO...End Type) and then passing TOOLINFO with
TTM_GETTEXT to get lpszText. The post also mentioned that memory needs to be
allocated that both processes can access. (Unfortunately, the post didn't
provide any syntax, just the concept written down). I have been unsuccessful
in this getting lpszText from TOOLINFO (despite being able to get
TBBUTTON.idCommand or TBBUTTON.iString when sending TB_GETBUTTON). (Maybe
the toolbar class I'm working with doesn't provide information for TOOLINFO).

As an aside: This code (and the concept behind it) is completely new to me,
and I’ve had to do a lot of learning along the way. There is a lot of
information that I'm still picking up.

Let me know if you can help.

Thanks,

Matthew Herbert
 
M

Matthew Herbert

Matthew,

Here is an answer from, well, yourself. I'm sure that there will be others
out there who will be looking to do the same sort of thing, i.e. going across
process to get data from a third-party application. With a lot of
researching and forum digging, I was able to come across some code to get
tooltip text from a third-party application (depending on whether that
application will actually return the text). The handle feeded to the
function can be any handle, but if a TTM_GETTOOLCOUNT message (from
SendMessage) returns a count, then there is a greater likelihood that the
function below will return a tooltip text for the given tooltip handle and
specified zero-based index for the tooltip class. (I have yet to comment the
code, but may do so later).

Enjoy,

Matthew Herbert

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 bInheritHandle As Long, ByVal dwProcessId As Long) As Long

Private Declare Function VirtualAllocEx Lib "kernel32" (ByVal hProcess As
Long, ByVal lpAddress As Long, ByVal dwSize As Long, ByVal flAllocationType
As Long, ByVal flProtect As Long) As Long

Private Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess
As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long,
lpNumberOfBytesWritten As Long) 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

'last parameter data type changed from Any to Long
Private Declare Function SendMessageLong Lib "user32" Alias "SendMessageA"
(ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam
As Long) As Long

Private Declare Function VirtualFreeEx Lib "kernel32" (ByVal hProcess As
Long, lpAddress As Any, ByVal dwSize As Long, ByVal dwFreeType As Long) As
Long

Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long)
As Long

Private Type TOOLINFO
cbSize As Long
uFlags As Long
hWnd As Long
uId As Long
RC As RECT
hInst As Long
lpszText As Long 'String
lParam As Long
End Type

Private Const PAGE_READWRITE As Long = &H4
Private Const MEM_RESERVE As Long = &H2000&
Private Const MEM_RELEASE As Long = &H8000&
Private Const MEM_COMMIT As Long = &H1000&
Private Const PROCESS_VM_OPERATION As Long = &H8
Private Const PROCESS_VM_READ As Long = &H10
Private Const PROCESS_VM_WRITE As Long = &H20

Private Const WM_USER As Long = &H400&
Private Const TTM_ENUMTOOLSA = (WM_USER + 14)
Private Const TTM_ENUMTOOLSW = (WM_USER + 58)
Private Const TTM_GETTEXTA As Long = (WM_USER + 11)
Private Const TTM_GETTEXTW = (WM_USER + 56)

Private Const LPSTR_TEXTCALLBACK As Long = -1
Private Const lngTEXTMAX As Long = 256

Function GetTooltipText(lngHwnd As Long, _
lngIndex As Long) As String
Dim TI As TOOLINFO
Dim lngPtrTI As Long
Dim lngPtrTIText As Long
Dim lngRes As Long
Dim lngThreadId As Long
Dim lngID As Long
Dim lngProcess As Long
Dim lngCnt As Long
Dim lngCntBtn As Long
Dim strText As String
Dim bytArr() As Byte
Dim lngPos As Long

lngThreadId = GetWindowThreadProcessId(lngHwnd, lngID)
lngProcess = OpenProcess(PROCESS_VM_OPERATION Or PROCESS_VM_READ Or
PROCESS_VM_WRITE, False, lngID)
lngPtrTI = VirtualAllocEx(lngProcess, ByVal 0&, Len(TI), MEM_COMMIT Or
MEM_RESERVE, PAGE_READWRITE)
lngPtrTIText = VirtualAllocEx(lngProcess, ByVal 0&, lngTEXTMAX, MEM_COMMIT
Or MEM_RESERVE, PAGE_READWRITE)

With TI
.cbSize = Len(TI)
.lpszText = lngPtrTIText
End With

ReDim bytArr(lngTEXTMAX - 1)

WriteProcessMemory lngProcess, ByVal lngPtrTI, TI, Len(TI), 0
WriteProcessMemory lngProcess, ByVal lngPtrTIText, bytArr(0), lngTEXTMAX, 0

lngRes = SendMessageLong(lngHwnd, TTM_ENUMTOOLSW, lngIndex, lngPtrTI)

If lngRes > 0 Then
ReadProcessMemory lngProcess, ByVal lngPtrTI, TI, Len(TI), 0

If TI.lpszText = LPSTR_TEXTCALLBACK Then
TI.lpszText = lngPtrTIText
WriteProcessMemory lngProcess, ByVal lngPtrTI, TI, Len(TI), 0
SendMessageLong lngHwnd, TTM_GETTEXTW, ByVal lngTEXTMAX, ByVal
lngPtrTI
End If

ReadProcessMemory lngProcess, ByVal lngPtrTIText, ByVal
VarPtr(bytArr(0)), lngTEXTMAX, 0

strText = bytArr
lngPos = InStr(1, strText, vbNullChar)
If lngPos = 0 Then
strText = strText
Else
strText = Left$(strText, lngPos - 1)
End If
End If

VirtualFreeEx lngProcess, ByVal lngPtrTI, Len(TI), MEM_RELEASE
VirtualFreeEx lngProcess, ByVal lngPtrTIText, lngTEXTMAX, MEM_RELEASE
CloseHandle lngProcess

GetTooltipText = strText

End Function

Matthew Herbert said:
All,

Has anyone ever tried to get the tooltip text from a third party
application's button? (I'm working with an application called "Research
Wizard," but the code works just as well for Internet Explorer). I'm willing
to post the code (which is rather lengthy) if someone feels they can help
(but please read on to see if you think that you should reply to this post).
[Besides, this post is rather lengthy to begin with, even without the code.]

I am able to do the following (with help from a number of other posts):
- Enumerate the windows to get the target window handle
- Get the toolbar handle from the window handle
- Count the number of buttons on the toolbar (for looping purposes)
- Loop through each button
- Click each button

Despite being able to click the button, I can't seem to get the tooltip text
for the button.

To give you an idea of some of the APIs involved, I'm using the following:
FindWindow
EnumChildWindows
GetClassName
SendMessage
OpenProcess
VirtualAllocEx
VirtualFreeEx
ReadProcessMemory
CloseHandle
GetWindowThreadProcessId

Here are some of the SendMessage wMsg arguments I’m using:
TB_BUTTONCOUNT
TB_GETBUTTON
WM_COMMAND
TTM_ENUMTOOLS (trying to get this to work properly)
TTM_GETTEXT (trying to get this to work properly)

Structures used:
TBBUTTON
TOOLINFO

I found a post that suggests sending TTM_ENUMTOOLS with a pointer to the
TOOLINFO structure (Type TOOLINFO...End Type) and then passing TOOLINFO with
TTM_GETTEXT to get lpszText. The post also mentioned that memory needs to be
allocated that both processes can access. (Unfortunately, the post didn't
provide any syntax, just the concept written down). I have been unsuccessful
in this getting lpszText from TOOLINFO (despite being able to get
TBBUTTON.idCommand or TBBUTTON.iString when sending TB_GETBUTTON). (Maybe
the toolbar class I'm working with doesn't provide information for TOOLINFO).

As an aside: This code (and the concept behind it) is completely new to me,
and I’ve had to do a lot of learning along the way. There is a lot of
information that I'm still picking up.

Let me know if you can help.

Thanks,

Matthew Herbert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top