Option Explicit
Private Enum eSTYLE
TTS_ALWAYSTIP = &H1
TTS_NOPREFIX = &H2
TTS_NOANIMATE = &H10
TTS_NOFADE = &H20
TTS_BALLOON = &H40
TTS_CLOSE = &H80
End Enum
Private Enum eICON
TTI_NONE = 0
TTI_INFO = 1
TTI_WARNING = 2
TTI_ERROR = 3
End Enum
Private Type ToolTipData
Size As Long
Style As eSTYLE
BackColor As Long
SystemInfoBackColor As Boolean 'overrides BackColor
TextColor As Long
SystemInfoTextColor As Boolean 'overrides TextColor
Title As String * 64
Icon As eICON
Text As String * 1024
DelayTime As Long 'in Secs
Multiline As Boolean
BeepSound As Boolean
End Type
Private Type POINTAPI
X As Long
Y As Long
End Type
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Type TOOLINFO
cbSize As Long
uFlags As Long
#If Win64 Then
hwnd As LongLong
uId As LongLong
cRect As RECT
hinst As LongLong
#Else
hwnd As Long
uId As Long
cRect As RECT
hinst As Long
#End If
lpszText As String
End Type
Private Type InitCommonControlsEx
Size As Long
ICC As Long
End Type
Private Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(0 To 7) As Byte
End Type
#If VBA7 Then
#If Win64 Then
Private Declare PtrSafe Function AccessibleObjectFromPoint Lib "oleacc" (ByVal arg1 As LongLong, ppacc As Any, pvarChild As Variant) As Long
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongLong)
#Else
Private Declare PtrSafe Function AccessibleObjectFromPoint Lib "oleacc" (ByVal lX As Long, ByVal lY As Long, ppacc As IAccessible, pvarChild As Variant) As Long
#End If
Private Declare PtrSafe Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
Private Declare PtrSafe Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As LongPtr
Private Declare PtrSafe Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr
Private Declare PtrSafe Function SendMessageAny Lib "user32" Alias "SendMessageA" (ByVal hwnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, lParam As Any) As LongPtr
Private Declare PtrSafe Function CreateWindowEx Lib "user32" Alias "CreateWindowExA" (ByVal dwExStyle As Long, ByVal lpClassName As String, ByVal lpWindowName As String, ByVal dwStyle As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hWndParent As LongPtr, ByVal hMenu As LongPtr, ByVal hInstance As LongPtr, lpParam As Any) As LongPtr
Private Declare PtrSafe Function DestroyWindow Lib "user32" (ByVal hwnd As LongPtr) As Long
Private Declare PtrSafe Function ShowWindow Lib "user32" (ByVal hwnd As LongPtr, ByVal nCmdShow As Long) As Long
Private Declare PtrSafe Function SetProp Lib "user32" Alias "SetPropA" (ByVal hwnd As LongPtr, ByVal lpString As String, ByVal hData As LongPtr) As Long
Private Declare PtrSafe Function GetProp Lib "user32" Alias "GetPropA" (ByVal hwnd As LongPtr, ByVal lpString As String) As LongPtr
Private Declare PtrSafe Function RemoveProp Lib "user32" Alias "RemovePropA" (ByVal hwnd As LongPtr, ByVal lpString As String) As LongPtr
Private Declare PtrSafe Function ConnectToConnectionPoint Lib "shlwapi" Alias "#168" (ByVal Punk As stdole.IUnknown, ByRef riidEvent As GUID, ByVal fConnect As Long, ByVal PunkTarget As stdole.IUnknown, ByRef pdwCookie As Long, Optional ByVal ppcpOut As LongPtr) As Long
Private Declare PtrSafe Function InitCommonControlsEx Lib "comctl32.dll" (iccex As InitCommonControlsEx) As Long
Private Declare PtrSafe Function GetSysColor Lib "user32" (ByVal nIndex As Long) As Long
Private Declare PtrSafe Function IUnknown_GetWindow Lib "shlwapi" Alias "#172" (ByVal pIUnk As IUnknown, ByVal hUf As LongPtr) As Long
Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
Private hForm As LongPtr, hToolTip As LongPtr
#Else
Private Declare Function AccessibleObjectFromPoint Lib "oleacc" (ByVal lX As Long, ByVal lY As Long, ppacc As IAccessible, pvarChild As Variant) As Long
Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long
Private Declare Function SendMessage 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 SendMessageAny Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Function CreateWindowEx Lib "user32" Alias "CreateWindowExA" (ByVal dwExStyle As Long, ByVal lpClassName As String, ByVal lpWindowName As String, ByVal dwStyle As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hWndParent As Long, ByVal hMenu As Long, ByVal hInstance As Long, lpParam As Any) As Long
Private Declare Function DestroyWindow Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long, ByVal nCmdShow As Long) As Long
Private Declare Function SetProp Lib "user32" Alias "SetPropA" (ByVal hwnd As Long, ByVal lpString As String, ByVal hData As Long) As Long
Private Declare Function GetProp Lib "user32" Alias "GetPropA" (ByVal hwnd As Long, ByVal lpString As String) As Long
Private Declare Function RemoveProp Lib "user32" Alias "RemovePropA" (ByVal hwnd As Long, ByVal lpString As String) As Long
Private Declare Function ConnectToConnectionPoint Lib "shlwapi" Alias "#168" (ByVal punk As stdole.IUnknown, ByRef riidEvent As GUID, ByVal fConnect As Long, ByVal punkTarget As stdole.IUnknown, ByRef pdwCookie As Long, Optional ByVal ppcpOut As Long) As Long
Private Declare Function InitCommonControlsEx Lib "comctl32.dll" (iccex As InitCommonControlsEx) As Long
Private Declare Function GetSysColor Lib "user32" (ByVal nIndex As Long) As Long
Private Declare Function IUnknown_GetWindow Lib "shlwapi" Alias "#172" (ByVal pIUnk As IUnknown, ByVal hUf As Long) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private hForm As Long, hToolTip As Long
#End If
Private WithEvents oForm As MSForms.UserForm
Private tGUID As GUID
Private oCtrl As Object
Private lCookie As Long
Private bTimedOut As Boolean
Private bNoTimedOut As Boolean
Private sText As String, sTitle As String
Private lStyle As eSTYLE, lIcon As eICON
Private lBkColor As Long, lTextColor As Long
Private bSysBkColor As Boolean, bSysTextColor As Boolean
Private lTimeOut As Long
Private bMultiline As Boolean, bBeep As Boolean
'_________________________________________Class Public Methods__________________________________________________
#If Win64 Then
Public Sub AddTo(ByVal Ctrl As MSForms.Control, ByVal DataPtr As LongLong)
#Else
Public Sub AddTo(ByVal Ctrl As MSForms.Control, ByVal DataPtr As Long)
#End If
Const S_OK = 0
Dim tTTipData As ToolTipData
Set oCtrl = Ctrl
Set oForm = GetUserFormObject(Ctrl)
Call IUnknown_GetWindow(oForm, VarPtr(hForm))
With tGUID
.Data1 = &H20400
.Data4(0) = &HC0
.Data4(7) = &H46
End With
If ConnectToConnectionPoint(Me, tGUID, True, Ctrl, lCookie) = S_OK Then
Call CopyMemory(ByVal tTTipData, ByVal DataPtr, LenB(tTTipData))
With tTTipData
sText = Left(.Text, InStr(1, .Text, vbNullChar) - 1)
sTitle = Left(.Title, InStr(1, .Title, vbNullChar) - 1)
lStyle = .Style
lIcon = .Icon
lBkColor = .BackColor
lTextColor = .TextColor
bSysBkColor = .SystemInfoBackColor
bSysTextColor = .SystemInfoTextColor
lTimeOut = Int(.DelayTime)
If lTimeOut <= 0 Then bNoTimedOut = True
bMultiline = .Multiline
bBeep = .BeepSound
End With
Call CreateToolTip
Else
Err.Raise Number:=vbObjectError + 513, Description:="Unable to register the mouse event listener."
End If
End Sub
Public Sub Remove()
Call ConnectToConnectionPoint(Nothing, tGUID, False, oCtrl, lCookie)
Call DestroyWindow(GetProp(Application.hwnd, "ToolTip"))
Call RemoveProp(Application.hwnd, "ToolTip")
Set oCtrl = Nothing
Set oForm = Nothing
End Sub
Public Sub DO_NOT_USE(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
'Attribute DO_NOT_USE.VB_UserMemId = -606
Call RetrieveControlUnderMousePointer(oCtrl)
End Sub
'__________________________________________Class Private Routines__________________________________________________
Private Sub RetrieveControlUnderMousePointer(ByVal Ctrl As MSForms.Control)
Const CHILDID_SELF = &H0&
Const ROLE_SYSTEM_PANE = &H10
Static bDoLooping As Boolean
Dim tCurPos As POINTAPI, oCurAcc As IAccessible, oPrevAcc As IAccessible
Dim px1 As Long, py1 As Long, pw1 As Long, ph1 As Long
Dim px2 As Long, py2 As Long, pw2 As Long, ph2 As Long
Dim sCurAccLocation As String, sPrevAccLocation As String
Dim t As Single
t = Timer
If bDoLooping Then Exit Sub
Do
If bNoTimedOut = False Then
If Int(Timer - t) >= lTimeOut Or bTimedOut = True Then bTimedOut = True: Exit Do
End If
bDoLooping = True
Call GetCursorPos(tCurPos)
#If Win64 Then
Dim ptr As LongLong
Call CopyMemory(ptr, tCurPos, LenB(tCurPos))
Call AccessibleObjectFromPoint(ptr, oCurAcc, 0&)
#Else
Call AccessibleObjectFromPoint(tCurPos.X, tCurPos.Y, oCurAcc, 0&)
#End If
Call oCurAcc.accLocation(px1, py1, pw1, ph1, CHILDID_SELF)
If Not oPrevAcc Is Nothing Then
Call oPrevAcc.accLocation(px2, py2, pw2, ph2, CHILDID_SELF)
End If
sCurAccLocation = CStr(px1) & CStr(py1) & CStr(pw1) & CStr(ph1)
sPrevAccLocation = CStr(px2) & CStr(py2) & CStr(pw2) & CStr(ph2)
If oPrevAcc Is Nothing And sCurAccLocation <> sPrevAccLocation Then
Call UpdateToolTip(px1, py1, pw1, ph1)
End If
Set oPrevAcc = Ctrl
Call oPrevAcc.accLocation(px2, py2, pw2, ph2, CHILDID_SELF)
sPrevAccLocation = CStr(px2) & CStr(py2) & CStr(pw2) & CStr(ph2)
DoEvents
Loop Until sCurAccLocation <> sPrevAccLocation
bDoLooping = False
Call HideToolTip
End Sub
Private Function GetUserFormObject(ByVal Ctrl As MSForms.Control) As Object
Dim oTemp As Object
Set oTemp = Ctrl.Parent
Do While TypeOf oTemp Is MSForms.Control
Set oTemp = oTemp.Parent
DoEvents
Loop
Set GetUserFormObject = oTemp
End Function
Private Sub CreateToolTip()
Const WS_POPUP = &H80000000
Const WS_EX_NOACTIVATE = &H8000000
Const CW_USEDEFAULT = &H80000000
Const ICC_LINK_CLASS = &H8000&
Dim tIccex As InitCommonControlsEx
If FindWindow("tooltips_class32", "MyToolTip") = 0 Then
If hToolTip = 0 Then
With tIccex
.Size = LenB(tIccex)
.ICC = ICC_LINK_CLASS
End With
If InitCommonControlsEx(tIccex) Then
hToolTip = CreateWindowEx(WS_EX_NOACTIVATE, "tooltips_class32", "MyToolTip", WS_POPUP Or lStyle, _
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, GetModuleHandle(vbNullString), 0)
Else
Err.Raise Number:=vbObjectError + 513, Description:="Unable to load common controls."
End If
End If
End If
End Sub
Private Sub UpdateToolTip(ByVal px As Long, ByVal py As Long, ByVal pw As Long, ByVal ph As Long)
Const WM_USER = &H400
Const TTF_TRACK = &H20
Const TTF_TRANSPARENT = &H100
Const TTM_SETDELAYTIME = (WM_USER + 3)
Const TTM_ADDTOOL = (WM_USER + 4)
Const TTM_UPDATETIPTEXT = (WM_USER + 12)
Const TTM_TRACKACTIVATE = (WM_USER + 17)
Const TTM_TRACKPOSITION = (WM_USER + 18)
Const TTM_UPDATE = (WM_USER + 29)
Const TTM_SETTITLEA = (WM_USER + 32)
Const TTM_SETMAXTIPWIDTH = (WM_USER + 24)
Const TTM_SETTIPTEXTCOLOR = (WM_USER + 20)
Const TTM_SETTIPBKCOLOR = (WM_USER + 19)
Const COLOR_INFOBK = 24
Const COLOR_INFOTEXT = 23
Dim tToolInfo As TOOLINFO
hToolTip = FindWindow("tooltips_class32", "MyToolTip")
If hToolTip Then
With tToolInfo
.cbSize = LenB(tToolInfo)
.hwnd = hForm
.uFlags = TTF_TRACK Or TTF_TRANSPARENT
.lpszText = sText
End With
Call SendMessageAny(hToolTip, TTM_SETTITLEA, lIcon, ByVal sTitle)
Call SendMessageAny(hToolTip, TTM_ADDTOOL, 0, tToolInfo)
Call SendMessageAny(hToolTip, TTM_TRACKACTIVATE, True, tToolInfo)
Call SendMessageAny(hToolTip, TTM_TRACKPOSITION, ByVal 0&, ByVal MakeDWord(CInt(px + pw - 10), CInt(py + ph - 10)))
Call SendMessage(hToolTip, TTM_SETMAXTIPWIDTH, 0&, ByVal IIf(bMultiline, 1, -1))
Call SendMessage(hToolTip, TTM_SETTIPTEXTCOLOR, IIf(bSysTextColor, GetSysColor(COLOR_INFOTEXT), lTextColor), 0)
Call SendMessage(hToolTip, TTM_SETTIPBKCOLOR, IIf(bSysBkColor, GetSysColor(COLOR_INFOBK), lBkColor), 0)
Call SendMessageAny(hToolTip, TTM_UPDATETIPTEXT, 0, tToolInfo)
Call SendMessageAny(hToolTip, TTM_UPDATE, 0, 0)
Call SetProp(Application.hwnd, "ToolTip", hToolTip)
If bBeep Then Beep
End If
End Sub
Private Sub HideToolTip()
Call ShowWindow(GetProp(Application.hwnd, "ToolTip"), 0)
End Sub
Private Function MakeDWord(loword As Integer, hiword As Integer) As Long
MakeDWord = (hiword * &H10000) Or (loword And &HFFFF&)
End Function
Private Sub oForm_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
bTimedOut = False
End Sub